Skip to content

Commit 714d70f

Browse files
authored
Feat/persistent combobox updates (#1193)
* ✨ Added loading skeleton for items for persistent combo box * 🚧 Persistent combobox tests wip * ✅ Updates to tests for comboboxes * 🩹 minor fix to text * ✅ Added more coverage for persistentComboBox * 📦 Clean install * 🩹 Fix comments for test * ✅ Added tests for new env toggle functionality * ✅ Small fix to select test * ✅ More adjustments for coverage * ♻️ Changed name logic on select stories and moved test-only tag * 📦 Clean install * ✅ Fixed banner test
1 parent e20f1a7 commit 714d70f

20 files changed

Lines changed: 781 additions & 169 deletions

bun.lock

Lines changed: 55 additions & 45 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/atoms/utils/environmentToggle.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,6 @@ const VARIANT_COLORS: Record<StatusVariant, ColorConfig> = {
3838
},
3939
};
4040

41-
const DEFAULT_COLORS: ColorConfig = {
42-
border: colors.interactive.warning__resting.rgba,
43-
background: colors.interactive.warning__text.rgba,
44-
chipBackground: colors.interactive.warning__resting.rgba,
45-
outline: colors.interactive.warning__resting.rgba,
46-
};
47-
48-
export function getVariantColors(
49-
variant: StatusVariant | undefined
50-
): ColorConfig {
51-
return variant ? VARIANT_COLORS[variant] : DEFAULT_COLORS;
41+
export function getVariantColors(variant: StatusVariant): ColorConfig {
42+
return VARIANT_COLORS[variant];
5243
}

src/molecules/Banner/Banner.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,6 @@ export const Compact: Story = {
8989
},
9090
play: async ({ canvas, args }) => {
9191
const container = canvas.getByText(args.children as string);
92-
await expect(container.parentElement).toHaveStyle('padding: 8px');
92+
await expect(container.parentElement).toHaveStyle('padding: 4px 8px');
9393
},
9494
};

src/molecules/Select/ComboBox/ComboBox.stories.tsx

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ export const BasicComboBox: StoryFn = (args) => {
119119
);
120120
};
121121

