Skip to content

Commit 5feea95

Browse files
committed
Add filter input to <ColumnsButton> when there are many columns
1 parent 23ccbfe commit 5feea95

File tree

10 files changed

+111
-6
lines changed

10 files changed

+111
-6
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { createContext, useContext } from 'react';
2+
3+
export const DataTableColumnFilterContext = createContext<string | undefined>(
4+
undefined
5+
);
6+
7+
export const useDataTableColumnFilterContext = () =>
8+
useContext(DataTableColumnFilterContext);

packages/ra-core/src/dataTable/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export * from './DataTableBase';
22
export * from './DataTableCallbacksContext';
33
export * from './DataTableColumnRankContext';
4+
export * from './DataTableColumnFilterContext';
45
export * from './DataTableConfigContext';
56
export * from './DataTableDataContext';
67
export * from './DataTableRenderContext';

packages/ra-core/src/i18n/TranslationMessages.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export interface TranslationMessages extends StringMap {
2828
remove: string;
2929
save: string;
3030
search: string;
31+
search_columns: string;
3132
select_all: string;
3233
select_all_button: string;
3334
select_row: string;

packages/ra-language-english/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const englishMessages: TranslationMessages = {
2424
remove: 'Remove',
2525
save: 'Save',
2626
search: 'Search',
27+
search_columns: 'Search columns',
2728
select_all: 'Select all',
2829
select_all_button: 'Select all',
2930
select_row: 'Select this row',

packages/ra-language-french/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const frenchMessages: TranslationMessages = {
2828
select_all_button: 'Tout sélectionner',
2929
select_row: 'Sélectionner cette ligne',
3030
search: 'Rechercher',
31+
search_columns: 'Filtrer les colonnes',
3132
show: 'Afficher',
3233
sort: 'Trier',
3334
undo: 'Annuler',

packages/ra-ui-materialui/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
"autosuggest-highlight": "^3.1.1",
7373
"clsx": "^2.1.1",
7474
"css-mediaquery": "^0.1.2",
75+
"diacritic": "^0.0.2",
7576
"dompurify": "^3.2.4",
7677
"inflection": "^3.0.0",
7778
"jsonexport": "^3.2.0",

packages/ra-ui-materialui/src/list/datatable/ColumnsButton.stories.tsx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,39 @@ export const Basic = () => (
6565
</DataTable>
6666
</Wrapper>
6767
);
68+
69+
export const FewColumns = () => (
70+
<Wrapper aside={<ColumnsButton />} actions={null}>
71+
<DataTable bulkActionButtons={false}>
72+
<DataTable.Col source="col0" label="c_0" />
73+
<DataTable.Col source="col1" label="c_1" />
74+
<DataTable.Col source="col2" label="c_2" />
75+
<DataTable.Col source="col3" label="c_3" />
76+
<DataTable.Col source="col4" label="c_4" />
77+
</DataTable>
78+
</Wrapper>
79+
);
80+
81+
export const LabelTypes = () => (
82+
<Wrapper aside={<ColumnsButton />} actions={null}>
83+
<DataTable bulkActionButtons={false} hiddenColumns={['col5']}>
84+
<DataTable.Col source="col0" />
85+
<DataTable.Col source="col1" label="column 1" />
86+
<DataTable.Col source="col2" label="Testing Label Case" />
87+
<DataTable.Col source="col3" label="Téstïng diàcritics" />
88+
<DataTable.Col
89+
source="col4"
90+
label={
91+
<span>
92+
Testing React <i>Element</i>
93+
</span>
94+
}
95+
/>
96+
<DataTable.Col source="col5" />
97+
<HideMe>
98+
<DataTable.Col source="col6" />
99+
</HideMe>
100+
<DataTable.Col source="col7" />
101+
</DataTable>
102+
</Wrapper>
103+
);

packages/ra-ui-materialui/src/list/datatable/ColumnsSelector.tsx

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,22 @@ import {
55
useStore,
66
DataTableColumnRankContext,
77
useDataTableStoreContext,
8+
useTranslate,
9+
DataTableColumnFilterContext,
810
} from 'ra-core';
9-
import { Box } from '@mui/material';
11+
import { Box, InputAdornment } from '@mui/material';
12+
import SearchIcon from '@mui/icons-material/Search';
1013

1114
import { Button } from '../../button';
15+
import { ResettableTextField } from '../../input';
1216

1317
/**
1418
* Render DataTable.Col elements in the ColumnsButton selector using a React Portal.
1519
*
1620
* @see ColumnsButton
1721
*/
1822
export const ColumnsSelector = ({ children }: ColumnsSelectorProps) => {
23+
const translate = useTranslate();
1924
const { storeKey, defaultHiddenColumns } = useDataTableStoreContext();
2025
const [columnRanks, setColumnRanks] = useStore<number[] | undefined>(
2126
`${storeKey}_columnRanks`
@@ -53,19 +58,57 @@ export const ColumnsSelector = ({ children }: ColumnsSelectorProps) => {
5358
};
5459
}, [elementId, container]);
5560

61+
const [columnFilter, setColumnFilter] = React.useState<
62+
string | undefined
63+
>();
64+
5665
if (!container) return null;
5766

5867
const childrenArray = Children.toArray(children);
5968
const paddedColumnRanks = padRanks(columnRanks ?? [], childrenArray.length);
69+
const shouldDisplaySearchInput = childrenArray.length > 5;
6070

6171
return createPortal(
6272
<>
73+
{shouldDisplaySearchInput ? (
74+
<ResettableTextField
75+
hiddenLabel
76+
label=""
77+
value={columnFilter}
78+
onChange={e => {
79+
if (typeof e === 'string') {
80+
setColumnFilter(e);
81+
return;
82+
}
83+
setColumnFilter(e.target.value);
84+
}}
85+
placeholder={translate('ra.action.search_columns', {
86+
_: 'Search columns',
87+
})}
88+
InputProps={{
89+
endAdornment: (
90+
<InputAdornment position="end">
91+
<SearchIcon color="disabled" />
92+
</InputAdornment>
93+
),
94+
}}
95+
resettable
96+
autoFocus
97+
size="small"
98+
sx={{ mb: 1 }}
99+
/>
100+
) : null}
63101
{paddedColumnRanks.map((position, index) => (
64102
<DataTableColumnRankContext.Provider
65103
value={position}
66104
key={index}
67105
>
68-
{childrenArray[position]}
106+
<DataTableColumnFilterContext.Provider
107+
value={columnFilter}
108+
key={index}
109+
>
110+
{childrenArray[position]}
111+
</DataTableColumnFilterContext.Provider>
69112
</DataTableColumnRankContext.Provider>
70113
))}
71114
<Box

packages/ra-ui-materialui/src/list/datatable/ColumnsSelectorItem.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import {
55
useTranslateLabel,
66
useDataTableStoreContext,
77
useDataTableColumnRankContext,
8+
useDataTableColumnFilterContext,
89
} from 'ra-core';
10+
import * as diacritic from 'diacritic';
911

1012
import { FieldToggle } from '../../preferences';
1113
import { DataTableColumnProps } from './DataTableColumn';
@@ -24,14 +26,16 @@ export const ColumnsSelectorItem = ({
2426
const [columnRanks, setColumnRanks] = useStore<number[]>(
2527
`${storeKey}_columnRanks`
2628
);
29+
const columnFilter = useDataTableColumnFilterContext();
2730
const translateLabel = useTranslateLabel();
2831
if (!source && !label) return null;
2932
const fieldLabel = translateLabel({
3033
label: typeof label === 'string' ? label : undefined,
3134
resource,
3235
source,
33-
});
36+
}) as string;
3437
const isColumnHidden = hiddenColumns.includes(source!);
38+
const isColumnFiltered = fieldLabelMatchesFilter(fieldLabel, columnFilter);
3539

3640
const handleMove = (index1, index2) => {
3741
const colRanks = !columnRanks
@@ -69,7 +73,7 @@ export const ColumnsSelectorItem = ({
6973
setColumnRanks(newColumnRanks);
7074
};
7175

72-
return (
76+
return isColumnFiltered ? (
7377
<FieldToggle
7478
key={columnRank}
7579
source={source!}
@@ -85,7 +89,7 @@ export const ColumnsSelectorItem = ({
8589
}
8690
onMove={handleMove}
8791
/>
88-
);
92+
) : null;
8993
};
9094

9195
const padRanks = (ranks: number[], length: number) =>
@@ -95,3 +99,11 @@ const padRanks = (ranks: number[], length: number) =>
9599
(_, i) => ranks.length + i
96100
)
97101
);
102+
103+
const fieldLabelMatchesFilter = (fieldLabel: string, columnFilter?: string) =>
104+
columnFilter
105+
? diacritic
106+
.clean(fieldLabel)
107+
.toLowerCase()
108+
.includes(diacritic.clean(columnFilter).toLowerCase())
109+
: true;

yarn.lock

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8499,7 +8499,7 @@ __metadata:
84998499
languageName: node
85008500
linkType: hard
85018501

8502-
"diacritic@npm:0.0.2":
8502+
"diacritic@npm:0.0.2, diacritic@npm:^0.0.2":
85038503
version: 0.0.2
85048504
resolution: "diacritic@npm:0.0.2"
85058505
checksum: 1d9dd0a1188a8186d4fce4a695fc8cb0d65c31a8b3c59cd926636e49a05b30d6bb3f4144018be40bdf0a4937d16bb6705f3b1d1ff9684a426d922fb039f8d8ae
@@ -16316,6 +16316,7 @@ __metadata:
1631616316
cross-env: "npm:^5.2.0"
1631716317
css-mediaquery: "npm:^0.1.2"
1631816318
csstype: "npm:^3.1.3"
16319+
diacritic: "npm:^0.0.2"
1631916320
dompurify: "npm:^3.2.4"
1632016321
expect: "npm:^27.4.6"
1632116322
file-api: "npm:~0.10.4"

0 commit comments

Comments
 (0)