Skip to content

Commit

Permalink
ui: add badges for filter elements
Browse files Browse the repository at this point in the history
Adds badges for each of the selected filters on SQL Activity and
Insights pages.
Part Of #98891

Release note (ui change): Adds badges for each selected
filter on SQL Activity and Insights pages.
  • Loading branch information
maryliag committed Mar 21, 2023
1 parent 87e227e commit 81a866d
Show file tree
Hide file tree
Showing 11 changed files with 231 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
defaultFilters,
Filter,
getFullFiltersAsStringRecord,
SelectedFilters,
} from "../../queryFilter";
import { queryByName, syncHistory } from "../../util";
import { getTableSortFromURL } from "../../sortedtable/getTableSortFromURL";
Expand Down Expand Up @@ -220,6 +221,12 @@ export const SchemaInsightsView: React.FC<SchemaInsightsViewProps> = ({
/>
</PageConfigItem>
</PageConfig>
<SelectedFilters
filters={filters}
onRemoveFilter={onSubmitFilters}
onClearFilters={clearFilters}
className={cx("margin-adjusted")}
/>
<div className={cx("table-area")}>
<Loading
loading={schemaInsights === null}
Expand All @@ -236,7 +243,6 @@ export const SchemaInsightsView: React.FC<SchemaInsightsViewProps> = ({
totalCount={filteredSchemaInsights?.length}
arrayItemName="schema insights"
activeFilters={countActiveFilters}
onClearFilters={clearFilters}
/>
</div>
<InsightsSortedTable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
defaultFilters,
Filter,
getFullFiltersAsStringRecord,
SelectedFilters,
} from "src/queryFilter/filter";
import { getWorkloadInsightEventFiltersFromURL } from "src/queryFilter/utils";
import { Pagination } from "src/pagination";
Expand Down Expand Up @@ -279,6 +280,12 @@ export const StatementInsightsView: React.FC<StatementInsightsViewProps> = ({
/>
</PageConfigItem>
</PageConfig>
<SelectedFilters
filters={filters}
onRemoveFilter={onSubmitFilters}
onClearFilters={clearFilters}
className={cx("margin-adjusted")}
/>
<div className={cx("table-area")}>
<Loading
loading={isLoading}
Expand All @@ -300,7 +307,6 @@ export const StatementInsightsView: React.FC<StatementInsightsViewProps> = ({
totalCount={filteredStatements?.length}
arrayItemName="statement insights"
activeFilters={countActiveFilters}
onClearFilters={clearFilters}
/>
</div>
<StatementInsightsTable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
defaultFilters,
Filter,
getFullFiltersAsStringRecord,
SelectedFilters,
} from "src/queryFilter/filter";
import { getWorkloadInsightEventFiltersFromURL } from "src/queryFilter/utils";
import { Pagination } from "src/pagination";
Expand Down Expand Up @@ -257,6 +258,12 @@ export const TransactionInsightsView: React.FC<TransactionInsightsViewProps> = (
/>
</PageConfigItem>
</PageConfig>
<SelectedFilters
filters={filters}
onRemoveFilter={onSubmitFilters}
onClearFilters={clearFilters}
className={cx("margin-adjusted")}
/>
<div className={cx("table-area")}>
<Loading
loading={isLoading}
Expand All @@ -273,7 +280,6 @@ export const TransactionInsightsView: React.FC<TransactionInsightsViewProps> = (
totalCount={filteredTransactions?.length}
arrayItemName="transaction insights"
activeFilters={countActiveFilters}
onClearFilters={clearFilters}
/>
</div>
<TransactionInsightsTable
Expand Down
29 changes: 29 additions & 0 deletions pkg/ui/workspaces/cluster-ui/src/queryFilter/filter.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,32 @@ $dropdown-hover-color: darken($colors--background, 2.5%);
.hide {
display: none;
}

.badges-area {
display: flex;
margin-bottom: 5px;
}

.badge-wrapper {
background-color: $colors--neutral-2;
border-radius: 3px;
color: $colors--neutral-7;
display: flex;
font-family: $font-family--semi-bold;
font-size: $font-size--small;
line-height: $line-height--small;
margin: 5px 5px 5px 0;
padding: 3px 7px;
width: fit-content;
}

.close-area {
margin-top: 4px;
margin-left: 5px;
width: 12px;
cursor: pointer;
}

.clear-btn {
margin-top: 3px;
}
160 changes: 138 additions & 22 deletions pkg/ui/workspaces/cluster-ui/src/queryFilter/filter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import React from "react";
import Select from "react-select";
import { Button } from "../button";
import { CaretDown } from "@cockroachlabs/icons";
import { CaretDown, Cancel } from "@cockroachlabs/icons";
import { Input } from "antd";
import "antd/lib/input/style";
import { History } from "history";
Expand All @@ -26,6 +26,8 @@ import {
hidden,
caretDown,
checkbox,
badge,
clearBnt,
} from "./filterClasses";
import { MultiSelectCheckbox } from "../multiSelectCheckbox/multiSelectCheckbox";
import { syncHistory } from "../util";
Expand Down Expand Up @@ -247,7 +249,7 @@ export const updateFiltersQueryParamsOnTab = (
};

/**
* The State of the filter that is consider inactive.
* The State of the filter that is considered inactive.
* It's different from defaultFilters because we don't want to take
* timeUnit into consideration.
* For example, if the timeUnit changes, but the timeValue is still 0,
Expand All @@ -265,20 +267,21 @@ export const inactiveFiltersState: Required<Omit<Filters, "timeUnit">> = {
workloadInsightType: "",
schemaInsightType: "",
executionStatus: "",
username: "",
};

export const calculateActiveFilters = (filters: Filters): number => {
return Object.keys(inactiveFiltersState).reduce(
(active, filter: keyof Filters) => {
return filters[filter] != null &&
inactiveFiltersState[filter] !== filters[filter]
? (active += 1)
: active;
},
0,
const getActiveFilters = (filters: Filters): string[] => {
return Object.keys(inactiveFiltersState).filter(
filter =>
filters[filter] != null &&
inactiveFiltersState[filter] !== filters[filter],
);
};

export const calculateActiveFilters = (filters: Filters): number => {
return getActiveFilters(filters).length;
};

export const getTimeValueInSeconds = (filters: Filters): number | "empty" => {
if (filters.timeNumber === "0") return "empty";
switch (filters.timeUnit) {
Expand Down Expand Up @@ -437,7 +440,7 @@ export class Filter extends React.Component<QueryFilter, FilterState> {
});
const appFilter = (
<div>
<div className={filterLabel.margin}>Application Name</div>
<div className={filterLabel.margin}>{getLabelFromKey("app")}</div>
<MultiSelectCheckbox
options={appsOptions}
placeholder="All"
Expand All @@ -460,7 +463,7 @@ export class Filter extends React.Component<QueryFilter, FilterState> {
});
const dbFilter = (
<div>
<div className={filterLabel.margin}>Database</div>
<div className={filterLabel.margin}>{getLabelFromKey("database")}</div>
<MultiSelectCheckbox
options={databasesOptions}
placeholder="All"
Expand All @@ -483,7 +486,7 @@ export class Filter extends React.Component<QueryFilter, FilterState> {
});
const usernameFilter = (
<div>
<div className={filterLabel.margin}>User Name</div>
<div className={filterLabel.margin}>{getLabelFromKey("username")}</div>
<MultiSelectCheckbox
options={usernameOptions}
placeholder="All"
Expand All @@ -509,7 +512,9 @@ export class Filter extends React.Component<QueryFilter, FilterState> {
});
const sessionStatusFilter = (
<div>
<div className={filterLabel.margin}>Session Status</div>
<div className={filterLabel.margin}>
{getLabelFromKey("sessionStatus")}
</div>
<MultiSelectCheckbox
options={sessionStatusOptions}
placeholder="All"
Expand All @@ -535,7 +540,9 @@ export class Filter extends React.Component<QueryFilter, FilterState> {
);
const executionStatusFilter = (
<div>
<div className={filterLabel.margin}>Execution Status</div>
<div className={filterLabel.margin}>
{getLabelFromKey("executionStatus")}
</div>
<MultiSelectCheckbox
options={executionStatusOptions}
placeholder="All"
Expand All @@ -561,7 +568,9 @@ export class Filter extends React.Component<QueryFilter, FilterState> {
});
const schemaInsightTypeFilter = (
<div>
<div className={filterLabel.margin}>Schema Insight Type</div>
<div className={filterLabel.margin}>
{getLabelFromKey("schemaInsightType")}
</div>
<MultiSelectCheckbox
options={schemaInsightTypeOptions}
placeholder="All"
Expand All @@ -584,12 +593,14 @@ export class Filter extends React.Component<QueryFilter, FilterState> {
: [];
const workloadInsightTypeValue = workloadInsightTypeOptions.filter(
option => {
return filters.workloadInsightType.split(",").includes(option.label);
return filters.workloadInsightType?.split(",").includes(option.label);
},
);
const workloadInsightTypeFilter = (
<div>
<div className={filterLabel.margin}>Workload Insight Type</div>
<div className={filterLabel.margin}>
{getLabelFromKey("workloadInsightType")}
</div>
<MultiSelectCheckbox
options={workloadInsightTypeOptions}
placeholder="All"
Expand All @@ -612,7 +623,7 @@ export class Filter extends React.Component<QueryFilter, FilterState> {
);
const regionsFilter = (
<div>
<div className={filterLabel.margin}>Region</div>
<div className={filterLabel.margin}>{getLabelFromKey("regions")}</div>
<MultiSelectCheckbox
options={regionsOptions}
placeholder="All"
Expand All @@ -635,7 +646,7 @@ export class Filter extends React.Component<QueryFilter, FilterState> {
});
const nodesFilter = (
<div>
<div className={filterLabel.margin}>Node</div>
<div className={filterLabel.margin}>{getLabelFromKey("nodes")}</div>
<MultiSelectCheckbox
options={nodesOptions}
placeholder="All"
Expand Down Expand Up @@ -676,7 +687,7 @@ export class Filter extends React.Component<QueryFilter, FilterState> {
});
const sqlTypeFilter = (
<div>
<div className={filterLabel.margin}>Statement Type</div>
<div className={filterLabel.margin}>{getLabelFromKey("sqlType")}</div>
<MultiSelectCheckbox
options={sqlTypes}
placeholder="All"
Expand Down Expand Up @@ -765,3 +776,108 @@ export class Filter extends React.Component<QueryFilter, FilterState> {
);
}
}

interface SelectedFilterProps {
filters: Filters;
onRemoveFilter: (filters: Filters) => void;
onClearFilters: () => void;
className?: string;
}
export function SelectedFilters(
props: SelectedFilterProps,
): React.ReactElement {
const { filters, onRemoveFilter, onClearFilters, className } = props;
const activeFilters = getActiveFilters(filters);
const badges = activeFilters.map(filter => {
return (
<FilterBadge
filters={filters}
name={filter}
values={filters[filter]}
unit={filters["timeUnit"]}
key={filter}
onRemoveFilter={onRemoveFilter}
/>
);
});

return (
<div className={`${badge.area} ${className}`}>
{badges}
{activeFilters.length > 0 && (
<Button
onClick={() => onClearFilters()}
type="flat"
size="small"
className={clearBnt.btn}
>
Clear filters
</Button>
)}
</div>
);
}

function removeFilter(
filters: Filters,
filter: string,
onRemoveFilter: (filters: Filters) => void,
): void {
filters[filter] = inactiveFiltersState[filter];
onRemoveFilter({ ...filters });
}
interface FilterBadgeProps {
filters: Filters;
name: string;
values: string | boolean;
unit: string;
onRemoveFilter: (filters: Filters) => void;
}
function FilterBadge(props: FilterBadgeProps): React.ReactElement {
const { filters, name, values, onRemoveFilter } = props;
const unit = name === "timeNumber" ? props.unit : "";
let value = `${getLabelFromKey(name)}: ${values.toString()} ${unit}`;
if (value.length > 100) {
value = value.substring(0, 100) + "...";
}
return (
<div className={badge.wrapper}>
{value}
<Cancel
className={badge.closeArea}
onClick={() => removeFilter(filters, name, onRemoveFilter)}
/>
</div>
);
}

function getLabelFromKey(key: string): string {
switch (key) {
case "app":
return "Application Name";
case "database":
return "Database";
case "executionStatus":
return "Execution Status";
case "fullScan":
return "Full Scan";
case "nodes":
return "Node";
case "regions":
return "Region";
case "schemaInsightType":
return "Schema Insight Type";
case "sessionStatus":
return "Session Status";
case "sqlType":
return "Statement Type";
case "timeNumber":
return "Runs Longer Than";
case "username":
return "User Name";
case "workloadInsightType":
return "Workload Insight Type";
default:
return key;
}
}
Loading

0 comments on commit 81a866d

Please sign in to comment.