From 6125fb4e5ade116e2eb9692e9b39766410ce2fa3 Mon Sep 17 00:00:00 2001 From: Xin Hao Zhang Date: Tue, 5 Oct 2021 13:55:37 -0400 Subject: [PATCH 1/9] ui/cluster-ui: make app name a query search parameter in stmts page Fixes: #70790 Previously, the selected app was derived from a route param on the statements page. All other filters are derived from query search parameters on the page. This commit makes the app name a query search parameter, as is the case in the transactions page. Release note: None --- .../cluster-ui/src/queryFilter/filter.tsx | 30 ++++++++++++----- .../statementDetails.selectors.ts | 6 +++- .../src/statementDetails/statementDetails.tsx | 4 ++- .../statementsPage.selectors.ts | 4 +-- .../src/statementsPage/statementsPage.tsx | 33 ++++++------------- pkg/ui/workspaces/db-console/src/app.tsx | 6 ++-- .../src/views/statements/statementDetails.tsx | 6 +++- .../src/views/statements/statements.spec.tsx | 20 +++++++++-- .../src/views/statements/statementsPage.tsx | 4 +-- 9 files changed, 70 insertions(+), 43 deletions(-) diff --git a/pkg/ui/workspaces/cluster-ui/src/queryFilter/filter.tsx b/pkg/ui/workspaces/cluster-ui/src/queryFilter/filter.tsx index 808e834698bf..37e3011a5473 100644 --- a/pkg/ui/workspaces/cluster-ui/src/queryFilter/filter.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/queryFilter/filter.tsx @@ -69,7 +69,7 @@ const timeUnit = [ ]; export const defaultFilters: Filters = { - app: "All", + app: "", timeNumber: "0", timeUnit: "seconds", fullScan: false, @@ -89,7 +89,9 @@ export const defaultFilters: Filters = { * @return Filters: the default filters with updated keys existing on * queryString */ -export const getFiltersFromQueryString = (queryString: string) => { +export const getFiltersFromQueryString = ( + queryString: string, +): Record => { const searchParams = new URLSearchParams(queryString); return Object.keys(defaultFilters).reduce( @@ -97,7 +99,7 @@ export const getFiltersFromQueryString = (queryString: string) => { const defaultValue = defaultFilters[filter]; const queryStringFilter = searchParams.get(filter); const filterValue = - queryStringFilter === null + queryStringFilter == null ? defaultValue : defaultValue.constructor(searchParams.get(filter)); return { [filter]: filterValue, ...filters }; @@ -114,7 +116,7 @@ export const getFiltersFromQueryString = (queryString: string) => { * we want to consider 0 active Filters */ export const inactiveFiltersState: Filters = { - app: "All", + app: "", timeNumber: "0", fullScan: false, sqlType: "", @@ -123,7 +125,7 @@ export const inactiveFiltersState: Filters = { nodes: "", }; -export const calculateActiveFilters = (filters: Filters) => { +export const calculateActiveFilters = (filters: Filters): number => { return Object.keys(inactiveFiltersState).reduce( (active, filter: keyof Filters) => { return inactiveFiltersState[filter] !== filters[filter] @@ -185,7 +187,19 @@ export class Filter extends React.Component { this.setState({ hide: true }); }; - handleChange = (event: any, field: string) => { + handleSelectChange = ( + event: { label: string; value: string }, + field: string, + ): void => { + this.setState({ + filters: { + ...this.state.filters, + [field]: event.value, + }, + }); + }; + + handleChange = (event: any, field: string): void => { this.setState({ filters: { ...this.state.filters, @@ -419,7 +433,7 @@ export class Filter extends React.Component {
App
unit.label == filters.timeUnit)} - onChange={e => this.handleChange(e, "timeUnit")} + onChange={e => this.handleSelectChange(e, "timeUnit")} className={timePair.timeUnit} styles={customStylesSmall} /> diff --git a/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.selectors.ts b/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.selectors.ts index c981aefae1fc..9269956bca97 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.selectors.ts +++ b/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.selectors.ts @@ -150,7 +150,11 @@ export const selectStatement = createSelector( statement, stats: combineStatementStats(results.map(s => s.stats)), byNode: coalesceNodeStats(results), - app: _.uniq(results.map(s => s.app)), + app: _.uniq( + results.map(s => + s.app.startsWith(internalAppNamePrefix) ? "(internal)" : s.app, + ), + ), database: queryByName(props.location, databaseAttr), distSQL: fractionMatching(results, s => s.distSQL), vec: fractionMatching(results, s => s.vec), diff --git a/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx b/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx index 3dd30f2f7f6c..883f5af7e27f 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx @@ -177,10 +177,12 @@ function AppLink(props: { app: string }) { return (unset); } + const searchParams = new URLSearchParams({ [appAttr]: props.app }); + return ( {props.app} diff --git a/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.selectors.ts b/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.selectors.ts index 4e39104e0c49..adcc70464717 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.selectors.ts +++ b/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.selectors.ts @@ -17,7 +17,7 @@ import { ExecutionStatistics, flattenStatementStats, formatDate, - getMatchParamByName, + queryByName, statementKey, StatementStatistics, TimestampToMoment, @@ -144,7 +144,7 @@ export const selectStatements = createSelector( return null; } let statements = flattenStatementStats(state.data.statements); - const app = getMatchParamByName(props.match, appAttr); + const app = queryByName(props.location, appAttr); const isInternal = (statement: ExecutionStatistics) => statement.app.startsWith(state.data.internal_app_name_prefix); diff --git a/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.tsx b/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.tsx index f3dcddfb2192..71acddb3d2e9 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.tsx @@ -10,7 +10,7 @@ import React from "react"; import { RouteComponentProps } from "react-router-dom"; -import { isNil, merge, forIn } from "lodash"; +import { isNil, merge } from "lodash"; import Helmet from "react-helmet"; import moment, { Moment } from "moment"; import classNames from "classnames/bind"; @@ -179,19 +179,19 @@ export class StatementsPage extends React.Component< }; }; - syncHistory = (params: Record) => { + syncHistory = (params: Record): void => { const { history } = this.props; - const currentSearchParams = new URLSearchParams(history.location.search); + const nextSearchParams = new URLSearchParams(history.location.search); - forIn(params, (value, key) => { + Object.entries(params).forEach(([key, value]) => { if (!value) { - currentSearchParams.delete(key); + nextSearchParams.delete(key); } else { - currentSearchParams.set(key, value); + nextSearchParams.set(key, value); } }); - history.location.search = currentSearchParams.toString(); + history.location.search = nextSearchParams.toString(); history.replace(history.location); }; @@ -223,17 +223,6 @@ export class StatementsPage extends React.Component< ); }; - selectApp = (value: string): void => { - if (value == "All") value = ""; - const { history, onFilterChange } = this.props; - history.location.pathname = `/statements/${encodeURIComponent(value)}`; - history.replace(history.location); - this.resetPagination(); - if (onFilterChange) { - onFilterChange(value); - } - }; - resetPagination = (): void => { this.setState(prevState => { return { @@ -307,7 +296,6 @@ export class StatementsPage extends React.Component< regions: filters.regions, nodes: filters.nodes, }); - this.selectApp(filters.app); }; onClearSearchField = (): void => { @@ -334,7 +322,6 @@ export class StatementsPage extends React.Component< regions: undefined, nodes: undefined, }); - this.selectApp(""); }; filteredStatementsData = (): AggregateStatistics[] => { @@ -422,8 +409,8 @@ export class StatementsPage extends React.Component< const { statements, databases, - match, lastReset, + location, onDiagnosticsReportDownload, onStatementClick, resetSQLStats, @@ -432,9 +419,9 @@ export class StatementsPage extends React.Component< nodeRegions, isTenant, } = this.props; - const appAttrValue = getMatchParamByName(match, appAttr); + const appAttrValue = queryByName(location, appAttr); const selectedApp = appAttrValue || ""; - const appOptions = [{ value: "all", label: "All" }]; + const appOptions = [{ value: "", label: "All" }]; this.props.apps.forEach(app => appOptions.push({ value: app, label: app })); const data = this.filteredStatementsData(); const totalWorkload = calculateTotalWorkload(data); diff --git a/pkg/ui/workspaces/db-console/src/app.tsx b/pkg/ui/workspaces/db-console/src/app.tsx index a4a3a14177ba..2d56fc9e7746 100644 --- a/pkg/ui/workspaces/db-console/src/app.tsx +++ b/pkg/ui/workspaces/db-console/src/app.tsx @@ -179,10 +179,10 @@ export const App: React.FC = (props: AppProps) => { {/* statement statistics */} - s.stats)), byNode: coalesceNodeStats(results), - app: _.uniq(results.map(s => s.app)), + app: _.uniq( + results.map(s => + s.app.startsWith(internalAppNamePrefix) ? "(internal)" : s.app, + ), + ), database: queryByName(props.location, databaseAttr), distSQL: fractionMatching(results, s => s.distSQL), vec: fractionMatching(results, s => s.vec), diff --git a/pkg/ui/workspaces/db-console/src/views/statements/statements.spec.tsx b/pkg/ui/workspaces/db-console/src/views/statements/statements.spec.tsx index d542444e2aac..10de5bd72683 100644 --- a/pkg/ui/workspaces/db-console/src/views/statements/statements.spec.tsx +++ b/pkg/ui/workspaces/db-console/src/views/statements/statements.spec.tsx @@ -445,7 +445,8 @@ describe("selectStatement", () => { assert.equal(result.statement, stmtA.key.key_data.query); assert.equal(result.stats.count.toNumber(), stmtA.stats.count.toNumber()); - assert.deepEqual(result.app, [stmtA.key.key_data.app]); + // Statements with internal app prefix should have "(internal)" as app name + assert.deepEqual(result.app, ["(internal)"]); assert.deepEqual(result.distSQL, { numerator: 0, denominator: 1 }); assert.deepEqual(result.vec, { numerator: 0, denominator: 1 }); assert.deepEqual(result.opt, { numerator: 0, denominator: 1 }); @@ -596,6 +597,21 @@ function makeRoutePropsWithParams(params: { [key: string]: string }) { }; } +function makeRoutePropsWithSearchParams(params: { [key: string]: string }) { + const history = H.createHashHistory(); + history.location.search = new URLSearchParams(params).toString(); + return { + location: history.location, + history, + match: { + url: "", + path: history.location.pathname, + isExact: false, + params: {}, + }, + }; +} + function makeEmptyRouteProps(): RouteComponentProps { const history = H.createHashHistory(); return { @@ -611,7 +627,7 @@ function makeEmptyRouteProps(): RouteComponentProps { } function makeRoutePropsWithApp(app: string) { - return makeRoutePropsWithParams({ + return makeRoutePropsWithSearchParams({ [appAttr]: app, }); } diff --git a/pkg/ui/workspaces/db-console/src/views/statements/statementsPage.tsx b/pkg/ui/workspaces/db-console/src/views/statements/statementsPage.tsx index 1f0e46f2b6e9..dbd55813b905 100644 --- a/pkg/ui/workspaces/db-console/src/views/statements/statementsPage.tsx +++ b/pkg/ui/workspaces/db-console/src/views/statements/statementsPage.tsx @@ -34,7 +34,7 @@ import { PrintTime } from "src/views/reports/containers/range/print"; import { selectDiagnosticsReportsPerStatement } from "src/redux/statements/statementsSelectors"; import { createStatementDiagnosticsAlertLocalSetting } from "src/redux/alerts"; import { statementsDateRangeLocalSetting } from "src/redux/statementsDateRange"; -import { getMatchParamByName } from "src/util/query"; +import { queryByName } from "src/util/query"; import { StatementsPage, AggregateStatistics } from "@cockroachlabs/cluster-ui"; import { @@ -79,7 +79,7 @@ export const selectStatements = createSelector( return null; } let statements = flattenStatementStats(state.data.statements); - const app = getMatchParamByName(props.match, appAttr); + const app = queryByName(props.location, appAttr); const isInternal = (statement: ExecutionStatistics) => statement.app.startsWith(state.data.internal_app_name_prefix); From 6522b404a8b297ef00092603fd3f7d982a991c09 Mon Sep 17 00:00:00 2001 From: Marylia Gutierrez Date: Fri, 8 Oct 2021 11:25:35 -0400 Subject: [PATCH 2/9] ui: new sql activity page New SQL Activity page that includes Sessions, Statement and Transaction pages. Partially addresses #66052 Release note (ui change): Session, Statement and Transaction pages are now grouped inside the new SQL Activity page. --- .../cluster-ui/src/common/index.tsx | 14 +++++ .../cluster-ui/src/common/styles.module.scss | 60 +++++++++++++++++++ .../databaseTablePage.module.scss | 28 +-------- .../databaseTablePage/databaseTablePage.tsx | 3 +- pkg/ui/workspaces/cluster-ui/src/index.ts | 1 + .../cluster-ui/src/pageConfig/pageConfig.tsx | 11 +++- .../cluster-ui/src/queryFilter/filter.tsx | 2 +- .../src/sessions/sessionDetails.tsx | 23 +++---- .../cluster-ui/src/sessions/sessionsPage.tsx | 19 +++--- .../cluster-ui/src/sqlActivity/clearStats.tsx | 59 ++++++++++++++++++ .../src/sqlActivity/sqlActivity.module.scss | 15 +++++ .../statementDetails.module.scss | 50 ---------------- .../src/statementDetails/statementDetails.tsx | 13 ++-- .../statementsPage/statementsPage.module.scss | 21 ------- .../src/statementsPage/statementsPage.tsx | 15 +++-- .../tableStatistics.module.scss | 14 ----- .../src/tableStatistics/tableStatistics.tsx | 56 ++--------------- .../transactionDetails/transactionDetails.tsx | 11 +--- .../src/transactionsPage/transactionsPage.tsx | 25 ++++---- .../transactionsPageClasses.ts | 4 +- .../cluster-ui/src/util/query/query.ts | 6 +- pkg/ui/workspaces/db-console/src/app.spec.tsx | 28 ++++++++- pkg/ui/workspaces/db-console/src/app.tsx | 38 ++++++++---- .../db-console/src/util/constants.ts | 1 + .../app/components/layoutSidebar/index.tsx | 8 +-- .../src/views/sqlActivity/sqlActivityPage.tsx | 56 +++++++++++++++++ 26 files changed, 336 insertions(+), 245 deletions(-) create mode 100644 pkg/ui/workspaces/cluster-ui/src/common/index.tsx create mode 100644 pkg/ui/workspaces/cluster-ui/src/common/styles.module.scss create mode 100644 pkg/ui/workspaces/cluster-ui/src/sqlActivity/clearStats.tsx create mode 100644 pkg/ui/workspaces/cluster-ui/src/sqlActivity/sqlActivity.module.scss create mode 100644 pkg/ui/workspaces/db-console/src/views/sqlActivity/sqlActivityPage.tsx diff --git a/pkg/ui/workspaces/cluster-ui/src/common/index.tsx b/pkg/ui/workspaces/cluster-ui/src/common/index.tsx new file mode 100644 index 000000000000..96ff5b297bb8 --- /dev/null +++ b/pkg/ui/workspaces/cluster-ui/src/common/index.tsx @@ -0,0 +1,14 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +import classNames from "classnames/bind"; +import styles from "./styles.module.scss"; + +export const commonStyles = classNames.bind(styles); diff --git a/pkg/ui/workspaces/cluster-ui/src/common/styles.module.scss b/pkg/ui/workspaces/cluster-ui/src/common/styles.module.scss new file mode 100644 index 000000000000..a32756b7cb95 --- /dev/null +++ b/pkg/ui/workspaces/cluster-ui/src/common/styles.module.scss @@ -0,0 +1,60 @@ +@import "src/core/index.module"; + +.cockroach--tabs { + overflow: visible !important; + :global(.ant-tabs-bar) { + border-bottom: 1px solid $grey2; + } + + :global(.ant-tabs-tab) { + font-family: $font-family--base; + font-size: 16px; + line-height: 1.5; + letter-spacing: normal; + color: $colors--neutral-7; + } + + :global(.ant-tabs-nav .ant-tabs-tab-active) { + color: $colors--link; + } + + :global(.ant-tabs-nav .ant-tabs-tab:hover) { + color: $colors--link; + } + + :global(.ant-tabs-ink-bar) { + height: 3px; + border-radius: 40px; + background-color: $blue; + } +} + +h1.base-heading { + font-size: 24px; + font-family: $font-family--base; +} + +h2.base-heading { + padding: 12px 0; + font-size: 24px; + font-family: $font-family--base +} + +h3.base-heading { + color: $colors--neutral-7; + font-family: $font-family--sans-serif; + font-weight: 600; + font-style: normal; + font-stretch: normal; + font-size: 20px; + padding-bottom: 12px; +} + +.no-margin-bottom { + margin-bottom: 0px; +} + +.separator { + border-left: 1px solid #C0C6D9; + padding-left: 25px; +} diff --git a/pkg/ui/workspaces/cluster-ui/src/databaseTablePage/databaseTablePage.module.scss b/pkg/ui/workspaces/cluster-ui/src/databaseTablePage/databaseTablePage.module.scss index f11e5503982e..e63861b7bfde 100644 --- a/pkg/ui/workspaces/cluster-ui/src/databaseTablePage/databaseTablePage.module.scss +++ b/pkg/ui/workspaces/cluster-ui/src/databaseTablePage/databaseTablePage.module.scss @@ -18,32 +18,6 @@ } } -.cockroach--tabs { - :global(.ant-tabs-bar) { - border-bottom: 1px solid $grey2; - } - - :global(.ant-tabs-tab) { - @include text--heading-5; - font-weight: $font-weight--medium; - color: $colors--neutral-7; - } - - :global(.ant-tabs-tab-active) { - color: $colors--neutral-8; - } - - :global(.ant-tabs-tab:hover) { - color: $colors--link; - } - - :global(.ant-tabs-ink-bar) { - height: 3px; - border-radius: 40px; - background-color: $colors--link; - } -} - .summary-card { h4 { @include text--body-strong; @@ -79,4 +53,4 @@ &--primary { fill: $colors--primary-text; } -} \ No newline at end of file +} diff --git a/pkg/ui/workspaces/cluster-ui/src/databaseTablePage/databaseTablePage.tsx b/pkg/ui/workspaces/cluster-ui/src/databaseTablePage/databaseTablePage.tsx index 1064e3b9565a..db0d80988f9f 100644 --- a/pkg/ui/workspaces/cluster-ui/src/databaseTablePage/databaseTablePage.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/databaseTablePage/databaseTablePage.tsx @@ -23,6 +23,7 @@ import { SummaryCard, SummaryCardItem } from "src/summaryCard"; import * as format from "src/util/format"; import styles from "./databaseTablePage.module.scss"; +import { commonStyles } from "src/common"; import { baseHeadingClasses } from "src/transactionsPage/transactionsPageClasses"; const cx = classNames.bind(styles); @@ -200,7 +201,7 @@ export class DatabaseTablePage extends React.Component<
- + diff --git a/pkg/ui/workspaces/cluster-ui/src/index.ts b/pkg/ui/workspaces/cluster-ui/src/index.ts index 13097a9df947..f858961f13dc 100644 --- a/pkg/ui/workspaces/cluster-ui/src/index.ts +++ b/pkg/ui/workspaces/cluster-ui/src/index.ts @@ -15,6 +15,7 @@ export * from "./anchor"; export * from "./badge"; export * from "./barCharts"; export * from "./button"; +export * from "./common"; export * from "./databaseDetailsPage"; export * from "./databaseTablePage"; export * from "./databasesPage"; diff --git a/pkg/ui/workspaces/cluster-ui/src/pageConfig/pageConfig.tsx b/pkg/ui/workspaces/cluster-ui/src/pageConfig/pageConfig.tsx index 300ee569eb6f..e4f9fb8c3129 100644 --- a/pkg/ui/workspaces/cluster-ui/src/pageConfig/pageConfig.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/pageConfig/pageConfig.tsx @@ -19,7 +19,7 @@ export interface PageConfigProps { const cx = classnames.bind(styles); -export function PageConfig(props: PageConfigProps) { +export function PageConfig(props: PageConfigProps): React.ReactElement { const classes = cx({ "page-config__list": props.layout !== "spread", "page-config__spread": props.layout === "spread", @@ -34,8 +34,13 @@ export function PageConfig(props: PageConfigProps) { export interface PageConfigItemProps { children?: React.ReactNode; + className?: string; } -export function PageConfigItem(props: PageConfigItemProps) { - return
  • {props.children}
  • ; +export function PageConfigItem(props: PageConfigItemProps): React.ReactElement { + return ( +
  • + {props.children} +
  • + ); } diff --git a/pkg/ui/workspaces/cluster-ui/src/queryFilter/filter.tsx b/pkg/ui/workspaces/cluster-ui/src/queryFilter/filter.tsx index 37e3011a5473..559490cf6166 100644 --- a/pkg/ui/workspaces/cluster-ui/src/queryFilter/filter.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/queryFilter/filter.tsx @@ -236,7 +236,7 @@ export class Filter extends React.Component { }); }; - isOptionSelected = (option: string, field: string) => { + isOptionSelected = (option: string, field: string): boolean => { const selection = field.split(","); if (selection.length > 0 && selection.includes(option)) return true; return false; diff --git a/pkg/ui/workspaces/cluster-ui/src/sessions/sessionDetails.tsx b/pkg/ui/workspaces/cluster-ui/src/sessions/sessionDetails.tsx index 30913792e888..bf27a336d530 100644 --- a/pkg/ui/workspaces/cluster-ui/src/sessions/sessionDetails.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/sessions/sessionDetails.tsx @@ -48,6 +48,7 @@ import { UIConfigState } from "src/store"; import statementsPageStyles from "src/statementsPage/statementsPage.module.scss"; import styles from "./sessionDetails.module.scss"; import classNames from "classnames/bind"; +import { commonStyles } from "src/common"; const cx = classNames.bind(styles); const statementsPageCx = classNames.bind(statementsPageStyles); @@ -98,7 +99,7 @@ export class SessionDetails extends React.Component { isTenant: false, }; - componentDidMount() { + componentDidMount(): void { if (!this.props.isTenant) { this.props.refreshNodes(); this.props.refreshNodesLiveness(); @@ -106,7 +107,7 @@ export class SessionDetails extends React.Component { this.props.refreshSessions(); } - componentDidUpdate() { + componentDidUpdate(): void { // Normally, we would refresh the sessions here, but we don't want to // have the per-session page update whenever our data source updates // because in real workloads, sessions change what they're doing very @@ -122,16 +123,16 @@ export class SessionDetails extends React.Component { this.terminateQueryRef = React.createRef(); } - backToSessionsPage = () => { + backToSessionsPage = (): void => { const { history, location, onBackButtonClick } = this.props; onBackButtonClick && onBackButtonClick(); history.push({ ...location, - pathname: "/sessions", + pathname: "/sql-activity/sessions", }); }; - render() { + render(): React.ReactElement { const sessionID = getMatchParamByName(this.props.match, sessionAttr); const { sessionError, @@ -159,7 +160,7 @@ export class SessionDetails extends React.Component {

    @@ -223,7 +224,7 @@ export class SessionDetails extends React.Component { ); } - renderContent = () => { + renderContent = (): React.ReactElement => { if (!this.props.session) { return null; } @@ -273,7 +274,7 @@ export class SessionDetails extends React.Component { className={cx("details-item")} /> - + { const stmt = session.active_queries[0]; curStmtInfo = ( - + @@ -331,7 +332,7 @@ export class SessionDetails extends React.Component { View Statement Details - + { /> )} - + ) => { + syncHistory = (params: Record): void => { const { history } = this.props; const currentSearchParams = new URLSearchParams(history.location.search); forIn(params, (value, key) => { @@ -128,7 +128,7 @@ export class SessionsPage extends React.Component< history.replace(history.location); }; - changeSortSetting = (ss: SortSetting) => { + changeSortSetting = (ss: SortSetting): void => { const { onSortingChange } = this.props; onSortingChange && onSortingChange(ss.columnTitle); @@ -142,7 +142,7 @@ export class SessionsPage extends React.Component< }); }; - resetPagination = () => { + resetPagination = (): void => { this.setState(prevState => { return { pagination: { @@ -153,27 +153,27 @@ export class SessionsPage extends React.Component< }); }; - componentDidMount() { + componentDidMount(): void { this.props.refreshSessions(); } - componentDidUpdate = (__: SessionsPageProps, _: SessionsPageState) => { + componentDidUpdate = (__: SessionsPageProps, _: SessionsPageState): void => { this.props.refreshSessions(); }; - onChangePage = (current: number) => { + onChangePage = (current: number): void => { const { pagination } = this.state; this.setState({ pagination: { ...pagination, current } }); this.props.onPageChanged(current); }; - renderSessions = () => { + renderSessions = (): React.ReactElement => { const sessionsData = this.props.sessions; const { pagination } = this.state; return ( <> -
    +

    -

    Sessions

    void; + tooltipType: StatisticType; +} + +const ClearStats = (props: clearStatsProps): React.ReactElement => { + let statsType = ""; + switch (props.tooltipType) { + case "transaction": + statsType = contentModifiers.transactionCapital; + break; + case "statement": + statsType = contentModifiers.statementCapital; + break; + case "transactionDetails": + statsType = contentModifiers.statementCapital; + break; + default: + break; + } + const toolTipText = `${statsType} statistics are aggregated once an hour by default and organized by the start time. + Statistics between two hourly intervals belong to the nearest hour rounded down. + For example, a ${statsType} execution ending at 1:50 would have its statistics aggregated in the 1:00 interval + start time. Clicking ‘clear SQL stats’ will reset SQL stats on the Statements and Transactions pages and + crdb_internal tables.`; + return ( + + + clear SQL stats + + + ); +}; + +export default ClearStats; diff --git a/pkg/ui/workspaces/cluster-ui/src/sqlActivity/sqlActivity.module.scss b/pkg/ui/workspaces/cluster-ui/src/sqlActivity/sqlActivity.module.scss new file mode 100644 index 000000000000..30b9152d3332 --- /dev/null +++ b/pkg/ui/workspaces/cluster-ui/src/sqlActivity/sqlActivity.module.scss @@ -0,0 +1,15 @@ +@import "src/core/index.module"; + +.tooltip-info { + border-bottom: 1px dashed $colors--neutral-5; +} + +.action { + color: $colors--link; + display: flex; + line-height: 24px; + &:hover { + color: $colors--link; + text-decoration: underline; + } +} diff --git a/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.module.scss b/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.module.scss index 491d3926c63e..fc174b48e30f 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.module.scss +++ b/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.module.scss @@ -38,61 +38,11 @@ } } -h1.base-heading { - font-size: 24px; - font-family: $font-family--base; -} - -h2.base-heading { - padding: 12px 0; - font-size: 24px; - font-family: $font-family--base -} - -h3.base-heading { - color: $colors--neutral-7; - font-family: $font-family--sans-serif; - font-weight: 600; - font-style: normal; - font-stretch: normal; - font-size: 20px; - padding-bottom: 12px; -} - .back-link { text-decoration: none; color: $link-color; } -.cockroach--tabs { - overflow: visible !important; - :global(.ant-tabs-bar) { - border-bottom: 1px solid $grey2; - } - - :global(.ant-tabs-tab) { - font-family: $font-family--base; - font-size: 16px; - line-height: 1.5; - letter-spacing: normal; - color: $colors--neutral-7; - } - - :global(.ant-tabs-nav .ant-tabs-tab-active) { - color: $colors--link; - } - - :global(.ant-tabs-nav .ant-tabs-tab:hover) { - color: $colors--link; - } - - :global(.ant-tabs-ink-bar) { - height: 3px; - border-radius: 40px; - background-color: $blue; - } -} - .fit-content-width { width: fit-content; } diff --git a/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx b/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx index 883f5af7e27f..0eadb6b3f0bc 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx @@ -59,6 +59,7 @@ import { DiagnosticsView } from "./diagnostics/diagnosticsView"; import sortedTableStyles from "src/sortedtable/sortedtable.module.scss"; import summaryCardStyles from "src/summaryCard/summaryCard.module.scss"; import styles from "./statementDetails.module.scss"; +import { commonStyles } from "src/common"; import { NodeSummaryStats } from "../nodes"; import { UIConfigState } from "../store"; import moment, { Moment } from "moment"; @@ -385,7 +386,7 @@ export class StatementDetails extends React.Component< }; backToStatementsClick = (): void => { - this.props.history.push("/statements"); + this.props.history.push("/sql-activity/statements"); if (this.props.onBackToStatementsClick) { this.props.onBackToStatementsClick(); } @@ -407,7 +408,7 @@ export class StatementDetails extends React.Component< > Statements -

    +

    Statement Details

    @@ -531,7 +532,7 @@ export class StatementDetails extends React.Component< return ( @@ -787,7 +788,7 @@ export class StatementDetails extends React.Component<

    @@ -830,7 +831,7 @@ export class StatementDetails extends React.Component<

    @@ -880,7 +881,7 @@ export class StatementDetails extends React.Component<

    diff --git a/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.module.scss b/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.module.scss index 569e95bf7018..da7ca2fbcfe8 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.module.scss +++ b/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.module.scss @@ -37,27 +37,6 @@ } } -h1.base-heading { - font-size: 24px; - font-family: $font-family--base; -} - -h2.base-heading { - padding: 12px 0; - font-size: 24px; - font-family: $font-family--base; -} - -h3.base-heading { - color: $colors--neutral-7; - font-family: $font-family--sans-serif; - font-weight: 600; - font-style: normal; - font-stretch: normal; - font-size: 20px; - padding-bottom: 12px; -} - .no-margin-bottom { margin-bottom: 0px; } diff --git a/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.tsx b/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.tsx index 71acddb3d2e9..64877eda468f 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.tsx @@ -32,7 +32,6 @@ import { import { appAttr, - getMatchParamByName, calculateTotalWorkload, unique, containAny, @@ -64,6 +63,8 @@ import { SelectOption } from "../multiSelectCheckbox/multiSelectCheckbox"; import { UIConfigState } from "../store"; import { StatementsRequest } from "src/api/statementsApi"; import Long from "long"; +import ClearStats from "../sqlActivity/clearStats"; +import { commonStyles } from "../common"; const cx = classNames.bind(styles); const sortableTableCx = classNames.bind(sortableTableStyles); @@ -404,12 +405,11 @@ export class StatementsPage extends React.Component< ); }; - renderStatements = () => { + renderStatements = (): React.ReactElement => { const { pagination, search, filters, activeFilters } = this.state; const { statements, databases, - lastReset, location, onDiagnosticsReportDownload, onStatementClick, @@ -520,6 +520,9 @@ export class StatementsPage extends React.Component< reset time + + +
    @@ -529,14 +532,11 @@ export class StatementsPage extends React.Component< />
    -

    Statements

    void; - resetSQLStats: () => void; } +// TableStatistics shows statistics about the results being +// displayed on the table, including total results count, +// how many are currently being displayed on the page and +// if there is an active filter. +// This component has also a clear filter option. export const TableStatistics: React.FC = ({ pagination, totalCount, - lastReset, search, arrayItemName, - tooltipType, onClearFilters, activeFilters, - resetSQLStats, }) => { const resultsPerPageLabel = ( = ({ ); - let statsType = ""; - switch (tooltipType) { - case "transaction": - statsType = contentModifiers.transactionCapital; - break; - case "statement": - statsType = contentModifiers.statementCapital; - break; - case "transactionDetails": - statsType = contentModifiers.statementCapital; - break; - default: - break; - } - const toolTipText = `${statsType} statistics are aggregated once an hour and organized by the start time. - Statistics between two hourly intervals belong to the nearest hour rounded down. - For example, a ${statsType} execution ending at 1:50 would have its statistics aggregated in the 1:00 interval - start time. Clicking ‘clear SQL stats’ will reset SQL stats on the Statements and Transactions pages and - crdb_internal tables.`; - return (

    {activeFilters ? resultsCountAndClear : resultsPerPageLabel}

    -
    - -
    - -
    -
    - -
    ); }; diff --git a/pkg/ui/workspaces/cluster-ui/src/transactionDetails/transactionDetails.tsx b/pkg/ui/workspaces/cluster-ui/src/transactionDetails/transactionDetails.tsx index dc8509c70698..feed24d9c0e5 100644 --- a/pkg/ui/workspaces/cluster-ui/src/transactionDetails/transactionDetails.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/transactionDetails/transactionDetails.tsx @@ -109,7 +109,6 @@ export class TransactionDetails extends React.Component< transactionStats, handleDetails, error, - resetSQLStats, nodeRegions, } = this.props; return ( @@ -131,12 +130,7 @@ export class TransactionDetails extends React.Component< error={error} loading={!statements || !transactionStats} render={() => { - const { - statements, - transactionStats, - lastReset, - isTenant, - } = this.props; + const { statements, transactionStats, isTenant } = this.props; const { sortSetting, pagination } = this.state; const aggregatedStatements = aggregateStatements(statements); populateRegionNodeForStatements( @@ -272,13 +266,10 @@ export class TransactionDetails extends React.Component<
    ) => { + syncHistory = (params: Record): void => { const { history } = this.props; const currentSearchParams = new URLSearchParams(history.location.search); @@ -270,10 +269,9 @@ export class TransactionsPage extends React.Component< ); }; - renderTransactionsList() { + renderTransactionsList(): React.ReactElement { return (
    -

    Transactions

    + + +
    { }); describe("'/statement' path", () => { - it("redirected to '/statements'", () => { + it("redirected to '/sql-activity/statements'", () => { navigateToPath("/statement"); const location = history.location; - assert.equal(location.pathname, "/statements"); + assert.equal(location.pathname, "/sql-activity/statements"); }); }); @@ -582,4 +582,28 @@ describe("Routing to", () => { assert.lengthOf(appWrapper.find(NotFound), 1); }); }); + + describe("'/statements' path", () => { + it("redirected to '/sql-activity/statements'", () => { + navigateToPath("/statements"); + const location = history.location; + assert.equal(location.pathname, "/sql-activity/statements"); + }); + }); + + describe("'/sessions' path", () => { + it("redirected to '/sql-activity/sessions'", () => { + navigateToPath("/sessions"); + const location = history.location; + assert.equal(location.pathname, "/sql-activity/sessions"); + }); + }); + + describe("'/transactions' path", () => { + it("redirected to '/sql-activity/transactions'", () => { + navigateToPath("/transactions"); + const location = history.location; + assert.equal(location.pathname, "/sql-activity/transactions"); + }); + }); }); diff --git a/pkg/ui/workspaces/db-console/src/app.tsx b/pkg/ui/workspaces/db-console/src/app.tsx index 2d56fc9e7746..31dfd297d696 100644 --- a/pkg/ui/workspaces/db-console/src/app.tsx +++ b/pkg/ui/workspaces/db-console/src/app.tsx @@ -29,6 +29,7 @@ import { rangeIDAttr, sessionAttr, statementAttr, + tabAttr, tableNameAttr, } from "src/util/constants"; import NotFound from "src/views/app/components/NotFound"; @@ -59,11 +60,9 @@ import Range from "src/views/reports/containers/range"; import ReduxDebug from "src/views/reports/containers/redux"; import Settings from "src/views/reports/containers/settings"; import Stores from "src/views/reports/containers/stores"; +import SQLActivityPage from "src/views/sqlActivity/sqlActivityPage"; import StatementDetails from "src/views/statements/statementDetails"; -import StatementsPage from "src/views/statements/statementsPage"; -import SessionsPage from "src/views/sessions/sessionsPage"; import SessionDetails from "src/views/sessions/sessionDetails"; -import TransactionsPage from "src/views/transactions/transactionsPage"; import StatementsDiagnosticsHistoryView from "src/views/reports/containers/statementDiagnosticsHistory"; import { RedirectToStatementDetails } from "src/routes/RedirectToStatementDetails"; import "styl/app.styl"; @@ -177,8 +176,20 @@ export const App: React.FC = (props: AppProps) => { component={DataDistributionPage} /> + {/* SQL activity */} + + + {/* statement statistics */} - + = (props: AppProps) => { path={`/statement/:${databaseAttr}/:${implicitTxnAttr}/:${statementAttr}`} render={RedirectToStatementDetails} /> - } + from={`/statement`} + to={`/sql-activity/statements`} /> {/* sessions */} - + + {/* transactions */} - {/* debug pages */} diff --git a/pkg/ui/workspaces/db-console/src/util/constants.ts b/pkg/ui/workspaces/db-console/src/util/constants.ts index 28e6c65ff33c..d99a7f9d73ea 100644 --- a/pkg/ui/workspaces/db-console/src/util/constants.ts +++ b/pkg/ui/workspaces/db-console/src/util/constants.ts @@ -21,6 +21,7 @@ export const databaseAttr = "database"; export const sessionAttr = "session"; export const tableNameAttr = "table_name"; export const aggregatedTsAttr = "aggregated_ts"; +export const tabAttr = "tab"; export const REMOTE_DEBUGGING_ERROR_TEXT = "This information is not available due to the current value of the 'server.remote_debugging.mode' setting."; diff --git a/pkg/ui/workspaces/db-console/src/views/app/components/layoutSidebar/index.tsx b/pkg/ui/workspaces/db-console/src/views/app/components/layoutSidebar/index.tsx index 68da15bf52aa..5ddf8a86189a 100644 --- a/pkg/ui/workspaces/db-console/src/views/app/components/layoutSidebar/index.tsx +++ b/pkg/ui/workspaces/db-console/src/views/app/components/layoutSidebar/index.tsx @@ -39,13 +39,11 @@ export class Sidebar extends React.Component { { path: "/overview", text: "Overview", activeFor: ["/node"] }, { path: "/metrics", text: "Metrics", activeFor: [] }, { path: "/databases", text: "Databases", activeFor: ["/database"] }, - { path: "/sessions", text: "Sessions", activeFor: ["/session"] }, { - path: "/transactions", - text: "Transactions", - activeFor: ["/transactions"], + path: "/sql-activity", + text: "SQL Activity", + activeFor: ["/sql-activity", "/session", "/transaction", "/statement"], }, - { path: "/statements", text: "Statements", activeFor: ["/statement"] }, { path: "/reports/network", text: "Network Latency", diff --git a/pkg/ui/workspaces/db-console/src/views/sqlActivity/sqlActivityPage.tsx b/pkg/ui/workspaces/db-console/src/views/sqlActivity/sqlActivityPage.tsx new file mode 100644 index 000000000000..2c8c7438fc03 --- /dev/null +++ b/pkg/ui/workspaces/db-console/src/views/sqlActivity/sqlActivityPage.tsx @@ -0,0 +1,56 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +// All changes made on this file, should also be done on the equivalent +// file on managed-service repo. + +import React from "react"; +import { Tabs } from "antd"; +import { commonStyles } from "@cockroachlabs/cluster-ui"; +import SessionsPageConnected from "src/views/sessions/sessionsPage"; +import TransactionsPageConnected from "src/views/transactions/transactionsPage"; +import StatementsPageConnected from "src/views/statements/statementsPage"; +import { getMatchParamByName } from "src/util/query"; +import { RouteComponentProps } from "react-router-dom"; + +const { TabPane } = Tabs; + +const SQLActivityPage = (props: RouteComponentProps) => { + const defaultTab = getMatchParamByName(props.match, "tab") || "sessions"; + const [currentTab, setCurrentTab] = React.useState(defaultTab); + + const onTabChange = (tabId: string): void => { + setCurrentTab(tabId); + }; + + return ( +
    +

    SQL Activity

    + + + + + + + + + + + +
    + ); +}; + +export default SQLActivityPage; From 3220ba221bbfd859f0976aa4fcdefdf44495cf31 Mon Sep 17 00:00:00 2001 From: Marylia Gutierrez Date: Wed, 13 Oct 2021 10:23:11 -0400 Subject: [PATCH 3/9] ui: update ui-components version Update ui-components version to include a fix on scrolling on pages with tooltip Release note: None --- pkg/ui/workspaces/cluster-ui/package.json | 2 +- pkg/ui/yarn-vendor | 1 + pkg/ui/yarn.lock | 33 +++++++++++++---------- 3 files changed, 21 insertions(+), 15 deletions(-) create mode 160000 pkg/ui/yarn-vendor diff --git a/pkg/ui/workspaces/cluster-ui/package.json b/pkg/ui/workspaces/cluster-ui/package.json index 2d62ae357db1..83f33be77862 100644 --- a/pkg/ui/workspaces/cluster-ui/package.json +++ b/pkg/ui/workspaces/cluster-ui/package.json @@ -41,7 +41,7 @@ "@cockroachlabs/crdb-protobuf-client": "link:../db-console/src/js", "@cockroachlabs/eslint-config": "^0.1.3", "@cockroachlabs/icons": "0.3.0", - "@cockroachlabs/ui-components": "0.2.19-alpha.11", + "@cockroachlabs/ui-components": "0.2.20", "@popperjs/core": "^2.4.0", "@reduxjs/toolkit": "^1.5.0", "@storybook/addon-actions": "^6.1.21", diff --git a/pkg/ui/yarn-vendor b/pkg/ui/yarn-vendor new file mode 160000 index 000000000000..7a60011cc2ae --- /dev/null +++ b/pkg/ui/yarn-vendor @@ -0,0 +1 @@ +Subproject commit 7a60011cc2ae52abdcbf36087a79e7e4464f690c diff --git a/pkg/ui/yarn.lock b/pkg/ui/yarn.lock index d9dd695aba67..6793141472cd 100644 --- a/pkg/ui/yarn.lock +++ b/pkg/ui/yarn.lock @@ -1384,7 +1384,7 @@ minimist "^1.2.0" "@cockroachlabs/cluster-ui@link:workspaces/cluster-ui": - version "21.2.0-prerelease-3" + version "22.1.0-prerelease-1" dependencies: "@babel/runtime" "^7.12.13" @@ -1408,20 +1408,20 @@ resolved "https://registry.yarnpkg.com/@cockroachlabs/icons/-/icons-0.3.0.tgz#160573074396f266e92fcbe5e520c5ba1d8750f9" integrity sha512-GJxhlXy8Z3/PYFb9C3iM1dvU9wajGoaA/+VCj0an2ipfbkI2fhToq+h0b33vu7JuZ3dS4QMRjfVE4uhlyIUH2Q== -"@cockroachlabs/icons@^0.4.1": - version "0.4.1" - resolved "https://registry.yarnpkg.com/@cockroachlabs/icons/-/icons-0.4.1.tgz#5772f94ce625279a4789f8e48c9b4e903c5bc5d7" - integrity sha512-JaiYFDhqhvGPqAFkHGCnEYiBe6Gas2HjqhLOQa4T5FRRMJ07liswOPARXyX120hTPwqEUaQyaHLxzqYDFwfdzQ== +"@cockroachlabs/icons@^0.4.3": + version "0.4.3" + resolved "https://registry.yarnpkg.com/@cockroachlabs/icons/-/icons-0.4.3.tgz#cf42bd0398a71c3d70ff83f631689310e42091fb" + integrity sha512-PLbsDsZmgj9p/2fZkWQ+MENvxEPpdWhJBm6AqiWIYQ4A1X7+F6sfgXeHBE19qceu299MjxsjLDK+UlyU0uvlMA== -"@cockroachlabs/ui-components@0.2.19-alpha.11": - version "0.2.19-alpha.11" - resolved "https://registry.yarnpkg.com/@cockroachlabs/ui-components/-/ui-components-0.2.19-alpha.11.tgz#41e285d65e1d1bb20000c43e473f05ba84e050b3" - integrity sha512-HoSNnpxiWa7ZBamexRf/ihV6NDN+e+OsWZha7Bt3v9TBqXznY+nCdTNGperJAK+RXTBM/WhpmiLtlgC8MRWuxQ== +"@cockroachlabs/ui-components@0.2.20": + version "0.2.20" + resolved "https://registry.yarnpkg.com/@cockroachlabs/ui-components/-/ui-components-0.2.20.tgz#7b4102f5063683aed625c4399fb1fec149a5c5e5" + integrity sha512-MXjcDrFVoP2/EKwe7pixol9l2+5r6hb4/FXvbNU0NW5wkufYBLp26CX3GjMbxobwy8NG+AkSZ3X0Vkhh9O9OXw== dependencies: - "@cockroachlabs/icons" "^0.4.1" - "@popperjs/core" "^2.4.3" + "@cockroachlabs/icons" "^0.4.3" + "@popperjs/core" "^2.9.2" npm-run-all "^4.1.5" - react-popper "^2.2.3" + react-popper "^2.2.5" "@cypress/listr-verbose-renderer@^0.4.1": version "0.4.1" @@ -2049,11 +2049,16 @@ schema-utils "^2.6.5" source-map "^0.7.3" -"@popperjs/core@^2.4.0", "@popperjs/core@^2.4.3", "@popperjs/core@^2.5.4", "@popperjs/core@^2.6.0": +"@popperjs/core@^2.4.0", "@popperjs/core@^2.5.4", "@popperjs/core@^2.6.0": version "2.9.2" resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.9.2.tgz#adea7b6953cbb34651766b0548468e743c6a2353" integrity sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q== +"@popperjs/core@^2.9.2": + version "2.10.2" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.10.2.tgz#0798c03351f0dea1a5a4cabddf26a55a7cbee590" + integrity sha512-IXf3XA7+XyN7CP9gGh/XB0UxVMlvARGEgGXLubFICsUMGz6Q+DU+i4gGlpOxTjKvXjkJDJC8YdqdKkDj9qZHEQ== + "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" @@ -14634,7 +14639,7 @@ react-popper-tooltip@^3.1.1: "@popperjs/core" "^2.5.4" react-popper "^2.2.4" -react-popper@^2.2.3, react-popper@^2.2.4: +react-popper@^2.2.3, react-popper@^2.2.4, react-popper@^2.2.5: version "2.2.5" resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-2.2.5.tgz#1214ef3cec86330a171671a4fbcbeeb65ee58e96" integrity sha512-kxGkS80eQGtLl18+uig1UIf9MKixFSyPxglsgLBxlYnyDf65BiY9B3nZSc6C9XUNDgStROB0fMQlTEz1KxGddw== From 0f19d468f5878595323db03906611b80deec2a43 Mon Sep 17 00:00:00 2001 From: Marylia Gutierrez Date: Wed, 20 Oct 2021 11:46:23 -0400 Subject: [PATCH 4/9] ui: update url params when switching tabs on sql activity When the user clicks on a tab on the new SQL Activity page now the search params on the url is updated with the tab selection. This commit also create a common function to sync history so all the pages can use it and remove duplicate code. Release note: None --- .../src/sessions/sessionDetails.tsx | 7 +- .../cluster-ui/src/sessions/sessionsPage.tsx | 32 ++----- .../src/statementDetails/statementDetails.tsx | 2 +- .../src/statementsPage/statementsPage.tsx | 88 +++++++++---------- .../src/transactionsPage/transactionsPage.tsx | 84 +++++++++--------- .../cluster-ui/src/util/query/query.ts | 25 +++++- pkg/ui/workspaces/db-console/src/app.spec.tsx | 28 ++++-- pkg/ui/workspaces/db-console/src/app.tsx | 13 +-- .../src/views/sqlActivity/sqlActivityPage.tsx | 7 +- 9 files changed, 148 insertions(+), 138 deletions(-) diff --git a/pkg/ui/workspaces/cluster-ui/src/sessions/sessionDetails.tsx b/pkg/ui/workspaces/cluster-ui/src/sessions/sessionDetails.tsx index bf27a336d530..40450d93799d 100644 --- a/pkg/ui/workspaces/cluster-ui/src/sessions/sessionDetails.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/sessions/sessionDetails.tsx @@ -124,12 +124,9 @@ export class SessionDetails extends React.Component { } backToSessionsPage = (): void => { - const { history, location, onBackButtonClick } = this.props; + const { history, onBackButtonClick } = this.props; onBackButtonClick && onBackButtonClick(); - history.push({ - ...location, - pathname: "/sql-activity/sessions", - }); + history.push("/sql-activity?tab=sessions"); }; render(): React.ReactElement { diff --git a/pkg/ui/workspaces/cluster-ui/src/sessions/sessionsPage.tsx b/pkg/ui/workspaces/cluster-ui/src/sessions/sessionsPage.tsx index 8a66c03107b7..dc317b232fb2 100644 --- a/pkg/ui/workspaces/cluster-ui/src/sessions/sessionsPage.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/sessions/sessionsPage.tsx @@ -9,9 +9,9 @@ // licenses/APL.txt. import React from "react"; -import { forIn, isNil, merge } from "lodash"; +import { isNil, merge } from "lodash"; -import { getMatchParamByName } from "src/util/query"; +import { getMatchParamByName, syncHistory } from "src/util/query"; import { appAttr } from "src/util/constants"; import { makeSessionsColumns, @@ -43,11 +43,9 @@ import { ICancelQueryRequest, } from "src/store/terminateQuery"; -import sortedTableStyles from "src/sortedtable/sortedtable.module.scss"; import statementsPageStyles from "src/statementsPage/statementsPage.module.scss"; import sessionPageStyles from "./sessionPage.module.scss"; -const sortableTableCx = classNames.bind(sortedTableStyles); const statementsPageCx = classNames.bind(statementsPageStyles); const sessionsPageCx = classNames.bind(sessionPageStyles); @@ -113,21 +111,6 @@ export class SessionsPage extends React.Component< }; }; - syncHistory = (params: Record): void => { - const { history } = this.props; - const currentSearchParams = new URLSearchParams(history.location.search); - forIn(params, (value, key) => { - if (!value) { - currentSearchParams.delete(key); - } else { - currentSearchParams.set(key, value); - } - }); - - history.location.search = currentSearchParams.toString(); - history.replace(history.location); - }; - changeSortSetting = (ss: SortSetting): void => { const { onSortingChange } = this.props; onSortingChange && onSortingChange(ss.columnTitle); @@ -136,10 +119,13 @@ export class SessionsPage extends React.Component< sortSetting: ss, }); - this.syncHistory({ - ascending: ss.ascending.toString(), - columnTitle: ss.columnTitle, - }); + syncHistory( + { + ascending: ss.ascending.toString(), + columnTitle: ss.columnTitle, + }, + this.props.history, + ); }; resetPagination = (): void => { diff --git a/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx b/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx index 0eadb6b3f0bc..d59829336b39 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx @@ -386,7 +386,7 @@ export class StatementDetails extends React.Component< }; backToStatementsClick = (): void => { - this.props.history.push("/sql-activity/statements"); + this.props.history.push("/sql-activity?tab=statements"); if (this.props.onBackToStatementsClick) { this.props.onBackToStatementsClick(); } diff --git a/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.tsx b/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.tsx index 64877eda468f..b9be2351ca80 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.tsx @@ -36,6 +36,7 @@ import { unique, containAny, queryByName, + syncHistory, } from "src/util"; import { AggregateStatistics, @@ -180,31 +181,18 @@ export class StatementsPage extends React.Component< }; }; - syncHistory = (params: Record): void => { - const { history } = this.props; - const nextSearchParams = new URLSearchParams(history.location.search); - - Object.entries(params).forEach(([key, value]) => { - if (!value) { - nextSearchParams.delete(key); - } else { - nextSearchParams.set(key, value); - } - }); - - history.location.search = nextSearchParams.toString(); - history.replace(history.location); - }; - changeSortSetting = (ss: SortSetting): void => { this.setState({ sortSetting: ss, }); - this.syncHistory({ - ascending: ss.ascending.toString(), - columnTitle: ss.columnTitle, - }); + syncHistory( + { + ascending: ss.ascending.toString(), + columnTitle: ss.columnTitle, + }, + this.props.history, + ); if (this.props.onSortingChange) { this.props.onSortingChange("Statements", ss.columnTitle, ss.ascending); } @@ -273,9 +261,12 @@ export class StatementsPage extends React.Component< onSubmitSearchField = (search: string): void => { this.setState({ search }); this.resetPagination(); - this.syncHistory({ - q: search, - }); + syncHistory( + { + q: search, + }, + this.props.history, + ); }; onSubmitFilters = (filters: Filters): void => { @@ -288,22 +279,28 @@ export class StatementsPage extends React.Component< }); this.resetPagination(); - this.syncHistory({ - app: filters.app, - timeNumber: filters.timeNumber, - timeUnit: filters.timeUnit, - sqlType: filters.sqlType, - database: filters.database, - regions: filters.regions, - nodes: filters.nodes, - }); + syncHistory( + { + app: filters.app, + timeNumber: filters.timeNumber, + timeUnit: filters.timeUnit, + sqlType: filters.sqlType, + database: filters.database, + regions: filters.regions, + nodes: filters.nodes, + }, + this.props.history, + ); }; onClearSearchField = (): void => { this.setState({ search: "" }); - this.syncHistory({ - q: undefined, - }); + syncHistory( + { + q: undefined, + }, + this.props.history, + ); }; onClearFilters = (): void => { @@ -314,15 +311,18 @@ export class StatementsPage extends React.Component< activeFilters: 0, }); this.resetPagination(); - this.syncHistory({ - app: undefined, - timeNumber: undefined, - timeUnit: undefined, - sqlType: undefined, - database: undefined, - regions: undefined, - nodes: undefined, - }); + syncHistory( + { + app: undefined, + timeNumber: undefined, + timeUnit: undefined, + sqlType: undefined, + database: undefined, + regions: undefined, + nodes: undefined, + }, + this.props.history, + ); }; filteredStatementsData = (): AggregateStatistics[] => { diff --git a/pkg/ui/workspaces/cluster-ui/src/transactionsPage/transactionsPage.tsx b/pkg/ui/workspaces/cluster-ui/src/transactionsPage/transactionsPage.tsx index 76ff011111f5..7c6c7e74c83c 100644 --- a/pkg/ui/workspaces/cluster-ui/src/transactionsPage/transactionsPage.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/transactionsPage/transactionsPage.tsx @@ -34,15 +34,12 @@ import { generateRegionNode, getTrxAppFilterOptions, statementFingerprintIdsToText, -} from "./utils"; -import { searchTransactionsData, filterTransactions, getStatementsByFingerprintIdAndTime, } from "./utils"; -import { forIn } from "lodash"; import Long from "long"; -import { getSearchParams, unique } from "src/util"; +import { getSearchParams, unique, syncHistory } from "src/util"; import { EmptyTransactionsPlaceholder } from "./emptyTransactionsPlaceholder"; import { Loading } from "../loading"; import { PageConfig, PageConfigItem } from "../pageConfig"; @@ -152,30 +149,17 @@ export class TransactionsPage extends React.Component< this.refreshData(); } - syncHistory = (params: Record): void => { - const { history } = this.props; - const currentSearchParams = new URLSearchParams(history.location.search); - - forIn(params, (value, key) => { - if (!value) { - currentSearchParams.delete(key); - } else { - currentSearchParams.set(key, value); - } - }); - - history.location.search = currentSearchParams.toString(); - history.replace(history.location); - }; - onChangeSortSetting = (ss: SortSetting): void => { this.setState({ sortSetting: ss, }); - this.syncHistory({ - ascending: ss.ascending.toString(), - columnTitle: ss.columnTitle, - }); + syncHistory( + { + ascending: ss.ascending.toString(), + columnTitle: ss.columnTitle, + }, + this.props.history, + ); }; onChangePage = (current: number): void => { @@ -196,17 +180,23 @@ export class TransactionsPage extends React.Component< onClearSearchField = (): void => { this.setState({ search: "" }); - this.syncHistory({ - q: undefined, - }); + syncHistory( + { + q: undefined, + }, + this.props.history, + ); }; onSubmitSearchField = (search: string): void => { this.setState({ search }); this.resetPagination(); - this.syncHistory({ - q: search, - }); + syncHistory( + { + q: search, + }, + this.props.history, + ); }; onSubmitFilters = (filters: Filters): void => { @@ -217,13 +207,16 @@ export class TransactionsPage extends React.Component< }, }); this.resetPagination(); - this.syncHistory({ - app: filters.app, - timeNumber: filters.timeNumber, - timeUnit: filters.timeUnit, - regions: filters.regions, - nodes: filters.nodes, - }); + syncHistory( + { + app: filters.app, + timeNumber: filters.timeNumber, + timeUnit: filters.timeUnit, + regions: filters.regions, + nodes: filters.nodes, + }, + this.props.history, + ); }; onClearFilters = (): void => { @@ -233,13 +226,16 @@ export class TransactionsPage extends React.Component< }, }); this.resetPagination(); - this.syncHistory({ - app: undefined, - timeNumber: undefined, - timeUnit: undefined, - regions: undefined, - nodes: undefined, - }); + syncHistory( + { + app: undefined, + timeNumber: undefined, + timeUnit: undefined, + regions: undefined, + nodes: undefined, + }, + this.props.history, + ); }; handleDetails = (transaction?: TransactionInfo): void => { diff --git a/pkg/ui/workspaces/cluster-ui/src/util/query/query.ts b/pkg/ui/workspaces/cluster-ui/src/util/query/query.ts index 14dba7f7acf2..fdc27b9fe3e7 100644 --- a/pkg/ui/workspaces/cluster-ui/src/util/query/query.ts +++ b/pkg/ui/workspaces/cluster-ui/src/util/query/query.ts @@ -8,7 +8,7 @@ // by the Apache License, Version 2.0, included in the file // licenses/APL.txt. -import { Location } from "history"; +import { Location, History } from "history"; import { match as Match } from "react-router-dom"; interface ParamsObj { @@ -64,3 +64,26 @@ export function getMatchParamByName( } return null; } + +export function syncHistory( + params: Record, + history: History, +): void { + const nextSearchParams = new URLSearchParams(history.location.search); + + Object.entries(params).forEach(([key, value]) => { + if (!value) { + nextSearchParams.delete(key); + } else { + nextSearchParams.set(key, value); + } + }); + + history.location.search = nextSearchParams.toString(); + history.replace(history.location); +} + +export function clearHistory(history: History): void { + history.location.search = ""; + history.replace(history.location); +} diff --git a/pkg/ui/workspaces/db-console/src/app.spec.tsx b/pkg/ui/workspaces/db-console/src/app.spec.tsx index eef31140041b..0290cf512cbd 100644 --- a/pkg/ui/workspaces/db-console/src/app.spec.tsx +++ b/pkg/ui/workspaces/db-console/src/app.spec.tsx @@ -339,10 +339,13 @@ describe("Routing to", () => { }); describe("'/statement' path", () => { - it("redirected to '/sql-activity/statements'", () => { + it("redirected to '/sql-activity?tab=statements'", () => { navigateToPath("/statement"); const location = history.location; - assert.equal(location.pathname, "/sql-activity/statements"); + assert.equal( + location.pathname + location.search, + "/sql-activity?tab=statements", + ); }); }); @@ -584,26 +587,35 @@ describe("Routing to", () => { }); describe("'/statements' path", () => { - it("redirected to '/sql-activity/statements'", () => { + it("redirected to '/sql-activity?tab=statements'", () => { navigateToPath("/statements"); const location = history.location; - assert.equal(location.pathname, "/sql-activity/statements"); + assert.equal( + location.pathname + location.search, + "/sql-activity?tab=statements", + ); }); }); describe("'/sessions' path", () => { - it("redirected to '/sql-activity/sessions'", () => { + it("redirected to '/sql-activity?tab=sessions'", () => { navigateToPath("/sessions"); const location = history.location; - assert.equal(location.pathname, "/sql-activity/sessions"); + assert.equal( + location.pathname + location.search, + "/sql-activity?tab=sessions", + ); }); }); describe("'/transactions' path", () => { - it("redirected to '/sql-activity/transactions'", () => { + it("redirected to '/sql-activity?tab=transactions'", () => { navigateToPath("/transactions"); const location = history.location; - assert.equal(location.pathname, "/sql-activity/transactions"); + assert.equal( + location.pathname + location.search, + "/sql-activity?tab=transactions", + ); }); }); }); diff --git a/pkg/ui/workspaces/db-console/src/app.tsx b/pkg/ui/workspaces/db-console/src/app.tsx index 31dfd297d696..d3e3c8f2065f 100644 --- a/pkg/ui/workspaces/db-console/src/app.tsx +++ b/pkg/ui/workspaces/db-console/src/app.tsx @@ -178,17 +178,12 @@ export const App: React.FC = (props: AppProps) => { {/* SQL activity */} - {/* statement statistics */} = (props: AppProps) => { {/* sessions */} = (props: AppProps) => { {/* debug pages */} diff --git a/pkg/ui/workspaces/db-console/src/views/sqlActivity/sqlActivityPage.tsx b/pkg/ui/workspaces/db-console/src/views/sqlActivity/sqlActivityPage.tsx index 2c8c7438fc03..de1ff8990347 100644 --- a/pkg/ui/workspaces/db-console/src/views/sqlActivity/sqlActivityPage.tsx +++ b/pkg/ui/workspaces/db-console/src/views/sqlActivity/sqlActivityPage.tsx @@ -13,21 +13,22 @@ import React from "react"; import { Tabs } from "antd"; -import { commonStyles } from "@cockroachlabs/cluster-ui"; +import { commonStyles, util } from "@cockroachlabs/cluster-ui"; import SessionsPageConnected from "src/views/sessions/sessionsPage"; import TransactionsPageConnected from "src/views/transactions/transactionsPage"; import StatementsPageConnected from "src/views/statements/statementsPage"; -import { getMatchParamByName } from "src/util/query"; import { RouteComponentProps } from "react-router-dom"; const { TabPane } = Tabs; const SQLActivityPage = (props: RouteComponentProps) => { - const defaultTab = getMatchParamByName(props.match, "tab") || "sessions"; + const defaultTab = util.queryByName(props.location, "tab") || "sessions"; const [currentTab, setCurrentTab] = React.useState(defaultTab); const onTabChange = (tabId: string): void => { setCurrentTab(tabId); + util.clearHistory(props.history); + util.syncHistory({ tab: tabId }, props.history); }; return ( From d81a5afd86e7800d9c58c959870ff1adb8f17b51 Mon Sep 17 00:00:00 2001 From: Marylia Gutierrez Date: Thu, 21 Oct 2021 17:31:45 -0400 Subject: [PATCH 5/9] ui: update return route from stmt details not found When a statement was not found, the return to statements link was returning to the SQL Activity page without the Statement tab selected. This commit adds the right tab to the return option. Release note: None --- .../cluster-ui/src/statementDetails/statementDetails.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx b/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx index d59829336b39..6be263e8bc69 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx @@ -451,7 +451,9 @@ export class StatementDetails extends React.Component< if (!stats) { const sourceApp = queryByName(this.props.location, appAttr); - const listUrl = "/statements" + (sourceApp ? "/" + sourceApp : ""); + const listUrl = + "/sql-activity?tab=statements" + + (sourceApp ? "&" + appAttr + "=" + sourceApp : ""); return ( From a7acfe33883beeea559afb22039e9460cdc3cb59 Mon Sep 17 00:00:00 2001 From: Marylia Gutierrez Date: Mon, 25 Oct 2021 11:56:58 -0400 Subject: [PATCH 6/9] ui: use push instead of replace on browser history Previously, when changing tabs on SQL Activity page the history would be replaced, this commits change to push to history, meaning when the user clicks Back on the browser it will return for the previous tab. Release note: None --- .../workspaces/cluster-ui/src/util/query/query.ts | 12 ++++++------ .../src/views/sqlActivity/sqlActivityPage.tsx | 15 +++++++++++---- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/pkg/ui/workspaces/cluster-ui/src/util/query/query.ts b/pkg/ui/workspaces/cluster-ui/src/util/query/query.ts index fdc27b9fe3e7..3ba96cfc59b1 100644 --- a/pkg/ui/workspaces/cluster-ui/src/util/query/query.ts +++ b/pkg/ui/workspaces/cluster-ui/src/util/query/query.ts @@ -68,6 +68,7 @@ export function getMatchParamByName( export function syncHistory( params: Record, history: History, + push?: boolean, ): void { const nextSearchParams = new URLSearchParams(history.location.search); @@ -80,10 +81,9 @@ export function syncHistory( }); history.location.search = nextSearchParams.toString(); - history.replace(history.location); -} - -export function clearHistory(history: History): void { - history.location.search = ""; - history.replace(history.location); + if (push) { + history.push(history.location); + } else { + history.replace(history.location); + } } diff --git a/pkg/ui/workspaces/db-console/src/views/sqlActivity/sqlActivityPage.tsx b/pkg/ui/workspaces/db-console/src/views/sqlActivity/sqlActivityPage.tsx index de1ff8990347..e797f44e8bed 100644 --- a/pkg/ui/workspaces/db-console/src/views/sqlActivity/sqlActivityPage.tsx +++ b/pkg/ui/workspaces/db-console/src/views/sqlActivity/sqlActivityPage.tsx @@ -11,7 +11,7 @@ // All changes made on this file, should also be done on the equivalent // file on managed-service repo. -import React from "react"; +import React, { useState, useEffect } from "react"; import { Tabs } from "antd"; import { commonStyles, util } from "@cockroachlabs/cluster-ui"; import SessionsPageConnected from "src/views/sessions/sessionsPage"; @@ -23,14 +23,21 @@ const { TabPane } = Tabs; const SQLActivityPage = (props: RouteComponentProps) => { const defaultTab = util.queryByName(props.location, "tab") || "sessions"; - const [currentTab, setCurrentTab] = React.useState(defaultTab); + const [currentTab, setCurrentTab] = useState(defaultTab); const onTabChange = (tabId: string): void => { setCurrentTab(tabId); - util.clearHistory(props.history); - util.syncHistory({ tab: tabId }, props.history); + props.history.location.search = ""; + util.syncHistory({ tab: tabId }, props.history, true); }; + useEffect(() => { + const queryTab = util.queryByName(props.location, "tab") || "sessions"; + if (queryTab !== currentTab) { + setCurrentTab(queryTab); + } + }, [props.location, currentTab]); + return (

    SQL Activity

    From da5626c75d7d9933be352c5b0e2da4d7dd80573c Mon Sep 17 00:00:00 2001 From: Marylia Gutierrez Date: Thu, 28 Oct 2021 14:57:41 -0400 Subject: [PATCH 7/9] v21.2.0-prerelease-8 Release note (): --- pkg/ui/workspaces/cluster-ui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/ui/workspaces/cluster-ui/package.json b/pkg/ui/workspaces/cluster-ui/package.json index 83f33be77862..f94032f6c143 100644 --- a/pkg/ui/workspaces/cluster-ui/package.json +++ b/pkg/ui/workspaces/cluster-ui/package.json @@ -1,6 +1,6 @@ { "name": "@cockroachlabs/cluster-ui", - "version": "21.2.0-prerelease-7", + "version": "21.2.0-prerelease-8", "description": "Cluster UI is a library of large features shared between CockroachDB and CockroachCloud", "repository": { "type": "git", From 6125f7f18b177423414498025aac857eac03cc0e Mon Sep 17 00:00:00 2001 From: Marylia Gutierrez Date: Thu, 14 Oct 2021 10:32:36 -0400 Subject: [PATCH 8/9] ui: fix default value of filter to All A recent commit changed the default value of filter to be an empty string, but we do have transactions that have no associated app names to it, so All and empty string should be treated as different cases. This commit reverse that changes and use All Release note (bug fix): Show All statements when filter All is selected and not only the ones with empty string --- pkg/ui/workspaces/cluster-ui/src/queryFilter/filter.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/ui/workspaces/cluster-ui/src/queryFilter/filter.tsx b/pkg/ui/workspaces/cluster-ui/src/queryFilter/filter.tsx index 559490cf6166..08567235be7f 100644 --- a/pkg/ui/workspaces/cluster-ui/src/queryFilter/filter.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/queryFilter/filter.tsx @@ -69,7 +69,7 @@ const timeUnit = [ ]; export const defaultFilters: Filters = { - app: "", + app: "All", timeNumber: "0", timeUnit: "seconds", fullScan: false, @@ -116,7 +116,7 @@ export const getFiltersFromQueryString = ( * we want to consider 0 active Filters */ export const inactiveFiltersState: Filters = { - app: "", + app: "All", timeNumber: "0", fullScan: false, sqlType: "", From d7ab805d28cec27abbb8ff9d23cae98445249d27 Mon Sep 17 00:00:00 2001 From: Marylia Gutierrez Date: Tue, 9 Nov 2021 11:00:27 -0500 Subject: [PATCH 9/9] v21.2.0-prerelease-9 --- pkg/ui/workspaces/cluster-ui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/ui/workspaces/cluster-ui/package.json b/pkg/ui/workspaces/cluster-ui/package.json index f94032f6c143..b13ed5a15d07 100644 --- a/pkg/ui/workspaces/cluster-ui/package.json +++ b/pkg/ui/workspaces/cluster-ui/package.json @@ -1,6 +1,6 @@ { "name": "@cockroachlabs/cluster-ui", - "version": "21.2.0-prerelease-8", + "version": "21.2.0-prerelease-9", "description": "Cluster UI is a library of large features shared between CockroachDB and CockroachCloud", "repository": { "type": "git",