@bitrise/bitkit 12.70.0-alpha.1 → 12.70.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +31 -31
- package/src/Components/Avatar/Avatar.tsx +4 -1
- package/src/Components/DatePicker/DatePickerFooter.tsx +1 -1
- package/src/Components/DatePicker/DatePickerHeader.tsx +2 -0
- package/src/Components/DatePicker/DatePickerMonth.tsx +8 -1
- package/src/Components/ExpandableCard/ExpandableCard.theme.ts +0 -3
- package/src/Components/Filter/Filter.storyData.ts +29 -4
- package/src/Components/Filter/Filter.tsx +57 -40
- package/src/Components/Filter/Filter.types.ts +13 -3
- package/src/Components/Filter/Filter.utils.ts +9 -2
- package/src/Components/Filter/FilterAdd/FilterAdd.tsx +23 -14
- package/src/Components/Filter/FilterDate/FilterDate.tsx +7 -8
- package/src/Components/Filter/FilterForm/FilterForm.tsx +7 -2
- package/src/Components/Filter/FilterItem/FilterItem.tsx +14 -17
- package/src/Components/Filter/FilterSearch/FilterSearch.tsx +7 -6
- package/src/Components/Filter/FilterSwitch/FilterSwitch.theme.ts +16 -1
- package/src/Components/Filter/FilterSwitch/FilterSwitch.tsx +2 -2
- package/src/Components/Filter/FilterSwitch/FilterSwitchGroup.tsx +4 -18
- package/src/Components/Filter/FilterSwitchAdapter/FilterSwitchAdapter.tsx +34 -0
- package/src/Components/Form/Checkbox/Checkbox.theme.ts +3 -0
- package/src/Components/Form/Input/Input.tsx +1 -0
- package/src/Components/Form/Radio/Radio.theme.ts +3 -0
- package/src/Components/Form/Textarea/Textarea.tsx +10 -1
- package/src/Components/Icons/16x16/Filter.tsx +3 -4
- package/src/Components/Icons/24x24/Filter.tsx +3 -4
- package/src/Components/OverflowMenu/OverflowMenu.tsx +9 -2
- package/src/Components/Select/Select.tsx +1 -0
- package/src/index.ts +4 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitrise/bitkit",
|
|
3
3
|
"description": "Bitrise React component library",
|
|
4
|
-
"version": "12.70.0
|
|
4
|
+
"version": "12.70.0",
|
|
5
5
|
"repository": "git@github.com:bitrise-io/bitkit.git",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"license": "UNLICENSED",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"@emotion/react": "^11.11.1",
|
|
29
29
|
"@emotion/styled": "^11.11.0",
|
|
30
30
|
"@floating-ui/react-dom-interactions": "^0.8.1",
|
|
31
|
-
"framer-motion": "^10.16.
|
|
31
|
+
"framer-motion": "^10.16.16",
|
|
32
32
|
"luxon": "^3.4.4",
|
|
33
33
|
"react": "^18.2.0",
|
|
34
34
|
"react-dom": "^18.2.0",
|
|
@@ -40,54 +40,54 @@
|
|
|
40
40
|
"react-dom": "^18.2.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@babel/core": "^7.23.
|
|
44
|
-
"@babel/preset-env": "^7.23.
|
|
43
|
+
"@babel/core": "^7.23.6",
|
|
44
|
+
"@babel/preset-env": "^7.23.6",
|
|
45
45
|
"@babel/preset-react": "^7.23.3",
|
|
46
46
|
"@babel/preset-typescript": "^7.23.3",
|
|
47
|
-
"@bitrise/eslint-plugin": "^2.3.
|
|
47
|
+
"@bitrise/eslint-plugin": "^2.3.3",
|
|
48
48
|
"@commitlint/cli": "^17.8.0",
|
|
49
49
|
"@commitlint/config-conventional": "^17.8.0",
|
|
50
|
-
"@google-cloud/storage": "^7.
|
|
50
|
+
"@google-cloud/storage": "^7.7.0",
|
|
51
51
|
"@semantic-release/commit-analyzer": "^11.1.0",
|
|
52
52
|
"@semantic-release/git": "^10.0.1",
|
|
53
|
-
"@storybook/addon-actions": "^7.
|
|
54
|
-
"@storybook/addon-essentials": "^7.
|
|
55
|
-
"@storybook/addon-interactions": "^7.
|
|
56
|
-
"@storybook/addon-links": "^7.
|
|
57
|
-
"@storybook/addons": "^7.
|
|
58
|
-
"@storybook/blocks": "^7.
|
|
59
|
-
"@storybook/react": "^7.
|
|
60
|
-
"@storybook/react-webpack5": "^7.
|
|
53
|
+
"@storybook/addon-actions": "^7.6.4",
|
|
54
|
+
"@storybook/addon-essentials": "^7.6.4",
|
|
55
|
+
"@storybook/addon-interactions": "^7.6.4",
|
|
56
|
+
"@storybook/addon-links": "^7.6.4",
|
|
57
|
+
"@storybook/addons": "^7.6.4",
|
|
58
|
+
"@storybook/blocks": "^7.6.4",
|
|
59
|
+
"@storybook/react": "^7.6.4",
|
|
60
|
+
"@storybook/react-webpack5": "^7.6.4",
|
|
61
61
|
"@storybook/testing-library": "^0.2.2",
|
|
62
|
-
"@storybook/theming": "^7.
|
|
62
|
+
"@storybook/theming": "^7.6.4",
|
|
63
63
|
"@testing-library/dom": "^9.3.3",
|
|
64
|
-
"@testing-library/jest-dom": "^6.1.
|
|
65
|
-
"@testing-library/react": "^14.1.
|
|
64
|
+
"@testing-library/jest-dom": "^6.1.5",
|
|
65
|
+
"@testing-library/react": "^14.1.2",
|
|
66
66
|
"@testing-library/user-event": "^14.5.1",
|
|
67
|
-
"@types/jest": "^29.5.
|
|
68
|
-
"@types/luxon": "^3.3.
|
|
69
|
-
"@types/react": "^18.2.
|
|
70
|
-
"@types/react-dom": "^18.2.
|
|
71
|
-
"@typescript-eslint/eslint-plugin": "^6.
|
|
72
|
-
"@typescript-eslint/parser": "^6.
|
|
67
|
+
"@types/jest": "^29.5.11",
|
|
68
|
+
"@types/luxon": "^3.3.7",
|
|
69
|
+
"@types/react": "^18.2.45",
|
|
70
|
+
"@types/react-dom": "^18.2.17",
|
|
71
|
+
"@typescript-eslint/eslint-plugin": "^6.14.0",
|
|
72
|
+
"@typescript-eslint/parser": "^6.14.0",
|
|
73
73
|
"axios": "^1.6.2",
|
|
74
|
-
"eslint": "^8.
|
|
75
|
-
"eslint-plugin-import": "^2.29.
|
|
74
|
+
"eslint": "^8.55.0",
|
|
75
|
+
"eslint-plugin-import": "^2.29.1",
|
|
76
76
|
"eslint-plugin-jest": "^27.6.0",
|
|
77
77
|
"eslint-plugin-jsx-a11y": "^6.8.0",
|
|
78
78
|
"eslint-plugin-prettier": "^5.0.1",
|
|
79
79
|
"eslint-plugin-react": "^7.33.2",
|
|
80
80
|
"eslint-plugin-react-hooks": "^4.6.0",
|
|
81
81
|
"eslint-plugin-storybook": "^0.6.15",
|
|
82
|
-
"eslint-plugin-testing-library": "^6.
|
|
82
|
+
"eslint-plugin-testing-library": "^6.2.0",
|
|
83
83
|
"glob": "^10.3.10",
|
|
84
84
|
"jest": "^29.7.0",
|
|
85
85
|
"jest-environment-jsdom": "^29.7.0",
|
|
86
|
-
"jsdom": "^
|
|
87
|
-
"prettier": "^3.1.
|
|
88
|
-
"react-hook-form": "^7.
|
|
89
|
-
"semantic-release": "^22.0.
|
|
90
|
-
"storybook": "^7.
|
|
86
|
+
"jsdom": "^23.0.1",
|
|
87
|
+
"prettier": "^3.1.1",
|
|
88
|
+
"react-hook-form": "^7.49.2",
|
|
89
|
+
"semantic-release": "^22.0.12",
|
|
90
|
+
"storybook": "^7.6.4",
|
|
91
91
|
"ts-jest": "^29.1.1",
|
|
92
92
|
"typescript": "^4.8.4"
|
|
93
93
|
},
|
|
@@ -12,7 +12,10 @@ const getInitials = (name: string) => {
|
|
|
12
12
|
if (name.length < 3) {
|
|
13
13
|
return name;
|
|
14
14
|
}
|
|
15
|
-
|
|
15
|
+
|
|
16
|
+
const nameArray = Array.from(name);
|
|
17
|
+
|
|
18
|
+
return `${nameArray[0]}${nameArray[nameArray.length - 1]}`;
|
|
16
19
|
};
|
|
17
20
|
|
|
18
21
|
const Avatar = forwardRef<AvatarProps, 'span'>((props, ref) => (
|
|
@@ -23,7 +23,7 @@ const DatePickerFooter = ({
|
|
|
23
23
|
gap="24"
|
|
24
24
|
>
|
|
25
25
|
{!!onClear && (
|
|
26
|
-
<Button size="small" variant="tertiary" width="fit-content" onClick={() => onClear()}>
|
|
26
|
+
<Button size="small" variant="tertiary" color="purple.10" width="fit-content" onClick={() => onClear()}>
|
|
27
27
|
Clear
|
|
28
28
|
</Button>
|
|
29
29
|
)}
|
|
@@ -21,6 +21,7 @@ export const DatePickerHeaderPrevious = ({ label }: { label: string }) => {
|
|
|
21
21
|
visibility={controls === 'right' ? 'hidden' : undefined}
|
|
22
22
|
aria-label={label}
|
|
23
23
|
variant="tertiary"
|
|
24
|
+
color="purple.10"
|
|
24
25
|
as="button"
|
|
25
26
|
onClick={onPrevious}
|
|
26
27
|
iconName="ChevronLeft"
|
|
@@ -37,6 +38,7 @@ export const DatePickerHeaderNext = ({ label }: { label: string }) => {
|
|
|
37
38
|
size="small"
|
|
38
39
|
aria-label={label}
|
|
39
40
|
variant="tertiary"
|
|
41
|
+
color="purple.10"
|
|
40
42
|
onClick={onNext}
|
|
41
43
|
iconName="ChevronRight"
|
|
42
44
|
isTooltipDisabled
|
|
@@ -50,7 +50,14 @@ const DatePickerMonth = ({ controls, onViewDateChange, viewDate, onMonthClick }:
|
|
|
50
50
|
<DatePickerHeader onPrevious={onPreviousMonth} onNext={onNextMonth} controls={controls}>
|
|
51
51
|
<DatePickerHeaderPrevious label="previous month" />
|
|
52
52
|
<DatePickerHeaderContent id={monthLabelId}>
|
|
53
|
-
<Button
|
|
53
|
+
<Button
|
|
54
|
+
onClick={onMonthClick}
|
|
55
|
+
size="small"
|
|
56
|
+
variant="tertiary"
|
|
57
|
+
color="purple.10"
|
|
58
|
+
flexShrink={0}
|
|
59
|
+
rightIconName="ChevronDown"
|
|
60
|
+
>
|
|
54
61
|
{viewDate.monthLong}
|
|
55
62
|
</Button>
|
|
56
63
|
<NumberInput
|
|
@@ -4,9 +4,6 @@ const { defineMultiStyleConfig } = createMultiStyleConfigHelpers(['button', 'che
|
|
|
4
4
|
|
|
5
5
|
const ExpandableCardTheme = defineMultiStyleConfig({
|
|
6
6
|
baseStyle: ({ isOpen }) => ({
|
|
7
|
-
box: {
|
|
8
|
-
zIndex: 1,
|
|
9
|
-
},
|
|
10
7
|
button: {
|
|
11
8
|
borderTopStartRadius: '8',
|
|
12
9
|
borderTopEndRadius: '8',
|
|
@@ -9,23 +9,26 @@ export const FILTER_STORY_DATA: FilterData = {
|
|
|
9
9
|
date_range: {
|
|
10
10
|
categoryName: 'Date',
|
|
11
11
|
categoryNamePlural: 'dates',
|
|
12
|
-
|
|
12
|
+
type: 'dateRange',
|
|
13
13
|
},
|
|
14
14
|
pipeline: {
|
|
15
15
|
categoryName: 'Pipeline',
|
|
16
16
|
categoryNamePlural: 'Pipelines',
|
|
17
17
|
options: FILTER_STORY_OPTIONS,
|
|
18
|
+
type: 'tag',
|
|
18
19
|
},
|
|
19
20
|
stage: {
|
|
20
21
|
categoryName: 'Stage',
|
|
21
22
|
categoryNamePlural: 'Stages',
|
|
22
23
|
options: FILTER_STORY_OPTIONS,
|
|
23
|
-
dependsOn:
|
|
24
|
+
dependsOn: { pipeline: 'Select a pipeline first' },
|
|
25
|
+
type: 'tag',
|
|
24
26
|
},
|
|
25
27
|
workflow: {
|
|
26
28
|
categoryName: 'Workflow',
|
|
27
29
|
isMultiple: true,
|
|
28
30
|
options: FILTER_STORY_OPTIONS,
|
|
31
|
+
type: 'tag',
|
|
29
32
|
},
|
|
30
33
|
branch: {
|
|
31
34
|
options: [
|
|
@@ -34,16 +37,22 @@ export const FILTER_STORY_DATA: FilterData = {
|
|
|
34
37
|
'master',
|
|
35
38
|
'CI-2264-consolidate-other-provider-type-to-custom',
|
|
36
39
|
],
|
|
40
|
+
type: 'tag',
|
|
37
41
|
},
|
|
38
42
|
app: {
|
|
39
43
|
categoryName: 'App',
|
|
40
|
-
|
|
44
|
+
iconsMap: {
|
|
45
|
+
'46b6b9a78a418ee8': 'AppleFill',
|
|
46
|
+
'32b14416be4b7b24': 'Android',
|
|
47
|
+
'0a248b278e135ea7': 'Other',
|
|
48
|
+
},
|
|
41
49
|
options: ['46b6b9a78a418ee8', '32b14416be4b7b24', '0a248b278e135ea7'],
|
|
42
50
|
optionsMap: {
|
|
43
51
|
'46b6b9a78a418ee8': 'bitrise-website',
|
|
44
52
|
'32b14416be4b7b24': 'bitkit',
|
|
45
53
|
'0a248b278e135ea7': 'pipeline-service',
|
|
46
54
|
},
|
|
55
|
+
type: 'select',
|
|
47
56
|
},
|
|
48
57
|
test_case: {
|
|
49
58
|
categoryName: 'Test case',
|
|
@@ -51,21 +60,37 @@ export const FILTER_STORY_DATA: FilterData = {
|
|
|
51
60
|
console.log('onAsyncSearch', { category, q });
|
|
52
61
|
return new Promise((resolve) => {
|
|
53
62
|
setTimeout(() => {
|
|
54
|
-
resolve(['found 1', 'found 2']);
|
|
63
|
+
resolve({ options: ['found 1', 'found 2'] });
|
|
55
64
|
}, 2000);
|
|
56
65
|
});
|
|
57
66
|
},
|
|
58
67
|
options: ['default 1', 'default 2', 'default 3'],
|
|
68
|
+
type: 'tag',
|
|
69
|
+
},
|
|
70
|
+
cache_type: {
|
|
71
|
+
iconsMap: {
|
|
72
|
+
bazel: 'Bazel',
|
|
73
|
+
gradle: 'Gradle',
|
|
74
|
+
},
|
|
75
|
+
options: ['gradle', 'bazel'],
|
|
76
|
+
optionsMap: {
|
|
77
|
+
bazel: 'Bazel',
|
|
78
|
+
gradle: 'Gradle',
|
|
79
|
+
},
|
|
80
|
+
type: 'switch',
|
|
59
81
|
},
|
|
60
82
|
};
|
|
61
83
|
|
|
62
84
|
export const FILTER_STORY_INIT_STATE: FilterState = {
|
|
63
85
|
pipeline: ['ipsum'],
|
|
64
86
|
app: ['46b6b9a78a418ee8'],
|
|
87
|
+
cache_type: ['gradle'],
|
|
65
88
|
};
|
|
66
89
|
|
|
67
90
|
export const FILTER_STORY_CONTEXT: FilterContextType = {
|
|
68
91
|
data: FILTER_STORY_DATA,
|
|
69
92
|
setPopoverOpen: () => {},
|
|
70
93
|
state: FILTER_STORY_INIT_STATE,
|
|
94
|
+
onFilterChange: () => {},
|
|
95
|
+
onFilterClear: () => {},
|
|
71
96
|
};
|
|
@@ -6,29 +6,47 @@ import Divider from '../Divider/Divider';
|
|
|
6
6
|
import Icon from '../Icon/Icon';
|
|
7
7
|
import { FilterContext } from './Filter.context';
|
|
8
8
|
import { FilterStyle } from './Filter.theme';
|
|
9
|
-
import {
|
|
10
|
-
|
|
9
|
+
import {
|
|
10
|
+
FilterCategoryProps,
|
|
11
|
+
FilterContextType,
|
|
12
|
+
FilterData,
|
|
13
|
+
FilterState,
|
|
14
|
+
FilterType,
|
|
15
|
+
FilterValue,
|
|
16
|
+
} from './Filter.types';
|
|
17
|
+
import { getDependents } from './Filter.utils';
|
|
11
18
|
import FilterAdd from './FilterAdd/FilterAdd';
|
|
12
19
|
import FilterItem from './FilterItem/FilterItem';
|
|
13
20
|
import FilterSearch from './FilterSearch/FilterSearch';
|
|
14
21
|
import FilterDate from './FilterDate/FilterDate';
|
|
22
|
+
import FilterSwitchAdapter from './FilterSwitchAdapter/FilterSwitchAdapter';
|
|
15
23
|
|
|
16
24
|
export interface FilterProps extends Omit<BoxProps, 'onChange'> {
|
|
17
25
|
filtersDependOn?: string[];
|
|
18
26
|
initialData: FilterData;
|
|
27
|
+
initialState: FilterState;
|
|
19
28
|
isLoading?: boolean;
|
|
20
29
|
onChange: (state: FilterState) => void;
|
|
21
30
|
showSearch?: boolean;
|
|
22
|
-
state: FilterState;
|
|
23
31
|
}
|
|
24
32
|
|
|
25
33
|
const Filter = (props: FilterProps) => {
|
|
26
|
-
const { filtersDependOn, initialData, isLoading, onChange, showSearch,
|
|
34
|
+
const { filtersDependOn, initialData, initialState, isLoading, onChange, showSearch, ...rest } = props;
|
|
27
35
|
|
|
28
36
|
const isInited = useRef<boolean>(false);
|
|
29
37
|
|
|
30
38
|
const filterStyle = useMultiStyleConfig('Filter') as FilterStyle;
|
|
31
39
|
|
|
40
|
+
const state: FilterState = {};
|
|
41
|
+
Object.entries(initialState).forEach(([category, values]) => {
|
|
42
|
+
if (values?.length) {
|
|
43
|
+
const cleanValues = values.filter((v) => v !== null && v !== '' && v !== undefined);
|
|
44
|
+
if (cleanValues.length) {
|
|
45
|
+
state[category] = cleanValues;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
32
50
|
const [data] = useState<FilterData>(initialData);
|
|
33
51
|
const [isPopoverOpen, setPopoverOpen] = useState<boolean>(false);
|
|
34
52
|
|
|
@@ -58,7 +76,7 @@ const Filter = (props: FilterProps) => {
|
|
|
58
76
|
onChange(newState);
|
|
59
77
|
};
|
|
60
78
|
|
|
61
|
-
const
|
|
79
|
+
const onFilterClear = (category: string) => {
|
|
62
80
|
onChange(deleteFromState(category, state));
|
|
63
81
|
};
|
|
64
82
|
|
|
@@ -66,14 +84,19 @@ const Filter = (props: FilterProps) => {
|
|
|
66
84
|
onChange({});
|
|
67
85
|
};
|
|
68
86
|
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
87
|
+
const filters = {
|
|
88
|
+
search: {},
|
|
89
|
+
select: {},
|
|
90
|
+
switch: {},
|
|
91
|
+
dateRange: {},
|
|
92
|
+
tag: {},
|
|
93
|
+
} as Record<FilterType, Record<string, FilterCategoryProps>>;
|
|
72
94
|
|
|
73
|
-
|
|
74
|
-
|
|
95
|
+
Object.entries(data).forEach(([category, value]) => {
|
|
96
|
+
filters[value.type || 'tag'][category] = value;
|
|
97
|
+
});
|
|
75
98
|
|
|
76
|
-
const
|
|
99
|
+
const stateCategories = Object.keys(state).filter((c) => !['date_range', 'search'].includes(c));
|
|
77
100
|
|
|
78
101
|
const showClearFilters = stateCategories.length > 0 || (state.search && state.search.length > 0);
|
|
79
102
|
|
|
@@ -82,10 +105,12 @@ const Filter = (props: FilterProps) => {
|
|
|
82
105
|
data: initialData,
|
|
83
106
|
filtersDependOn,
|
|
84
107
|
isLoading,
|
|
108
|
+
onFilterChange,
|
|
109
|
+
onFilterClear,
|
|
85
110
|
setPopoverOpen,
|
|
86
111
|
state,
|
|
87
112
|
}),
|
|
88
|
-
[filtersDependOn, isLoading, initialData, setPopoverOpen, state],
|
|
113
|
+
[filtersDependOn, isLoading, initialData, onFilterChange, onFilterClear, setPopoverOpen, state],
|
|
89
114
|
);
|
|
90
115
|
|
|
91
116
|
useEffect(() => {
|
|
@@ -102,27 +127,25 @@ const Filter = (props: FilterProps) => {
|
|
|
102
127
|
</Modal>
|
|
103
128
|
<Box sx={filterStyle.content}>
|
|
104
129
|
<Icon name="Filter" sx={filterStyle.icon} />
|
|
105
|
-
|
|
106
|
-
{
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
return null;
|
|
125
|
-
})}
|
|
130
|
+
|
|
131
|
+
{Object.keys(filters.switch).map((category) => (
|
|
132
|
+
<FilterSwitchAdapter category={category} key={category} />
|
|
133
|
+
))}
|
|
134
|
+
|
|
135
|
+
{Object.keys(filters.dateRange).map((category) => (
|
|
136
|
+
<FilterDate category={category} key={category} />
|
|
137
|
+
))}
|
|
138
|
+
|
|
139
|
+
{Object.keys(filters.select).map((category) => (
|
|
140
|
+
<FilterItem category={category} key={category} />
|
|
141
|
+
))}
|
|
142
|
+
{Object.keys(filters.tag).map((category) => {
|
|
143
|
+
if (!state[category]) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
return <FilterItem category={category} key={category} />;
|
|
147
|
+
})}
|
|
148
|
+
|
|
126
149
|
<FilterAdd onChange={onFilterChange} />
|
|
127
150
|
</Box>
|
|
128
151
|
{(showClearFilters || showSearch) && (
|
|
@@ -142,13 +165,7 @@ const Filter = (props: FilterProps) => {
|
|
|
142
165
|
{showClearFilters && showSearch && (
|
|
143
166
|
<Divider orientation="vertical" size="1" variant="solid" flexShrink="0" />
|
|
144
167
|
)}
|
|
145
|
-
{showSearch && (
|
|
146
|
-
<FilterSearch
|
|
147
|
-
onChange={onFilterChange}
|
|
148
|
-
onClear={onClear}
|
|
149
|
-
value={(state.Search && state.Search[0]) || ''}
|
|
150
|
-
/>
|
|
151
|
-
)}
|
|
168
|
+
{showSearch && <FilterSearch onChange={onFilterChange} value={(state.Search && state.Search[0]) || ''} />}
|
|
152
169
|
</Box>
|
|
153
170
|
)}
|
|
154
171
|
</Box>
|
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
import { Dispatch, SetStateAction } from 'react';
|
|
2
|
+
import { TypeIconName } from '../Icon/Icon';
|
|
3
|
+
|
|
4
|
+
export type FilterType = 'dateRange' | 'search' | 'select' | 'switch' | 'tag';
|
|
2
5
|
|
|
3
6
|
export type FilterOptions = string[];
|
|
4
7
|
export type FilterValue = string[];
|
|
5
8
|
export type FilterOptionsMap = Record<string, string>;
|
|
9
|
+
export type FilterIconsMap = Record<string, TypeIconName>;
|
|
6
10
|
|
|
7
|
-
export type FilterSearchCallback = (
|
|
11
|
+
export type FilterSearchCallback = (
|
|
12
|
+
category: string,
|
|
13
|
+
q: string,
|
|
14
|
+
) => Promise<{ iconsMap?: FilterIconsMap; options: FilterOptions; optionsMap?: FilterOptionsMap }>;
|
|
8
15
|
|
|
9
16
|
export type FilterCategoryProps = {
|
|
10
17
|
categoryName?: string;
|
|
11
18
|
categoryNamePlural?: string;
|
|
12
|
-
dependsOn?: string
|
|
19
|
+
dependsOn?: Record<string, string>;
|
|
20
|
+
iconsMap?: FilterIconsMap;
|
|
13
21
|
isMultiple?: boolean;
|
|
14
|
-
isPermanent?: boolean;
|
|
15
22
|
onAsyncSearch?: FilterSearchCallback;
|
|
16
23
|
options?: FilterOptions;
|
|
17
24
|
optionsMap?: FilterOptionsMap;
|
|
25
|
+
type?: FilterType;
|
|
18
26
|
};
|
|
19
27
|
|
|
20
28
|
export type FilterData = Record<string, FilterCategoryProps>;
|
|
@@ -24,6 +32,8 @@ export interface FilterContextType {
|
|
|
24
32
|
data: FilterData;
|
|
25
33
|
filtersDependOn?: string[];
|
|
26
34
|
isLoading?: boolean;
|
|
35
|
+
onFilterClear: (category: string) => void;
|
|
36
|
+
onFilterChange: (category: string, value: FilterValue) => void;
|
|
27
37
|
setPopoverOpen: Dispatch<SetStateAction<boolean>>;
|
|
28
38
|
state: FilterState;
|
|
29
39
|
}
|
|
@@ -7,17 +7,24 @@ export const hasAllDependencies = (stateKeys: string[], dependsOn?: string[]): b
|
|
|
7
7
|
return dependsOn.every((key) => stateKeys.includes(key));
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
+
export const getMissingDependencies = (stateKeys: string[], dependsOn?: string[]): string[] => {
|
|
11
|
+
if (!dependsOn || dependsOn.length === 0) {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
return dependsOn.filter((category) => !stateKeys.includes(category));
|
|
15
|
+
};
|
|
16
|
+
|
|
10
17
|
export const getDependents = (data: FilterData, categoryKey: string, filtersDependOn?: string[]): string[] => {
|
|
11
18
|
const dependents: string[] = [];
|
|
12
19
|
if (filtersDependOn && filtersDependOn.includes(categoryKey)) {
|
|
13
20
|
Object.keys(data).forEach((category) => {
|
|
14
|
-
if (
|
|
21
|
+
if (data[category].type !== 'select') {
|
|
15
22
|
dependents.push(category);
|
|
16
23
|
}
|
|
17
24
|
});
|
|
18
25
|
} else {
|
|
19
26
|
Object.keys(data).forEach((category) => {
|
|
20
|
-
if (data[category].dependsOn
|
|
27
|
+
if (Object.keys(data[category].dependsOn || {}).includes(categoryKey)) {
|
|
21
28
|
dependents.push(category);
|
|
22
29
|
}
|
|
23
30
|
});
|
|
@@ -2,10 +2,11 @@ import { useState } from 'react';
|
|
|
2
2
|
import { Menu, MenuButton, MenuList, useDisclosure } from '@chakra-ui/react';
|
|
3
3
|
import Button from '../../Button/Button';
|
|
4
4
|
import MenuItem from '../../Menu/MenuItem';
|
|
5
|
+
import Tooltip from '../../Tooltip/Tooltip';
|
|
5
6
|
import { useFilterContext } from '../Filter.context';
|
|
6
7
|
import FilterForm from '../FilterForm/FilterForm';
|
|
7
8
|
import { FilterValue } from '../Filter.types';
|
|
8
|
-
import { hasAllDependencies } from '../Filter.utils';
|
|
9
|
+
import { getMissingDependencies, hasAllDependencies } from '../Filter.utils';
|
|
9
10
|
|
|
10
11
|
export interface FilterAddProps {
|
|
11
12
|
onChange: (category: string, selected: FilterValue) => void;
|
|
@@ -42,7 +43,11 @@ const FilterAdd = (props: FilterAddProps) => {
|
|
|
42
43
|
|
|
43
44
|
const stateKeys = Object.keys(state);
|
|
44
45
|
|
|
45
|
-
const
|
|
46
|
+
const categoryList = Object.keys(data).filter((category) => {
|
|
47
|
+
return !stateKeys.includes(category) && data[category].type === 'tag';
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const isDisabled = !hasAllDependencies(stateKeys, filtersDependOn) || categoryList.length === 0;
|
|
46
51
|
|
|
47
52
|
return (
|
|
48
53
|
<Menu closeOnSelect={false} isOpen={isOpen} onClose={onClose} onOpen={onOpen}>
|
|
@@ -66,21 +71,25 @@ const FilterAdd = (props: FilterAddProps) => {
|
|
|
66
71
|
{selectedCategory ? (
|
|
67
72
|
<FilterForm category={selectedCategory} onChange={onFilterChange} onCancel={onClose} />
|
|
68
73
|
) : (
|
|
69
|
-
|
|
70
|
-
const { categoryName, dependsOn
|
|
71
|
-
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
74
|
+
categoryList.map((category) => {
|
|
75
|
+
const { categoryName, dependsOn } = data[category];
|
|
76
|
+
const missingDependencies = getMissingDependencies(stateKeys, Object.keys(dependsOn || []));
|
|
74
77
|
return (
|
|
75
|
-
<
|
|
76
|
-
isDisabled={
|
|
78
|
+
<Tooltip
|
|
79
|
+
isDisabled={missingDependencies.length === 0}
|
|
80
|
+
label={dependsOn?.[missingDependencies[0]]}
|
|
81
|
+
placement="right"
|
|
77
82
|
key={category}
|
|
78
|
-
onClick={() => onCategorySelect(category)}
|
|
79
|
-
pointerEvents="all"
|
|
80
|
-
rightIconName="ChevronRight"
|
|
81
83
|
>
|
|
82
|
-
|
|
83
|
-
|
|
84
|
+
<MenuItem
|
|
85
|
+
isDisabled={missingDependencies.length > 0}
|
|
86
|
+
onClick={() => onCategorySelect(category)}
|
|
87
|
+
pointerEvents="all"
|
|
88
|
+
rightIconName="ChevronRight"
|
|
89
|
+
>
|
|
90
|
+
{categoryName || category}
|
|
91
|
+
</MenuItem>
|
|
92
|
+
</Tooltip>
|
|
84
93
|
);
|
|
85
94
|
})
|
|
86
95
|
)}
|
|
@@ -9,30 +9,29 @@ import Text from '../../Text/Text';
|
|
|
9
9
|
import Tooltip from '../../Tooltip/Tooltip';
|
|
10
10
|
import { useFilterContext } from '../Filter.context';
|
|
11
11
|
import { FilterStyle } from '../Filter.theme';
|
|
12
|
-
import { FilterValue } from '../Filter.types';
|
|
13
12
|
|
|
14
13
|
export type FilterDateProps = {
|
|
15
|
-
|
|
16
|
-
onClear: (category: string) => void;
|
|
17
|
-
value: FilterValue;
|
|
14
|
+
category: string;
|
|
18
15
|
};
|
|
19
16
|
|
|
20
17
|
const FilterDate = (props: FilterDateProps) => {
|
|
21
|
-
const {
|
|
18
|
+
const { category } = props;
|
|
22
19
|
const filterStyle = useMultiStyleConfig('Filter') as FilterStyle;
|
|
23
20
|
|
|
24
|
-
const { isLoading, setPopoverOpen } = useFilterContext();
|
|
21
|
+
const { isLoading, onFilterChange, onFilterClear, setPopoverOpen, state } = useFilterContext();
|
|
25
22
|
|
|
26
23
|
const { isOpen, onClose, onToggle } = useDisclosure();
|
|
27
24
|
|
|
25
|
+
const value = state[category];
|
|
26
|
+
|
|
28
27
|
const onDateRangeApply = (range: DateRange) => {
|
|
29
28
|
if (range.from && range.to) {
|
|
30
|
-
|
|
29
|
+
onFilterChange('date_range', [String(range.from.toMillis()), String(range.to.toMillis())]);
|
|
31
30
|
}
|
|
32
31
|
};
|
|
33
32
|
|
|
34
33
|
const onClearClick = () => {
|
|
35
|
-
|
|
34
|
+
onFilterClear('date_range');
|
|
36
35
|
onClose();
|
|
37
36
|
};
|
|
38
37
|
|
|
@@ -6,6 +6,7 @@ import Button from '../../Button/Button';
|
|
|
6
6
|
import ButtonGroup from '../../ButtonGroup/ButtonGroup';
|
|
7
7
|
import Checkbox from '../../Form/Checkbox/Checkbox';
|
|
8
8
|
import CheckboxGroup from '../../Form/Checkbox/CheckboxGroup';
|
|
9
|
+
import Icon from '../../Icon/Icon';
|
|
9
10
|
import Radio from '../../Form/Radio/Radio';
|
|
10
11
|
import RadioGroup from '../../Form/Radio/RadioGroup';
|
|
11
12
|
import SearchInput from '../../SearchInput/SearchInput';
|
|
@@ -29,7 +30,7 @@ const FilterForm = (props: FilterFormProps) => {
|
|
|
29
30
|
const filterStyle = useMultiStyleConfig('Filter') as FilterStyle;
|
|
30
31
|
|
|
31
32
|
const { data, state } = useFilterContext();
|
|
32
|
-
const { isMultiple, onAsyncSearch, options, optionsMap } = data[category];
|
|
33
|
+
const { iconsMap, isMultiple, onAsyncSearch, options, optionsMap } = data[category];
|
|
33
34
|
const value = state[category] || [];
|
|
34
35
|
|
|
35
36
|
const [selected, setSelected] = useState<FilterValue>(value);
|
|
@@ -90,13 +91,15 @@ const FilterForm = (props: FilterFormProps) => {
|
|
|
90
91
|
if (onAsyncSearch) {
|
|
91
92
|
const response = await onAsyncSearch(category, searchValue);
|
|
92
93
|
setLoading(false);
|
|
93
|
-
setFoundOptions(response);
|
|
94
|
+
setFoundOptions(response.options);
|
|
94
95
|
}
|
|
95
96
|
};
|
|
96
97
|
|
|
97
98
|
useEffect(() => {
|
|
98
99
|
if (debouncedSearchValue.length > 0) {
|
|
99
100
|
getAsyncList();
|
|
101
|
+
} else {
|
|
102
|
+
setLoading(false);
|
|
100
103
|
}
|
|
101
104
|
}, [debouncedSearchValue]);
|
|
102
105
|
|
|
@@ -129,6 +132,7 @@ const FilterForm = (props: FilterFormProps) => {
|
|
|
129
132
|
{items.length
|
|
130
133
|
? items.map((opt) => (
|
|
131
134
|
<Checkbox key={opt} value={opt}>
|
|
135
|
+
{iconsMap && iconsMap[opt] && <Icon name={iconsMap[opt]} />}
|
|
132
136
|
{getOptionLabel(opt, optionsMap)}
|
|
133
137
|
</Checkbox>
|
|
134
138
|
))
|
|
@@ -145,6 +149,7 @@ const FilterForm = (props: FilterFormProps) => {
|
|
|
145
149
|
{items.length
|
|
146
150
|
? items.map((opt) => (
|
|
147
151
|
<Radio key={opt} value={opt}>
|
|
152
|
+
{iconsMap && iconsMap[opt] && <Icon name={iconsMap[opt]} />}
|
|
148
153
|
{getOptionLabel(opt, optionsMap)}
|
|
149
154
|
</Radio>
|
|
150
155
|
))
|
|
@@ -9,24 +9,23 @@ import PopoverTrigger from '../../Popover/PopoverTrigger';
|
|
|
9
9
|
import Text from '../../Text/Text';
|
|
10
10
|
import Tooltip from '../../Tooltip/Tooltip';
|
|
11
11
|
import { FilterStyle } from '../Filter.theme';
|
|
12
|
-
import {
|
|
12
|
+
import { FilterValue } from '../Filter.types';
|
|
13
13
|
import FilterForm from '../FilterForm/FilterForm';
|
|
14
14
|
import { useFilterContext } from '../Filter.context';
|
|
15
15
|
import { getOptionLabel } from '../Filter.utils';
|
|
16
16
|
|
|
17
17
|
export type FilterItemProps = {
|
|
18
18
|
category: string;
|
|
19
|
-
categoryName?: string;
|
|
20
|
-
categoryNamePlural?: string;
|
|
21
|
-
isPermanent?: boolean;
|
|
22
|
-
onChange: (category: string, value: FilterValue) => void;
|
|
23
|
-
onClear: (category: string) => void;
|
|
24
|
-
optionsMap?: FilterOptionsMap;
|
|
25
|
-
value: FilterValue;
|
|
26
19
|
};
|
|
27
20
|
|
|
28
21
|
const FilterItem = (props: FilterItemProps) => {
|
|
29
|
-
const { category
|
|
22
|
+
const { category } = props;
|
|
23
|
+
|
|
24
|
+
const { data, isLoading, onFilterChange, onFilterClear, setPopoverOpen, state } = useFilterContext();
|
|
25
|
+
|
|
26
|
+
const { categoryName, categoryNamePlural, optionsMap, type } = data[category];
|
|
27
|
+
|
|
28
|
+
const value = state[category];
|
|
30
29
|
|
|
31
30
|
const pluralCategoryString = (categoryNamePlural || `${category}s`).toLowerCase();
|
|
32
31
|
|
|
@@ -34,8 +33,6 @@ const FilterItem = (props: FilterItemProps) => {
|
|
|
34
33
|
|
|
35
34
|
const filterStyle = useMultiStyleConfig('Filter') as FilterStyle;
|
|
36
35
|
|
|
37
|
-
const { isLoading, setPopoverOpen } = useFilterContext();
|
|
38
|
-
|
|
39
36
|
const onToggle = () => {
|
|
40
37
|
togglePopover();
|
|
41
38
|
};
|
|
@@ -45,9 +42,9 @@ const FilterItem = (props: FilterItemProps) => {
|
|
|
45
42
|
setPopoverOpen(false);
|
|
46
43
|
};
|
|
47
44
|
|
|
48
|
-
const
|
|
45
|
+
const onChange = (newCategory: string, newValue: FilterValue) => {
|
|
49
46
|
onClose();
|
|
50
|
-
|
|
47
|
+
onFilterChange(newCategory, newValue);
|
|
51
48
|
};
|
|
52
49
|
|
|
53
50
|
const getText = () => {
|
|
@@ -73,15 +70,15 @@ const FilterItem = (props: FilterItemProps) => {
|
|
|
73
70
|
<Tooltip isDisabled={isLoading} label="Edit">
|
|
74
71
|
<Text as="button" disabled={isLoading} onClick={onToggle} size="2" sx={filterStyle.tagEdit}>
|
|
75
72
|
{getText()}
|
|
76
|
-
{
|
|
73
|
+
{type === 'select' && <Icon name="ChevronDown" size="16" />}
|
|
77
74
|
</Text>
|
|
78
75
|
</Tooltip>
|
|
79
|
-
{
|
|
76
|
+
{type !== 'select' && (
|
|
80
77
|
<IconButton
|
|
81
78
|
aria-label={isLoading ? '' : 'Clear'}
|
|
82
79
|
iconName="CloseSmall"
|
|
83
80
|
isDisabled={isLoading}
|
|
84
|
-
onClick={() =>
|
|
81
|
+
onClick={() => onFilterClear(category)}
|
|
85
82
|
size="small"
|
|
86
83
|
variant="tertiary"
|
|
87
84
|
sx={filterStyle.tagClear}
|
|
@@ -91,7 +88,7 @@ const FilterItem = (props: FilterItemProps) => {
|
|
|
91
88
|
</Box>
|
|
92
89
|
</PopoverTrigger>
|
|
93
90
|
<PopoverContent>
|
|
94
|
-
<FilterForm category={category} categoryName={categoryName} onChange={
|
|
91
|
+
<FilterForm category={category} categoryName={categoryName} onChange={onChange} onCancel={onClose} />
|
|
95
92
|
</PopoverContent>
|
|
96
93
|
</Popover>
|
|
97
94
|
);
|
|
@@ -15,12 +15,11 @@ import { FilterValue } from '../Filter.types';
|
|
|
15
15
|
|
|
16
16
|
export interface FilterSearchProps extends Omit<InputProps, 'onChange' | 'value'> {
|
|
17
17
|
onChange: (category: string, selected: FilterValue) => void;
|
|
18
|
-
onClear: (category: string) => void;
|
|
19
18
|
value: string;
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
const FilterSearch = (props: FilterSearchProps) => {
|
|
23
|
-
const { onChange,
|
|
22
|
+
const { onChange, value, ...rest } = props;
|
|
24
23
|
const filterStyle = useMultiStyleConfig('Filter') as FilterStyle;
|
|
25
24
|
|
|
26
25
|
const [searchValue, setSearchValue] = useState(value);
|
|
@@ -31,7 +30,7 @@ const FilterSearch = (props: FilterSearchProps) => {
|
|
|
31
30
|
};
|
|
32
31
|
|
|
33
32
|
const onClearClick = () => {
|
|
34
|
-
|
|
33
|
+
setSearchValue('');
|
|
35
34
|
};
|
|
36
35
|
|
|
37
36
|
useEffect(() => {
|
|
@@ -55,9 +54,11 @@ const FilterSearch = (props: FilterSearchProps) => {
|
|
|
55
54
|
<Icon color="neutral.60" name="Magnifier" size="16" />
|
|
56
55
|
</InputLeftElement>
|
|
57
56
|
<Input {...inputProps} />
|
|
58
|
-
|
|
59
|
-
<
|
|
60
|
-
|
|
57
|
+
{!!searchValue && (
|
|
58
|
+
<InputRightElement>
|
|
59
|
+
<IconButton aria-label="Clear" iconName="CloseSmall" onClick={onClearClick} size="small" variant="tertiary" />
|
|
60
|
+
</InputRightElement>
|
|
61
|
+
)}
|
|
61
62
|
</InputGroup>
|
|
62
63
|
);
|
|
63
64
|
};
|
|
@@ -6,9 +6,12 @@ const FilterSwitch = {
|
|
|
6
6
|
container: {
|
|
7
7
|
color: 'neutral.40',
|
|
8
8
|
background: 'neutral.95',
|
|
9
|
-
borderRadius: '4',
|
|
10
9
|
border: '1px solid',
|
|
11
10
|
borderColor: 'neutral.80',
|
|
11
|
+
display: 'flex',
|
|
12
|
+
borderRadius: '4',
|
|
13
|
+
},
|
|
14
|
+
item: {
|
|
12
15
|
cursor: 'pointer',
|
|
13
16
|
position: 'relative',
|
|
14
17
|
textOverflow: 'ellipsis',
|
|
@@ -19,6 +22,18 @@ const FilterSwitch = {
|
|
|
19
22
|
zIndex: 0,
|
|
20
23
|
fontSize: rem(14),
|
|
21
24
|
lineHeight: rem(20),
|
|
25
|
+
display: 'flex',
|
|
26
|
+
alignItems: 'center',
|
|
27
|
+
gap: '4',
|
|
28
|
+
borderLeft: '1px solid',
|
|
29
|
+
borderLeftColor: 'neutral.80',
|
|
30
|
+
_first: {
|
|
31
|
+
borderLeftRadius: '4',
|
|
32
|
+
borderLeft: 'none',
|
|
33
|
+
},
|
|
34
|
+
_last: {
|
|
35
|
+
borderRightRadius: '4',
|
|
36
|
+
},
|
|
22
37
|
_focusVisible: {
|
|
23
38
|
boxShadow: 'outline',
|
|
24
39
|
zIndex: 1,
|
|
@@ -20,7 +20,7 @@ const FilterSwitch = forwardRef<FilterSwitchProps, 'input'>((props, ref) => {
|
|
|
20
20
|
const group = useRadioGroupContext();
|
|
21
21
|
const { value: valueProp } = props;
|
|
22
22
|
|
|
23
|
-
const styles = useMultiStyleConfig('FilterSwitch'
|
|
23
|
+
const styles = useMultiStyleConfig('FilterSwitch');
|
|
24
24
|
|
|
25
25
|
const ownProps = omitThemingProps(props);
|
|
26
26
|
|
|
@@ -46,7 +46,7 @@ const FilterSwitch = forwardRef<FilterSwitchProps, 'input'>((props, ref) => {
|
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
return (
|
|
49
|
-
<chakra.label {...getLabelProps()} {...getCheckboxProps()} __css={styles.
|
|
49
|
+
<chakra.label {...getLabelProps()} {...getCheckboxProps()} __css={styles.item}>
|
|
50
50
|
<chakra.input {...getInputProps(htmlInputProps, ref)} />
|
|
51
51
|
{children}
|
|
52
52
|
</chakra.label>
|
|
@@ -1,25 +1,11 @@
|
|
|
1
|
-
import { RadioGroup as ChakraRadioGroup, RadioGroupProps } from '@chakra-ui/react';
|
|
1
|
+
import { RadioGroup as ChakraRadioGroup, RadioGroupProps, useMultiStyleConfig } from '@chakra-ui/react';
|
|
2
2
|
|
|
3
3
|
export type { RadioGroupProps };
|
|
4
4
|
|
|
5
5
|
const FilterSwitchGroup = (props: RadioGroupProps) => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
{...props}
|
|
10
|
-
sx={{
|
|
11
|
-
label: {
|
|
12
|
-
_notFirst: {
|
|
13
|
-
borderLeftRadius: 0,
|
|
14
|
-
borderLeft: 'none',
|
|
15
|
-
},
|
|
16
|
-
_notLast: {
|
|
17
|
-
borderRightRadius: 0,
|
|
18
|
-
},
|
|
19
|
-
},
|
|
20
|
-
}}
|
|
21
|
-
/>
|
|
22
|
-
);
|
|
6
|
+
const { container } = useMultiStyleConfig('FilterSwitch');
|
|
7
|
+
|
|
8
|
+
return <ChakraRadioGroup sx={container} {...props} />;
|
|
23
9
|
};
|
|
24
10
|
|
|
25
11
|
export default FilterSwitchGroup;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { useFilterContext } from '../Filter.context';
|
|
2
|
+
import { getOptionLabel } from '../Filter.utils';
|
|
3
|
+
import Icon from '../../Icon/Icon';
|
|
4
|
+
import FilterSwitch from '../FilterSwitch/FilterSwitch';
|
|
5
|
+
import FilterSwitchGroup from '../FilterSwitch/FilterSwitchGroup';
|
|
6
|
+
|
|
7
|
+
type FilterSwitchAdapterProps = {
|
|
8
|
+
category: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const FilterSwitchAdapter = (props: FilterSwitchAdapterProps) => {
|
|
12
|
+
const { category } = props;
|
|
13
|
+
const { data, onFilterChange, state } = useFilterContext();
|
|
14
|
+
const { iconsMap, options, optionsMap } = data[category];
|
|
15
|
+
|
|
16
|
+
if (!options?.length) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const value = state[category]?.[0] || '';
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<FilterSwitchGroup onChange={(newValue) => onFilterChange(category, [newValue])} value={value}>
|
|
24
|
+
{options.map((opt) => (
|
|
25
|
+
<FilterSwitch key={opt} value={opt}>
|
|
26
|
+
{iconsMap && iconsMap[opt] && <Icon name={iconsMap[opt]} size="16" />}
|
|
27
|
+
{getOptionLabel(opt, optionsMap)}
|
|
28
|
+
</FilterSwitch>
|
|
29
|
+
))}
|
|
30
|
+
</FilterSwitchGroup>
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default FilterSwitchAdapter;
|
|
@@ -17,7 +17,16 @@ import Tooltip, { TooltipProps } from '../../Tooltip/Tooltip';
|
|
|
17
17
|
type UsedFormControlProps = Omit<FormControlProps, 'label' | 'onBlur' | 'onChange'>;
|
|
18
18
|
type UsedChakraTextProps = Pick<
|
|
19
19
|
ChakraTextareaProps,
|
|
20
|
-
|
|
20
|
+
| 'onBlur'
|
|
21
|
+
| 'onChange'
|
|
22
|
+
| 'role'
|
|
23
|
+
| 'name'
|
|
24
|
+
| 'value'
|
|
25
|
+
| 'autoComplete'
|
|
26
|
+
| 'autoFocus'
|
|
27
|
+
| 'maxLength'
|
|
28
|
+
| 'minLength'
|
|
29
|
+
| 'placeholder'
|
|
21
30
|
>;
|
|
22
31
|
|
|
23
32
|
export interface TextareaProps extends UsedFormControlProps, UsedChakraTextProps {
|
|
@@ -2,10 +2,9 @@ import { Icon, IconProps, forwardRef } from '@chakra-ui/react';
|
|
|
2
2
|
|
|
3
3
|
const Filter = forwardRef<IconProps, 'svg'>((props, ref) => (
|
|
4
4
|
<Icon ref={ref} viewBox="0 0 16 16" {...props}>
|
|
5
|
-
<path
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
/>
|
|
5
|
+
<path d="M2 4.5H14V6H2V4.5Z" fill="currentColor" />
|
|
6
|
+
<path d="M6 10.5H10V12H6V10.5Z" fill="currentColor" />
|
|
7
|
+
<path d="M12 7.5H4V9H12V7.5Z" fill="currentColor" />
|
|
9
8
|
</Icon>
|
|
10
9
|
));
|
|
11
10
|
|
|
@@ -2,10 +2,9 @@ import { Icon, IconProps, forwardRef } from '@chakra-ui/react';
|
|
|
2
2
|
|
|
3
3
|
const Filter = forwardRef<IconProps, 'svg'>((props, ref) => (
|
|
4
4
|
<Icon ref={ref} viewBox="0 0 24 24" {...props}>
|
|
5
|
-
<path
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
/>
|
|
5
|
+
<path d="m3 7h18v2h-18z" fill="currentColor" />
|
|
6
|
+
<path d="m9 15h6v2h-6z" fill="currentColor" />
|
|
7
|
+
<path d="m18 11h-12v2h12z" fill="currentColor" />
|
|
9
8
|
</Icon>
|
|
10
9
|
));
|
|
11
10
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Fragment } from 'react';
|
|
2
|
+
import { Portal } from '@chakra-ui/react';
|
|
1
3
|
import IconButton, { IconButtonProps } from '../IconButton/IconButton';
|
|
2
4
|
import Menu, { MenuProps } from '../Menu/Menu';
|
|
3
5
|
import MenuButton from '../Menu/MenuButton';
|
|
@@ -7,9 +9,11 @@ export interface OverflowMenuProps extends MenuProps {
|
|
|
7
9
|
children: MenuListProps['children'];
|
|
8
10
|
buttonSize?: IconButtonProps['size'];
|
|
9
11
|
triggerLabel?: string;
|
|
12
|
+
withPortal?: boolean;
|
|
10
13
|
}
|
|
11
14
|
|
|
12
|
-
const OverflowMenu = ({ buttonSize = 'small', children, triggerLabel }: OverflowMenuProps) => {
|
|
15
|
+
const OverflowMenu = ({ buttonSize = 'small', children, triggerLabel, withPortal }: OverflowMenuProps) => {
|
|
16
|
+
const Wrapper = withPortal ? Portal : Fragment;
|
|
13
17
|
return (
|
|
14
18
|
<Menu isLazy>
|
|
15
19
|
<MenuButton
|
|
@@ -20,13 +24,16 @@ const OverflowMenu = ({ buttonSize = 'small', children, triggerLabel }: Overflow
|
|
|
20
24
|
size={buttonSize}
|
|
21
25
|
variant="tertiary"
|
|
22
26
|
/>
|
|
23
|
-
<
|
|
27
|
+
<Wrapper>
|
|
28
|
+
<MenuList>{children}</MenuList>
|
|
29
|
+
</Wrapper>
|
|
24
30
|
</Menu>
|
|
25
31
|
);
|
|
26
32
|
};
|
|
27
33
|
|
|
28
34
|
OverflowMenu.defaultProps = {
|
|
29
35
|
triggerLabel: 'Open menu',
|
|
36
|
+
withPortal: false,
|
|
30
37
|
};
|
|
31
38
|
|
|
32
39
|
export default OverflowMenu;
|
|
@@ -20,6 +20,7 @@ export interface SelectProps extends Omit<FormControlProps, 'label' | 'onBlur' |
|
|
|
20
20
|
isLoading?: boolean;
|
|
21
21
|
label?: ReactNode;
|
|
22
22
|
name?: string;
|
|
23
|
+
placeholder?: ChakraSelectProps['placeholder'];
|
|
23
24
|
onBlur?: ChakraSelectProps['onBlur'];
|
|
24
25
|
onChange?: ChakraSelectProps['onChange'];
|
|
25
26
|
size?: 'small' | 'medium';
|
package/src/index.ts
CHANGED
|
@@ -323,3 +323,7 @@ export { default as TablePagination } from './Components/Table/TablePagination';
|
|
|
323
323
|
|
|
324
324
|
export type { ProgressIndicatorProps } from './Components/ProgressIndicator/ProgressIndicator';
|
|
325
325
|
export { default as ProgressIndicator } from './Components/ProgressIndicator/ProgressIndicator';
|
|
326
|
+
|
|
327
|
+
export type { FilterProps } from './Components/Filter/Filter';
|
|
328
|
+
export { default as Filter } from './Components/Filter/Filter';
|
|
329
|
+
export * from './Components/Filter/Filter.types';
|