@bundle-stats/ui 4.13.0-beta.8 → 4.13.0-beta.9

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.
Files changed (63) hide show
  1. package/lib/components/bundle-modules/bundle-modules.js +2 -1
  2. package/lib/components/bundle-modules/bundle-modules.js.map +1 -1
  3. package/lib/components/metrics-display-selector/metrics-display-selector.js +2 -2
  4. package/lib/components/metrics-display-selector/metrics-display-selector.js.map +1 -1
  5. package/lib/components/metrics-table-header/metrics-table-header.js +7 -7
  6. package/lib/components/metrics-table-header/metrics-table-header.js.map +1 -1
  7. package/lib/components/metrics-table-header/metrics-table-header.module.css +11 -4
  8. package/lib/components/metrics-table-options/metrics-table-options.js +2 -2
  9. package/lib/components/metrics-table-options/metrics-table-options.js.map +1 -1
  10. package/lib/components/metrics-table-title/metrics-table-title.js +1 -1
  11. package/lib/components/metrics-table-title/metrics-table-title.js.map +1 -1
  12. package/lib/components/metrics-table-title/metrics-table-title.module.css +1 -0
  13. package/lib/components/metrics-treemap/metrics-treemap.js +4 -4
  14. package/lib/components/metrics-treemap/metrics-treemap.js.map +1 -1
  15. package/lib/components/metrics-treemap/metrics-treemap.module.css +1 -1
  16. package/lib/ui/dropdown/dropdown.js +9 -39
  17. package/lib/ui/dropdown/dropdown.js.map +1 -1
  18. package/lib/ui/dropdown/index.js.map +1 -1
  19. package/lib/ui/filters/filters.js +32 -34
  20. package/lib/ui/filters/filters.js.map +1 -1
  21. package/lib/ui/filters/filters.module.css +48 -67
  22. package/lib-esm/components/bundle-modules/bundle-modules.js +2 -1
  23. package/lib-esm/components/bundle-modules/bundle-modules.js.map +1 -1
  24. package/lib-esm/components/metrics-display-selector/metrics-display-selector.js +2 -2
  25. package/lib-esm/components/metrics-display-selector/metrics-display-selector.js.map +1 -1
  26. package/lib-esm/components/metrics-table-header/metrics-table-header.js +7 -7
  27. package/lib-esm/components/metrics-table-header/metrics-table-header.js.map +1 -1
  28. package/lib-esm/components/metrics-table-header/metrics-table-header.module.css +11 -4
  29. package/lib-esm/components/metrics-table-options/metrics-table-options.js +3 -3
  30. package/lib-esm/components/metrics-table-options/metrics-table-options.js.map +1 -1
  31. package/lib-esm/components/metrics-table-title/metrics-table-title.js +1 -1
  32. package/lib-esm/components/metrics-table-title/metrics-table-title.js.map +1 -1
  33. package/lib-esm/components/metrics-table-title/metrics-table-title.module.css +1 -0
  34. package/lib-esm/components/metrics-treemap/metrics-treemap.js +4 -4
  35. package/lib-esm/components/metrics-treemap/metrics-treemap.js.map +1 -1
  36. package/lib-esm/components/metrics-treemap/metrics-treemap.module.css +1 -1
  37. package/lib-esm/ui/dropdown/dropdown.js +7 -38
  38. package/lib-esm/ui/dropdown/dropdown.js.map +1 -1
  39. package/lib-esm/ui/dropdown/index.js.map +1 -1
  40. package/lib-esm/ui/filters/filters.js +33 -35
  41. package/lib-esm/ui/filters/filters.js.map +1 -1
  42. package/lib-esm/ui/filters/filters.module.css +48 -67
  43. package/package.json +5 -5
  44. package/src/components/bundle-modules/bundle-modules.tsx +3 -2
  45. package/src/components/metrics-display-selector/metrics-display-selector.tsx +4 -4
  46. package/src/components/metrics-table-header/metrics-table-header.module.css +11 -4
  47. package/src/components/metrics-table-header/metrics-table-header.tsx +7 -7
  48. package/src/components/metrics-table-options/metrics-table-options.jsx +4 -10
  49. package/src/components/metrics-table-title/metrics-table-title.jsx +1 -1
  50. package/src/components/metrics-table-title/metrics-table-title.module.css +1 -0
  51. package/src/components/metrics-treemap/metrics-treemap.module.css +1 -1
  52. package/src/components/metrics-treemap/metrics-treemap.tsx +4 -3
  53. package/src/ui/dropdown/dropdown.stories.tsx +25 -0
  54. package/src/ui/dropdown/dropdown.tsx +67 -0
  55. package/src/ui/filters/filters.module.css +48 -67
  56. package/src/ui/filters/filters.stories.tsx +109 -0
  57. package/src/ui/filters/filters.tsx +128 -166
  58. package/types/components/metrics-display-selector/metrics-display-selector.d.ts +2 -2
  59. package/types/ui/dropdown/dropdown.d.ts +13 -47
  60. package/types/ui/dropdown/index.d.ts +1 -1
  61. package/types/ui/filters/filters.d.ts +2 -7
  62. package/src/ui/dropdown/dropdown.jsx +0 -88
  63. /package/src/ui/dropdown/{index.js → index.ts} +0 -0
