Table: Preserve filtered value state (#83631)

* Hoist state

* codeincarnate/table-filter-state/ lint

---------

Co-authored-by: jev forsberg <jev.forsberg@grafana.com>
Co-authored-by: nmarrs <nathanielmarrs@gmail.com>
This commit is contained in:
Kyle Cunningham 2024-03-01 11:19:21 -06:00 committed by GitHub
parent 96127dce62
commit 33cb4f9bf4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 50 additions and 8 deletions

View File

@ -1,12 +1,13 @@
import { css, cx } from '@emotion/css';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { Field, GrafanaTheme2 } from '@grafana/data';
import { Field, GrafanaTheme2, SelectableValue } from '@grafana/data';
import { Popover } from '..';
import { useStyles2 } from '../../themes';
import { Icon } from '../Icon/Icon';
import { REGEX_OPERATOR } from './FilterList';
import { FilterPopup } from './FilterPopup';
import { TableStyles } from './styles';
@ -23,6 +24,8 @@ export const Filter = ({ column, field, tableStyles }: Props) => {
const filterEnabled = useMemo(() => Boolean(column.filterValue), [column.filterValue]);
const onShowPopover = useCallback(() => setPopoverVisible(true), [setPopoverVisible]);
const onClosePopover = useCallback(() => setPopoverVisible(false), [setPopoverVisible]);
const [searchFilter, setSearchFilter] = useState('');
const [operator, setOperator] = useState<SelectableValue<string>>(REGEX_OPERATOR);
if (!field || !field.config.custom?.filterable) {
return null;
@ -37,7 +40,18 @@ export const Filter = ({ column, field, tableStyles }: Props) => {
<Icon name="filter" />
{isPopoverVisible && ref.current && (
<Popover
content={<FilterPopup column={column} tableStyles={tableStyles} field={field} onClose={onClosePopover} />}
content={
<FilterPopup
column={column}
tableStyles={tableStyles}
field={field}
onClose={onClosePopover}
searchFilter={searchFilter}
setSearchFilter={setSearchFilter}
operator={operator}
setOperator={setOperator}
/>
}
placement="bottom-start"
referenceElement={ref.current}
show

View File

@ -1,5 +1,5 @@
import { css, cx } from '@emotion/css';
import React, { useCallback, useMemo, useState } from 'react';
import React, { useCallback, useMemo } from 'react';
import { FixedSizeList as List } from 'react-window';
import { GrafanaTheme2, formattedValueToString, getValueFormat, SelectableValue } from '@grafana/data';
@ -13,6 +13,10 @@ interface Props {
onChange: (options: SelectableValue[]) => void;
caseSensitive?: boolean;
showOperators?: boolean;
searchFilter: string;
setSearchFilter: (value: string) => void;
operator: SelectableValue<string>;
setOperator: (item: SelectableValue<string>) => void;
}
const ITEM_HEIGHT = 28;
@ -33,7 +37,7 @@ const operatorSelectableValues: { [key: string]: SelectableValue<string> } = {
},
};
const OPERATORS = Object.values(operatorSelectableValues);
const REGEX_OPERATOR = operatorSelectableValues['Contains'];
export const REGEX_OPERATOR = operatorSelectableValues['Contains'];
const XPR_OPERATOR = operatorSelectableValues['Expression'];
const comparableValue = (value: string): string | number | Date | boolean => {
@ -61,9 +65,17 @@ const comparableValue = (value: string): string | number | Date | boolean => {
return value;
};
export const FilterList = ({ options, values, caseSensitive, showOperators, onChange }: Props) => {
const [operator, setOperator] = useState<SelectableValue<string>>(REGEX_OPERATOR);
const [searchFilter, setSearchFilter] = useState('');
export const FilterList = ({
options,
values,
caseSensitive,
showOperators,
onChange,
searchFilter,
setSearchFilter,
operator,
setOperator,
}: Props) => {
const regex = useMemo(() => new RegExp(searchFilter, caseSensitive ? undefined : 'i'), [searchFilter, caseSensitive]);
const items = useMemo(
() =>

View File

@ -15,9 +15,21 @@ interface Props {
tableStyles: TableStyles;
onClose: () => void;
field?: Field;
searchFilter: string;
setSearchFilter: (value: string) => void;
operator: SelectableValue<string>;
setOperator: (item: SelectableValue<string>) => void;
}
export const FilterPopup = ({ column: { preFilteredRows, filterValue, setFilter }, onClose, field }: Props) => {
export const FilterPopup = ({
column: { preFilteredRows, filterValue, setFilter },
onClose,
field,
searchFilter,
setSearchFilter,
operator,
setOperator,
}: Props) => {
const theme = useTheme2();
const uniqueValues = useMemo(() => calculateUniqueFieldValues(preFilteredRows, field), [preFilteredRows, field]);
const options = useMemo(() => valuesToOptions(uniqueValues), [uniqueValues]);
@ -73,6 +85,10 @@ export const FilterPopup = ({ column: { preFilteredRows, filterValue, setFilter
options={options}
caseSensitive={matchCase}
showOperators={true}
searchFilter={searchFilter}
setSearchFilter={setSearchFilter}
operator={operator}
setOperator={setOperator}
/>
</VerticalGroup>
<HorizontalGroup spacing="lg">