@kaizen/components 1.68.3 → 1.68.4
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/dist/cjs/Filter/FilterBar/context/FilterBarContext.cjs +13 -3
- package/dist/cjs/Filter/FilterBar/context/reducer/filterBarStateReducer.cjs +1 -1
- package/dist/cjs/Filter/FilterBar/context/reducer/setupFilterBarState.cjs +4 -0
- package/dist/cjs/Filter/FilterBar/context/utils/updateDependentFilters.cjs +1 -1
- package/dist/cjs/Filter/FilterBar/subcomponents/ClearAllButton/ClearAllButton.cjs +7 -2
- package/dist/cjs/Filter/FilterBar/subcomponents/ClearAllButton/ClearAllButton.module.scss.cjs +2 -1
- package/dist/cjs/Filter/FilterDateRangePicker/subcomponents/FilterDateRangePickerField/FilterDateRangePickerField.cjs +3 -0
- package/dist/esm/Filter/FilterBar/context/FilterBarContext.mjs +13 -3
- package/dist/esm/Filter/FilterBar/context/reducer/filterBarStateReducer.mjs +1 -1
- package/dist/esm/Filter/FilterBar/context/reducer/setupFilterBarState.mjs +4 -0
- package/dist/esm/Filter/FilterBar/context/utils/updateDependentFilters.mjs +1 -1
- package/dist/esm/Filter/FilterBar/subcomponents/ClearAllButton/ClearAllButton.mjs +6 -2
- package/dist/esm/Filter/FilterBar/subcomponents/ClearAllButton/ClearAllButton.module.scss.mjs +2 -1
- package/dist/esm/Filter/FilterDateRangePicker/subcomponents/FilterDateRangePickerField/FilterDateRangePickerField.mjs +3 -0
- package/dist/styles.css +96 -92
- package/dist/types/Filter/FilterBar/context/FilterBarContext.d.ts +1 -0
- package/dist/types/Filter/FilterBar/context/types.d.ts +1 -0
- package/package.json +1 -1
- package/src/Filter/FilterBar/FilterBar.spec.tsx +0 -64
- package/src/Filter/FilterBar/_docs/FilterBar.spec.stories.tsx +249 -0
- package/src/Filter/FilterBar/_docs/FilterBar.stickersheet.stories.tsx +1 -1
- package/src/Filter/FilterBar/_docs/FilterBar.stories.tsx +1 -1
- package/src/Filter/FilterBar/context/FilterBarContext.tsx +17 -5
- package/src/Filter/FilterBar/context/reducer/filterBarStateReducer.spec.ts +3 -0
- package/src/Filter/FilterBar/context/reducer/filterBarStateReducer.ts +1 -1
- package/src/Filter/FilterBar/context/reducer/setupFilterBarState.spec.tsx +40 -0
- package/src/Filter/FilterBar/context/reducer/setupFilterBarState.ts +5 -0
- package/src/Filter/FilterBar/context/reducer/updateSingleFilter.spec.ts +2 -0
- package/src/Filter/FilterBar/context/reducer/updateValues.spec.ts +5 -0
- package/src/Filter/FilterBar/context/types.ts +1 -0
- package/src/Filter/FilterBar/context/utils/checkShouldUpdateValues.spec.ts +1 -0
- package/src/Filter/FilterBar/context/utils/getInactiveFilters.spec.ts +2 -0
- package/src/Filter/FilterBar/context/utils/getIsUsableWhenArgs.spec.ts +1 -0
- package/src/Filter/FilterBar/context/utils/updateDependentFilters.spec.ts +8 -0
- package/src/Filter/FilterBar/context/utils/updateDependentFilters.ts +1 -1
- package/src/Filter/FilterBar/subcomponents/ClearAllButton/ClearAllButton.module.scss +4 -0
- package/src/Filter/FilterBar/subcomponents/ClearAllButton/ClearAllButton.tsx +5 -2
- package/src/Filter/FilterDateRangePicker/subcomponents/FilterDateRangePickerField/FilterDateRangePickerField.tsx +4 -0
|
@@ -35,6 +35,18 @@ var FilterBarProvider = function (_a) {
|
|
|
35
35
|
var _b = React.useReducer(filterBarStateReducer.filterBarStateReducer, setupFilterBarState.setupFilterBarState(filters, values)),
|
|
36
36
|
state = _b[0],
|
|
37
37
|
dispatch = _b[1];
|
|
38
|
+
var activeFilters = Array.from(state.activeFilterIds, function (id) {
|
|
39
|
+
return mappedFilters[id];
|
|
40
|
+
});
|
|
41
|
+
// Workaround for DateRangePicker populating the values object before the value is valid
|
|
42
|
+
// (it purposefully persists a state with a 'from' date but no 'to' date, but hides it on the filter button)
|
|
43
|
+
var isDraftDateRange = function (v) {
|
|
44
|
+
return v && v.from !== undefined && v.to === undefined;
|
|
45
|
+
};
|
|
46
|
+
var hasDraftDateRangeOnly = Object.values(values).every(isDraftDateRange);
|
|
47
|
+
var isClearable = Object.keys(values).length > 0 && !hasDraftDateRangeOnly || state.hasRemovableFilter && activeFilters.some(function (f) {
|
|
48
|
+
return f.isRemovable;
|
|
49
|
+
});
|
|
38
50
|
var value = {
|
|
39
51
|
getFilterState: function (id) {
|
|
40
52
|
return tslib.__assign(tslib.__assign({}, state.filters[id]), {
|
|
@@ -42,6 +54,7 @@ var FilterBarProvider = function (_a) {
|
|
|
42
54
|
value: values[id]
|
|
43
55
|
});
|
|
44
56
|
},
|
|
57
|
+
isClearable: isClearable,
|
|
45
58
|
getActiveFilterValues: function () {
|
|
46
59
|
return values;
|
|
47
60
|
},
|
|
@@ -147,9 +160,6 @@ var FilterBarProvider = function (_a) {
|
|
|
147
160
|
});
|
|
148
161
|
}
|
|
149
162
|
}, [filters]);
|
|
150
|
-
var activeFilters = Array.from(state.activeFilterIds, function (id) {
|
|
151
|
-
return mappedFilters[id];
|
|
152
|
-
});
|
|
153
163
|
return React__default.default.createElement(FilterBarContext.Provider
|
|
154
164
|
// @note: Context object cannot be generic, thus the type-casting to a looser type
|
|
155
165
|
, {
|
|
@@ -25,7 +25,7 @@ var filterBarStateReducer = function (state, action) {
|
|
|
25
25
|
return tslib.__assign({}, updateDependentFilters.updateDependentFilters(state));
|
|
26
26
|
case "deactivate_filter":
|
|
27
27
|
state.activeFilterIds.delete(action.id);
|
|
28
|
-
state.values[action.id]
|
|
28
|
+
delete state.values[action.id];
|
|
29
29
|
return tslib.__assign(tslib.__assign({}, updateDependentFilters.updateDependentFilters(state)), {
|
|
30
30
|
hasUpdatedValues: true
|
|
31
31
|
});
|
|
@@ -22,6 +22,9 @@ var setupFilterBarState = function (filters, values) {
|
|
|
22
22
|
if (!isRemovable || values[id] !== undefined) {
|
|
23
23
|
baseState.activeFilterIds.add(id);
|
|
24
24
|
}
|
|
25
|
+
if (isRemovable) {
|
|
26
|
+
baseState.hasRemovableFilter = true;
|
|
27
|
+
}
|
|
25
28
|
return baseState;
|
|
26
29
|
}, {
|
|
27
30
|
filters: {},
|
|
@@ -29,6 +32,7 @@ var setupFilterBarState = function (filters, values) {
|
|
|
29
32
|
values: values,
|
|
30
33
|
dependentFilterIds: new Set(),
|
|
31
34
|
hasUpdatedValues: false,
|
|
35
|
+
hasRemovableFilter: false,
|
|
32
36
|
focusId: undefined
|
|
33
37
|
});
|
|
34
38
|
return updateDependentFilters.updateDependentFilters(state);
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var React = require('react');
|
|
4
4
|
var i18nReactIntl = require('@cultureamp/i18n-react-intl');
|
|
5
|
+
var classnames = require('classnames');
|
|
5
6
|
var FilterBarContext = require('../../context/FilterBarContext.cjs');
|
|
6
7
|
var ClearAllButton_module = require('./ClearAllButton.module.scss.cjs');
|
|
7
8
|
var Button = require('../../../../__actions__/Button/v1/Button/Button.cjs');
|
|
@@ -11,7 +12,9 @@ function _interopDefault(e) {
|
|
|
11
12
|
};
|
|
12
13
|
}
|
|
13
14
|
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
15
|
+
var classnames__default = /*#__PURE__*/_interopDefault(classnames);
|
|
14
16
|
var ClearAllButton = function () {
|
|
17
|
+
var _a;
|
|
15
18
|
var formatMessage = i18nReactIntl.useIntl().formatMessage;
|
|
16
19
|
var clearButtonLabel = formatMessage({
|
|
17
20
|
id: "filterBar.clearAllButton.label",
|
|
@@ -23,11 +26,13 @@ var ClearAllButton = function () {
|
|
|
23
26
|
defaultMessage: "Clear all filters",
|
|
24
27
|
description: "Button aria-label to clear all values within the filter bar"
|
|
25
28
|
});
|
|
26
|
-
var
|
|
29
|
+
var _b = FilterBarContext.useFilterBarContext(),
|
|
30
|
+
clearAllFilters = _b.clearAllFilters,
|
|
31
|
+
isClearable = _b.isClearable;
|
|
27
32
|
return React__default.default.createElement(Button.Button, {
|
|
28
33
|
label: clearButtonLabel,
|
|
29
34
|
"aria-label": clearButtonAriaLabel,
|
|
30
|
-
classNameOverride: ClearAllButton_module.clearAllButton,
|
|
35
|
+
classNameOverride: classnames__default.default(ClearAllButton_module.clearAllButton, (_a = {}, _a[ClearAllButton_module.hidden] = !isClearable, _a)),
|
|
31
36
|
secondary: true,
|
|
32
37
|
onClick: clearAllFilters
|
|
33
38
|
});
|
|
@@ -184,6 +184,9 @@ var FilterDateRangePickerField = function (_a) {
|
|
|
184
184
|
// Translations are loading
|
|
185
185
|
return;
|
|
186
186
|
}
|
|
187
|
+
if (state.inputStartValue === "" && state.inputEndValue === "") {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
187
190
|
var newStartDate = validateStartDate(selectedRange === null || selectedRange === void 0 ? void 0 : selectedRange.from, state.inputStartValue);
|
|
188
191
|
var newEndDate = validateEndDate(selectedRange === null || selectedRange === void 0 ? void 0 : selectedRange.to, state.inputEndValue);
|
|
189
192
|
if (newStartDate && !isValidRange.isValidRange(newStartDate, newEndDate)) {
|
|
@@ -27,6 +27,18 @@ var FilterBarProvider = function (_a) {
|
|
|
27
27
|
var _b = useReducer(filterBarStateReducer, setupFilterBarState(filters, values)),
|
|
28
28
|
state = _b[0],
|
|
29
29
|
dispatch = _b[1];
|
|
30
|
+
var activeFilters = Array.from(state.activeFilterIds, function (id) {
|
|
31
|
+
return mappedFilters[id];
|
|
32
|
+
});
|
|
33
|
+
// Workaround for DateRangePicker populating the values object before the value is valid
|
|
34
|
+
// (it purposefully persists a state with a 'from' date but no 'to' date, but hides it on the filter button)
|
|
35
|
+
var isDraftDateRange = function (v) {
|
|
36
|
+
return v && v.from !== undefined && v.to === undefined;
|
|
37
|
+
};
|
|
38
|
+
var hasDraftDateRangeOnly = Object.values(values).every(isDraftDateRange);
|
|
39
|
+
var isClearable = Object.keys(values).length > 0 && !hasDraftDateRangeOnly || state.hasRemovableFilter && activeFilters.some(function (f) {
|
|
40
|
+
return f.isRemovable;
|
|
41
|
+
});
|
|
30
42
|
var value = {
|
|
31
43
|
getFilterState: function (id) {
|
|
32
44
|
return __assign(__assign({}, state.filters[id]), {
|
|
@@ -34,6 +46,7 @@ var FilterBarProvider = function (_a) {
|
|
|
34
46
|
value: values[id]
|
|
35
47
|
});
|
|
36
48
|
},
|
|
49
|
+
isClearable: isClearable,
|
|
37
50
|
getActiveFilterValues: function () {
|
|
38
51
|
return values;
|
|
39
52
|
},
|
|
@@ -139,9 +152,6 @@ var FilterBarProvider = function (_a) {
|
|
|
139
152
|
});
|
|
140
153
|
}
|
|
141
154
|
}, [filters]);
|
|
142
|
-
var activeFilters = Array.from(state.activeFilterIds, function (id) {
|
|
143
|
-
return mappedFilters[id];
|
|
144
|
-
});
|
|
145
155
|
return /*#__PURE__*/React.createElement(FilterBarContext.Provider
|
|
146
156
|
// @note: Context object cannot be generic, thus the type-casting to a looser type
|
|
147
157
|
, {
|
|
@@ -23,7 +23,7 @@ var filterBarStateReducer = function (state, action) {
|
|
|
23
23
|
return __assign({}, updateDependentFilters(state));
|
|
24
24
|
case "deactivate_filter":
|
|
25
25
|
state.activeFilterIds.delete(action.id);
|
|
26
|
-
state.values[action.id]
|
|
26
|
+
delete state.values[action.id];
|
|
27
27
|
return __assign(__assign({}, updateDependentFilters(state)), {
|
|
28
28
|
hasUpdatedValues: true
|
|
29
29
|
});
|
|
@@ -20,6 +20,9 @@ var setupFilterBarState = function (filters, values) {
|
|
|
20
20
|
if (!isRemovable || values[id] !== undefined) {
|
|
21
21
|
baseState.activeFilterIds.add(id);
|
|
22
22
|
}
|
|
23
|
+
if (isRemovable) {
|
|
24
|
+
baseState.hasRemovableFilter = true;
|
|
25
|
+
}
|
|
23
26
|
return baseState;
|
|
24
27
|
}, {
|
|
25
28
|
filters: {},
|
|
@@ -27,6 +30,7 @@ var setupFilterBarState = function (filters, values) {
|
|
|
27
30
|
values: values,
|
|
28
31
|
dependentFilterIds: new Set(),
|
|
29
32
|
hasUpdatedValues: false,
|
|
33
|
+
hasRemovableFilter: false,
|
|
30
34
|
focusId: undefined
|
|
31
35
|
});
|
|
32
36
|
return updateDependentFilters(state);
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { useIntl } from '@cultureamp/i18n-react-intl';
|
|
3
|
+
import classnames from 'classnames';
|
|
3
4
|
import { useFilterBarContext } from '../../context/FilterBarContext.mjs';
|
|
4
5
|
import styles from './ClearAllButton.module.scss.mjs';
|
|
5
6
|
import { Button } from '../../../../__actions__/Button/v1/Button/Button.mjs';
|
|
6
7
|
const ClearAllButton = /*#__PURE__*/function () {
|
|
7
8
|
const ClearAllButton = function () {
|
|
9
|
+
var _a;
|
|
8
10
|
var formatMessage = useIntl().formatMessage;
|
|
9
11
|
var clearButtonLabel = formatMessage({
|
|
10
12
|
id: "filterBar.clearAllButton.label",
|
|
@@ -16,11 +18,13 @@ const ClearAllButton = /*#__PURE__*/function () {
|
|
|
16
18
|
defaultMessage: "Clear all filters",
|
|
17
19
|
description: "Button aria-label to clear all values within the filter bar"
|
|
18
20
|
});
|
|
19
|
-
var
|
|
21
|
+
var _b = useFilterBarContext(),
|
|
22
|
+
clearAllFilters = _b.clearAllFilters,
|
|
23
|
+
isClearable = _b.isClearable;
|
|
20
24
|
return /*#__PURE__*/React.createElement(Button, {
|
|
21
25
|
label: clearButtonLabel,
|
|
22
26
|
"aria-label": clearButtonAriaLabel,
|
|
23
|
-
classNameOverride: styles.clearAllButton,
|
|
27
|
+
classNameOverride: classnames(styles.clearAllButton, (_a = {}, _a[styles.hidden] = !isClearable, _a)),
|
|
24
28
|
secondary: true,
|
|
25
29
|
onClick: clearAllFilters
|
|
26
30
|
});
|
|
@@ -176,6 +176,9 @@ const FilterDateRangePickerField = /*#__PURE__*/function () {
|
|
|
176
176
|
// Translations are loading
|
|
177
177
|
return;
|
|
178
178
|
}
|
|
179
|
+
if (state.inputStartValue === "" && state.inputEndValue === "") {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
179
182
|
var newStartDate = validateStartDate(selectedRange === null || selectedRange === void 0 ? void 0 : selectedRange.from, state.inputStartValue);
|
|
180
183
|
var newEndDate = validateEndDate(selectedRange === null || selectedRange === void 0 ? void 0 : selectedRange.to, state.inputEndValue);
|
|
181
184
|
if (newStartDate && !isValidRange(newStartDate, newEndDate)) {
|
package/dist/styles.css
CHANGED
|
@@ -1,4 +1,85 @@
|
|
|
1
1
|
@layer tokens, normalize, reset;@layer tokens{:root{--theme-key:heart;--animation-easing-function-ease-in-out:cubic-bezier(0.455,0.03,0.515,0.955);--animation-easing-function-ease-in:cubic-bezier(0.55,0.085,0.68,0.53);--animation-easing-function-ease-out:cubic-bezier(0.25,0.46,0.45,0.94);--animation-easing-function-linear:linear;--animation-easing-function-bounce-in:cubic-bezier(0.485,0.155,0.24,1.245);--animation-easing-function-bounce-out:cubic-bezier(0.485,0.155,0.515,0.845);--animation-easing-function-bounce-in-out:cubic-bezier(0.76,-0.245,0.24,1.245);--animation-duration-instant:0ms;--animation-duration-immediate:100ms;--animation-duration-rapid:200ms;--animation-duration-fast:300ms;--animation-duration-slow:400ms;--animation-duration-deliberate:700ms;--border-solid-border-width:2px;--border-solid-border-radius:7px;--border-solid-border-style:solid;--border-solid-border-color:#e1e2ea;--border-solid-border-color-rgb:225,226,234;--border-dashed-border-width:2px;--border-dashed-border-radius:7px;--border-dashed-border-style:dashed;--border-borderless-border-width:2px;--border-borderless-border-radius:7px;--border-borderless-border-style:solid;--border-borderless-border-color:transparent;--border-borderless-border-color-rgb:0,0,0;--border-focus-ring-border-width:2px;--border-focus-ring-border-radius:10px;--border-focus-ring-border-style:solid;--border-width-1:1px;--color-purple-100:#f4edf8;--color-purple-100-rgb:244,237,248;--color-purple-200:#dfc9ea;--color-purple-200-rgb:223,201,234;--color-purple-300:#c9a5dd;--color-purple-300-rgb:201,165,221;--color-purple-400:#ae67b1;--color-purple-400-rgb:174,103,177;--color-purple-500:#844587;--color-purple-500-rgb:132,69,135;--color-purple-600:#5f3361;--color-purple-600-rgb:95,51,97;--color-purple-700:#4a234d;--color-purple-700-rgb:74,35,77;--color-purple-800:#2f2438;--color-purple-800-rgb:47,36,56;--color-blue-100:#e6f6ff;--color-blue-100-rgb:230,246,255;--color-blue-200:#bde2f5;--color-blue-200-rgb:189,226,245;--color-blue-300:#73c0e8;--color-blue-300-rgb:115,192,232;--color-blue-400:#008bd6;--color-blue-400-rgb:0,139,214;--color-blue-500:#0168b3;--color-blue-500-rgb:1,104,179;--color-blue-600:#004970;--color-blue-600-rgb:0,73,112;--color-blue-700:#003157;--color-blue-700-rgb:0,49,87;--color-green-100:#e8f8f4;--color-green-100-rgb:232,248,244;--color-green-200:#c4ede2;--color-green-200-rgb:196,237,226;--color-green-300:#8fdbc7;--color-green-300-rgb:143,219,199;--color-green-400:#5dcaad;--color-green-400-rgb:93,202,173;--color-green-500:#3f9a86;--color-green-500-rgb:63,154,134;--color-green-600:#2c7d67;--color-green-600-rgb:44,125,103;--color-green-700:#22594a;--color-green-700-rgb:34,89,74;--color-yellow-100:#fff9e4;--color-yellow-100-rgb:255,249,228;--color-yellow-200:#ffeeb3;--color-yellow-200-rgb:255,238,179;--color-yellow-300:#ffe36e;--color-yellow-300-rgb:255,227,110;--color-yellow-400:#ffca4d;--color-yellow-400-rgb:255,202,77;--color-yellow-500:#ffb600;--color-yellow-500-rgb:255,182,0;--color-yellow-600:#c68600;--color-yellow-600-rgb:198,134,0;--color-yellow-700:#876400;--color-yellow-700-rgb:135,100,0;--color-red-100:#fdeaee;--color-red-100-rgb:253,234,238;--color-red-200:#f9c2cb;--color-red-200-rgb:249,194,203;--color-red-300:#f597a8;--color-red-300-rgb:245,151,168;--color-red-400:#e0707d;--color-red-400-rgb:224,112,125;--color-red-500:#c93b55;--color-red-500-rgb:201,59,85;--color-red-600:#a82433;--color-red-600-rgb:168,36,51;--color-red-700:#6c1e20;--color-red-700-rgb:108,30,32;--color-orange-100:#fff0e8;--color-orange-100-rgb:255,240,232;--color-orange-200:#ffd1b9;--color-orange-200-rgb:255,209,185;--color-orange-300:#ffb08a;--color-orange-300-rgb:255,176,138;--color-orange-400:#ff9461;--color-orange-400-rgb:255,148,97;--color-orange-500:#e96c2f;--color-orange-500-rgb:233,108,47;--color-orange-600:#b74302;--color-orange-600-rgb:183,67,2;--color-orange-700:#903c00;--color-orange-700-rgb:144,60,0;--color-gray-100:#f9f9f9;--color-gray-100-rgb:249,249,249;--color-gray-200:#f4f4f5;--color-gray-200-rgb:244,244,245;--color-gray-300:#eaeaec;--color-gray-300-rgb:234,234,236;--color-gray-400:#cdcdd0;--color-gray-400-rgb:205,205,208;--color-gray-500:#878792;--color-gray-500-rgb:135,135,146;--color-gray-600:#524e56;--color-gray-600-rgb:82,78,86;--color-white:#fff;--color-white-rgb:255,255,255;--color-black:#000;--color-black-rgb:0,0,0;--data-viz-favorable:#7dd5bd;--data-viz-favorable-rgb:125,213,189;--data-viz-unfavorable:#e68d97;--data-viz-unfavorable-rgb:230,141,151;--layout-content-max-width:1392px;--layout-content-max-width-with-sidebar:1080px;--layout-content-side-margin:72px;--layout-mobile-actions-drawer-height:60px;--layout-navigation-bar-height:72px;--layout-breakpoints-medium:768px;--layout-breakpoints-large:1080px;--shadow-small-box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 3px 16px 0 rgba(0,0,0,.06);--shadow-large-box-shadow:0 3px 9px 0 rgba(0,0,0,.1),0 8px 40px 0 rgba(0,0,0,.08);--spacing-0:0;--spacing-1:.0625rem;--spacing-2:.125rem;--spacing-4:.25rem;--spacing-6:.375rem;--spacing-8:.5rem;--spacing-12:.75rem;--spacing-16:1rem;--spacing-20:1.25rem;--spacing-24:1.5rem;--spacing-32:2rem;--spacing-40:2.5rem;--spacing-48:3rem;--spacing-56:3.5rem;--spacing-64:4rem;--spacing-72:4.5rem;--spacing-80:5rem;--spacing-96:6rem;--spacing-112:7rem;--spacing-128:8rem;--spacing-160:10rem;--spacing-200:12.5rem;--spacing-240:15rem;--spacing-280:17.5rem;--spacing-320:20rem;--spacing-xs:0.375rem;--spacing-sm:0.75rem;--spacing-md:1.5rem;--spacing-lg:2.25rem;--spacing-xl:3rem;--spacing-xxl:3.75rem;--spacing-xxxl:4.5rem;--spacing-xxxxl:5.25rem;--spacing-xxxxxl:6rem;--typography-data-large-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-data-large-font-weight:700;--typography-data-large-font-size:5.25rem;--typography-data-large-line-height:5.25rem;--typography-data-large-letter-spacing:normal;--typography-data-large-units-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-data-large-units-font-weight:700;--typography-data-large-units-font-size:2.625rem;--typography-data-large-units-line-height:5.25rem;--typography-data-large-units-letter-spacing:normal;--typography-data-medium-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-data-medium-font-weight:700;--typography-data-medium-font-size:3rem;--typography-data-medium-line-height:5rem;--typography-data-medium-letter-spacing:normal;--typography-data-medium-units-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-data-medium-units-font-weight:700;--typography-data-medium-units-font-size:1.5rem;--typography-data-medium-units-line-height:5rem;--typography-data-medium-units-letter-spacing:normal;--typography-data-small-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-data-small-font-weight:700;--typography-data-small-font-size:1.5rem;--typography-data-small-line-height:1.5rem;--typography-data-small-letter-spacing:normal;--typography-data-small-units-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-data-small-units-font-weight:700;--typography-data-small-units-font-size:1.125rem;--typography-data-small-units-line-height:1.5rem;--typography-data-small-units-letter-spacing:normal;--typography-display-0-font-family:"Tiempos Headline",Georgia,serif;--typography-display-0-font-weight:800;--typography-display-0-font-size:4.5rem;--typography-display-0-line-height:5.25rem;--typography-display-0-letter-spacing:0em;--typography-heading-1-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-heading-1-font-weight:500;--typography-heading-1-font-size:2.125rem;--typography-heading-1-line-height:2.625rem;--typography-heading-1-letter-spacing:normal;--typography-heading-2-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-heading-2-font-weight:600;--typography-heading-2-font-size:1.75rem;--typography-heading-2-line-height:2.25rem;--typography-heading-2-letter-spacing:normal;--typography-heading-3-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-heading-3-font-weight:600;--typography-heading-3-font-size:1.375rem;--typography-heading-3-line-height:1.875rem;--typography-heading-3-letter-spacing:normal;--typography-heading-4-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-heading-4-font-weight:600;--typography-heading-4-font-size:1.125rem;--typography-heading-4-line-height:1.5rem;--typography-heading-4-letter-spacing:normal;--typography-heading-5-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-heading-5-font-weight:600;--typography-heading-5-font-size:1rem;--typography-heading-5-line-height:1.5rem;--typography-heading-5-letter-spacing:normal;--typography-heading-6-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-heading-6-font-weight:600;--typography-heading-6-font-size:0.875rem;--typography-heading-6-line-height:1.5rem;--typography-heading-6-letter-spacing:normal;--typography-paragraph-intro-lede-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-paragraph-intro-lede-font-weight:400;--typography-paragraph-intro-lede-font-size:1.25rem;--typography-paragraph-intro-lede-line-height:1.875rem;--typography-paragraph-intro-lede-letter-spacing:0;--typography-paragraph-intro-lede-max-width:975px;--typography-paragraph-body-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-paragraph-body-font-weight:400;--typography-paragraph-body-font-size:1rem;--typography-paragraph-body-line-height:1.5rem;--typography-paragraph-body-letter-spacing:normal;--typography-paragraph-body-max-width:780px;--typography-paragraph-small-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-paragraph-small-font-weight:400;--typography-paragraph-small-font-size:0.875rem;--typography-paragraph-small-line-height:1.125rem;--typography-paragraph-small-letter-spacing:normal;--typography-paragraph-small-max-width:680px;--typography-paragraph-extra-small-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-paragraph-extra-small-font-weight:400;--typography-paragraph-extra-small-font-size:0.75rem;--typography-paragraph-extra-small-line-height:1.125rem;--typography-paragraph-extra-small-letter-spacing:normal;--typography-paragraph-extra-small-max-width:600px;--typography-paragraph-bold-font-weight:600;--typography-button-primary-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-button-primary-font-weight:500;--typography-button-primary-font-size:1.125rem;--typography-button-primary-line-height:1.5rem;--typography-button-primary-letter-spacing:normal;--typography-button-secondary-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-button-secondary-font-weight:500;--typography-button-secondary-font-size:1rem;--typography-button-secondary-line-height:1.5rem;--typography-button-secondary-letter-spacing:normal}}@layer normalize{html{text-size-adjust:100%;line-height:1.15}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{appearance:none}::-webkit-file-upload-button{appearance:button;font:inherit}details{display:block}summary{display:list-item}[hidden],template{display:none}}@layer reset{@font-face{font-family:Tiempos Headline;font-weight:800;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/tiempos/tiempos-headline-bold.woff2),url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/tiempos/tiempos-headline-bold.woff)}@font-face{font-family:Tiempos Headline;font-weight:500;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/tiempos/tiempos-headline-medium.woff2),url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/tiempos/tiempos-headline-medium.woff)}@font-face{font-family:Greycliff CF;font-weight:300;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/greycliff/greycliff-cf-light.woff) format("woff")}@font-face{font-family:Greycliff CF;font-weight:400;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/greycliff/greycliff-cf-regular.woff) format("woff")}@font-face{font-family:Greycliff CF;font-weight:500;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/greycliff/greycliff-cf-medium.woff) format("woff")}@font-face{font-family:Greycliff CF;font-weight:600;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/greycliff/greycliff-cf-demi-bold.woff) format("woff")}@font-face{font-family:Greycliff CF;font-weight:700;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/greycliff/greycliff-cf-bold.woff) format("woff")}@font-face{font-family:Greycliff CF;font-weight:800;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/greycliff/greycliff-cf-extra-bold.woff) format("woff")}@font-face{font-family:Inter;font-weight:300;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-light.woff2),url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-light.woff)}@font-face{font-family:Inter;font-weight:400;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-regular.woff2),url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-regular.woff)}@font-face{font-family:Inter;font-weight:500;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-medium.woff2),url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-medium.woff)}@font-face{font-family:Inter;font-weight:600;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-demi-bold.woff2),url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-demi-bold.woff)}@font-face{font-family:Inter;font-weight:700;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-bold.woff2),url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-bold.woff)}@font-face{font-family:Inter;font-weight:800;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-extra-bold.woff2),url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-extra-bold.woff)}@font-face{font-family:IBM Plex Mono;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/ibm-plex-mono/ibm-plex-mono-regular.woff2),url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/ibm-plex-mono/ibm-plex-mono-regular.woff)}}@layer reset{*,:after,:before{border-color:var(--border-solid-border-color,"currentColor");border-style:solid;border-width:0}}
|
|
2
|
+
.MenuItem-module_item__uImZI {
|
|
3
|
+
display: block;
|
|
4
|
+
font-family: var(--typography-paragraph-body-font-family);
|
|
5
|
+
font-size: var(--typography-paragraph-body-font-size);
|
|
6
|
+
letter-spacing: var(--typography-paragraph-body-letter-spacing);
|
|
7
|
+
font-weight: var(--typography-paragraph-body-font-weight);
|
|
8
|
+
line-height: var(--typography-paragraph-body-line-height);
|
|
9
|
+
color: rgba(var(--color-purple-800-rgb), 0.7);
|
|
10
|
+
padding: var(--spacing-6) var(--spacing-8);
|
|
11
|
+
border: var(--border-focus-ring-border-width)
|
|
12
|
+
var(--border-focus-ring-border-style) transparent;
|
|
13
|
+
border-radius: 4px;
|
|
14
|
+
margin-inline: var(--spacing-6);
|
|
15
|
+
text-decoration: none;
|
|
16
|
+
cursor: pointer;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.MenuItem-module_flexWrapper__hiXro {
|
|
20
|
+
display: flex;
|
|
21
|
+
gap: 0 var(--spacing-8);
|
|
22
|
+
align-items: center;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.MenuItem-module_iconWrapper__QoZgd {
|
|
26
|
+
flex-shrink: 0;
|
|
27
|
+
display: flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.MenuItem-module_item__uImZI:focus {
|
|
32
|
+
outline: none;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.MenuItem-module_item__uImZI[data-hovered],
|
|
36
|
+
.MenuItem-module_item__uImZI[data-focus-visible] {
|
|
37
|
+
background-color: var(--color-blue-100);
|
|
38
|
+
color: var(--color-blue-500);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.MenuItem-module_item__uImZI[data-focus-visible] {
|
|
42
|
+
outline: var(--border-focus-ring-border-width)
|
|
43
|
+
var(--border-focus-ring-border-style) var(--color-blue-500);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.MenuItem-module_item__uImZI[data-disabled] {
|
|
47
|
+
opacity: 0.3;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.Menu-module_menu__AowD8 {
|
|
51
|
+
background-color: var(--color-white);
|
|
52
|
+
color: var(--color-purple-800);
|
|
53
|
+
width: 248px;
|
|
54
|
+
max-height: 22rem;
|
|
55
|
+
overflow: auto;
|
|
56
|
+
padding-block: var(--spacing-6);
|
|
57
|
+
outline: none;
|
|
58
|
+
border-radius: var(--border-solid-border-radius);
|
|
59
|
+
box-shadow: var(--shadow-large-box-shadow);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.Menu-module_menu__AowD8 .react-aria-Header {
|
|
63
|
+
font-family: var(--typography-heading-6-font-family);
|
|
64
|
+
font-size: var(--typography-heading-6-font-size);
|
|
65
|
+
letter-spacing: var(--typography-heading-6-letter-spacing);
|
|
66
|
+
font-weight: var(--typography-heading-6-font-weight);
|
|
67
|
+
line-height: var(--typography-heading-6-line-height);
|
|
68
|
+
padding: var(--spacing-6) 10px;
|
|
69
|
+
margin-inline: var(--spacing-6);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.Menu-module_menu__AowD8 section:not(:last-of-type) {
|
|
73
|
+
&::after {
|
|
74
|
+
width: 100%;
|
|
75
|
+
height: 1px;
|
|
76
|
+
background-color: var(--border-solid-border-color);
|
|
77
|
+
content: "";
|
|
78
|
+
display: block;
|
|
79
|
+
margin-block: var(--spacing-6);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
2
83
|
.Button-module_button__vlUCI {
|
|
3
84
|
/* RESET */
|
|
4
85
|
appearance: none;
|
|
@@ -235,85 +316,24 @@
|
|
|
235
316
|
visibility: hidden;
|
|
236
317
|
}
|
|
237
318
|
|
|
238
|
-
.
|
|
239
|
-
display:
|
|
240
|
-
font-family: var(--typography-paragraph-body-font-family);
|
|
241
|
-
font-size: var(--typography-paragraph-body-font-size);
|
|
242
|
-
letter-spacing: var(--typography-paragraph-body-letter-spacing);
|
|
243
|
-
font-weight: var(--typography-paragraph-body-font-weight);
|
|
244
|
-
line-height: var(--typography-paragraph-body-line-height);
|
|
245
|
-
color: rgba(var(--color-purple-800-rgb), 0.7);
|
|
246
|
-
padding: var(--spacing-6) var(--spacing-8);
|
|
247
|
-
border: var(--border-focus-ring-border-width)
|
|
248
|
-
var(--border-focus-ring-border-style) transparent;
|
|
249
|
-
border-radius: 4px;
|
|
250
|
-
margin-inline: var(--spacing-6);
|
|
251
|
-
text-decoration: none;
|
|
252
|
-
cursor: pointer;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
.MenuItem-module_flexWrapper__hiXro {
|
|
256
|
-
display: flex;
|
|
257
|
-
gap: 0 var(--spacing-8);
|
|
258
|
-
align-items: center;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
.MenuItem-module_iconWrapper__QoZgd {
|
|
262
|
-
flex-shrink: 0;
|
|
263
|
-
display: flex;
|
|
319
|
+
.ButtonContent-module_buttonContent__v5mHZ {
|
|
320
|
+
display: inline-flex;
|
|
264
321
|
align-items: center;
|
|
322
|
+
gap: var(--button-icon-gap, var(--spacing-6));
|
|
265
323
|
}
|
|
266
324
|
|
|
267
|
-
.
|
|
268
|
-
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
.MenuItem-module_item__uImZI[data-hovered],
|
|
272
|
-
.MenuItem-module_item__uImZI[data-focus-visible] {
|
|
273
|
-
background-color: var(--color-blue-100);
|
|
274
|
-
color: var(--color-blue-500);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
.MenuItem-module_item__uImZI[data-focus-visible] {
|
|
278
|
-
outline: var(--border-focus-ring-border-width)
|
|
279
|
-
var(--border-focus-ring-border-style) var(--color-blue-500);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
.MenuItem-module_item__uImZI[data-disabled] {
|
|
283
|
-
opacity: 0.3;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
.Menu-module_menu__AowD8 {
|
|
287
|
-
background-color: var(--color-white);
|
|
288
|
-
color: var(--color-purple-800);
|
|
289
|
-
width: 248px;
|
|
290
|
-
max-height: 22rem;
|
|
291
|
-
overflow: auto;
|
|
292
|
-
padding-block: var(--spacing-6);
|
|
293
|
-
outline: none;
|
|
294
|
-
border-radius: var(--border-solid-border-radius);
|
|
295
|
-
box-shadow: var(--shadow-large-box-shadow);
|
|
325
|
+
.ButtonContent-module_large__mLOdb {
|
|
326
|
+
--button-icon-gap: var(--spacing-8);
|
|
296
327
|
}
|
|
297
328
|
|
|
298
|
-
.
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
letter-spacing: var(--typography-heading-6-letter-spacing);
|
|
302
|
-
font-weight: var(--typography-heading-6-font-weight);
|
|
303
|
-
line-height: var(--typography-heading-6-line-height);
|
|
304
|
-
padding: var(--spacing-6) 10px;
|
|
305
|
-
margin-inline: var(--spacing-6);
|
|
329
|
+
.ButtonContent-module_buttonLabel__T5XAq {
|
|
330
|
+
display: inline-flex;
|
|
331
|
+
align-items: center;
|
|
306
332
|
}
|
|
307
333
|
|
|
308
|
-
.
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
height: 1px;
|
|
312
|
-
background-color: var(--border-solid-border-color);
|
|
313
|
-
content: "";
|
|
314
|
-
display: block;
|
|
315
|
-
margin-block: var(--spacing-6);
|
|
316
|
-
}
|
|
334
|
+
.ButtonContent-module_buttonIcon__qkAX- {
|
|
335
|
+
display: inline-flex;
|
|
336
|
+
align-items: center;
|
|
317
337
|
}
|
|
318
338
|
|
|
319
339
|
.PendingContent-module_pendingContent__c4IFS {
|
|
@@ -333,26 +353,6 @@
|
|
|
333
353
|
transform: translate(-50%, -50%);
|
|
334
354
|
}
|
|
335
355
|
|
|
336
|
-
.ButtonContent-module_buttonContent__v5mHZ {
|
|
337
|
-
display: inline-flex;
|
|
338
|
-
align-items: center;
|
|
339
|
-
gap: var(--button-icon-gap, var(--spacing-6));
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
.ButtonContent-module_large__mLOdb {
|
|
343
|
-
--button-icon-gap: var(--spacing-8);
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
.ButtonContent-module_buttonLabel__T5XAq {
|
|
347
|
-
display: inline-flex;
|
|
348
|
-
align-items: center;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
.ButtonContent-module_buttonIcon__qkAX- {
|
|
352
|
-
display: inline-flex;
|
|
353
|
-
align-items: center;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
356
|
/** THIS IS AN AUTOGENERATED FILE **/
|
|
357
357
|
/** THIS IS AN AUTOGENERATED FILE **/
|
|
358
358
|
/** THIS IS AN AUTOGENERATED FILE **/
|
|
@@ -4809,6 +4809,10 @@ input[type=range].InputRange-module_ratingScaleRange__gI-rs::-ms-thumb:not(:disa
|
|
|
4809
4809
|
.ClearAllButton-module_clearAllButton__XNkm2 {
|
|
4810
4810
|
white-space: nowrap;
|
|
4811
4811
|
}
|
|
4812
|
+
|
|
4813
|
+
.ClearAllButton-module_hidden__-L2eA {
|
|
4814
|
+
visibility: hidden;
|
|
4815
|
+
}
|
|
4812
4816
|
.FilterBar-module_filterBar__JEVKL {
|
|
4813
4817
|
display: flex;
|
|
4814
4818
|
padding: var(--spacing-8);
|
|
@@ -2,6 +2,7 @@ import { FilterAttributes, FilterState, Filters, FiltersValues } from "../types"
|
|
|
2
2
|
import { ActiveFiltersArray } from "./types";
|
|
3
3
|
export type FilterBarContextValue<Value, ValuesMap extends FiltersValues = Record<string, Value>> = {
|
|
4
4
|
getFilterState: <Id extends keyof ValuesMap>(id: Id) => FilterState<keyof ValuesMap, ValuesMap[Id]>;
|
|
5
|
+
isClearable: boolean;
|
|
5
6
|
getActiveFilterValues: () => Partial<ValuesMap>;
|
|
6
7
|
/**
|
|
7
8
|
* @deprecated Use `setFilterOpenState` instead.
|
|
@@ -20,6 +20,7 @@ export type FilterBarStateFilters<ValuesMap extends FiltersValues> = {
|
|
|
20
20
|
};
|
|
21
21
|
export type FilterBarState<ValuesMap extends FiltersValues> = {
|
|
22
22
|
hasUpdatedValues: boolean;
|
|
23
|
+
hasRemovableFilter: boolean;
|
|
23
24
|
filters: FilterBarStateFilters<ValuesMap>;
|
|
24
25
|
activeFilterIds: Set<keyof ValuesMap>;
|
|
25
26
|
values: Partial<ValuesMap>;
|
package/package.json
CHANGED
|
@@ -722,70 +722,6 @@ describe("<FilterBar />", () => {
|
|
|
722
722
|
})
|
|
723
723
|
})
|
|
724
724
|
|
|
725
|
-
describe("Clear all", () => {
|
|
726
|
-
it("clears all the values of all the filters", async () => {
|
|
727
|
-
const { getByRole } = render(
|
|
728
|
-
<FilterBarWrapper<ValuesSimple>
|
|
729
|
-
filters={simpleFilters}
|
|
730
|
-
defaultValues={{
|
|
731
|
-
flavour: "jasmine-milk-tea",
|
|
732
|
-
sugarLevel: 50,
|
|
733
|
-
iceLevel: 100,
|
|
734
|
-
}}
|
|
735
|
-
/>
|
|
736
|
-
)
|
|
737
|
-
await waitForI18nContent()
|
|
738
|
-
|
|
739
|
-
const flavourButton = getByRole("button", {
|
|
740
|
-
name: "Flavour : Jasmine Milk Tea",
|
|
741
|
-
})
|
|
742
|
-
const sugarLevelButton = getByRole("button", {
|
|
743
|
-
name: "Sugar Level : 50%",
|
|
744
|
-
})
|
|
745
|
-
const iceLevelButton = getByRole("button", { name: "Ice Level : 100%" })
|
|
746
|
-
|
|
747
|
-
expect(flavourButton).toHaveAccessibleName("Flavour : Jasmine Milk Tea")
|
|
748
|
-
expect(sugarLevelButton).toHaveAccessibleName("Sugar Level : 50%")
|
|
749
|
-
expect(iceLevelButton).toHaveAccessibleName("Ice Level : 100%")
|
|
750
|
-
|
|
751
|
-
await user.click(getByRole("button", { name: "Clear all filters" }))
|
|
752
|
-
|
|
753
|
-
await waitFor(() => {
|
|
754
|
-
expect(flavourButton).toHaveAccessibleName("Flavour")
|
|
755
|
-
expect(sugarLevelButton).toHaveAccessibleName("Sugar Level")
|
|
756
|
-
expect(iceLevelButton).toHaveAccessibleName("Ice Level")
|
|
757
|
-
})
|
|
758
|
-
})
|
|
759
|
-
|
|
760
|
-
it("removes all removable filters", async () => {
|
|
761
|
-
const { getByRole } = render(
|
|
762
|
-
<FilterBarWrapper<ValuesRemovable>
|
|
763
|
-
filters={filtersRemovable}
|
|
764
|
-
defaultValues={{
|
|
765
|
-
flavour: "jasmine-milk-tea",
|
|
766
|
-
topping: "pearls",
|
|
767
|
-
}}
|
|
768
|
-
/>
|
|
769
|
-
)
|
|
770
|
-
await waitForI18nContent()
|
|
771
|
-
|
|
772
|
-
const flavourButton = getByRole("button", {
|
|
773
|
-
name: "Flavour : Jasmine Milk Tea",
|
|
774
|
-
})
|
|
775
|
-
const toppingButton = getByRole("button", { name: "Topping : Pearls" })
|
|
776
|
-
|
|
777
|
-
expect(flavourButton).toBeVisible()
|
|
778
|
-
expect(toppingButton).toBeVisible()
|
|
779
|
-
|
|
780
|
-
await user.click(getByRole("button", { name: "Clear all filters" }))
|
|
781
|
-
|
|
782
|
-
await waitFor(() => {
|
|
783
|
-
expect(flavourButton).not.toBeInTheDocument()
|
|
784
|
-
expect(toppingButton).not.toBeInTheDocument()
|
|
785
|
-
})
|
|
786
|
-
})
|
|
787
|
-
})
|
|
788
|
-
|
|
789
725
|
describe("External events", () => {
|
|
790
726
|
it("allows updating the values via an external event", async () => {
|
|
791
727
|
const Wrapper = (): JSX.Element => {
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import React, { useState } from "react"
|
|
2
|
+
import { Meta, StoryObj } from "@storybook/react"
|
|
3
|
+
import { expect, userEvent, waitFor, within, fn } from "@storybook/test"
|
|
4
|
+
import { FilterMultiSelect } from "~components/Filter/FilterMultiSelect"
|
|
5
|
+
import { DateRange } from "~components/index"
|
|
6
|
+
import { FilterBar, Filters } from "../index"
|
|
7
|
+
|
|
8
|
+
const meta = {
|
|
9
|
+
title: "Components/FilterBar/FilterBar Tests",
|
|
10
|
+
component: FilterBar,
|
|
11
|
+
argTypes: {
|
|
12
|
+
filters: { control: false },
|
|
13
|
+
values: { control: false },
|
|
14
|
+
onValuesChange: { control: false },
|
|
15
|
+
},
|
|
16
|
+
args: {
|
|
17
|
+
filters: [], // Defined in stories
|
|
18
|
+
values: {}, // Defined in stories
|
|
19
|
+
onValuesChange: fn(),
|
|
20
|
+
},
|
|
21
|
+
} satisfies Meta<typeof FilterBar>
|
|
22
|
+
|
|
23
|
+
export default meta
|
|
24
|
+
|
|
25
|
+
type Story = StoryObj<typeof meta>
|
|
26
|
+
|
|
27
|
+
type Values = {
|
|
28
|
+
flavour: string
|
|
29
|
+
deliveryDates: DateRange
|
|
30
|
+
toppings: string[]
|
|
31
|
+
drank: Date
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const filters = [
|
|
35
|
+
{
|
|
36
|
+
id: "flavour",
|
|
37
|
+
name: "Flavour",
|
|
38
|
+
Component: (
|
|
39
|
+
<FilterBar.Select
|
|
40
|
+
items={[
|
|
41
|
+
{ value: "jasmine-milk-tea", label: "Jasmine Milk Tea" },
|
|
42
|
+
{ value: "honey-milk-tea", label: "Honey Milk Tea" },
|
|
43
|
+
{ value: "lychee-green-tea", label: "Lychee Green Tea" },
|
|
44
|
+
]}
|
|
45
|
+
/>
|
|
46
|
+
),
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: "deliveryDates",
|
|
50
|
+
name: "Delivery Dates",
|
|
51
|
+
Component: <FilterBar.DateRangePicker />,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: "toppings",
|
|
55
|
+
name: "Toppings",
|
|
56
|
+
Component: (
|
|
57
|
+
<FilterBar.MultiSelect
|
|
58
|
+
items={[
|
|
59
|
+
{ value: "none", label: "None" },
|
|
60
|
+
{ value: "pearls", label: "Pearls" },
|
|
61
|
+
{ value: "fruit-jelly", label: "Fruit Jelly" },
|
|
62
|
+
{ value: "peanuts", label: "Peanuts" },
|
|
63
|
+
{ value: "coconut", label: "Coconut" },
|
|
64
|
+
{ value: "aloe", label: "Aloe Vera" },
|
|
65
|
+
{ value: "mochi", label: "Mini Mochi" },
|
|
66
|
+
{ value: "popping-pearls", label: "Popping Pearls" },
|
|
67
|
+
]}
|
|
68
|
+
>
|
|
69
|
+
{(): JSX.Element => (
|
|
70
|
+
<>
|
|
71
|
+
<FilterMultiSelect.SearchInput />
|
|
72
|
+
<FilterMultiSelect.ListBox>
|
|
73
|
+
{({ allItems }): JSX.Element | JSX.Element[] =>
|
|
74
|
+
allItems.map(item => (
|
|
75
|
+
<FilterMultiSelect.Option key={item.key} item={item} />
|
|
76
|
+
))
|
|
77
|
+
}
|
|
78
|
+
</FilterMultiSelect.ListBox>
|
|
79
|
+
<FilterMultiSelect.MenuFooter>
|
|
80
|
+
<FilterMultiSelect.SelectAllButton />
|
|
81
|
+
<FilterMultiSelect.ClearButton />
|
|
82
|
+
</FilterMultiSelect.MenuFooter>
|
|
83
|
+
</>
|
|
84
|
+
)}
|
|
85
|
+
</FilterBar.MultiSelect>
|
|
86
|
+
),
|
|
87
|
+
isRemovable: true,
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: "drank",
|
|
91
|
+
name: "Drank",
|
|
92
|
+
Component: <FilterBar.DatePicker />,
|
|
93
|
+
isRemovable: true,
|
|
94
|
+
},
|
|
95
|
+
] satisfies Filters<Values>
|
|
96
|
+
|
|
97
|
+
export const ClearAllFromValue: Story = {
|
|
98
|
+
render: args => {
|
|
99
|
+
const [activeValues, onActiveValuesChange] = useState<Partial<Values>>({})
|
|
100
|
+
return (
|
|
101
|
+
<FilterBar<Values>
|
|
102
|
+
{...args}
|
|
103
|
+
filters={filters}
|
|
104
|
+
values={activeValues}
|
|
105
|
+
onValuesChange={onActiveValuesChange}
|
|
106
|
+
/>
|
|
107
|
+
)
|
|
108
|
+
},
|
|
109
|
+
play: async ({ canvasElement, step }) => {
|
|
110
|
+
const canvas = within(canvasElement.parentElement!)
|
|
111
|
+
|
|
112
|
+
await step(
|
|
113
|
+
"Clear all button hidden by default given no values",
|
|
114
|
+
async () => {
|
|
115
|
+
expect(
|
|
116
|
+
canvas.queryByRole("button", {
|
|
117
|
+
name: "Clear all filters",
|
|
118
|
+
})
|
|
119
|
+
).not.toBeInTheDocument()
|
|
120
|
+
}
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
await step("filter value is added", async () => {
|
|
124
|
+
await userEvent.click(canvas.getByRole("button", { name: "Flavour" }))
|
|
125
|
+
await userEvent.click(
|
|
126
|
+
canvas.getByRole("option", { name: "Jasmine Milk Tea" })
|
|
127
|
+
)
|
|
128
|
+
expect(
|
|
129
|
+
canvas.getByRole("button", { name: "Flavour: Jasmine Milk Tea" })
|
|
130
|
+
).toBeInTheDocument()
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
await step(
|
|
134
|
+
"'Clear all' press removes the value and hides itself",
|
|
135
|
+
async () => {
|
|
136
|
+
const clearAllButton = canvas.getByRole("button", {
|
|
137
|
+
name: "Clear all filters",
|
|
138
|
+
})
|
|
139
|
+
userEvent.click(clearAllButton)
|
|
140
|
+
|
|
141
|
+
waitFor(() =>
|
|
142
|
+
expect(
|
|
143
|
+
canvas.getByRole("button", { name: "Flavour" })
|
|
144
|
+
).toBeInTheDocument()
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
waitFor(() =>
|
|
148
|
+
expect(
|
|
149
|
+
canvas.queryByRole("button", {
|
|
150
|
+
name: "Clear all filters",
|
|
151
|
+
})
|
|
152
|
+
).not.toBeInTheDocument()
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
)
|
|
156
|
+
},
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export const ClearAllFromRemovable: Story = {
|
|
160
|
+
render: args => {
|
|
161
|
+
const [activeValues, onActiveValuesChange] = useState<Partial<Values>>({})
|
|
162
|
+
return (
|
|
163
|
+
<FilterBar<Values>
|
|
164
|
+
{...args}
|
|
165
|
+
filters={filters}
|
|
166
|
+
values={activeValues}
|
|
167
|
+
onValuesChange={onActiveValuesChange}
|
|
168
|
+
/>
|
|
169
|
+
)
|
|
170
|
+
},
|
|
171
|
+
play: async ({ canvasElement, step }) => {
|
|
172
|
+
const canvas = within(canvasElement.parentElement!)
|
|
173
|
+
|
|
174
|
+
await step("removable filter is added with no value", async () => {
|
|
175
|
+
await waitFor(() => {
|
|
176
|
+
userEvent.click(canvas.getByRole("button", { name: "Add Filters" }))
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
await waitFor(() => {
|
|
180
|
+
userEvent.click(canvas.getByRole("button", { name: "Toppings" }))
|
|
181
|
+
})
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
await step("'Clear all' press removes removable filter", async () => {
|
|
185
|
+
await waitFor(() => {
|
|
186
|
+
userEvent.click(
|
|
187
|
+
canvas.getByRole("button", {
|
|
188
|
+
name: "Clear all filters",
|
|
189
|
+
})
|
|
190
|
+
)
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
waitFor(() =>
|
|
194
|
+
expect(
|
|
195
|
+
canvas.queryByRole("button", { name: "Toppings" })
|
|
196
|
+
).not.toBeInTheDocument()
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
waitFor(() =>
|
|
200
|
+
expect(
|
|
201
|
+
canvas.queryByRole("button", {
|
|
202
|
+
name: "Clear all filters",
|
|
203
|
+
})
|
|
204
|
+
).not.toBeInTheDocument()
|
|
205
|
+
)
|
|
206
|
+
})
|
|
207
|
+
},
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export const ClearAllRemovesItself: Story = {
|
|
211
|
+
render: args => {
|
|
212
|
+
const [activeValues, onActiveValuesChange] = useState<Partial<Values>>({})
|
|
213
|
+
return (
|
|
214
|
+
<FilterBar<Values>
|
|
215
|
+
{...args}
|
|
216
|
+
filters={filters}
|
|
217
|
+
values={activeValues}
|
|
218
|
+
onValuesChange={onActiveValuesChange}
|
|
219
|
+
/>
|
|
220
|
+
)
|
|
221
|
+
},
|
|
222
|
+
play: async ({ canvasElement, step }) => {
|
|
223
|
+
const canvas = within(canvasElement.parentElement!)
|
|
224
|
+
|
|
225
|
+
await step("removable filter is added with no value", async () => {
|
|
226
|
+
await waitFor(() =>
|
|
227
|
+
userEvent.click(canvas.getByRole("button", { name: "Add Filters" }))
|
|
228
|
+
)
|
|
229
|
+
await userEvent.click(canvas.getByRole("button", { name: "Drank" }))
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
await step(
|
|
233
|
+
"Clear all button hides by itself after removing filter",
|
|
234
|
+
async () => {
|
|
235
|
+
await userEvent.click(
|
|
236
|
+
canvas.getByRole("button", { name: "Remove filter - Drank" })
|
|
237
|
+
)
|
|
238
|
+
}
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
waitFor(() =>
|
|
242
|
+
expect(
|
|
243
|
+
canvas.queryByRole("button", {
|
|
244
|
+
name: "Clear all filters",
|
|
245
|
+
})
|
|
246
|
+
).not.toBeInTheDocument()
|
|
247
|
+
)
|
|
248
|
+
},
|
|
249
|
+
}
|
|
@@ -21,7 +21,7 @@ import { FilterBar, Filters, useFilterBarContext } from "../index"
|
|
|
21
21
|
import { FilterBarMultiSelectProps } from "../subcomponents"
|
|
22
22
|
|
|
23
23
|
const meta = {
|
|
24
|
-
title: "Components/
|
|
24
|
+
title: "Components/FilterBar",
|
|
25
25
|
component: FilterBar,
|
|
26
26
|
argTypes: {
|
|
27
27
|
filters: { control: false },
|
|
@@ -22,6 +22,7 @@ export type FilterBarContextValue<
|
|
|
22
22
|
getFilterState: <Id extends keyof ValuesMap>(
|
|
23
23
|
id: Id
|
|
24
24
|
) => FilterState<keyof ValuesMap, ValuesMap[Id]>
|
|
25
|
+
isClearable: boolean
|
|
25
26
|
getActiveFilterValues: () => Partial<ValuesMap>
|
|
26
27
|
/**
|
|
27
28
|
* @deprecated Use `setFilterOpenState` instead.
|
|
@@ -90,12 +91,28 @@ export const FilterBarProvider = <ValuesMap extends FiltersValues>({
|
|
|
90
91
|
setupFilterBarState<ValuesMap>(filters, values)
|
|
91
92
|
)
|
|
92
93
|
|
|
94
|
+
const activeFilters = Array.from(
|
|
95
|
+
state.activeFilterIds,
|
|
96
|
+
id => mappedFilters[id]
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
// Workaround for DateRangePicker populating the values object before the value is valid
|
|
100
|
+
// (it purposefully persists a state with a 'from' date but no 'to' date, but hides it on the filter button)
|
|
101
|
+
const isDraftDateRange = (v: ValuesMap): boolean =>
|
|
102
|
+
v && v.from !== undefined && v.to === undefined
|
|
103
|
+
const hasDraftDateRangeOnly = Object.values(values).every(isDraftDateRange)
|
|
104
|
+
|
|
105
|
+
const isClearable =
|
|
106
|
+
(Object.keys(values).length > 0 && !hasDraftDateRangeOnly) ||
|
|
107
|
+
(state.hasRemovableFilter && activeFilters.some(f => f.isRemovable))
|
|
108
|
+
|
|
93
109
|
const value = {
|
|
94
110
|
getFilterState: <Id extends keyof ValuesMap>(id: Id) => ({
|
|
95
111
|
...state.filters[id],
|
|
96
112
|
isActive: state.activeFilterIds.has(id),
|
|
97
113
|
value: values[id],
|
|
98
114
|
}),
|
|
115
|
+
isClearable,
|
|
99
116
|
getActiveFilterValues: () => values,
|
|
100
117
|
toggleOpenFilter: <Id extends keyof ValuesMap>(
|
|
101
118
|
id: Id,
|
|
@@ -163,11 +180,6 @@ export const FilterBarProvider = <ValuesMap extends FiltersValues>({
|
|
|
163
180
|
}
|
|
164
181
|
}, [filters])
|
|
165
182
|
|
|
166
|
-
const activeFilters = Array.from(
|
|
167
|
-
state.activeFilterIds,
|
|
168
|
-
id => mappedFilters[id]
|
|
169
|
-
)
|
|
170
|
-
|
|
171
183
|
return (
|
|
172
184
|
<FilterBarContext.Provider
|
|
173
185
|
// @note: Context object cannot be generic, thus the type-casting to a looser type
|
|
@@ -32,6 +32,7 @@ describe("filterBarStateReducer", () => {
|
|
|
32
32
|
values: {},
|
|
33
33
|
dependentFilterIds: new Set(),
|
|
34
34
|
hasUpdatedValues: true,
|
|
35
|
+
hasRemovableFilter: false,
|
|
35
36
|
} satisfies FilterBarState<Values>
|
|
36
37
|
|
|
37
38
|
const newState = filterBarStateReducer<Values>(state, {
|
|
@@ -50,6 +51,7 @@ describe("filterBarStateReducer", () => {
|
|
|
50
51
|
values: {},
|
|
51
52
|
dependentFilterIds: new Set(),
|
|
52
53
|
hasUpdatedValues: false,
|
|
54
|
+
hasRemovableFilter: false,
|
|
53
55
|
} satisfies FilterBarState<Values>
|
|
54
56
|
|
|
55
57
|
const newState = filterBarStateReducer<Values>(state, {
|
|
@@ -71,6 +73,7 @@ describe("filterBarStateReducer", () => {
|
|
|
71
73
|
values: { flavour: "jasmine" },
|
|
72
74
|
dependentFilterIds: new Set(),
|
|
73
75
|
hasUpdatedValues: false,
|
|
76
|
+
hasRemovableFilter: false,
|
|
74
77
|
} satisfies FilterBarState<Values>
|
|
75
78
|
|
|
76
79
|
const newState = filterBarStateReducer<Values>(state, {
|
|
@@ -49,7 +49,7 @@ export const filterBarStateReducer = <ValuesMap extends FiltersValues>(
|
|
|
49
49
|
|
|
50
50
|
case "deactivate_filter":
|
|
51
51
|
state.activeFilterIds.delete(action.id)
|
|
52
|
-
state.values[action.id]
|
|
52
|
+
delete state.values[action.id]
|
|
53
53
|
return {
|
|
54
54
|
...updateDependentFilters(state),
|
|
55
55
|
hasUpdatedValues: true,
|
|
@@ -17,6 +17,16 @@ const filters = [
|
|
|
17
17
|
},
|
|
18
18
|
] satisfies Filters<Values>
|
|
19
19
|
|
|
20
|
+
const filtersNoRemovable = [
|
|
21
|
+
{ id: "flavour", name: "Flavour", Component: <div /> },
|
|
22
|
+
{
|
|
23
|
+
id: "sugarLevel",
|
|
24
|
+
name: "Sugar Level",
|
|
25
|
+
Component: <div />,
|
|
26
|
+
isRemovable: false,
|
|
27
|
+
},
|
|
28
|
+
] satisfies Filters<Values>
|
|
29
|
+
|
|
20
30
|
describe("setupFilterBarState()", () => {
|
|
21
31
|
it("sets up the base state correctly", () => {
|
|
22
32
|
const values = { flavour: "jasmine", sugarLevel: 50 }
|
|
@@ -41,6 +51,7 @@ describe("setupFilterBarState()", () => {
|
|
|
41
51
|
values,
|
|
42
52
|
dependentFilterIds: new Set<keyof Values>(),
|
|
43
53
|
hasUpdatedValues: false,
|
|
54
|
+
hasRemovableFilter: true,
|
|
44
55
|
})
|
|
45
56
|
})
|
|
46
57
|
|
|
@@ -65,4 +76,33 @@ describe("setupFilterBarState()", () => {
|
|
|
65
76
|
expect(state.hasUpdatedValues).toBe(true)
|
|
66
77
|
})
|
|
67
78
|
})
|
|
79
|
+
|
|
80
|
+
describe("Removable filters", () => {
|
|
81
|
+
it("hasRemovableFilter as false when there's no removable filters", () => {
|
|
82
|
+
const values = {}
|
|
83
|
+
expect(setupFilterBarState<Values>(filtersNoRemovable, values)).toEqual({
|
|
84
|
+
filters: {
|
|
85
|
+
flavour: {
|
|
86
|
+
id: "flavour",
|
|
87
|
+
name: "Flavour",
|
|
88
|
+
isRemovable: false,
|
|
89
|
+
isOpen: false,
|
|
90
|
+
isUsable: true,
|
|
91
|
+
},
|
|
92
|
+
sugarLevel: {
|
|
93
|
+
id: "sugarLevel",
|
|
94
|
+
name: "Sugar Level",
|
|
95
|
+
isRemovable: false,
|
|
96
|
+
isOpen: false,
|
|
97
|
+
isUsable: true,
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
activeFilterIds: new Set(["flavour", "sugarLevel"]),
|
|
101
|
+
values,
|
|
102
|
+
dependentFilterIds: new Set<keyof Values>(),
|
|
103
|
+
hasUpdatedValues: false,
|
|
104
|
+
hasRemovableFilter: false,
|
|
105
|
+
})
|
|
106
|
+
})
|
|
107
|
+
})
|
|
68
108
|
})
|
|
@@ -27,6 +27,10 @@ export const setupFilterBarState = <ValuesMap extends FiltersValues>(
|
|
|
27
27
|
baseState.activeFilterIds.add(id)
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
if (isRemovable) {
|
|
31
|
+
baseState.hasRemovableFilter = true
|
|
32
|
+
}
|
|
33
|
+
|
|
30
34
|
return baseState
|
|
31
35
|
},
|
|
32
36
|
{
|
|
@@ -35,6 +39,7 @@ export const setupFilterBarState = <ValuesMap extends FiltersValues>(
|
|
|
35
39
|
values,
|
|
36
40
|
dependentFilterIds: new Set(),
|
|
37
41
|
hasUpdatedValues: false,
|
|
42
|
+
hasRemovableFilter: false,
|
|
38
43
|
focusId: undefined,
|
|
39
44
|
} as FilterBarState<ValuesMap>
|
|
40
45
|
)
|
|
@@ -31,6 +31,7 @@ describe("filterBarStateReducer: update_single_filter", () => {
|
|
|
31
31
|
values: {},
|
|
32
32
|
dependentFilterIds: new Set(),
|
|
33
33
|
hasUpdatedValues: false,
|
|
34
|
+
hasRemovableFilter: false,
|
|
34
35
|
} satisfies FilterBarState<Values>
|
|
35
36
|
|
|
36
37
|
const newState = filterBarStateReducer<Values>(state, {
|
|
@@ -49,6 +50,7 @@ describe("filterBarStateReducer: update_single_filter", () => {
|
|
|
49
50
|
values: {},
|
|
50
51
|
dependentFilterIds: new Set(),
|
|
51
52
|
hasUpdatedValues: false,
|
|
53
|
+
hasRemovableFilter: false,
|
|
52
54
|
} satisfies FilterBarState<Values>
|
|
53
55
|
|
|
54
56
|
expect(state.filters.flavour.isOpen).toBe(false)
|
|
@@ -35,6 +35,7 @@ describe("filterBarStateReducer: update_values", () => {
|
|
|
35
35
|
values: {},
|
|
36
36
|
dependentFilterIds: new Set(),
|
|
37
37
|
hasUpdatedValues: false,
|
|
38
|
+
hasRemovableFilter: false,
|
|
38
39
|
} satisfies FilterBarState<Values>
|
|
39
40
|
|
|
40
41
|
const newState = filterBarStateReducer<Values>(state, {
|
|
@@ -65,6 +66,7 @@ describe("filterBarStateReducer: update_values", () => {
|
|
|
65
66
|
values: { sugarLevel: 50 },
|
|
66
67
|
dependentFilterIds: new Set<keyof Values>(["sugarLevel"]),
|
|
67
68
|
hasUpdatedValues: false,
|
|
69
|
+
hasRemovableFilter: false,
|
|
68
70
|
} satisfies FilterBarState<Values>
|
|
69
71
|
|
|
70
72
|
const newState = filterBarStateReducer<Values>(state, {
|
|
@@ -93,6 +95,7 @@ describe("filterBarStateReducer: update_values", () => {
|
|
|
93
95
|
values: {},
|
|
94
96
|
dependentFilterIds: new Set<keyof Values>(["sugarLevel"]),
|
|
95
97
|
hasUpdatedValues: false,
|
|
98
|
+
hasRemovableFilter: false,
|
|
96
99
|
} satisfies FilterBarState<Values>
|
|
97
100
|
|
|
98
101
|
const newState = filterBarStateReducer<Values>(state, {
|
|
@@ -122,6 +125,7 @@ describe("filterBarStateReducer: update_values", () => {
|
|
|
122
125
|
values: {},
|
|
123
126
|
dependentFilterIds: new Set<keyof Values>(["sugarLevel"]),
|
|
124
127
|
hasUpdatedValues: false,
|
|
128
|
+
hasRemovableFilter: false,
|
|
125
129
|
} satisfies FilterBarState<Values>
|
|
126
130
|
|
|
127
131
|
const newState = filterBarStateReducer<Values>(state, {
|
|
@@ -149,6 +153,7 @@ describe("filterBarStateReducer: update_values", () => {
|
|
|
149
153
|
values: {},
|
|
150
154
|
dependentFilterIds: new Set<keyof Values>(["sugarLevel"]),
|
|
151
155
|
hasUpdatedValues: false,
|
|
156
|
+
hasRemovableFilter: false,
|
|
152
157
|
} satisfies FilterBarState<Values>
|
|
153
158
|
|
|
154
159
|
const newState = filterBarStateReducer<Values>(state, {
|
|
@@ -28,6 +28,7 @@ export type FilterBarStateFilters<ValuesMap extends FiltersValues> = {
|
|
|
28
28
|
|
|
29
29
|
export type FilterBarState<ValuesMap extends FiltersValues> = {
|
|
30
30
|
hasUpdatedValues: boolean
|
|
31
|
+
hasRemovableFilter: boolean
|
|
31
32
|
filters: FilterBarStateFilters<ValuesMap>
|
|
32
33
|
activeFilterIds: Set<keyof ValuesMap>
|
|
33
34
|
values: Partial<ValuesMap>
|
|
@@ -31,6 +31,7 @@ describe("getInactiveFilters()", () => {
|
|
|
31
31
|
values: {},
|
|
32
32
|
dependentFilterIds: new Set(),
|
|
33
33
|
hasUpdatedValues: false,
|
|
34
|
+
hasRemovableFilter: false,
|
|
34
35
|
} satisfies FilterBarState<Values>
|
|
35
36
|
|
|
36
37
|
expect(getInactiveFilters<Values>(state)).toEqual([
|
|
@@ -61,6 +62,7 @@ describe("getInactiveFilters()", () => {
|
|
|
61
62
|
values: {},
|
|
62
63
|
dependentFilterIds: new Set<keyof Values>(["sugarLevel"]),
|
|
63
64
|
hasUpdatedValues: false,
|
|
65
|
+
hasRemovableFilter: false,
|
|
64
66
|
} satisfies FilterBarState<Values>
|
|
65
67
|
|
|
66
68
|
expect(getInactiveFilters<Values>(state)).toEqual([stateFilters.flavour])
|
|
@@ -31,6 +31,7 @@ describe("getIsUsableWhenArgs()", () => {
|
|
|
31
31
|
values: { flavour: "jasmine" },
|
|
32
32
|
dependentFilterIds: new Set(),
|
|
33
33
|
hasUpdatedValues: false,
|
|
34
|
+
hasRemovableFilter: false,
|
|
34
35
|
} satisfies FilterBarState<Values>
|
|
35
36
|
|
|
36
37
|
const usableArgs = getIsUsableWhenArgs<Values>(state)
|
|
@@ -38,6 +38,7 @@ describe("updateDependentFilters()", () => {
|
|
|
38
38
|
values: { flavour: "jasmine" },
|
|
39
39
|
dependentFilterIds: new Set(),
|
|
40
40
|
hasUpdatedValues: false,
|
|
41
|
+
hasRemovableFilter: false,
|
|
41
42
|
} satisfies FilterBarState<Values>
|
|
42
43
|
|
|
43
44
|
const newState = updateDependentFilters<Values>(state)
|
|
@@ -53,6 +54,7 @@ describe("updateDependentFilters()", () => {
|
|
|
53
54
|
values: { flavour: "jasmine" },
|
|
54
55
|
dependentFilterIds: new Set<keyof Values>(["sugarLevel"]),
|
|
55
56
|
hasUpdatedValues: false,
|
|
57
|
+
hasRemovableFilter: false,
|
|
56
58
|
} satisfies FilterBarState<Values>
|
|
57
59
|
|
|
58
60
|
const newState = updateDependentFilters<Values>(state)
|
|
@@ -77,6 +79,7 @@ describe("updateDependentFilters()", () => {
|
|
|
77
79
|
values: { flavour: "jasmine" },
|
|
78
80
|
dependentFilterIds: new Set<keyof Values>(["sugarLevel"]),
|
|
79
81
|
hasUpdatedValues: false,
|
|
82
|
+
hasRemovableFilter: false,
|
|
80
83
|
} satisfies FilterBarState<Values>
|
|
81
84
|
|
|
82
85
|
updateDependentFilters<Values>(state)
|
|
@@ -98,6 +101,7 @@ describe("updateDependentFilters()", () => {
|
|
|
98
101
|
values: { flavour: "jasmine" },
|
|
99
102
|
dependentFilterIds: new Set<keyof Values>(["sugarLevel"]),
|
|
100
103
|
hasUpdatedValues: false,
|
|
104
|
+
hasRemovableFilter: false,
|
|
101
105
|
} satisfies FilterBarState<Values>
|
|
102
106
|
|
|
103
107
|
const newState = updateDependentFilters<Values>(state)
|
|
@@ -117,6 +121,7 @@ describe("updateDependentFilters()", () => {
|
|
|
117
121
|
values: { flavour: "jasmine" },
|
|
118
122
|
dependentFilterIds: new Set<keyof Values>(["sugarLevel"]),
|
|
119
123
|
hasUpdatedValues: false,
|
|
124
|
+
hasRemovableFilter: false,
|
|
120
125
|
} satisfies FilterBarState<Values>
|
|
121
126
|
|
|
122
127
|
const newState = updateDependentFilters<Values>(state)
|
|
@@ -139,6 +144,7 @@ describe("updateDependentFilters()", () => {
|
|
|
139
144
|
values: { flavour: "jasmine", sugarLevel: 50 },
|
|
140
145
|
dependentFilterIds: new Set<keyof Values>(["sugarLevel"]),
|
|
141
146
|
hasUpdatedValues: false,
|
|
147
|
+
hasRemovableFilter: false,
|
|
142
148
|
} satisfies FilterBarState<Values>
|
|
143
149
|
|
|
144
150
|
const newState = updateDependentFilters<Values>(state)
|
|
@@ -161,6 +167,7 @@ describe("updateDependentFilters()", () => {
|
|
|
161
167
|
values: { flavour: "jasmine" },
|
|
162
168
|
dependentFilterIds: new Set<keyof Values>(["sugarLevel"]),
|
|
163
169
|
hasUpdatedValues: false,
|
|
170
|
+
hasRemovableFilter: false,
|
|
164
171
|
} satisfies FilterBarState<Values>
|
|
165
172
|
|
|
166
173
|
const newState = updateDependentFilters<Values>(state)
|
|
@@ -176,6 +183,7 @@ describe("updateDependentFilters()", () => {
|
|
|
176
183
|
values: { sugarLevel: 50 },
|
|
177
184
|
dependentFilterIds: new Set<keyof Values>(["sugarLevel"]),
|
|
178
185
|
hasUpdatedValues: false,
|
|
186
|
+
hasRemovableFilter: false,
|
|
179
187
|
} satisfies FilterBarState<Values>
|
|
180
188
|
|
|
181
189
|
const newState = updateDependentFilters<Values>(state)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from "react"
|
|
2
2
|
import { useIntl } from "@cultureamp/i18n-react-intl"
|
|
3
|
+
import classnames from "classnames"
|
|
3
4
|
import { Button } from "~components/__actions__/v2"
|
|
4
5
|
import { useFilterBarContext } from "../../context/FilterBarContext"
|
|
5
6
|
import styles from "./ClearAllButton.module.scss"
|
|
@@ -19,13 +20,15 @@ export const ClearAllButton = (): JSX.Element => {
|
|
|
19
20
|
description: "Button aria-label to clear all values within the filter bar",
|
|
20
21
|
})
|
|
21
22
|
|
|
22
|
-
const { clearAllFilters } = useFilterBarContext()
|
|
23
|
+
const { clearAllFilters, isClearable } = useFilterBarContext()
|
|
23
24
|
|
|
24
25
|
return (
|
|
25
26
|
<Button
|
|
26
27
|
label={clearButtonLabel}
|
|
27
28
|
aria-label={clearButtonAriaLabel}
|
|
28
|
-
classNameOverride={styles.clearAllButton
|
|
29
|
+
classNameOverride={classnames(styles.clearAllButton, {
|
|
30
|
+
[styles.hidden]: !isClearable,
|
|
31
|
+
})}
|
|
29
32
|
secondary
|
|
30
33
|
onClick={clearAllFilters}
|
|
31
34
|
/>
|
|
@@ -259,6 +259,10 @@ export const FilterDateRangePickerField = ({
|
|
|
259
259
|
return
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
+
if (state.inputStartValue === "" && state.inputEndValue === "") {
|
|
263
|
+
return
|
|
264
|
+
}
|
|
265
|
+
|
|
262
266
|
const newStartDate = validateStartDate(
|
|
263
267
|
selectedRange?.from,
|
|
264
268
|
state.inputStartValue
|