@@ -0,0 +1,67 @@
1
+ import React from 'react';
2
+ import cx from 'classnames';
3
+ import { Menu, MenuButton, MenuItem, useMenuState } from 'ariakit/menu';
4
+
5
+ import { Button, BUTTON_SIZE } from '../button';
6
+ import css from './dropdown.module.css';
7
+
8
+ interface DropdownItemProps {
9
+ isActive?: boolean;
10
+ }
11
+
12
+ export const DropdownItem = (props: DropdownItemProps & React.ComponentProps<typeof MenuItem>) => {
13
+ const { className = '', isActive = false, ...restProps } = props;
14
+
15
+ return (
16
+ <MenuItem className={cx(css.item, className, isActive && css.itemActive)} {...restProps} />
17
+ );
18
+ };
19
+
20
+ interface DropdownProps {
21
+ buttonClassName?: string;
22
+ dropdownClassName?: string;
23
+ label?: React.ReactNode;
24
+ ariaLabel?: string;
25
+ glyph?: string;
26
+ disabled?: boolean;
27
+ }
28
+
29
+ export const Dropdown = (props: DropdownProps & React.ComponentProps<'div'>) => {
30
+ const {
31
+ buttonClassName = '',
32
+ dropdownClassName = '',
33
+ label = null,
34
+ ariaLabel = '',
35
+ glyph = '',
36
+ disabled = false,
37
+ children,
38
+ } = props;
39
+
40
+ const dropdownAriaLabel = ariaLabel || (typeof label === 'string' ? label : '');
41
+ const menuState = useMenuState();
42
+
43
+ return (
44
+ <>
45
+ <MenuButton
46
+ as={Button}
47
+ outline
48
+ size={BUTTON_SIZE.SMALL}
49
+ glyph={glyph}
50
+ disabled={disabled}
51
+ state={menuState}
52
+ tabIndex={null}
53
+ className={cx(css.button, buttonClassName)}
54
+ >
55
+ {label}
56
+ </MenuButton>
57
+ <Menu
58
+ portal
59
+ state={menuState}
60
+ aria-label={dropdownAriaLabel}
61
+ className={cx(css.dropdown, dropdownClassName)}
62
+ >
63
+ {children}
64
+ </Menu>
65
+ </>
66
+ );
67
+ };
@@ -1,18 +1,18 @@
1
- /* Checkbox + label + button */
2
- .filter {
3
- /* Update MenuItem padding */
4
- padding-top: calc(var(--space-xxxsmall) / 2);
5
- padding-bottom: calc(var(--space-xxxsmall) / 2);
1
+ /* explicit ControlGroup last item styles to override ariakit injected sibling nodes */
2
+ .itemLast {
3
+ border-top-right-radius: var(--radius-small) !important;
4
+ border-bottom-right-radius: var(--radius-small) !important;
5
+ }
6
6
 
7
+ /* Filter (label with input + text) */
8
+ .filter {
7
9
  position: relative;
8
10
  width: 100%;
9
11
  max-width: 240px;
12
+ color: var(--color-text);
10
13
  }
11
14
 
12
- .filterCheckbox {
13
- }
14
-
15
- .filterInput {
15
+ .filterControl {
16
16
  flex: 0 0 auto;
17
17
  height: 1em;
18
18
  }
@@ -25,7 +25,42 @@
25
25
  text-overflow: ellipsis;
26
26
  }
27
27
 
28
- .filterOnlyButton {
28
+ .filterControl[disabled] + .filterLabel {
29
+ color: var(--color-text-light);
30
+ }
31
+
32
+ .filter:hover {
33
+ color: var(--color-dark);
34
+ }
35
+
36
+ /** Filter single */
37
+ .filterSingle {
38
+ padding: calc(var(--space-xxsmall) - 1px);
39
+ border: 1px solid var(--color-outline);
40
+ border-radius: var(--radius-small);
41
+ font-size: var(--size-small);
42
+ line-height: var(--space-small);
43
+ color: var(--color-text-ultra-light);
44
+ }
45
+
46
+ .filterSingle:hover,
47
+ .filterSingle:active,
48
+ .filterSingle:focus {
49
+ border-color: var(--color-outline-dark);
50
+ }
51
+
52
+ /** Filter group */
53
+ .filterGroupItems {
54
+ max-height: calc(10 * (var(--space-small) + 2 * var(--space-xxxsmall)));
55
+ overflow: auto;
56
+ }
57
+
58
+ .filterGroupItem {
59
+ position: relative;
60
+ padding-right: 10em;
61
+ }
62
+
63
+ .filterGroupOnlyButton {
29
64
  display: none;
30
65
  position: absolute;
31
66
  right: 2px;
@@ -37,7 +72,7 @@
37
72
  }
38
73
 
39
74
  /** cover item text */
40
- .filterOnlyButton::before {
75
+ .filterGroupOnlyButton::before {
41
76
  content: ' ';
42
77
  display: block;
43
78
  position: absolute;
@@ -48,53 +83,13 @@
48
83
  background: linear-gradient(90deg, rgba(255, 255, 255, 0), var(--color-highlight) 75%);
49
84
  }
50
85
 
51
- /** filter disabled state */
52
- .filterInput[disabled] + .filterLabel {
53
- color: var(--color-text-light);
54
- }
55
-
56
- .filterInput[disabled] + .filterOnlyButton {
57
- display: none;
58
- }
59
-
60
86
  /** filter hover state */
61
- .filter {
62
- color: var(--color-text);
63
- }
64
-
65
- .filter:hover {
66
- color: var(--color-dark);
67
- }
68
-
69
- .filter:hover .filterOnlyButton {
87
+ .filterGroupItem:hover .filterGroupOnlyButton {
70
88
  display: block;
71
89
  }
72
90
 
73
- /** Filter standard variation */
74
- .filterStandalone {
75
- padding: 0;
76
- width: auto;
77
- color: var(--color-text-light);
78
- z-index: var(--layout-xlow); /* show collapsed border */
79
- font-size: var(--size-small);
80
- line-height: var(--space-small);
81
- }
82
-
83
- .filterStandalone .filterCheckbox {
84
- padding: calc(var(--space-xxsmall) - 1px) calc(var(--space-xxsmall) - 1px);
85
- border: 1px solid var(--color-outline);
86
- }
87
-
88
- .filterStandalone:hover .filterCheckbox,
89
- .filterStandalone:active .filterCheckbox,
90
- .filterStandalone:focus .filterCheckbox {
91
- z-index: var(--layout-xlow); /* show collapsed border */
92
- box-shadow: var(--outline-hover);
93
- color: var(--color-text);
94
- }
95
-
96
91
  /** Filter group variation */
97
- .labelSuffixCustom {
92
+ .filterGroupLabelSuffixCustom {
98
93
  color: var(--color-text);
99
94
  text-transform: lowercase;
100
95
  }
@@ -112,11 +107,6 @@
112
107
  color: var(--color-text-light);
113
108
  }
114
109
 
115
- .filterGroupItems {
116
- max-height: calc(10 * (var(--space-small) + 2 * var(--space-xxxsmall)));
117
- overflow: auto;
118
- }
119
-
120
110
  .filterGroupActions {
121
111
  margin-top: var(--space-xxxsmall);
122
112
  padding-top: var(--space-xxxsmall);
@@ -124,12 +114,3 @@
124
114
  margin-bottom: calc(0px - var(--space-xxxsmall));
125
115
  border-top: 1px solid var(--color-outline);
126
116
  }
127
-
128
- .item {
129
- position: relative;
130
- flex: 0 0 auto;
131
- }
132
-
133
- .itemButton {
134
- border-radius: var(--radius-small);
135
- }
@@ -0,0 +1,109 @@
1
+ import React from 'react';
2
+ import { Meta } from '@storybook/react';
3
+ import { action } from '@storybook/addon-actions'; // eslint-disable-line
4
+
5
+ import { getWrapperDecorator } from '../../stories';
6
+ import { Filters } from '.';
7
+
8
+ export default {
9
+ title: 'UI/Filters',
10
+ component: Filters,
11
+ decorators: [getWrapperDecorator({ paddingLeft: '200px' })],
12
+ } as Meta;
13
+
14
+ export const Default = () => (
15
+ <Filters
16
+ onChange={action('CHANGE')}
17
+ filters={{
18
+ changed: {
19
+ label: 'Changed',
20
+ defaultValue: true,
21
+ },
22
+ fileTypes: {
23
+ label: 'File types',
24
+ children: [
25
+ {
26
+ key: 'CSS',
27
+ label: 'CSS',
28
+ defaultValue: true,
29
+ },
30
+ {
31
+ key: 'JS',
32
+ label: 'JS',
33
+ defaultValue: true,
34
+ },
35
+ {
36
+ key: 'HTML',
37
+ label: 'HTML',
38
+ defaultValue: true,
39
+ },
40
+ {
41
+ key: 'OTHER',
42
+ label: 'Asset that is not CSS, JS or HTML',
43
+ defaultValue: true,
44
+ },
45
+ ],
46
+ },
47
+ }}
48
+ />
49
+ );
50
+
51
+ export const Overflow = () => (
52
+ <Filters
53
+ onChange={action('CHANGE')}
54
+ filters={{
55
+ changed: {
56
+ label: 'Changed',
57
+ defaultValue: true,
58
+ },
59
+ chunks: {
60
+ label: 'Chunks',
61
+ children: Array(22)
62
+ .fill('')
63
+ .map((_, currentIndex) => ({
64
+ key: `chunk-${currentIndex}`,
65
+ label: `Chunk ${currentIndex + 1}`,
66
+ defaultValue: true,
67
+ })),
68
+ },
69
+ }}
70
+ />
71
+ );
72
+
73
+ export const DisableOptions = () => (
74
+ <Filters
75
+ onChange={action('CHANGE')}
76
+ filters={{
77
+ changed: {
78
+ label: 'Changed',
79
+ defaultValue: false,
80
+ disabled: true,
81
+ },
82
+ entrypoint: {
83
+ label: 'Entrypoint',
84
+ defaultValue: false,
85
+ },
86
+ fileTypes: {
87
+ label: 'File types',
88
+ children: [
89
+ {
90
+ key: 'CSS',
91
+ label: 'CSS',
92
+ defaultValue: true,
93
+ },
94
+ {
95
+ key: 'JS',
96
+ label: 'JS',
97
+ defaultValue: true,
98
+ disabled: true,
99
+ },
100
+ {
101
+ key: 'HTML',
102
+ label: 'HTML',
103
+ defaultValue: true,
104
+ },
105
+ ],
106
+ },
107
+ }}
108
+ />
109
+ );
@@ -5,85 +5,51 @@ import type { FilterFieldsData, FilterGroupFieldData } from '../../types';
5
5
  import { FlexStack } from '../../layout/flex-stack';
6
6
  import { Stack } from '../../layout/stack';
7
7
  import { Button } from '../button';
8
- import { Dropdown } from '../dropdown';
8
+ import { ControlGroup } from '../control-group';
9
+ import { Dropdown, DropdownItem } from '../dropdown';
9
10
  import { InputSearch } from '../input-search';
10
11
  import * as I18N from './filters.i18n';
11
12
  import { getGroupFiltersLabelSuffix, LABELS } from './filters.utils';
12
13
  import css from './filters.module.css';
13
14
 
14
- interface FilterProps extends React.ComponentProps<'input'> {
15
- as?: React.ElementType;
16
- buttonClassName?: string;
15
+ interface FilterBooleanProps extends React.ComponentProps<'input'> {
17
16
  label: string;
18
- name: string;
19
- getOnOnlyClick?: (name: string) => () => void;
20
17
  }
21
18
 
22
- const Filter = (props: FilterProps) => {
23
- const {
24
- className = '',
25
- as: Component = 'div',
26
- buttonClassName = '',
27
- label,
28
- name,
29
- getOnOnlyClick,
30
- ...inputProps
31
- } = props;
32
-
33
- const id = `filter-${name}`;
34
- const rootClassName = cx(css.filter, className);
19
+ const Filter = (props: FilterBooleanProps) => {
20
+ const { className = '', label, name, ...inputProps } = props;
35
21
 
36
22
  return (
37
- <Component className={rootClassName}>
38
- {/* eslint-disable */}
39
- <FlexStack
40
- space="xxxsmall"
41
- alignItems="center"
42
- as="label"
43
- className={cx(css.filterCheckbox, buttonClassName)}
44
- >
45
- {/* eslint-enabled */}
46
- <input type="checkbox" id={id} name={name} {...inputProps} className={css.filterInput} />
47
- <span title={label} className={css.filterLabel}>
48
- {label}
49
- </span>
50
- </FlexStack>
51
-
52
- {getOnOnlyClick && (
53
- <Button
54
- kind="info"
55
- solid
56
- size="small"
57
- type="button"
58
- onClick={getOnOnlyClick(name)}
59
- className={css.filterOnlyButton}
60
- >
61
- {I18N.ONLY}
62
- </Button>
63
- )}
64
- </Component>
23
+ <FlexStack
24
+ space="xxxsmall"
25
+ alignItems="center"
26
+ as="label"
27
+ className={cx(css.filter, className)}
28
+ >
29
+ <input
30
+ type="checkbox"
31
+ id={`filter-${name}`}
32
+ name={name}
33
+ {...inputProps}
34
+ className={css.filterControl}
35
+ />
36
+ <span title={label} className={css.filterLabel}>
37
+ {label}
38
+ </span>
39
+ </FlexStack>
65
40
  );
66
41
  };
67
42
 
68
43
  interface FilterGroupProps extends React.ComponentProps<'div'> {
69
- buttonClassName?: string;
70
44
  groupKey: string;
71
45
  data: FilterGroupFieldData;
72
46
  values: Record<string, boolean>;
73
- onCheckboxChange: FilterProps['onChange'];
47
+ onCheckboxChange: FilterBooleanProps['onChange'];
74
48
  toggleFilters: (newFilters: Record<string, boolean>) => void;
75
49
  }
76
50
 
77
51
  const FilterGroup = (props: FilterGroupProps) => {
78
- const {
79
- className = '',
80
- buttonClassName = '',
81
- groupKey,
82
- data,
83
- values,
84
- onCheckboxChange,
85
- toggleFilters,
86
- } = props;
52
+ const { className = '', groupKey, data, values, onCheckboxChange, toggleFilters } = props;
87
53
 
88
54
  const [search, setSearch] = useState('');
89
55
 
@@ -99,7 +65,9 @@ const FilterGroup = (props: FilterGroupProps) => {
99
65
  <>
100
66
  {`${groupLabel}:`}
101
67
  &nbsp;
102
- <span className={cx(hasCustomFilterSuffix && css.labelSuffixCustom)}>{filterSuffix}</span>
68
+ <span className={cx(hasCustomFilterSuffix && css.filterGroupLabelSuffixCustom)}>
69
+ {filterSuffix}
70
+ </span>
103
71
  </>
104
72
  );
105
73
 
@@ -130,88 +98,79 @@ const FilterGroup = (props: FilterGroupProps) => {
130
98
 
131
99
  return (
132
100
  <Dropdown
133
- className={className}
134
- buttonClassName={buttonClassName}
101
+ buttonClassName={className}
135
102
  label={dropdownLabel}
136
103
  ariaLabel={`${groupLabel}: ${filterSuffix}`}
137
104
  >
138
- {({ MenuItem, menuItemClassName }) => {
139
- return (
140
- <>
141
- {groupItems.length > 10 && (
142
- <div className={css.filterGroupSearchWrapper}>
143
- <InputSearch
144
- defaultValue={search}
145
- onChange={setSearch}
146
- placeholder={I18N.GROUP_SEARCH}
147
- debounceWait={0}
148
- />
149
- </div>
150
- )}
151
- <div className={css.filterGroupItems}>
152
- {filteredGroupItems.length === 0 && (
153
- <Stack className={css.filterGroupSearchNotFound}>
154
- <p>{I18N.GROUP_NOT_FOUND}</p>
155
- <div>
156
- <Button
157
- size="small"
158
- kind="primary"
159
- type="button"
160
- onClick={() => setSearch('')}
161
- className={css.filterGroupSearchNotFoundClear}
162
- >
163
- {I18N.GROUP_SEARCH_CLEAR}
164
- </Button>
165
- </div>
166
- </Stack>
167
- )}
168
- {filteredGroupItems.map(({ key: itemKey, ...itemData }) => {
169
- const id = [groupKey, itemKey].join('.');
170
- const getOnOnlyClick = () => getOnGroupCheck(false, { [id]: true });
171
-
172
- return (
173
- <Filter
174
- key={id}
175
- className={menuItemClassName}
176
- as={MenuItem}
177
- name={id}
178
- label={itemData.label}
179
- onChange={onCheckboxChange}
180
- checked={values[id]}
181
- disabled={itemData.disabled}
182
- getOnOnlyClick={getOnOnlyClick}
183
- />
184
- );
185
- })}
105
+ {groupItems.length > 10 && (
106
+ <div className={css.filterGroupSearchWrapper}>
107
+ <InputSearch
108
+ defaultValue={search}
109
+ onChange={setSearch}
110
+ placeholder={I18N.GROUP_SEARCH}
111
+ debounceWait={0}
112
+ />
113
+ </div>
114
+ )}
115
+ <div className={css.filterGroupItems}>
116
+ {filteredGroupItems.length === 0 && (
117
+ <Stack className={css.filterGroupSearchNotFound}>
118
+ <p>{I18N.GROUP_NOT_FOUND}</p>
119
+ <div>
120
+ <Button
121
+ size="small"
122
+ kind="primary"
123
+ type="button"
124
+ onClick={() => setSearch('')}
125
+ className={css.filterGroupSearchNotFoundClear}
126
+ >
127
+ {I18N.GROUP_SEARCH_CLEAR}
128
+ </Button>
186
129
  </div>
187
- {filteredGroupItems.length !== 0 && (
188
- <div className={css.filterGroupActions}>
189
- {areAllGroupItemsChecked ? (
190
- <MenuItem
191
- id="clear-all"
192
- as="button"
193
- className={menuItemClassName}
194
- type="button"
195
- onClick={getOnGroupCheck(false)}
196
- >
197
- {I18N.CLEAR}
198
- </MenuItem>
199
- ) : (
200
- <MenuItem
201
- id="clear-all"
202
- as="button"
203
- className={menuItemClassName}
204
- type="button"
205
- onClick={getOnGroupCheck(true)}
206
- >
207
- {I18N.CHECK}
208
- </MenuItem>
209
- )}
210
- </div>
211
- )}
212
- </>
213
- );
214
- }}
130
+ </Stack>
131
+ )}
132
+ {filteredGroupItems.map(({ key: itemKey, ...itemData }) => {
133
+ const id = [groupKey, itemKey].join('.');
134
+ const getOnOnlyClick = () => getOnGroupCheck(false, { [id]: true });
135
+
136
+ return (
137
+ <DropdownItem key={id} className={css.filterGroupItem}>
138
+ <Filter
139
+ label={itemData.label}
140
+ name={id}
141
+ onChange={onCheckboxChange}
142
+ checked={values[id]}
143
+ disabled={itemData.disabled}
144
+ className={css.filterGroupItemFilter}
145
+ />
146
+ <Button
147
+ kind="info"
148
+ solid
149
+ size="small"
150
+ type="button"
151
+ onClick={getOnOnlyClick()}
152
+ disabled={itemData.disabled}
153
+ className={css.filterGroupOnlyButton}
154
+ >
155
+ {I18N.ONLY}
156
+ </Button>
157
+ </DropdownItem>
158
+ );
159
+ })}
160
+ </div>
161
+ {filteredGroupItems.length !== 0 && (
162
+ <div className={css.filterGroupActions}>
163
+ {areAllGroupItemsChecked ? (
164
+ <DropdownItem id="clear-all" onClick={getOnGroupCheck(false)} role="button">
165
+ {I18N.CLEAR}
166
+ </DropdownItem>
167
+ ) : (
168
+ <DropdownItem id="clear-all" onClick={getOnGroupCheck(true)} role="button">
169
+ {I18N.CHECK}
170
+ </DropdownItem>
171
+ )}
172
+ </div>
173
+ )}
215
174
  </Dropdown>
216
175
  );
217
176
  };
@@ -226,6 +185,9 @@ interface FiltersProps extends React.ComponentProps<'div'> {
226
185
  export const Filters = (props: FiltersProps) => {
227
186
  const { className = '', values, filters, toggleFilter, toggleFilters } = props;
228
187
 
188
+ /**
189
+ * Toggle filter value on checkbox change
190
+ */
229
191
  const onCheckboxChange = useCallback(
230
192
  (event: any) => {
231
193
  const { name } = event.target;
@@ -233,41 +195,41 @@ export const Filters = (props: FiltersProps) => {
233
195
  },
234
196
  [toggleFilter, values],
235
197
  );
236
- const rootClassName = cx(css.root, className);
198
+
199
+ const filterList = Object.entries(filters);
200
+ const filterCount = filterList.length;
237
201
 
238
202
  return (
239
- <form className={rootClassName}>
240
- <FlexStack space="xxsmall" alignItems="top" className={css.items}>
241
- {Object.entries(filters).map(([name, data]) => {
242
- if ('defaultValue' in data) {
243
- return (
244
- <Filter
245
- key={name}
246
- className={cx(css.item, css.filterStandalone)}
247
- buttonClassName={css.itemButton}
248
- name={name}
249
- label={data.label}
250
- onChange={onCheckboxChange}
251
- checked={values[name]}
252
- disabled={data.disabled as boolean}
253
- />
254
- );
255
- }
203
+ <ControlGroup className={cx(css.root, className)}>
204
+ {filterList.map(([name, data], index) => {
205
+ const lastItem = index === filterCount - 1;
256
206
 
207
+ if ('defaultValue' in data) {
257
208
  return (
258
- <FilterGroup
259
- className={css.item}
260
- buttonClassName={css.itemButton}
209
+ <Filter
210
+ name={name}
211
+ label={data.label}
212
+ onChange={onCheckboxChange}
213
+ checked={values[name]}
214
+ disabled={data.disabled as boolean}
261
215
  key={name}
262
- groupKey={name}
263
- data={data}
264
- values={values}
265
- onCheckboxChange={onCheckboxChange}
266
- toggleFilters={toggleFilters}
216
+ className={cx(css.filterSingle, lastItem && css.itemLast)}
267
217
  />
268
218
  );
269
- })}
270
- </FlexStack>
271
- </form>
219
+ }
220
+
221
+ return (
222
+ <FilterGroup
223
+ groupKey={name}
224
+ data={data}
225
+ values={values}
226
+ onCheckboxChange={onCheckboxChange}
227
+ toggleFilters={toggleFilters}
228
+ className={cx(lastItem && css.itemLast)}
229
+ key={name}
230
+ />
231
+ );
232
+ })}
233
+ </ControlGroup>
272
234
  );
273
235
  };