Skip to content

Commit

Permalink
Merge pull request #101138 from maryliag/backport22.2-98988
Browse files Browse the repository at this point in the history
release-22.2: ui: add badges for filter elements
  • Loading branch information
maryliag authored Apr 10, 2023
2 parents 0f19dc5 + bea856e commit b70c6c5
Show file tree
Hide file tree
Showing 11 changed files with 228 additions and 49 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 @@ -218,6 +219,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 @@ -234,7 +241,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 @@ -22,6 +22,7 @@ import {
calculateActiveFilters,
Filter,
getFullFiltersAsStringRecord,
SelectedFilters,
} from "src/queryFilter/filter";
import { getWorkloadInsightEventFiltersFromURL } from "src/queryFilter/utils";
import { Pagination } from "src/pagination";
Expand Down Expand Up @@ -244,6 +245,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={statements === null}
Expand All @@ -265,7 +272,6 @@ export const StatementInsightsView: React.FC<StatementInsightsViewProps> = (
totalCount={statementInsights?.length}
arrayItemName="statement insights"
activeFilters={countActiveFilters}
onClearFilters={clearFilters}
/>
</div>
<StatementInsightsTable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
calculateActiveFilters,
Filter,
getFullFiltersAsStringRecord,
SelectedFilters,
} from "src/queryFilter/filter";
import { getWorkloadInsightEventFiltersFromURL } from "src/queryFilter/utils";
import { Pagination } from "src/pagination";
Expand Down Expand Up @@ -216,6 +217,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={transactions === null}
Expand All @@ -232,7 +239,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;
}
154 changes: 135 additions & 19 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 @@ -238,7 +240,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 @@ -252,20 +254,22 @@ export const inactiveFiltersState: Required<Omit<Filters, "timeUnit">> = {
database: "",
regions: "",
nodes: "",
schemaInsightType: "",
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 @@ -419,7 +423,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 @@ -442,7 +446,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 @@ -465,7 +469,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 @@ -491,7 +495,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 @@ -517,7 +523,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 @@ -540,7 +548,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 @@ -563,7 +571,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 @@ -604,7 +612,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 @@ -689,3 +697,111 @@ 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;
}
}
10 changes: 10 additions & 0 deletions pkg/ui/workspaces/cluster-ui/src/queryFilter/filterClasses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,13 @@ export const applyBtn = {
wrapper: cx("apply-btn__wrapper"),
btn: cx("apply-btn__btn"),
};

export const clearBnt = {
btn: cx("clear-btn"),
};

export const badge = {
area: cx("badges-area"),
wrapper: cx("badge-wrapper"),
closeArea: cx("close-area"),
};
Loading

0 comments on commit b70c6c5

Please sign in to comment.