diff --git a/pkg/server/combined_statement_stats.go b/pkg/server/combined_statement_stats.go
index 43b53b0407dd..e785baaaf3a4 100644
--- a/pkg/server/combined_statement_stats.go
+++ b/pkg/server/combined_statement_stats.go
@@ -563,6 +563,7 @@ func getStatementDetailsPerAggregatedTs(
GROUP BY
aggregated_ts,
aggregation_interval
+ ORDER BY aggregated_ts ASC
LIMIT $%d`, whereClause, len(args)+1)
args = append(args, limit)
diff --git a/pkg/ui/workspaces/cluster-ui/BUILD.bazel b/pkg/ui/workspaces/cluster-ui/BUILD.bazel
index 93230f70c465..5ea6bf6a6ab1 100644
--- a/pkg/ui/workspaces/cluster-ui/BUILD.bazel
+++ b/pkg/ui/workspaces/cluster-ui/BUILD.bazel
@@ -81,6 +81,7 @@ DEPENDENCIES = [
"@npm//highlight.js",
"@npm//http-proxy-middleware",
"@npm//identity-obj-proxy",
+ "@npm//jest-canvas-mock",
"@npm//jest-environment-enzyme",
"@npm//jest-enzyme",
"@npm//jest-fetch-mock",
diff --git a/pkg/ui/workspaces/cluster-ui/jest.config.js b/pkg/ui/workspaces/cluster-ui/jest.config.js
index 7ee8b57dca91..15f63754496c 100644
--- a/pkg/ui/workspaces/cluster-ui/jest.config.js
+++ b/pkg/ui/workspaces/cluster-ui/jest.config.js
@@ -43,7 +43,7 @@ module.exports = {
],
roots: ["/src"],
testEnvironment: "enzyme",
- setupFilesAfterEnv: ["./enzyme.setup.js", "jest-enzyme"],
+ setupFilesAfterEnv: ["./enzyme.setup.js", "./src/test-utils/matchMedia.mock.js", "jest-enzyme", "jest-canvas-mock"],
transform: {
"^.+\\.tsx?$": "ts-jest",
"^.+\\.jsx?$": ['babel-jest', { configFile: path.resolve(__dirname, 'babel.config.js') }],
diff --git a/pkg/ui/workspaces/cluster-ui/package.json b/pkg/ui/workspaces/cluster-ui/package.json
index 2c982c3de224..d79f876a550f 100644
--- a/pkg/ui/workspaces/cluster-ui/package.json
+++ b/pkg/ui/workspaces/cluster-ui/package.json
@@ -103,6 +103,7 @@
"http-proxy-middleware": "^1.0.5",
"identity-obj-proxy": "^3.0.0",
"jest": "^27.5.1",
+ "jest-canvas-mock": "^2.4.0",
"jest-cli": "^27.5.1",
"jest-environment-enzyme": "^7.1.2",
"jest-enzyme": "^7.1.2",
diff --git a/pkg/ui/workspaces/cluster-ui/src/graphs/bargraph/bars.ts b/pkg/ui/workspaces/cluster-ui/src/graphs/bargraph/bars.ts
index 14e4f0c9b410..f69305e734ca 100644
--- a/pkg/ui/workspaces/cluster-ui/src/graphs/bargraph/bars.ts
+++ b/pkg/ui/workspaces/cluster-ui/src/graphs/bargraph/bars.ts
@@ -14,8 +14,8 @@ import { AxisUnits, AxisDomain } from "../utils/domain";
import { barTooltipPlugin } from "./plugins";
const seriesPalette = [
- "#475872",
- "#FFCD02",
+ "#003EBD",
+ "#2AAF44",
"#F16969",
"#4E9FD1",
"#49D990",
@@ -24,7 +24,7 @@ const seriesPalette = [
"#A3415B",
"#B59153",
"#C9DB6D",
- "#203D9B",
+ "#475872",
"#748BF2",
"#91C8F2",
"#FF9696",
@@ -158,6 +158,9 @@ export const getBarChartOpts = (
x: {
range: () => [xAxisDomain.extent[0], xAxisDomain.extent[1]],
},
+ yAxis: {
+ range: () => [yAxisDomain.extent[0], yAxisDomain.extent[1]],
+ },
},
axes: [
{
diff --git a/pkg/ui/workspaces/cluster-ui/src/pageConfig/pageConfig.module.scss b/pkg/ui/workspaces/cluster-ui/src/pageConfig/pageConfig.module.scss
index 58eac363b887..3e010f09be21 100644
--- a/pkg/ui/workspaces/cluster-ui/src/pageConfig/pageConfig.module.scss
+++ b/pkg/ui/workspaces/cluster-ui/src/pageConfig/pageConfig.module.scss
@@ -14,7 +14,7 @@
margin-bottom: -10px;
margin-top: -10px;
- z-index: 1;
+ z-index: 2;
background-color: $colors--background;
&__list {
diff --git a/pkg/ui/workspaces/cluster-ui/src/sortedtable/sortedtable.module.scss b/pkg/ui/workspaces/cluster-ui/src/sortedtable/sortedtable.module.scss
index e63b530b9d24..4d0f5c827ae5 100644
--- a/pkg/ui/workspaces/cluster-ui/src/sortedtable/sortedtable.module.scss
+++ b/pkg/ui/workspaces/cluster-ui/src/sortedtable/sortedtable.module.scss
@@ -61,7 +61,8 @@
position: relative;
padding: 11px;
- &:after, &:before {
+ &:after,
+ &:before {
content: "";
position: absolute;
right: 5px;
@@ -94,15 +95,14 @@
}
}
}
-
}
.break-line {
- white-space:pre-wrap !important;
+ white-space: pre-wrap !important;
}
.cl-table-container {
- padding: 18px 24px 18px 0px;
+ padding: 20px 24px 18px 0px;
}
.cl-table-wrapper {
padding: 9.55px 20px 17px;
@@ -116,7 +116,8 @@
align-items: center;
flex-direction: column;
padding: 50px 0;
- &--title, &--description {
+ &--title,
+ &--description {
font-family: $font-family--base;
margin: 0;
}
diff --git a/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx b/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx
index 40e1e53f0662..b6044ddef6d4 100644
--- a/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx
+++ b/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx
@@ -8,19 +8,22 @@
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
+import React, { ReactNode } from "react";
import { Col, Row, Tabs } from "antd";
-import { Text, Heading, InlineAlert } from "@cockroachlabs/ui-components";
-import { PageConfig, PageConfigItem } from "src/pageConfig";
+import { cockroach, google } from "@cockroachlabs/crdb-protobuf-client";
+import { Text, InlineAlert } from "@cockroachlabs/ui-components";
+import { ArrowLeft } from "@cockroachlabs/icons";
+import { Location } from "history";
import _ from "lodash";
-import React, { ReactNode } from "react";
+import Long from "long";
+import { format as d3Format } from "d3-format";
import { Helmet } from "react-helmet";
import { Link, RouteComponentProps } from "react-router-dom";
import classNames from "classnames/bind";
-import { format as d3Format } from "d3-format";
-import { ArrowLeft } from "@cockroachlabs/icons";
-import { cockroach, google } from "@cockroachlabs/crdb-protobuf-client";
-import Long from "long";
-import { Location } from "history";
+import { PageConfig, PageConfigItem } from "src/pageConfig";
+import { BarGraphTimeSeries } from "../graphs/bargraph";
+import { AxisUnits } from "../graphs";
+import { AlignedData, Options } from "uplot";
import {
NumericStat,
@@ -28,9 +31,7 @@ import {
Bytes,
Duration,
FixLong,
- longToInt,
stdDev,
- formatNumberForDisplay,
unique,
queryByName,
appAttr,
@@ -44,11 +45,7 @@ import { SortSetting } from "src/sortedtable";
import { Tooltip } from "@cockroachlabs/ui-components";
import { PlanDetails } from "./planDetails";
import { SummaryCard } from "src/summaryCard";
-import {
- latencyBreakdown,
- genericBarChart,
- formatTwoPlaces,
-} from "src/barCharts";
+import { latencyBreakdown, genericBarChart } from "src/barCharts";
import { DiagnosticsView } from "./diagnostics/diagnosticsView";
import sortedTableStyles from "src/sortedtable/sortedtable.module.scss";
import summaryCardStyles from "src/summaryCard/summaryCard.module.scss";
@@ -68,6 +65,13 @@ import {
ActivateDiagnosticsModalRef,
ActivateStatementDiagnosticsModal,
} from "../statementsDiagnostics";
+import {
+ generateExecCountTimeseries,
+ generateExecRetriesTimeseries,
+ generateExecuteAndPlanningTimeseries,
+ generateRowsProcessedTimeseries,
+ generateContentionTimeseries,
+} from "./timeseriesUtils";
type IDuration = google.protobuf.IDuration;
type StatementDetailsResponse = cockroach.server.serverpb.StatementDetailsResponse;
type IStatementDiagnosticsReport = cockroach.server.serverpb.IStatementDiagnosticsReport;
@@ -332,15 +336,6 @@ export class StatementDetails extends React.Component<
hasDiagnosticReports = (): boolean =>
this.props.diagnosticsReports.length > 0;
- changeSortSetting = (ss: SortSetting): void => {
- this.setState({
- sortSetting: ss,
- });
- if (this.props.onSortingChange) {
- this.props.onSortingChange("Stats By Node", ss.columnTitle, ss.ascending);
- }
- };
-
refreshStatementDetails = (
timeScale: TimeScale,
statementFingerprintID: string,
@@ -470,13 +465,13 @@ export class StatementDetails extends React.Component<
Statements
- Statement Details
+ Statement Fingerprint
@@ -582,22 +577,15 @@ export class StatementDetails extends React.Component<
const {
app_names,
databases,
- dist_sql_count,
failed_count,
full_scan_count,
vec_count,
total_count,
implicit_txn,
} = this.props.statementDetails.statement.metadata;
-
- const { statement } = this.props.statementDetails;
-
- const totalCountBarChart = longToInt(statement.stats.count);
- const firstAttemptsBarChart = longToInt(
- statement.stats.first_attempt_count,
- );
- const retriesBarChart = totalCountBarChart - firstAttemptsBarChart;
- const maxRetriesBarChart = longToInt(statement.stats.max_retries);
+ const {
+ statement_statistics_per_aggregated_ts,
+ } = this.props.statementDetails;
const nodes: string[] = unique(
(stats.nodes || []).map(node => node.toString()),
@@ -606,7 +594,6 @@ export class StatementDetails extends React.Component<
(stats.nodes || []).map(node => nodeRegions[node.toString()]),
).sort();
- const duration = (v: number) => Duration(v * 1e9);
const lastExec =
stats.last_exec_timestamp &&
moment(stats.last_exec_timestamp.seconds.low * 1e3).format(
@@ -614,25 +601,76 @@ export class StatementDetails extends React.Component<
);
const statementSampled = stats.exec_stats.count > Long.fromNumber(0);
const unavailableTooltip = !statementSampled && (
-
- This metric is part of the statement execution and therefore will
- not be available until the statement is sampled via tracing.
-
- }
- >
- unavailable
-
+
+ This metric is part of the statement execution and therefore will not be
+ available until the statement is sampled.
+
);
+ const noSamples = statementSampled ? "" : " (no samples)";
const db = databases ? (
{databases}
) : (
(unset)
);
+
+ const statsPerAggregatedTs = statement_statistics_per_aggregated_ts.sort(
+ (a, b) =>
+ a.aggregated_ts.seconds < b.aggregated_ts.seconds
+ ? -1
+ : a.aggregated_ts.seconds > b.aggregated_ts.seconds
+ ? 1
+ : 0,
+ );
+
+ const executionAndPlanningTimeseries: AlignedData = generateExecuteAndPlanningTimeseries(
+ statsPerAggregatedTs,
+ );
+ const executionAndPlanningOps: Partial = {
+ axes: [{}, { label: "Time Spent" }],
+ series: [{}, { label: "Execution" }, { label: "Planning" }],
+ width: 735,
+ };
+
+ const rowsProcessedTimeseries: AlignedData = generateRowsProcessedTimeseries(
+ statsPerAggregatedTs,
+ );
+ const rowsProcessedOps: Partial = {
+ axes: [{}, { label: "Rows" }],
+ series: [{}, { label: "Rows Read" }, { label: "Rows Written" }],
+ width: 735,
+ };
+
+ const execRetriesTimeseries: AlignedData = generateExecRetriesTimeseries(
+ statsPerAggregatedTs,
+ );
+ const execRetriesOps: Partial = {
+ axes: [{}, { label: "Retries" }],
+ series: [{}, { label: "Retries" }],
+ legend: { show: false },
+ width: 735,
+ };
+
+ const execCountTimeseries: AlignedData = generateExecCountTimeseries(
+ statsPerAggregatedTs,
+ );
+ const execCountOps: Partial = {
+ axes: [{}, { label: "Execution Counts" }],
+ series: [{}, { label: "Execution Counts" }],
+ legend: { show: false },
+ width: 735,
+ };
+
+ const contentionTimeseries: AlignedData = generateContentionTimeseries(
+ statsPerAggregatedTs,
+ );
+ const contentionOps: Partial = {
+ axes: [{}, { label: "Contention" }],
+ series: [{}, { label: "Contention" }],
+ legend: { show: false },
+ width: 735,
+ };
+
return (
<>
@@ -655,110 +693,6 @@ export class StatementDetails extends React.Component<
-
-
-
- Mean statement time
-
- {formatNumberForDisplay(
- stats.service_lat.mean,
- duration,
- )}
-
-
-
- Planning time
-
- {formatNumberForDisplay(stats.plan_lat.mean, duration)}
-
-
-
-
- Execution time
-
- {formatNumberForDisplay(stats.run_lat.mean, duration)}
-
-
-
-
-
-
-
-
-
-
- Resource usage
-
-
- Mean rows/bytes read
- {statementSampled && (
-
- {formatNumberForDisplay(
- stats.rows_read.mean,
- formatTwoPlaces,
- )}
- {" / "}
- {formatNumberForDisplay(stats.bytes_read.mean, Bytes)}
-
- )}
- {unavailableTooltip}
-
-
- Mean rows written
-
- {formatNumberForDisplay(
- stats.rows_written?.mean,
- formatTwoPlaces,
- )}
-
-
-
- Max memory usage
- {statementSampled && (
-
- {formatNumberForDisplay(
- stats.exec_stats.max_mem_usage.mean,
- Bytes,
- )}
-
- )}
- {unavailableTooltip}
-
-
- Network usage
- {statementSampled && (
-
- {formatNumberForDisplay(
- stats.exec_stats.network_bytes.mean,
- Bytes,
- )}
-
- )}
- {unavailableTooltip}
-
-
- Max scratch disk usage
- {statementSampled && (
-
- {formatNumberForDisplay(
- stats.exec_stats.max_disk_usage.mean,
- Bytes,
- )}
-
- )}
- {unavailableTooltip}
-
-
-
-
-
-
-
- Statement details
{!isTenant && (
@@ -776,16 +710,10 @@ export class StatementDetails extends React.Component<
)}
-
Database
{db}
-
App
@@ -795,16 +723,16 @@ export class StatementDetails extends React.Component<
)}
+
+
+
+
Failed?
{RenderCount(failed_count, total_count)}
- Distributed execution?
- {RenderCount(dist_sql_count, total_count)}
-
-
- Full Scan?
+ Full scan?
{RenderCount(full_scan_count, total_count)}
@@ -819,50 +747,57 @@ export class StatementDetails extends React.Component<
Last execution time
{lastExec}
-
- Execution counts
-
- First attempts
- {firstAttemptsBarChart}
-
-
- Total executions
- {totalCountBarChart}
-
-
- Retries
- 0,
- },
- )}
- >
- {retriesBarChart}
-
-
-
- Max retries
- 0,
- },
- )}
- >
- {maxRetriesBarChart}
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
>
);
diff --git a/pkg/ui/workspaces/cluster-ui/src/statementDetails/timeseriesUtils.ts b/pkg/ui/workspaces/cluster-ui/src/statementDetails/timeseriesUtils.ts
new file mode 100644
index 000000000000..313b89db7595
--- /dev/null
+++ b/pkg/ui/workspaces/cluster-ui/src/statementDetails/timeseriesUtils.ts
@@ -0,0 +1,92 @@
+// Copyright 2022 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 { cockroach } from "@cockroachlabs/crdb-protobuf-client";
+import { AlignedData } from "uplot";
+import { longToInt, TimestampToNumber } from "../util";
+
+type statementStatisticsPerAggregatedTs = cockroach.server.serverpb.StatementDetailsResponse.ICollectedStatementGroupedByAggregatedTs;
+
+export function generateExecuteAndPlanningTimeseries(
+ stats: statementStatisticsPerAggregatedTs[],
+): AlignedData {
+ const ts: Array = [];
+ const execution: Array = [];
+ const planning: Array = [];
+
+ stats.forEach(function(stat: statementStatisticsPerAggregatedTs) {
+ ts.push(TimestampToNumber(stat.aggregated_ts) * 1e3);
+ execution.push(stat.stats.run_lat.mean * 1e9);
+ planning.push(stat.stats.plan_lat.mean * 1e9);
+ });
+
+ return [ts, execution, planning];
+}
+
+export function generateRowsProcessedTimeseries(
+ stats: statementStatisticsPerAggregatedTs[],
+): AlignedData {
+ const ts: Array = [];
+ const read: Array = [];
+ const written: Array = [];
+
+ stats.forEach(function(stat: statementStatisticsPerAggregatedTs) {
+ ts.push(TimestampToNumber(stat.aggregated_ts) * 1e3);
+ read.push(stat.stats.rows_read?.mean);
+ written.push(stat.stats.rows_written?.mean);
+ });
+
+ return [ts, read, written];
+}
+
+export function generateExecRetriesTimeseries(
+ stats: statementStatisticsPerAggregatedTs[],
+): AlignedData {
+ const ts: Array = [];
+ const retries: Array = [];
+
+ stats.forEach(function(stat: statementStatisticsPerAggregatedTs) {
+ ts.push(TimestampToNumber(stat.aggregated_ts) * 1e3);
+
+ const totalCountBarChart = longToInt(stat.stats.count);
+ const firstAttemptsBarChart = longToInt(stat.stats.first_attempt_count);
+ retries.push(totalCountBarChart - firstAttemptsBarChart);
+ });
+
+ return [ts, retries];
+}
+
+export function generateExecCountTimeseries(
+ stats: statementStatisticsPerAggregatedTs[],
+): AlignedData {
+ const ts: Array = [];
+ const count: Array = [];
+
+ stats.forEach(function(stat: statementStatisticsPerAggregatedTs) {
+ ts.push(TimestampToNumber(stat.aggregated_ts) * 1e3);
+ count.push(longToInt(stat.stats.count));
+ });
+
+ return [ts, count];
+}
+
+export function generateContentionTimeseries(
+ stats: statementStatisticsPerAggregatedTs[],
+): AlignedData {
+ const ts: Array = [];
+ const count: Array = [];
+
+ stats.forEach(function(stat: statementStatisticsPerAggregatedTs) {
+ ts.push(TimestampToNumber(stat.aggregated_ts) * 1e3);
+ count.push(stat.stats.exec_stats.contention_time.mean * 1e9);
+ });
+
+ return [ts, count];
+}
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 c0697df4a4f2..de6dc09a9fb7 100644
--- a/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.module.scss
+++ b/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.module.scss
@@ -49,7 +49,7 @@
.table-area {
position: relative;
overflow-x: scroll;
- min-height: 480px;
+ min-height: 590px;
}
.reset-btn-area {
diff --git a/pkg/ui/workspaces/cluster-ui/src/test-utils/matchMedia.mock.js b/pkg/ui/workspaces/cluster-ui/src/test-utils/matchMedia.mock.js
new file mode 100644
index 000000000000..6889b882f76a
--- /dev/null
+++ b/pkg/ui/workspaces/cluster-ui/src/test-utils/matchMedia.mock.js
@@ -0,0 +1,29 @@
+// Copyright 2022 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.
+
+// Jest uses jsdom to simulate a browser, which doesn’t include window.matchMedia.
+// This property was created and added to `setupFilesAfterEnv` on jest.config.js to
+// handle test cases where window.matchMedia and its functions (e.g. addEventListener)
+// are used.
+Object.defineProperty(window, "matchMedia", {
+ writable: true,
+ value: jest.fn().mockImplementation(query => ({
+ matches: false,
+ media: query,
+ onchange: null,
+ addListener: jest.fn(), // deprecated
+ removeListener: jest.fn(), // deprecated
+ addEventListener: jest.fn(),
+ removeEventListener: jest.fn(),
+ dispatchEvent: jest.fn(),
+ })),
+});
+
+export {};
diff --git a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/utils.spec.tsx b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/utils.spec.tsx
index 160570c49def..d4d860ecc1e9 100644
--- a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/utils.spec.tsx
+++ b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/utils.spec.tsx
@@ -28,7 +28,7 @@ describe("timescale utils", (): void => {
};
const [start, end] = toRoundedDateRange(ts);
assert.equal(start.format("YYYY.MM.DD HH:mm:ss"), "2022.01.05 13:00:00");
- assert.equal(end.format("YYYY.MM.DD HH:mm:ss"), "2022.01.10 14:00:00");
+ assert.equal(end.format("YYYY.MM.DD HH:mm:ss"), "2022.01.10 13:59:59");
});
it("already rounded values", () => {
@@ -40,7 +40,7 @@ describe("timescale utils", (): void => {
};
const [start, end] = toRoundedDateRange(ts);
assert.equal(start.format("YYYY.MM.DD HH:mm:ss"), "2022.01.05 13:00:00");
- assert.equal(end.format("YYYY.MM.DD HH:mm:ss"), "2022.01.10 14:00:00");
+ assert.equal(end.format("YYYY.MM.DD HH:mm:ss"), "2022.01.10 13:59:59");
});
});
diff --git a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/utils.ts b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/utils.ts
index fab8b50db684..eea81a368279 100644
--- a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/utils.ts
+++ b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/utils.ts
@@ -88,10 +88,10 @@ export const toDateRange = (ts: TimeScale): [moment.Moment, moment.Moment] => {
};
// toRoundedDateRange round the TimeScale selected, with the start
-// rounded down and end rounded up one hour.
+// rounded down and end rounded up to the limit before the next hour.
// e.g.
// start: 17:45:23 -> 17:00:00
-// end: 20:14:32 -> 21:00:00
+// end: 20:14:32 -> 20:59:59
export const toRoundedDateRange = (
ts: TimeScale,
): [moment.Moment, moment.Moment] => {
@@ -99,7 +99,8 @@ export const toRoundedDateRange = (
const startRounded = start.set({ minute: 0, second: 0, millisecond: 0 });
const endRounded = end
.set({ minute: 0, second: 0, millisecond: 0 })
- .add(1, "hours");
+ .add(59, "minutes")
+ .add(59, "seconds");
return [startRounded, endRounded];
};
diff --git a/pkg/ui/workspaces/db-console/styl/base/layout-vars.styl b/pkg/ui/workspaces/db-console/styl/base/layout-vars.styl
index 03012aeaa1eb..1149c7a58aac 100644
--- a/pkg/ui/workspaces/db-console/styl/base/layout-vars.styl
+++ b/pkg/ui/workspaces/db-console/styl/base/layout-vars.styl
@@ -20,4 +20,4 @@ $max-window-width = 1350px
$z-index-tooltip = 4
$z-index-banner = 3
$z-index-navigation = 2
-$z-index-page-config = 1
+$z-index-page-config = 2
diff --git a/pkg/ui/yarn-vendor b/pkg/ui/yarn-vendor
index e647b312256b..8d6039256dff 160000
--- a/pkg/ui/yarn-vendor
+++ b/pkg/ui/yarn-vendor
@@ -1 +1 @@
-Subproject commit e647b312256b749d2ed9dcdbc12acf7b04d32523
+Subproject commit 8d6039256dff7d456d81d59e10edf34961b0515f
diff --git a/pkg/ui/yarn.lock b/pkg/ui/yarn.lock
index 32a209bb37f0..bfdab91d60f4 100644
--- a/pkg/ui/yarn.lock
+++ b/pkg/ui/yarn.lock
@@ -1586,7 +1586,7 @@
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@cockroachlabs/cluster-ui@link:workspaces/cluster-ui":
- version "22.1.0-prerelease-3"
+ version "22.1.3"
dependencies:
"@babel/runtime" "^7.12.13"
@@ -5955,7 +5955,7 @@ color-name@1.1.3:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
-color-name@~1.1.4:
+color-name@^1.1.4, color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
@@ -6505,6 +6505,11 @@ cssesc@^3.0.0:
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
+cssfontparser@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/cssfontparser/-/cssfontparser-1.2.1.tgz#f4022fc8f9700c68029d542084afbaf425a3f3e3"
+ integrity sha1-9AIvyPlwDGgCnVQghK+69CWj8+M=
+
csso@1.3.x:
version "1.3.12"
resolved "https://registry.yarnpkg.com/csso/-/csso-1.3.12.tgz#fc628694a2d38938aaac4996753218fd311cdb9e"
@@ -10513,6 +10518,14 @@ iterm2-version@^4.1.0:
app-path "^3.2.0"
plist "^3.0.1"
+jest-canvas-mock@^2.4.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/jest-canvas-mock/-/jest-canvas-mock-2.4.0.tgz#947b71442d7719f8e055decaecdb334809465341"
+ integrity sha512-mmMpZzpmLzn5vepIaHk5HoH3Ka4WykbSoLuG/EKoJd0x0ID/t+INo1l8ByfcUJuDM+RIsL4QDg/gDnBbrj2/IQ==
+ dependencies:
+ cssfontparser "^1.2.1"
+ moo-color "^1.0.2"
+
jest-changed-files@^27.5.1:
version "27.5.1"
resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.5.1.tgz#a348aed00ec9bf671cc58a66fcbe7c3dfd6a68f5"
@@ -12387,6 +12400,13 @@ moment@2.x, moment@^2.19.3, moment@^2.24.0, moment@^2.27.0:
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
+moo-color@^1.0.2:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/moo-color/-/moo-color-1.0.3.tgz#d56435f8359c8284d83ac58016df7427febece74"
+ integrity sha512-i/+ZKXMDf6aqYtBhuOcej71YSlbjT3wCO/4H1j8rPvxDJEifdwgg5MaFyu6iYAT8GBZJg2z0dkgK4YMzvURALQ==
+ dependencies:
+ color-name "^1.1.4"
+
moo@^0.5.0:
version "0.5.1"
resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.1.tgz#7aae7f384b9b09f620b6abf6f74ebbcd1b65dbc4"