@arbor-education/design-system.components 0.1.2 → 0.1.3

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 (148) hide show
  1. package/.changeset/neat-experts-repair.md +2 -0
  2. package/.changeset/slick-places-grin.md +5 -0
  3. package/.changeset/sunny-cars-sell.md +2 -0
  4. package/.changeset/tough-facts-check.md +5 -0
  5. package/.changeset/true-rats-add.md +5 -0
  6. package/.github/workflows/chromatic.yml +2 -1
  7. package/.github/workflows/release.yml +124 -0
  8. package/CHANGELOG.md +0 -1
  9. package/bin/createComponent.sh +39 -9
  10. package/dist/components/dropdown/Dropdown.d.ts +1 -0
  11. package/dist/components/dropdown/Dropdown.d.ts.map +1 -1
  12. package/dist/components/dropdown/DropdownContent.d.ts +1 -0
  13. package/dist/components/dropdown/DropdownContent.d.ts.map +1 -1
  14. package/dist/components/dropdown/DropdownContent.js +3 -2
  15. package/dist/components/dropdown/DropdownContent.js.map +1 -1
  16. package/dist/components/formField/FormField.d.ts +8 -0
  17. package/dist/components/formField/FormField.d.ts.map +1 -1
  18. package/dist/components/formField/FormField.js +3 -1
  19. package/dist/components/formField/FormField.js.map +1 -1
  20. package/dist/components/formField/FormField.stories.d.ts.map +1 -1
  21. package/dist/components/formField/FormField.stories.js +5 -0
  22. package/dist/components/formField/FormField.stories.js.map +1 -1
  23. package/dist/components/formField/FormField.test.js +11 -1
  24. package/dist/components/formField/FormField.test.js.map +1 -1
  25. package/dist/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.d.ts +12 -0
  26. package/dist/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.d.ts.map +1 -0
  27. package/dist/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.js +17 -0
  28. package/dist/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.js.map +1 -0
  29. package/dist/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.stories.d.ts +10 -0
  30. package/dist/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.stories.d.ts.map +1 -0
  31. package/dist/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.stories.js +24 -0
  32. package/dist/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.stories.js.map +1 -0
  33. package/dist/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.test.d.ts +2 -0
  34. package/dist/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.test.d.ts.map +1 -0
  35. package/dist/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.test.js +88 -0
  36. package/dist/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.test.js.map +1 -0
  37. package/dist/components/formField/inputs/dropdown/Dropdown.d.ts +11 -0
  38. package/dist/components/formField/inputs/dropdown/Dropdown.d.ts.map +1 -0
  39. package/dist/components/formField/inputs/dropdown/Dropdown.js +43 -0
  40. package/dist/components/formField/inputs/dropdown/Dropdown.js.map +1 -0
  41. package/dist/components/formField/inputs/dropdown/Dropdown.stories.d.ts +161 -0
  42. package/dist/components/formField/inputs/dropdown/Dropdown.stories.d.ts.map +1 -0
  43. package/dist/components/formField/inputs/dropdown/Dropdown.stories.js +172 -0
  44. package/dist/components/formField/inputs/dropdown/Dropdown.stories.js.map +1 -0
  45. package/dist/components/formField/inputs/dropdown/Dropdown.test.d.ts +2 -0
  46. package/dist/components/formField/inputs/dropdown/Dropdown.test.d.ts.map +1 -0
  47. package/dist/components/formField/inputs/dropdown/Dropdown.test.js +93 -0
  48. package/dist/components/formField/inputs/dropdown/Dropdown.test.js.map +1 -0
  49. package/dist/components/formField/inputs/dropdown/buttons/dropdownButton/DropdownButton.d.ts +11 -0
  50. package/dist/components/formField/inputs/dropdown/buttons/dropdownButton/DropdownButton.d.ts.map +1 -0
  51. package/dist/components/formField/inputs/dropdown/buttons/dropdownButton/DropdownButton.js +15 -0
  52. package/dist/components/formField/inputs/dropdown/buttons/dropdownButton/DropdownButton.js.map +1 -0
  53. package/dist/components/formField/inputs/dropdown/items/DropdownItemRenderer.d.ts +10 -0
  54. package/dist/components/formField/inputs/dropdown/items/DropdownItemRenderer.d.ts.map +1 -0
  55. package/dist/components/formField/inputs/dropdown/items/DropdownItemRenderer.js +12 -0
  56. package/dist/components/formField/inputs/dropdown/items/DropdownItemRenderer.js.map +1 -0
  57. package/dist/components/formField/inputs/dropdown/items/dropdownItem/DropdownItem.d.ts +9 -0
  58. package/dist/components/formField/inputs/dropdown/items/dropdownItem/DropdownItem.d.ts.map +1 -0
  59. package/dist/components/formField/inputs/dropdown/items/dropdownItem/DropdownItem.js +17 -0
  60. package/dist/components/formField/inputs/dropdown/items/dropdownItem/DropdownItem.js.map +1 -0
  61. package/dist/components/formField/inputs/dropdown/items/dropdownMultiLineItem/DropdownMultiLineItem.d.ts +7 -0
  62. package/dist/components/formField/inputs/dropdown/items/dropdownMultiLineItem/DropdownMultiLineItem.d.ts.map +1 -0
  63. package/dist/components/formField/inputs/dropdown/items/dropdownMultiLineItem/DropdownMultiLineItem.js +16 -0
  64. package/dist/components/formField/inputs/dropdown/items/dropdownMultiLineItem/DropdownMultiLineItem.js.map +1 -0
  65. package/dist/components/formField/inputs/dropdown/wrapper/DropdownWrapper.d.ts +16 -0
  66. package/dist/components/formField/inputs/dropdown/wrapper/DropdownWrapper.d.ts.map +1 -0
  67. package/dist/components/formField/inputs/dropdown/wrapper/DropdownWrapper.js +73 -0
  68. package/dist/components/formField/inputs/dropdown/wrapper/DropdownWrapper.js.map +1 -0
  69. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.d.ts +10 -6
  70. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.d.ts.map +1 -1
  71. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.js +10 -6
  72. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.js.map +1 -1
  73. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.test.js +1 -1
  74. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.test.js.map +1 -1
  75. package/dist/components/heading/HeadingInnerContainer.d.ts +5 -0
  76. package/dist/components/heading/HeadingInnerContainer.d.ts.map +1 -0
  77. package/dist/components/heading/HeadingInnerContainer.js +7 -0
  78. package/dist/components/heading/HeadingInnerContainer.js.map +1 -0
  79. package/dist/components/icon/Icon.d.ts +0 -1
  80. package/dist/components/icon/Icon.d.ts.map +1 -1
  81. package/dist/components/icon/Icon.js +1 -1
  82. package/dist/components/icon/Icon.js.map +1 -1
  83. package/dist/components/searchBar/SearchBar.d.ts +8 -0
  84. package/dist/components/searchBar/SearchBar.d.ts.map +1 -0
  85. package/dist/components/searchBar/SearchBar.js +38 -0
  86. package/dist/components/searchBar/SearchBar.js.map +1 -0
  87. package/dist/components/searchBar/SearchBar.test.d.ts +2 -0
  88. package/dist/components/searchBar/SearchBar.test.d.ts.map +1 -0
  89. package/dist/components/searchBar/SearchBar.test.js +36 -0
  90. package/dist/components/searchBar/SearchBar.test.js.map +1 -0
  91. package/dist/components/slideover/Slideover.d.ts +1 -0
  92. package/dist/components/slideover/Slideover.d.ts.map +1 -1
  93. package/dist/components/slideover/Slideover.js +2 -2
  94. package/dist/components/slideover/Slideover.js.map +1 -1
  95. package/dist/components/table/HideColumnsDropdown.d.ts +9 -0
  96. package/dist/components/table/HideColumnsDropdown.d.ts.map +1 -0
  97. package/dist/components/table/HideColumnsDropdown.js +33 -0
  98. package/dist/components/table/HideColumnsDropdown.js.map +1 -0
  99. package/dist/components/table/Table.d.ts +14 -0
  100. package/dist/components/table/Table.d.ts.map +1 -1
  101. package/dist/components/table/Table.js +8 -3
  102. package/dist/components/table/Table.js.map +1 -1
  103. package/dist/components/table/Table.stories.d.ts +3 -0
  104. package/dist/components/table/Table.stories.d.ts.map +1 -1
  105. package/dist/components/table/Table.stories.js +53 -1
  106. package/dist/components/table/Table.stories.js.map +1 -1
  107. package/dist/components/table/Table.test.js +132 -0
  108. package/dist/components/table/Table.test.js.map +1 -1
  109. package/dist/components/table/TableHeader.d.ts +3 -0
  110. package/dist/components/table/TableHeader.d.ts.map +1 -1
  111. package/dist/components/table/TableHeader.js +4 -3
  112. package/dist/components/table/TableHeader.js.map +1 -1
  113. package/dist/index.css +93 -1
  114. package/dist/index.css.map +1 -1
  115. package/dist/index.d.ts +1 -0
  116. package/dist/index.d.ts.map +1 -1
  117. package/dist/index.js +1 -0
  118. package/dist/index.js.map +1 -1
  119. package/dist/tailwind.css +229 -0
  120. package/package.json +2 -1
  121. package/src/components/dropdown/DropdownContent.tsx +4 -2
  122. package/src/components/formField/FormField.stories.tsx +17 -0
  123. package/src/components/formField/FormField.test.tsx +13 -1
  124. package/src/components/formField/FormField.tsx +10 -0
  125. package/src/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.stories.tsx +29 -0
  126. package/src/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.test.tsx +121 -0
  127. package/src/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.tsx +53 -0
  128. package/src/components/formField/inputs/colourPickerDropdown/colourPickerDropdown.scss +12 -0
  129. package/src/components/formField/inputs/selectDropdown/SelectDropdown.test.tsx +1 -1
  130. package/src/components/formField/inputs/selectDropdown/SelectDropdown.tsx +55 -41
  131. package/src/components/heading/heading.scss +1 -0
  132. package/src/components/icon/Icon.tsx +1 -2
  133. package/src/components/searchBar/SearchBar.test.tsx +40 -0
  134. package/src/components/searchBar/SearchBar.tsx +97 -0
  135. package/src/components/searchBar/searchBar.scss +69 -0
  136. package/src/components/slideover/Slideover.tsx +10 -5
  137. package/src/components/table/HideColumnsDropdown.tsx +57 -0
  138. package/src/components/table/Table.stories.tsx +96 -2
  139. package/src/components/table/Table.test.tsx +240 -0
  140. package/src/components/table/Table.tsx +10 -1
  141. package/src/components/table/TableHeader.tsx +8 -0
  142. package/src/components/table/table.scss +22 -0
  143. package/src/index.scss +24 -22
  144. package/src/index.ts +1 -0
  145. package/.github/workflows/changeset-version.yml +0 -39
  146. package/.github/workflows/merge-version-packages-pr.yml +0 -31
  147. package/.github/workflows/production-build.yml +0 -130
  148. package/release/design-system.components.tgz +0 -0
