Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JNG-6075 improve card filters #502

Merged
merged 1 commit into from
Jan 2, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,29 +1,38 @@
{{> fragment.header.hbs }}

import { type FC, useState, useCallback } from 'react';
import Box from '@mui/material/Box';
import Grid from '@mui/material/Grid';
import Typography from '@mui/material/Typography';
import Button from '@mui/material/Button';
import FormGroup from '@mui/material/FormGroup';
import FormControlLabel from '@mui/material/FormControlLabel';
import Checkbox from '@mui/material/Checkbox';
import TextField from '@mui/material/TextField';
import { debounce } from '@mui/material/utils';
import { FilterType } from '~/components-api';
import { useL10N } from '~/l10n/l10n-context';
import { useTranslation } from 'react-i18next';
import { debounceInputs } from '~/config';

export interface CardsFilterDefinition<T> {
type: FilterType;
operator?: any;
field: keyof T;
label: string;
values: { value: any, label: string }[];
inputType: 'options' | 'text',
searchLabel?: string;
allowSearch?: boolean;
values?: { value: any, label: string }[];
}

export const CardsFilter: FC<{ filterDefinitions: CardsFilterDefinition<any>[], onFiltersChanged?: (values: Record<string, any>) => void }> = ({ filterDefinitions, onFiltersChanged }) => {
const { locale: l10nLocale } = useL10N();
const { t } = useTranslation();

const [values, setValues] = useState<Record<string, any>>({});
const [searchValues, setSearchValues] = useState<Record<string, any>>({});
const [visibleValues, setVisibleValues] = useState<Record<string, any[]>>({});

const updateValue = useCallback((field: string, value: any) => {
const newValues = {
Expand All @@ -34,6 +43,11 @@ export const CardsFilter: FC<{ filterDefinitions: CardsFilterDefinition<any>[],
onFiltersChanged?.(newValues);
}, [values, onFiltersChanged]);

const updateValueDebounced = useCallback(
debounce(updateValue, debounceInputs),
[values, onFiltersChanged, updateValue],
);

const clearFilters = useCallback(() => {
// We need to explicitly null out values because our filter may refer to Transfer fields which are not in the list
// of columns. Other framework features rely on column info for e.g. queryCustomizer cleanup.
Expand All @@ -57,22 +71,49 @@ export const CardsFilter: FC<{ filterDefinitions: CardsFilterDefinition<any>[],
</Grid>
{filterDefinitions.map(d => (
<Grid item xs={12} key={d.field as string}>
<Typography variant="subtitle1">
{d.label}:
</Typography>
<FormGroup>
{d.values.map(v => (
<FormControlLabel
key={`${d.label}-${v.value}`}
control={<Checkbox
size="small"
checked={values[d.field as string] === v.value}
onClick={() => updateValue(d.field as string, v.value)}
/>}
label={v.label}
/>
))}
</FormGroup>
<Typography variant="subtitle1">{d.label}:</Typography>
{d.allowSearch ? (
<TextField
size="small"
variant="standard"
label={d.searchLabel}
defaultValue={searchValues[d.field as string]}
onChange={(evt) => {
setSearchValues((prev) => {
return {
...prev,
[d.field as string]: evt.target.value,
};
});
if (d.inputType === 'text') {
updateValueDebounced(d.field as string, evt.target.value);
}
} }
/>
) : null}
{d.values ? <Box sx={ { maxHeight: 170, overflowX: 'scroll' } }>
<FormGroup>
{d.values
.filter((v) =>
searchValues[d.field as string]
? v.label.toLowerCase().includes(searchValues[d.field as string].toLowerCase())
: true,
)
.map((v) => (
<FormControlLabel
key={`${d.label}-${v.value}`}
control={
<Checkbox
size="small"
checked={values[d.field as string] === v.value}
onClick={() => updateValue(d.field as string, v.value)}
/>
}
label={v.label}
/>
))}
</FormGroup>
</Box> : null}
</Grid>
))}
</Grid>
Expand Down
Loading