Skip to content

Commit 86b8cac

Browse files
authored
FE: Add frontend filter for ACL table (#1172)
1 parent ff0e451 commit 86b8cac

File tree

12 files changed

+166
-83
lines changed

12 files changed

+166
-83
lines changed

frontend/src/components/ACLPage/List/List.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { ControlPanelWrapper } from 'components/common/ControlPanel/ControlPanel
2424
import Search from 'components/common/Search/Search';
2525
import ResourcePageHeading from 'components/common/ResourcePageHeading/ResourcePageHeading';
2626
import BreakableTextCell from 'components/common/NewTable/BreakableTextCell';
27+
import { useQueryPersister } from 'components/common/NewTable/ColumnFilter';
2728

2829
import * as S from './List.styled';
2930

@@ -82,6 +83,10 @@ const ACList: React.FC = () => {
8283
cell: ({ getValue }) => (
8384
<S.EnumCell>{getValue<string>().toLowerCase()}</S.EnumCell>
8485
),
86+
filterFn: 'arrIncludesSome',
87+
meta: {
88+
filterVariant: 'multi-select',
89+
},
8590
size: 145,
8691
},
8792
{
@@ -112,6 +117,10 @@ const ACList: React.FC = () => {
112117
</S.PatternCell>
113118
);
114119
},
120+
filterFn: 'includesString',
121+
meta: {
122+
filterVariant: 'text',
123+
},
115124
size: 257,
116125
},
117126
{
@@ -126,6 +135,10 @@ const ACList: React.FC = () => {
126135
cell: ({ getValue }) => (
127136
<S.EnumCell>{getValue<string>().toLowerCase()}</S.EnumCell>
128137
),
138+
filterFn: 'arrIncludesSome',
139+
meta: {
140+
filterVariant: 'multi-select',
141+
},
129142
size: 121,
130143
},
131144
{
@@ -165,6 +178,8 @@ const ACList: React.FC = () => {
165178
[rowId]
166179
);
167180

181+
const filterPersister = useQueryPersister(columns);
182+
168183
return (
169184
<S.Container>
170185
<ResourcePageHeading text="Access Control List">
@@ -193,6 +208,7 @@ const ACList: React.FC = () => {
193208
emptyMessage="No ACL items found"
194209
onRowHover={handleRowHover}
195210
onMouseLeave={() => setRowId('')}
211+
filterPersister={filterPersister}
196212
enableSorting
197213
/>
198214
<ACLFormContext.Provider

frontend/src/components/common/NewTable/ColumnFilter/Filter.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ export const ColumnFilter = <T,>(props: FilterProps<T>) => {
1414
case 'multi-select': {
1515
return <Variant.MultiSelect column={column} />;
1616
}
17+
case 'text': {
18+
return <Variant.Text column={column} />;
19+
}
1720
default: {
1821
throw Error('Not implemented filter');
1922
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import styled from 'styled-components';
2+
3+
export const Container = styled.div`
4+
display: flex;
5+
height: 24px;
6+
align-items: center;
7+
position: relative;
8+
padding-right: 8px;
9+
`;
10+
11+
export const Positioner = styled.div`
12+
position: absolute;
13+
z-index: 30;
14+
`;
15+
16+
export const Count = styled.span`
17+
padding-left: 4px;
18+
color: ${({ theme }) => theme.table.filter.multiSelect.value.color};
19+
`;
20+
21+
export const FilterIcon = styled.div`
22+
height: 12px;
23+
margin: 2px;
24+
`;
25+
26+
export const ResetIcon = styled.div`
27+
margin: 3px;
28+
`;
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import React, { useState, useRef, ReactNode } from 'react';
2+
import { Column } from '@tanstack/react-table';
3+
import useBoolean from 'lib/hooks/useBoolean';
4+
import Portal from 'components/common/Portal/Portal';
5+
import useClickOutside from 'lib/hooks/useClickOutside';
6+
7+
import FilterIcon from './FilterIcon';
8+
import ClearIcon from './ClearIcon';
9+
import * as S from './FilterContainer.styled';
10+
11+
interface Props<T> {
12+
column: Column<T, unknown>;
13+
hasFilterValue: boolean;
14+
valueComponent?: ReactNode;
15+
filterComponent?: ReactNode;
16+
}
17+
18+
const TOP_PADDING = 8;
19+
20+
export const FilterContainer = <T,>(props: Props<T>) => {
21+
const { column, valueComponent, filterComponent, hasFilterValue } = props;
22+
const { value: opened, toggle } = useBoolean(false);
23+
const [coords, setCoords] = useState<{ right: number; top: number }>({
24+
right: 0,
25+
top: 0,
26+
});
27+
28+
const ref = useRef(null);
29+
useClickOutside(ref, toggle);
30+
31+
const resetFilter = () => column.setFilterValue('');
32+
const onFilterClick = (
33+
event: React.MouseEvent<HTMLDivElement, MouseEvent>
34+
) => {
35+
const node = event.target as HTMLElement;
36+
const rect = node.getBoundingClientRect();
37+
setCoords({
38+
right: window.innerWidth - rect.right,
39+
top: rect.bottom + TOP_PADDING,
40+
});
41+
toggle();
42+
};
43+
44+
return (
45+
<S.Container>
46+
<S.FilterIcon onClick={onFilterClick}>
47+
<FilterIcon active={opened || hasFilterValue} />
48+
</S.FilterIcon>
49+
50+
{hasFilterValue && (
51+
<>
52+
{valueComponent}
53+
<S.ResetIcon onClick={resetFilter}>
54+
<ClearIcon />
55+
</S.ResetIcon>
56+
</>
57+
)}
58+
59+
<Portal isOpen={opened}>
60+
<S.Positioner
61+
ref={ref}
62+
style={{
63+
right: coords.right,
64+
top: coords.top,
65+
}}
66+
>
67+
{filterComponent}
68+
</S.Positioner>
69+
</Portal>
70+
</S.Container>
71+
);
72+
};

frontend/src/components/common/NewTable/ColumnFilter/variants/MultiSelect/MultiSelect.styled.tsx

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export const SelectPanel = styled(ReactMultiSelect)<{
55
minWidth?: string;
66
height?: string;
77
}>`
8+
min-width: 160px;
89
font-size: 14px;
910
padding-right: 12px;
1011
.dropdown-container:focus-within {
@@ -50,7 +51,6 @@ export const SelectPanel = styled(ReactMultiSelect)<{
5051
5152
& > .dropdown-content {
5253
width: fit-content;
53-
min-width: 120px;
5454
right: 0px;
5555
top: 0;
5656
padding-top: 0;
@@ -67,29 +67,7 @@ export const SelectPanel = styled(ReactMultiSelect)<{
6767
}
6868
`;
6969

70-
export const Container = styled.div`
71-
display: flex;
72-
height: 24px;
73-
align-items: center;
74-
position: relative;
75-
padding-right: 8px;
76-
`;
77-
78-
export const Positioner = styled.div`
79-
position: absolute;
80-
z-index: 30;
81-
`;
82-
8370
export const Count = styled.span`
8471
padding-left: 4px;
8572
color: ${({ theme }) => theme.table.filter.multiSelect.value.color};
8673
`;
87-
88-
export const FilterIcon = styled.div`
89-
height: 12px;
90-
margin: 2px;
91-
`;
92-
93-
export const ResetIcon = styled.div`
94-
margin: 3px;
95-
`;
Lines changed: 8 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,16 @@
1-
import React, { useState, useRef, useMemo } from 'react';
1+
import React, { useMemo } from 'react';
22
import { Column } from '@tanstack/react-table';
3-
import useBoolean from 'lib/hooks/useBoolean';
4-
import Portal from 'components/common/Portal/Portal';
5-
import useClickOutside from 'lib/hooks/useClickOutside';
3+
import { FilterContainer } from 'components/common/NewTable/ColumnFilter/ui/FilterContainer/FilterContainer';
64

7-
import ClearIcon from './ui/ClearIcon';
85
import SelectPanel from './SelectPanel';
96
import * as S from './MultiSelect.styled';
10-
import FilterIcon from './ui/FilterIcon';
117

128
interface FilterProps<T> {
139
column: Column<T, unknown>;
1410
}
1511

16-
const TOP_PADDING = 8;
17-
const LEFT_PADDING = 16;
18-
1912
export const MultiSelect = <T,>(props: FilterProps<T>) => {
2013
const { column } = props;
21-
const { value: opened, toggle } = useBoolean(false);
22-
const [coords, setCoords] = useState<{ left: number; top: number }>({
23-
left: 0,
24-
top: 0,
25-
});
26-
27-
const ref = useRef(null);
28-
useClickOutside(ref, toggle);
29-
30-
const resetFilter = () => column.setFilterValue('');
31-
const onFilterClick = (
32-
event: React.MouseEvent<HTMLDivElement, MouseEvent>
33-
) => {
34-
const node = event.target as HTMLElement;
35-
const rect = node.getBoundingClientRect();
36-
setCoords({
37-
left: rect.left + LEFT_PADDING,
38-
top: rect.bottom + TOP_PADDING,
39-
});
40-
toggle();
41-
};
4214

4315
const value: string[] = useMemo(() => {
4416
const filterValue = column.getFilterValue();
@@ -50,31 +22,11 @@ export const MultiSelect = <T,>(props: FilterProps<T>) => {
5022
}, [column.getFilterValue()]);
5123

5224
return (
53-
<S.Container>
54-
<S.FilterIcon onClick={onFilterClick}>
55-
<FilterIcon active={opened || !!value.length} />
56-
</S.FilterIcon>
57-
58-
{!!value.length && (
59-
<>
60-
<S.Count>{value.length}</S.Count>
61-
<S.ResetIcon onClick={resetFilter}>
62-
<ClearIcon />
63-
</S.ResetIcon>
64-
</>
65-
)}
66-
67-
<Portal isOpen={opened}>
68-
<S.Positioner
69-
ref={ref}
70-
style={{
71-
left: coords.left,
72-
top: coords.top,
73-
}}
74-
>
75-
<SelectPanel column={column} />
76-
</S.Positioner>
77-
</Portal>
78-
</S.Container>
25+
<FilterContainer
26+
column={column}
27+
hasFilterValue={value.length > 0}
28+
valueComponent={<S.Count>{value.length}</S.Count>}
29+
filterComponent={<SelectPanel column={column} />}
30+
/>
7931
);
8032
};
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React from 'react';
2+
import { Column } from '@tanstack/react-table';
3+
import { FilterContainer } from 'components/common/NewTable/ColumnFilter/ui/FilterContainer/FilterContainer';
4+
import Input from 'components/common/Input/Input';
5+
6+
interface FilterProps<T> {
7+
column: Column<T, unknown>;
8+
}
9+
10+
const TextFilterInput = <T,>(props: FilterProps<T>) => {
11+
const { column } = props;
12+
const value = column.getFilterValue() as string;
13+
return (
14+
<Input
15+
autoFocus
16+
value={value}
17+
onChange={({ target }) => {
18+
column.setFilterValue(target?.value ?? '');
19+
}}
20+
/>
21+
);
22+
};
23+
24+
export const Text = <T,>(props: FilterProps<T>) => {
25+
const { column } = props;
26+
27+
const value = column.getFilterValue() as string | undefined;
28+
29+
return (
30+
<FilterContainer
31+
column={column}
32+
filterComponent={<TextFilterInput column={column} />}
33+
hasFilterValue={!!value?.length}
34+
/>
35+
);
36+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export { MultiSelect } from './MultiSelect/MultiSelect';
2+
export { Text } from './Text/Text';

0 commit comments

Comments
 (0)