@@ -138,7 +138,7 @@ describe('SelectDropdown component', () => {
138
138
  onSelectionChange={vi.fn()}
139
139
  placeholder="Select"
140
140
  disabled={false}
141
- errorText="Something went wrong"
141
+ hasError
142
142
  options={[{ label: 'Label 1', value: 'label1' }]}
143
143
  />,
144
144
  );
@@ -5,25 +5,33 @@ import { Dropdown } from 'Components/dropdown/Dropdown';
5
5
  import { Button } from 'Components/button/Button';
6
6
 
7
7
  export type SelectDropdownInputProps = {
8
- placeholder?: string;
9
- options: SelectDropdownItemProps[];
10
- multiple?: boolean;
11
- disabled?: boolean;
12
- errorText?: string;
13
- onSelectionChange: (value: string[]) => void;
8
+ 'placeholder'?: string;
9
+ 'options': SelectDropdownItemProps[];
10
+ 'multiple'?: boolean;
11
+ 'disabled'?: boolean;
12
+ 'hasError'?: boolean;
13
+ 'aria-describedBy'?: string;
14
+ 'aria-invalid'?: boolean;
15
+ 'onSelectionChange'?: (value: string[]) => void;
16
+ 'id'?: string;
17
+ 'alwaysShowPlaceholder'?: boolean;
14
18
  };
15
19
 
16
20
  export const SelectDropdown = (props: SelectDropdownInputProps) => {
17
- const { options, disabled, multiple, placeholder, errorText, onSelectionChange } = props;
21
+ const { options, disabled, multiple, placeholder, hasError, onSelectionChange, id, 'aria-describedBy': ariaDescribedBy, 'aria-invalid': ariaInvalid, alwaysShowPlaceholder = false } = props;
18
22
 
19
23
  const [selectedValues, setSelectedValues] = useState<string[]>([]);
20
24
  const [renderedSelectContent, setRenderedSelectContent] = useState('');
21
25
 
22
26
  useEffect(() => {
23
- onSelectionChange(selectedValues);
27
+ onSelectionChange?.(selectedValues);
24
28
  }, [selectedValues]);
25
29
 
26
30
  useEffect(() => {
31
+ if (alwaysShowPlaceholder) {
32
+ setRenderedSelectContent(placeholder ?? 'Select');
33
+ return;
34
+ }
27
35
  if (selectedValues.length === 0) {
28
36
  setRenderedSelectContent(placeholder ?? 'Select');
29
37
  }
@@ -65,38 +73,44 @@ export const SelectDropdown = (props: SelectDropdownInputProps) => {
65
73
  };
66
74
 
67
75
  return (
68
- <Dropdown>
69
- <Dropdown.Trigger disabled={disabled}>
70
- <Button
71
- variant="dropdown"
72
- error={!!errorText}
73
- iconRightName="chevron-down"
74
- >
75
- {renderedSelectContent}
76
- </Button>
77
- </Dropdown.Trigger>
78
- <Dropdown.Content>
79
- {flatOptions.map(item =>
80
- 'headerLabel' in item
81
- ? (
82
- <h3 key={`${item.headerLabel}-header`} className="ds-select-dropdown__items--header">
83
- {item.headerLabel}
84
- </h3>
85
- )
86
- : (
87
- <SelectDropdownItem
88
- value={item.value}
89
- header={item.header}
90
- label={item.label}
91
- icon={item.icon}
92
- key={item.value}
93
- onSelection={handleItemClick}
94
- selected={selectedValues.includes(item.value)}
95
- closeAfterSelection={!multiple}
96
- />
97
- ),
98
- )}
99
- </Dropdown.Content>
100
- </Dropdown>
76
+ <>
77
+ <input type="hidden" name={id} value={selectedValues.join(',')} />
78
+ <Dropdown>
79
+ <Dropdown.Trigger disabled={disabled}>
80
+ <Button
81
+ variant="dropdown"
82
+ error={hasError}
83
+ iconRightName="chevron-down"
84
+ id={id}
85
+ aria-describedby={ariaDescribedBy}
86
+ aria-invalid={ariaInvalid}
87
+ >
88
+ {renderedSelectContent}
89
+ </Button>
90
+ </Dropdown.Trigger>
91
+ <Dropdown.Content>
92
+ {flatOptions.map(item =>
93
+ 'headerLabel' in item
94
+ ? (
95
+ <h3 key={`${item.headerLabel}-header`} className="ds-select-dropdown__items--header">
96
+ {item.headerLabel}
97
+ </h3>
98
+ )
99
+ : (
100
+ <SelectDropdownItem
101
+ value={item.value}
102
+ header={item.header}
103
+ label={item.label}
104
+ icon={item.icon}
105
+ key={item.value}
106
+ onSelection={handleItemClick}
107
+ selected={selectedValues.includes(item.value)}
108
+ closeAfterSelection={!multiple}
109
+ />
110
+ ),
111
+ )}
112
+ </Dropdown.Content>
113
+ </Dropdown>
114
+ </>
101
115
  );
102
116
  };
@@ -7,6 +7,7 @@
7
7
  justify-content: space-between;
8
8
  box-sizing: border-box;
9
9
  align-items: center;
10
+ margin: 0;
10
11
 
11
12
  &__inner-container {
12
13
  display: flex;
@@ -8,7 +8,6 @@ type IconProps = {
8
8
  screenReaderText?: string;
9
9
  name: IconName;
10
10
  className?: string;
11
- ariaLabel?: string;
12
11
  };
13
12
 
14
13
  export const Icon = (props: IconProps) => {
@@ -18,7 +17,7 @@ export const Icon = (props: IconProps) => {
18
17
  return (
19
18
  <>
20
19
  {/* why aria-hidden? https://gomakethings.com/revisting-aria-label-versus-a-visually-hidden-class/ */}
21
- <Component size={size} color={color} className={classes} role="img" aria-hidden="true" />
20
+ <Component size={size} color={color} className={classes} role="img" aria-hidden />
22
21
  {!!screenReaderText && (
23
22
  <span className="sr-only">{screenReaderText}</span>
24
23
  )}
@@ -0,0 +1,40 @@
1
+ import { describe, expect, test, vi, beforeEach } from 'vitest';
2
+ import { render, screen, fireEvent } from '@testing-library/react';
3
+ import '@testing-library/jest-dom/vitest';
4
+ import { SearchBar } from './SearchBar';
5
+ import userEvent from '@testing-library/user-event';
6
+
7
+ const setSearchMock = vi.fn();
8
+
9
+ describe('SearchBar component', () => {
10
+ beforeEach(() => {
11
+ setSearchMock.mockClear();
12
+ });
13
+
14
+ test('renders search bar with placeholder text', () => {
15
+ render(<SearchBar setSearchValue={setSearchMock} placeholderText="Search for something" />);
16
+ expect(screen.getByText('Search for something')).toBeInTheDocument();
17
+ });
18
+
19
+ test('renders search bar with icon only', () => {
20
+ render(<SearchBar setSearchValue={setSearchMock} />);
21
+ expect(screen.getByTestId('search-bar-inactive')).toHaveClass('ds-search-bar--inactive-icon-only');
22
+ });
23
+
24
+ test('runs setSearch when input changes', async () => {
25
+ render(<SearchBar setSearchValue={setSearchMock} />);
26
+ await userEvent.click(screen.getByTestId('search-bar-inactive'));
27
+ const input = screen.getByLabelText('Search input');
28
+ fireEvent.change(input, { target: { value: 'Hello' } });
29
+ expect(setSearchMock).toHaveBeenCalledTimes(1);
30
+ expect(setSearchMock).toHaveBeenCalledWith('Hello');
31
+ });
32
+
33
+ test('runs handleClear when clear button is clicked', async () => {
34
+ render(<SearchBar setSearchValue={setSearchMock} />);
35
+ await userEvent.click(screen.getByTestId('search-bar-inactive'));
36
+ await userEvent.click(screen.getByRole('button', { name: 'Clear search' }));
37
+ expect(setSearchMock).toHaveBeenCalledTimes(1);
38
+ expect(setSearchMock).toHaveBeenCalledWith('');
39
+ });
40
+ });
@@ -0,0 +1,97 @@
1
+ import classNames from 'classnames';
2
+ import { Button } from 'Components/button/Button';
3
+ import { Icon } from 'Components/icon/Icon';
4
+ import { useRef, useState } from 'react';
5
+
6
+ type SearchBarProps = {
7
+ searchValue?: string;
8
+ setSearchValue?: (searchValue: string) => void;
9
+ placeholderText?: string;
10
+ };
11
+
12
+ export const SearchBar = (props: SearchBarProps) => {
13
+ const { searchValue, setSearchValue, placeholderText } = props;
14
+
15
+ const [isSearchVisible, setIsSearchVisible] = useState(!!searchValue);
16
+ const searchInputRef = useRef<HTMLInputElement>(null);
17
+
18
+ const handleSearch = (event: React.ChangeEvent<HTMLInputElement>) => {
19
+ setSearchValue?.(event.target.value);
20
+ };
21
+
22
+ const handleClear = () => {
23
+ setSearchValue?.('');
24
+ setIsSearchVisible(false);
25
+ };
26
+
27
+ const handleSearchIconClick = () => {
28
+ setIsSearchVisible(true);
29
+ setTimeout(() => {
30
+ searchInputRef.current?.focus();
31
+ }, 0);
32
+ };
33
+
34
+ const handleClearKeyDown = (event: React.KeyboardEvent<HTMLSpanElement>) => {
35
+ if (event.key === 'Enter' || event.key === ' ') {
36
+ event.preventDefault();
37
+ handleClear();
38
+ }
39
+ };
40
+
41
+ if (!isSearchVisible) {
42
+ return (
43
+ <Button
44
+ data-testid="search-bar-inactive"
45
+ className={
46
+ classNames('ds-search-bar--inactive', {
47
+ 'ds-search-bar--inactive-icon-only': !placeholderText,
48
+ 'ds-search-bar--inactive-with-placeholder': placeholderText,
49
+ })
50
+ }
51
+ onClick={handleSearchIconClick}
52
+ >
53
+ <Icon
54
+ name="search"
55
+ size={16}
56
+ color="var(--search-global-default-color-icon)"
57
+ className="ds-search-bar__icon"
58
+ />
59
+ {placeholderText
60
+ && (
61
+ <span className="ds-search-bar--inactive__placeholder">
62
+ {placeholderText}
63
+ </span>
64
+ )}
65
+ </Button>
66
+ );
67
+ }
68
+
69
+ return (
70
+ <span className="ds-search-bar">
71
+ <Icon
72
+ name="search"
73
+ size={16}
74
+ className="ds-search-bar__icon"
75
+ screenReaderText="Search icon"
76
+ />
77
+ <input
78
+ value={searchValue ?? ''}
79
+ onChange={handleSearch}
80
+ className="ds-search-bar__input"
81
+ type="text"
82
+ ref={searchInputRef}
83
+ aria-label="Search input"
84
+ />
85
+ <span
86
+ className="ds-search-bar__clear-container"
87
+ role="button"
88
+ tabIndex={0}
89
+ onClick={handleClear}
90
+ onKeyDown={handleClearKeyDown}
91
+ aria-label="Clear search"
92
+ >
93
+ <Icon name="x" size={12} className="ds-search-bar__clear" />
94
+ </span>
95
+ </span>
96
+ );
97
+ };
@@ -0,0 +1,69 @@
1
+ .ds-search-bar__icon {
2
+ color: var(--search-global-default-color-icon);
3
+ }
4
+
5
+ .ds-search-bar {
6
+ display: flex;
7
+ align-items: center;
8
+ gap: var(--search-global-spacing-horizontal-gap);
9
+ border-radius: var(--search-global-radius);
10
+ background: var(--search-global-default-color-background);
11
+ padding: var(--search-global-spacing-vertical) var(--search-global-spacing-horizontal);
12
+
13
+ &:focus-within {
14
+ background: var(--search-global-focus-color-background);
15
+ box-shadow: 0 0 0 3px var(--color-brand-500);
16
+
17
+ .ds-search-bar__icon {
18
+ color: var(--search-global-focus-color-icon);
19
+ }
20
+ }
21
+ }
22
+
23
+ .ds-search-bar__clear {
24
+ cursor: pointer;
25
+ align-items: center;
26
+ justify-content: center;
27
+ display: flex;
28
+ }
29
+
30
+ .ds-search-bar__input {
31
+ color: var(--search-global-default-color-text);
32
+ background-color: var(--search-global-default-color-background);
33
+ border: none;
34
+
35
+ &:focus {
36
+ outline: none;
37
+ }
38
+ }
39
+
40
+ .ds-search-bar--inactive {
41
+ display: flex;
42
+ cursor: pointer;
43
+ width: fit-content;
44
+ height: fit-content;
45
+ align-items: center;
46
+ justify-content: center;
47
+ border: none;
48
+ padding: var(--search-global-spacing-vertical) var(--search-global-spacing-horizontal);
49
+ gap: var(--search-global-spacing-horizontal-gap);
50
+
51
+ &__placeholder {
52
+ color: var(--search-global-default-color-text);
53
+ font-family: var(--type-body-p-family);
54
+ font-size: var(--type-body-p-size);
55
+ font-weight: var(--type-body-p-weight);
56
+ }
57
+
58
+ &-icon-only {
59
+ padding: var(--search-global-spacing-vertical);
60
+ }
61
+
62
+ &:hover {
63
+ background: var(--search-global-hover-color-background);
64
+
65
+ .ds-search-bar__icon {
66
+ color: var(--search-global-hover-color-icon);
67
+ }
68
+ }
69
+ }
@@ -12,18 +12,23 @@ export type SlideoverProps = {
12
12
  footerContents?: ReactNode;
13
13
  headerIcon?: IconName;
14
14
  centerHeaderText?: boolean;
15
+ hideBackButton?: boolean;
15
16
  };
16
17
 
17
18
  export const Slideover = (props: SlideoverProps) => {
18
- const { title, children, footerContents, headerIcon, centerHeaderText = true } = props;
19
+ const { title, children, footerContents, headerIcon, centerHeaderText = true, hideBackButton } = props;
19
20
 
20
21
  return (
21
22
  <aside className="ds-slideover">
22
23
  <div className={classNames('ds-slideover__header', { 'ds-slideover__header--center': centerHeaderText })}>
23
- <Button variant="tertiary" onClick={SlideoverUtils.removeSlideover}>
24
- <Icon name="chevrons-left" />
25
- Back
26
- </Button>
24
+ {
25
+ !hideBackButton && (
26
+ <Button variant="tertiary" onClick={SlideoverUtils.removeSlideover}>
27
+ <Icon name="chevrons-left" />
28
+ Back
29
+ </Button>
30
+ )
31
+ }
27
32
  <Heading level={2}>
28
33
  {title}
29
34
  {headerIcon && <Icon name={headerIcon} />}
@@ -0,0 +1,57 @@
1
+ import { SelectDropdown } from 'Components/formField/inputs/selectDropdown/SelectDropdown';
2
+ import { useContext, useState } from 'react';
3
+ import { GridApiContext } from './GridApiContext';
4
+ import type { Column } from 'ag-grid-community';
5
+
6
+ type HideColumnsDropdownProps = {
7
+ columns?: Column[];
8
+ onSelectionChanged?: (cols: string[]) => void;
9
+ overrideColumnHiding?: boolean;
10
+ };
11
+
12
+ export const HideColumnsDropdown = (props: HideColumnsDropdownProps) => {
13
+ const {
14
+ columns: rawColumns,
15
+ onSelectionChanged,
16
+ overrideColumnHiding = false,
17
+ } = props;
18
+
19
+ const gridApi = useContext(GridApiContext);
20
+ const currentColumns = rawColumns || gridApi?.getColumns();
21
+ const [hiddenColumns, setHiddenColumns] = useState<string[]>([]);
22
+
23
+ if (!currentColumns || currentColumns.length === 0) return null;
24
+
25
+ const options = currentColumns.map(column => ({
26
+ label: column.getColDef().headerName,
27
+ selected: column.isVisible(),
28
+ value: column.getColId(),
29
+ }));
30
+
31
+ const handleVisibilityChange = (columnsToHide: string[]) => {
32
+ if (onSelectionChanged) {
33
+ onSelectionChanged(columnsToHide);
34
+ }
35
+ setHiddenColumns(columnsToHide);
36
+ if (!overrideColumnHiding) {
37
+ const columnsToShow = currentColumns.filter((col) => {
38
+ return !columnsToHide.includes(col.getColId());
39
+ });
40
+
41
+ gridApi?.setColumnsVisible(columnsToHide, false);
42
+ gridApi?.setColumnsVisible(columnsToShow, true);
43
+ }
44
+ };
45
+
46
+ const placeholderText = hiddenColumns.length > 0 ? `Hide (${hiddenColumns.length})` : 'Hide Columns';
47
+
48
+ return (
49
+ <SelectDropdown
50
+ multiple
51
+ placeholder={placeholderText}
52
+ options={options}
53
+ onSelectionChange={handleVisibilityChange}
54
+ alwaysShowPlaceholder
55
+ />
56
+ );
57
+ };
@@ -1,5 +1,5 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite';
2
- import type { ColDef, ColGroupDef } from 'ag-grid-enterprise';
2
+ import { type Column, type ColDef, type ColGroupDef } from 'ag-grid-enterprise';
3
3
  import { Table } from './Table';
4
4
  import { Button } from 'Components/button/Button';
5
5
  import { Icon } from 'Components/icon/Icon';
@@ -8,6 +8,7 @@ import { BulkActionsDropdown } from './BulkActionsDropdown';
8
8
  import { useState, type ComponentProps } from 'react';
9
9
  import { RowCountInfo } from './pagination/RowCountInfo';
10
10
  import { PaginationPanel } from './pagination/PaginationPanel';
11
+ import { HideColumnsDropdown } from './HideColumnsDropdown';
11
12
 
12
13
  type TableProps = ComponentProps<typeof Table>;
13
14
 
@@ -62,7 +63,6 @@ const HeaderContent = [
62
63
  <Button key={1} variant="secondary" size="S">Button 2</Button>
63
64
  </div>,
64
65
  <div key={1} style={{ display: 'flex', gap: '1rem' }}>
65
- <Icon name="search" />
66
66
  <Icon name="download" />
67
67
  <Icon name="settings" />
68
68
  <Icon name="circle-help" />
@@ -105,6 +105,17 @@ export const WithHeader: Story = {
105
105
  },
106
106
  };
107
107
 
108
+ export const WithHeaderAndNoSearch: Story = {
109
+ args: {
110
+ rowData: sampleData,
111
+ columnDefs: sampleColumnDefs,
112
+ defaultColDef: defaultColDef,
113
+ domLayout: 'autoHeight',
114
+ headerContent: HeaderContent,
115
+ hasSearch: false,
116
+ },
117
+ };
118
+
108
119
  export const WithHeaderAndFooter: Story = {
109
120
  args: {
110
121
  rowData: sampleData,
@@ -142,6 +153,89 @@ export const WithBulkActions: Story = {
142
153
  },
143
154
  };
144
155
 
156
+ export const WithHideColumnsDropdown: Story = {
157
+ args: {
158
+ rowData: sampleData,
159
+ columnDefs: sampleColumnDefs,
160
+ defaultColDef: defaultColDef,
161
+ domLayout: 'autoHeight',
162
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
163
+ // @ts-ignore
164
+ overrideColumnHiding: false,
165
+ },
166
+ render: (args) => {
167
+ const {
168
+ rowData,
169
+ columnDefs,
170
+ defaultColDef,
171
+ domLayout,
172
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
173
+ // @ts-ignore
174
+ overrideColumnHiding,
175
+ } = args;
176
+
177
+ return (
178
+ <Table
179
+ rowData={rowData}
180
+ columnDefs={columnDefs}
181
+ defaultColDef={defaultColDef}
182
+ domLayout={domLayout}
183
+ headerContent={(
184
+ <HideColumnsDropdown
185
+ overrideColumnHiding={overrideColumnHiding}
186
+ onSelectionChanged={console.log}
187
+ />
188
+ )}
189
+ />
190
+ );
191
+ },
192
+ };
193
+
194
+ export const WithRestrictedHideColumnsDropdown: Story = {
195
+ args: {
196
+ rowData: sampleData,
197
+ columnDefs: sampleColumnDefs,
198
+ defaultColDef: defaultColDef,
199
+ domLayout: 'autoHeight',
200
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
201
+ // @ts-ignore
202
+ overrideColumnHiding: false,
203
+ },
204
+ render: (args) => {
205
+ const {
206
+ rowData,
207
+ columnDefs,
208
+ defaultColDef,
209
+ domLayout,
210
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
211
+ // @ts-ignore
212
+ overrideColumnHiding,
213
+ } = args;
214
+
215
+ const [columns, setColumns] = useState<Column[]>([]);
216
+
217
+ return (
218
+ <Table
219
+ rowData={rowData}
220
+ columnDefs={columnDefs}
221
+ defaultColDef={defaultColDef}
222
+ domLayout={domLayout}
223
+ onGridReady={(params) => {
224
+ const { api } = params;
225
+ setColumns(api.getColumns()?.slice(1) || []);
226
+ }}
227
+ headerContent={(
228
+ <HideColumnsDropdown
229
+ overrideColumnHiding={overrideColumnHiding}
230
+ onSelectionChanged={console.log}
231
+ columns={columns}
232
+ />
233
+ )}
234
+ />
235
+ );
236
+ },
237
+ };
238
+
145
239
  export const WithClientSidePagination: StoryObj<TableProps> = {
146
240
  args: {
147
241
  rowData: sampleData,