From 5aea457e5ac3c8b84e674322758c2d168b9e093b Mon Sep 17 00:00:00 2001 From: Marylia Gutierrez Date: Tue, 14 Dec 2021 13:37:09 -0500 Subject: [PATCH] ui: save sort settings and view type on cache for Database Details page Previously, sort setting and the View type on Databases Details page were not being stored. With this commits we save the info about sort settings and the view type so the value is the same when the user goes back to those pages. This commit also updates the value on query params and that value take priority over the cached valued. Partially addresses #68199 Release note: None --- .../databaseDetailsPage.stories.tsx | 61 +++++++++ .../databaseDetailsPage.tsx | 120 ++++++++++++++---- .../databaseDetailsPage/redux.spec.ts | 7 + .../databases/databaseDetailsPage/redux.ts | 43 ++++++- 4 files changed, 203 insertions(+), 28 deletions(-) diff --git a/pkg/ui/workspaces/cluster-ui/src/databaseDetailsPage/databaseDetailsPage.stories.tsx b/pkg/ui/workspaces/cluster-ui/src/databaseDetailsPage/databaseDetailsPage.stories.tsx index 857dcb7c9975..738b9b5dadb8 100644 --- a/pkg/ui/workspaces/cluster-ui/src/databaseDetailsPage/databaseDetailsPage.stories.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/databaseDetailsPage/databaseDetailsPage.stories.tsx @@ -21,16 +21,39 @@ import { import { DatabaseDetailsPage, DatabaseDetailsPageProps, + ViewMode, } from "./databaseDetailsPage"; +import * as H from "history"; +const history = H.createHashHistory(); + const withLoadingIndicator: DatabaseDetailsPageProps = { loading: true, loaded: false, name: randomName(), tables: [], + viewMode: ViewMode.Tables, + sortSettingTables: { + ascending: false, + columnTitle: "name", + }, + sortSettingGrants: { + ascending: false, + columnTitle: "name", + }, + onSortingTablesChange: () => {}, + onSortingGrantsChange: () => {}, refreshDatabaseDetails: () => {}, refreshTableDetails: () => {}, refreshTableStats: () => {}, + location: history.location, + history, + match: { + url: "", + path: history.location.pathname, + isExact: false, + params: {}, + }, }; const withoutData: DatabaseDetailsPageProps = { @@ -38,9 +61,28 @@ const withoutData: DatabaseDetailsPageProps = { loaded: true, name: randomName(), tables: [], + viewMode: ViewMode.Tables, + sortSettingTables: { + ascending: false, + columnTitle: "name", + }, + sortSettingGrants: { + ascending: false, + columnTitle: "name", + }, + onSortingTablesChange: () => {}, + onSortingGrantsChange: () => {}, refreshDatabaseDetails: () => {}, refreshTableDetails: () => {}, refreshTableStats: () => {}, + location: history.location, + history, + match: { + url: "", + path: history.location.pathname, + isExact: false, + params: {}, + }, }; const withData: DatabaseDetailsPageProps = { @@ -77,9 +119,28 @@ const withData: DatabaseDetailsPageProps = { }, }; }), + viewMode: ViewMode.Tables, + sortSettingTables: { + ascending: false, + columnTitle: "name", + }, + sortSettingGrants: { + ascending: false, + columnTitle: "name", + }, + onSortingTablesChange: () => {}, + onSortingGrantsChange: () => {}, refreshDatabaseDetails: () => {}, refreshTableDetails: () => {}, refreshTableStats: () => {}, + location: history.location, + history, + match: { + url: "", + path: history.location.pathname, + isExact: false, + params: {}, + }, }; storiesOf("Database Details Page", module) diff --git a/pkg/ui/workspaces/cluster-ui/src/databaseDetailsPage/databaseDetailsPage.tsx b/pkg/ui/workspaces/cluster-ui/src/databaseDetailsPage/databaseDetailsPage.tsx index bc0370810331..f45bdb6d54fc 100644 --- a/pkg/ui/workspaces/cluster-ui/src/databaseDetailsPage/databaseDetailsPage.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/databaseDetailsPage/databaseDetailsPage.tsx @@ -9,7 +9,7 @@ // licenses/APL.txt. import React from "react"; -import { Link } from "react-router-dom"; +import { Link, RouteComponentProps } from "react-router-dom"; import { Tooltip } from "antd"; import classNames from "classnames/bind"; import _ from "lodash"; @@ -28,6 +28,7 @@ import { SortedTable, } from "src/sortedtable"; import * as format from "src/util/format"; +import { syncHistory } from "../util"; import styles from "./databaseDetailsPage.module.scss"; import sortableTableStyles from "src/sortedtable/sortedtable.module.scss"; @@ -52,6 +53,9 @@ const sortableTableCx = classNames.bind(sortableTableStyles); // loading: boolean; // loaded: boolean; // name: string; +// sortSettingTables: SortSetting; +// sortSettingGrants: SortSetting; +// viewMode: ViewMode; // tables: { // DatabaseDetailsPageDataTable[] // name: string; // details: { // DatabaseDetailsPageDataTableDetails @@ -77,6 +81,9 @@ export interface DatabaseDetailsPageData { loaded: boolean; name: string; tables: DatabaseDetailsPageDataTable[]; + sortSettingTables: SortSetting; + sortSettingGrants: SortSetting; + viewMode: ViewMode; showNodeRegionsColumn?: boolean; } @@ -108,20 +115,22 @@ export interface DatabaseDetailsPageActions { refreshDatabaseDetails: (database: string) => void; refreshTableDetails: (database: string, table: string) => void; refreshTableStats: (database: string, table: string) => void; + onSortingTablesChange?: (columnTitle: string, ascending: boolean) => void; + onSortingGrantsChange?: (columnTitle: string, ascending: boolean) => void; + onViewModeChange?: (viewMode: ViewMode) => void; } export type DatabaseDetailsPageProps = DatabaseDetailsPageData & - DatabaseDetailsPageActions; + DatabaseDetailsPageActions & + RouteComponentProps; -enum ViewMode { +export enum ViewMode { Tables = "Tables", Grants = "Grants", } interface DatabaseDetailsPageState { pagination: ISortedTablePagination; - sortSetting: SortSetting; - viewMode: ViewMode; } class DatabaseSortedTable extends SortedTable {} @@ -132,28 +141,62 @@ export class DatabaseDetailsPage extends React.Component< > { constructor(props: DatabaseDetailsPageProps) { super(props); - this.state = { pagination: { current: 1, pageSize: 20, }, - sortSetting: { - ascending: true, - }, - viewMode: ViewMode.Tables, }; + + const { history } = this.props; + const searchParams = new URLSearchParams(history.location.search); + + // View Mode. + const view = searchParams.get("viewMode") || undefined; + let viewMode = ViewMode.Tables; + if (view == ViewMode.Grants.toString()) { + viewMode = ViewMode.Grants; + } + if ( + this.props.onViewModeChange && + view && + viewMode != this.props.viewMode + ) { + this.props.onViewModeChange(viewMode); + } + + // Sort Settings. + const ascending = (searchParams.get("ascending") || undefined) === "true"; + const columnTitle = searchParams.get("columnTitle") || undefined; + const sortSetting = + viewMode == ViewMode.Tables + ? this.props.sortSettingTables + : this.props.sortSettingGrants; + const onSortingChange = + viewMode == ViewMode.Tables + ? this.props.onSortingTablesChange + : this.props.onSortingGrantsChange; + + if ( + onSortingChange && + columnTitle && + (sortSetting.columnTitle != columnTitle || + sortSetting.ascending != ascending) + ) { + onSortingChange(columnTitle, ascending); + } } - componentDidMount() { + componentDidMount(): void { this.refresh(); } - componentDidUpdate() { + componentDidUpdate(): void { this.refresh(); } - private refresh() { + private refresh(): void { + console.log("VIEW ", this.props.viewMode); if (!this.props.loaded && !this.props.loading) { return this.props.refreshDatabaseDetails(this.props.name); } @@ -173,22 +216,44 @@ export class DatabaseDetailsPage extends React.Component< this.setState({ pagination: { ...this.state.pagination, current } }); } - private changeSortSetting(sortSetting: SortSetting) { - this.setState({ sortSetting }); - } + changeSortSetting = (ss: SortSetting): void => { + syncHistory( + { + ascending: ss.ascending.toString(), + columnTitle: ss.columnTitle, + }, + this.props.history, + ); + const onSortingChange = + this.props.viewMode == ViewMode.Tables + ? this.props.onSortingTablesChange + : this.props.onSortingGrantsChange; + + if (onSortingChange) { + onSortingChange(ss.columnTitle, ss.ascending); + } + }; private changeViewMode(viewMode: ViewMode) { - this.setState({ viewMode }); + syncHistory( + { + viewMode: viewMode.toString(), + }, + this.props.history, + ); + if (this.props.onViewModeChange) { + this.props.onViewModeChange(viewMode); + } } private columns(): ColumnDescriptor[] { - switch (this.state.viewMode) { + switch (this.props.viewMode) { case ViewMode.Tables: return this.columnsForTablesViewMode(); case ViewMode.Grants: return this.columnsForGrantsViewMode(); default: - throw new Error(`Unknown view mode ${this.state.viewMode}`); + throw new Error(`Unknown view mode ${this.props.viewMode}`); } } @@ -349,7 +414,7 @@ export class DatabaseDetailsPage extends React.Component< ]; } - private viewOptions(): DropdownOption[] { + private static viewOptions(): DropdownOption[] { return [ { name: "Tables", @@ -362,7 +427,12 @@ export class DatabaseDetailsPage extends React.Component< ]; } - render() { + render(): React.ReactElement { + const sortSetting = + this.props.viewMode == ViewMode.Tables + ? this.props.sortSettingTables + : this.props.sortSettingGrants; + return (
@@ -389,10 +459,10 @@ export class DatabaseDetailsPage extends React.Component< - View: {this.state.viewMode} + View: {this.props.viewMode} @@ -414,8 +484,8 @@ export class DatabaseDetailsPage extends React.Component< className={cx("database-table")} data={this.props.tables} columns={this.columns()} - sortSetting={this.state.sortSetting} - onChangeSortSetting={this.changeSortSetting.bind(this)} + sortSetting={sortSetting} + onChangeSortSetting={this.changeSortSetting} pagination={this.state.pagination} loading={this.props.loading} renderNoResult={ diff --git a/pkg/ui/workspaces/db-console/src/views/databases/databaseDetailsPage/redux.spec.ts b/pkg/ui/workspaces/db-console/src/views/databases/databaseDetailsPage/redux.spec.ts index 600a071ef519..89624b130c62 100644 --- a/pkg/ui/workspaces/db-console/src/views/databases/databaseDetailsPage/redux.spec.ts +++ b/pkg/ui/workspaces/db-console/src/views/databases/databaseDetailsPage/redux.spec.ts @@ -19,6 +19,7 @@ import { DatabaseDetailsPageData, DatabaseDetailsPageDataTableDetails, DatabaseDetailsPageDataTableStats, + ViewMode, } from "@cockroachlabs/cluster-ui"; import { AdminUIState, createAdminUIStore } from "src/redux/state"; @@ -125,6 +126,9 @@ describe("Database Details Page", function() { loaded: false, name: "things", showNodeRegionsColumn: false, + viewMode: ViewMode.Tables, + sortSettingTables: { ascending: true, columnTitle: "name" }, + sortSettingGrants: { ascending: true, columnTitle: "name" }, tables: [], }); }); @@ -141,6 +145,9 @@ describe("Database Details Page", function() { loaded: true, name: "things", showNodeRegionsColumn: false, + viewMode: ViewMode.Tables, + sortSettingTables: { ascending: true, columnTitle: "name" }, + sortSettingGrants: { ascending: true, columnTitle: "name" }, tables: [ { name: "foo", diff --git a/pkg/ui/workspaces/db-console/src/views/databases/databaseDetailsPage/redux.ts b/pkg/ui/workspaces/db-console/src/views/databases/databaseDetailsPage/redux.ts index 05ba37d75f82..96052d4a1a5a 100644 --- a/pkg/ui/workspaces/db-console/src/views/databases/databaseDetailsPage/redux.ts +++ b/pkg/ui/workspaces/db-console/src/views/databases/databaseDetailsPage/redux.ts @@ -10,8 +10,9 @@ import { RouteComponentProps } from "react-router"; import { createSelector } from "reselect"; +import { LocalSetting } from "src/redux/localsettings"; import _ from "lodash"; -import { DatabaseDetailsPageData } from "@cockroachlabs/cluster-ui"; +import { DatabaseDetailsPageData, ViewMode } from "@cockroachlabs/cluster-ui"; import { cockroach } from "src/js/protos"; import { @@ -68,6 +69,24 @@ function normalizePrivileges(raw: string[]): string[] { ); } +const sortSettingTablesLocalSetting = new LocalSetting( + "sortSetting/DatabasesDetailsTablesPage", + (state: AdminUIState) => state.localSettings, + { ascending: true, columnTitle: "name" }, +); + +const sortSettingGrantsLocalSetting = new LocalSetting( + "sortSetting/DatabasesDetailsGrantsPage", + (state: AdminUIState) => state.localSettings, + { ascending: true, columnTitle: "name" }, +); + +const viewModeLocalSetting = new LocalSetting( + "viewMode/DatabasesDetailsPage", + (state: AdminUIState) => state.localSettings, + ViewMode.Tables, +); + export const mapStateToProps = createSelector( (_state: AdminUIState, props: RouteComponentProps): string => getMatchParamByName(props.match, databaseNameAttr), @@ -77,6 +96,9 @@ export const mapStateToProps = createSelector( state => state.cachedData.tableStats, state => nodeRegionsByIDSelector(state), state => selectIsMoreThanOneNode(state), + state => viewModeLocalSetting.selector(state), + state => sortSettingTablesLocalSetting.selector(state), + state => sortSettingGrantsLocalSetting.selector(state), ( database, databaseDetails, @@ -84,12 +106,18 @@ export const mapStateToProps = createSelector( tableStats, nodeRegions, showNodeRegionsColumn, + viewMode, + sortSettingTables, + sortSettingGrants, ): DatabaseDetailsPageData => { return { loading: !!databaseDetails[database]?.inFlight, loaded: !!databaseDetails[database]?.valid, name: database, showNodeRegionsColumn, + viewMode, + sortSettingTables, + sortSettingGrants, tables: _.map(databaseDetails[database]?.data?.table_names, table => { const tableId = generateTableID(database, table); @@ -137,12 +165,21 @@ export const mapDispatchToProps = { new DatabaseDetailsRequest({ database, include_stats: true }), ); }, - refreshTableDetails: (database: string, table: string) => { return refreshTableDetails(new TableDetailsRequest({ database, table })); }, - refreshTableStats: (database: string, table: string) => { return refreshTableStats(new TableStatsRequest({ database, table })); }, + onViewModeChange: (viewMode: ViewMode) => viewModeLocalSetting.set(viewMode), + onSortingTablesChange: (columnName: string, ascending: boolean) => + sortSettingTablesLocalSetting.set({ + ascending: ascending, + columnTitle: columnName, + }), + onSortingGrantsChange: (columnName: string, ascending: boolean) => + sortSettingGrantsLocalSetting.set({ + ascending: ascending, + columnTitle: columnName, + }), };