122-
export const ComboBoxWithReallyLongName: StoryFn = (args) => {
122+
export const ReallyLongName: StoryFn = (args) => {
123123
const [values, setValues] = useState<SelectOption<Item>[]>([]);
124124

125125
const handleOnSelect = (
@@ -140,7 +140,7 @@ export const ComboBoxWithReallyLongName: StoryFn = (args) => {
140140
);
141141
};
142142

143-
export const ComboBoxWithGroups: StoryFn = (args) => {
143+
export const Groups: StoryFn = (args) => {
144144
const [values, setValues] = useState<SelectOption<Item>[]>([]);
145145

146146
const handleOnSelect = (
@@ -161,7 +161,7 @@ export const ComboBoxWithGroups: StoryFn = (args) => {
161161
);
162162
};
163163

164-
export const ComboBoxParented: StoryFn = (args) => {
164+
export const Parented: StoryFn = (args) => {
165165
const [values, setValues] = useState<SelectOption<Item>[]>([]);
166166

167167
const handleOnSelect = (
@@ -182,7 +182,7 @@ export const ComboBoxParented: StoryFn = (args) => {
182182
);
183183
};
184184

185-
export const ComboBoxInDialog: StoryFn = (args) => {
185+
export const InDialog: StoryFn = (args) => {
186186
const [openDialog, setOpenDialog] = useState(false);
187187
const [values, setValues] = useState<SelectOption<Item>[]>([]);
188188
const [openComboBox, setOpenComboBox] = useState(false);
@@ -225,7 +225,7 @@ export const ComboBoxInDialog: StoryFn = (args) => {
225225
);
226226
};
227227

228-
export const ComboBoxWithAdd: StoryFn = (args) => {
228+
export const AddFunctionality: StoryFn = (args) => {
229229
const [items, setItems] = useState([...FAKE_ITEMS]);
230230
const [values, setValues] = useState<SelectOption<Item>[]>([]);
231231
const handleOnSelect = (newValues: SelectOptionRequired[]) => {
@@ -277,7 +277,7 @@ const CustomValueElement: FC<{
277277
</ComboBoxChip>
278278
);
279279

280-
export const ComboBoxWithCustomValueElements: StoryFn = (args) => {
280+
export const CustomizableValueElement: StoryFn = (args) => {
281281
const [values, setValues] = useState<SelectOption<Item>[]>([]);
282282

283283
const handleOnSelect = (
@@ -299,7 +299,7 @@ export const ComboBoxWithCustomValueElements: StoryFn = (args) => {
299299
);
300300
};
301301

302-
export const ComboBoxWithSelectedValuesAsText: StoryFn = (args) => {
302+
export const SelectedValuesAsText: StoryFn = (args) => {
303303
const [values, setValues] = useState<SelectOption<Item>[]>([]);
304304

305305
const handleOnSelect = (
@@ -321,9 +321,7 @@ export const ComboBoxWithSelectedValuesAsText: StoryFn = (args) => {
321321
);
322322
};
323323

324-
export const ComboBoxWithCustomSelectedValuesAsTextFunction: StoryFn = (
325-
args
326-
) => {
324+
export const CustomSelectedValuesAsTextFunction: StoryFn = (args) => {
327325
const [values, setValues] = useState<SelectOption<Item>[]>([]);
328326

329327
const handleOnSelect = (
@@ -358,7 +356,7 @@ const CustomMenuItem: FC<{
358356
</>
359357
);
360358

361-
export const ComboboxWithCustomizableSelectMenuItem: StoryFn = (args) => {
359+
export const CustomizableMenuItem: StoryFn = (args) => {
362360
const [values, setValues] = useState<SelectOption<Item>[]>([]);
363361

364362
const handleOnSelect = (

src/molecules/Select/ComboBox/ComboBox.test.tsx

Lines changed: 94 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -664,22 +664,102 @@ const CustomValueElement: FC<{
664664
</ComboBoxChip>
665665
);
666666

667-
test('Custom value component', () => {
668-
const label = faker.animal.bear();
669-
const items = fakeSelectItems();
670-
const handler = vi.fn();
667+
describe('Custom value component', () => {
668+
test('Renders', () => {
669+
const label = faker.animal.bear();
670+
const items = fakeSelectItems();
671+
const handler = vi.fn();
671672

672-
render(
673-
<ComboBox
674-
label={label}
675-
onSelect={handler}
676-
items={items}
677-
values={[items[0]]}
678-
customValueComponent={CustomValueElement}
679-
/>
680-
);
673+
render(
674+
<ComboBox
675+
label={label}
676+
onSelect={handler}
677+
items={items}
678+
values={[items[0]]}
679+
customValueComponent={CustomValueElement}
680+
/>
681+
);
682+
683+
const customChip = screen
684+
.getByText('custom')
685+
.closest('.amplify-combo-box-chip');
686+
687+
expect(customChip).toBeInTheDocument();
688+
});
689+
690+
test('Removal by clicking', async () => {
691+
const label = faker.animal.bear();
692+
const items = fakeSelectItems();
693+
const handler = vi.fn();
694+
695+
const { rerender } = render(
696+
<ComboBox
697+
label={label}
698+
onSelect={handler}
699+
items={items}
700+
values={[items[0]]}
701+
customValueComponent={CustomValueElement}
702+
/>
703+
);
681704

682-
expect(screen.getByText('custom')).toBeInTheDocument();
705+
const user = userEvent.setup();
706+
const customChip = screen
707+
.getByText('custom')
708+
.closest('.amplify-combo-box-chip');
709+
710+
await user.click(customChip!);
711+
712+
expect(handler).toHaveBeenCalledWith([], items[0]);
713+
714+
rerender(
715+
<ComboBox
716+
label={label}
717+
onSelect={handler}
718+
items={items}
719+
values={[]}
720+
customValueComponent={CustomValueElement}
721+
/>
722+
);
723+
724+
expect(screen.queryByText('custom')).not.toBeInTheDocument();
725+
});
726+
727+
test('Removal by backspace', async () => {
728+
const label = faker.animal.bear();
729+
const items = fakeSelectItems();
730+
const handler = vi.fn();
731+
732+
const { rerender } = render(
733+
<ComboBox
734+
label={label}
735+
onSelect={handler}
736+
items={items}
737+
values={[items[0]]}
738+
customValueComponent={CustomValueElement}
739+
/>
740+
);
741+
742+
const user = userEvent.setup();
743+
const searchField = screen.getByRole('combobox');
744+
745+
await user.click(searchField);
746+
await user.keyboard('{Backspace}');
747+
await user.keyboard('{Backspace}');
748+
749+
expect(handler).toHaveBeenCalledWith([], items[0]);
750+
751+
rerender(
752+
<ComboBox
753+
label={label}
754+
onSelect={handler}
755+
items={items}
756+
values={[]}
757+
customValueComponent={CustomValueElement}
758+
/>
759+
);
760+
761+
expect(screen.queryByText('custom')).not.toBeInTheDocument();
762+
});
683763
});
684764

685765
test('showSelectedValuesAsText', async () => {

src/molecules/Select/DynamicMenuItem.tsx

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { KeyboardEvent, MouseEvent, ReactNode, useMemo } from 'react';
1+
import { KeyboardEvent, MouseEvent, useMemo } from 'react';
22

33
import { checkbox, checkbox_outline } from '@equinor/eds-icons';
44

@@ -22,14 +22,12 @@ interface DynamicMenuItemProps<T extends SelectOptionRequired> {
2222
menuItemProps: SingleSelectMenuItemProps<T> | MultiSelectMenuItemProps<T>;
2323
isSelected: boolean;
2424
handleOnParentKeyDown?: (event: KeyboardEvent<HTMLButtonElement>) => void;
25-
children?: ReactNode;
2625
}
2726

2827
export const DynamicMenuItem = <T extends SelectOptionRequired>({
2928
menuItemProps,
3029
isSelected,
3130
handleOnParentKeyDown,
32-
children,
3331
}: DynamicMenuItemProps<T>) => {
3432
const {
3533
index,
@@ -64,8 +62,6 @@ export const DynamicMenuItem = <T extends SelectOptionRequired>({
6462
};
6563

6664
const itemContent = useMemo(() => {
67-
if (children) return children;
68-
6965
if (CustomMenuItemComponent) {
7066
return (
7167
<CustomMenuItemComponent item={item} selectedState={selectedState} />
@@ -90,7 +86,6 @@ export const DynamicMenuItem = <T extends SelectOptionRequired>({
9086
}, [
9187
CustomMenuItemComponent,
9288
checkboxIcon,
93-
children,
9489
item,
9590
menuItemProps,
9691
selectedState,

src/molecules/Select/GroupedSelectPersistent.tsx

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,25 @@ import {
77
NoItemsFoundText,
88
PersistentGroupsWrapper,
99
} from 'src/molecules/Select/Select.styles';
10-
import { GroupedSelectPropsCombined } from 'src/molecules/Select/Select.types';
10+
import {
11+
CustomMenuItemComponentProps,
12+
GroupedSelectProps,
13+
MenuModeSelectProps,
14+
MultiSelectCommon,
15+
PersistentModeSelectProps,
16+
SelectMenuProps,
17+
SingleSelectCommon,
18+
} from 'src/molecules/Select/Select.types';
1119
import { SelectMenuItem } from 'src/molecules/Select/SelectMenuItem';
1220

21+
// Ignored because <T extends SelectOptionRequired> is marked at not covered, and is removed at runtime so it cannot be covered.
22+
/* c8 ignore next */
1323
export const GroupedSelectPersistent = <T extends SelectOptionRequired>(
14-
props: GroupedSelectPropsCombined<T>
24+
props: GroupedSelectProps<T> &
25+
SelectMenuProps<T> &
26+
CustomMenuItemComponentProps<T> &
27+
(MultiSelectCommon<T> | SingleSelectCommon<T>) &
28+
(PersistentModeSelectProps | MenuModeSelectProps)
1529
) => {
1630
const {
1731
onItemSelect,
@@ -27,9 +41,9 @@ export const GroupedSelectPersistent = <T extends SelectOptionRequired>(
2741
return <NoItemsFoundText>No items found</NoItemsFoundText>;
2842
}
2943

30-
if ('value' in props) {
31-
throw new Error('You cannot use SingleSelect with persistent mode');
32-
}
44+
// This case never happens, since there is a check in select.tsx. This check gives the correct typescript inference.
45+
/* c8 ignore next */
46+
if ('value' in props) return null;
3347

3448
return (
3549
<PersistentGroupsWrapper>

src/molecules/Select/ListSelectPersistent.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import {
2222
import { getChildOffset } from 'src/molecules/Select/Select.utils';
2323
import { SelectMenuItem } from 'src/molecules/Select/SelectMenuItem';
2424

25+
// Ignored because <T extends SelectOptionRequired> is marked at not covered, and is removed at runtime so it cannot be covered.
26+
/* c8 ignore next */
2527
export const ListSelectPersistent = <T extends SelectOptionRequired>(
2628
props: Omit<ListSelectProps<T>, 'onAddItem'> &
2729
ListSelectMenuProps &

0 commit comments

Comments
 (0)