@transferwise/components 46.122.0 → 46.123.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/build/inputs/SelectInput.js +55 -6
  2. package/build/inputs/SelectInput.js.map +1 -1
  3. package/build/inputs/SelectInput.mjs +55 -6
  4. package/build/inputs/SelectInput.mjs.map +1 -1
  5. package/build/main.css +97 -77
  6. package/build/prompt/InlinePrompt/InlinePrompt.js +8 -10
  7. package/build/prompt/InlinePrompt/InlinePrompt.js.map +1 -1
  8. package/build/prompt/InlinePrompt/InlinePrompt.mjs +9 -11
  9. package/build/prompt/InlinePrompt/InlinePrompt.mjs.map +1 -1
  10. package/build/prompt/PrimitivePrompt/PrimitivePrompt.js +45 -0
  11. package/build/prompt/PrimitivePrompt/PrimitivePrompt.js.map +1 -0
  12. package/build/prompt/PrimitivePrompt/PrimitivePrompt.mjs +43 -0
  13. package/build/prompt/PrimitivePrompt/PrimitivePrompt.mjs.map +1 -0
  14. package/build/styles/main.css +97 -77
  15. package/build/styles/prompt/InlinePrompt/InlinePrompt.css +2 -23
  16. package/build/styles/prompt/PrimitivePrompt/PrimitivePrompt.css +41 -0
  17. package/build/types/inputs/SelectInput.d.ts +2 -1
  18. package/build/types/inputs/SelectInput.d.ts.map +1 -1
  19. package/build/types/prompt/InlinePrompt/InlinePrompt.d.ts +1 -1
  20. package/build/types/prompt/InlinePrompt/InlinePrompt.d.ts.map +1 -1
  21. package/build/types/prompt/PrimitivePrompt/PrimitivePrompt.d.ts +30 -0
  22. package/build/types/prompt/PrimitivePrompt/PrimitivePrompt.d.ts.map +1 -0
  23. package/build/types/prompt/PrimitivePrompt/index.d.ts +3 -0
  24. package/build/types/prompt/PrimitivePrompt/index.d.ts.map +1 -0
  25. package/package.json +8 -8
  26. package/src/alert/Alert.tests.story.tsx +1 -0
  27. package/src/button/_stories/Button.brightGreen.tests.story.tsx +1 -1
  28. package/src/button/_stories/Button.dark.tests.story.tsx +1 -1
  29. package/src/button/_stories/Button.default.tests.story.tsx +1 -1
  30. package/src/button/_stories/Button.forestGreen.tests.story.tsx +1 -1
  31. package/src/button/_stories/Button.tests.story.tsx +1 -1
  32. package/src/circularButton/CircularButton.tests.story.tsx +1 -0
  33. package/src/dateInput/DateInput.tests.story.tsx +1 -0
  34. package/src/dateLookup/DateLookup.tests.story.tsx +1 -0
  35. package/src/header/Header.tests.story.tsx +1 -1
  36. package/src/inputs/SelectInput.spec.tsx +177 -0
  37. package/src/inputs/SelectInput.story.tsx +68 -0
  38. package/src/inputs/SelectInput.tsx +95 -10
  39. package/src/legacylistItem/LegacyListItem.tests.story.tsx +1 -1
  40. package/src/listItem/ListItem.spec.tsx +6 -8
  41. package/src/listItem/_stories/ListItem.focus.test.story.tsx +1 -1
  42. package/src/listItem/_stories/ListItem.layout.test.story.tsx +1 -1
  43. package/src/listItem/_stories/variants/ListItem.brightGreen.test.story.tsx +1 -1
  44. package/src/listItem/_stories/variants/ListItem.dark.test.story.tsx +1 -1
  45. package/src/listItem/_stories/variants/ListItem.forestGreen.test.story.tsx +1 -1
  46. package/src/listItem/_stories/variants/ListItem.medium.test.story.tsx +1 -1
  47. package/src/listItem/_stories/variants/ListItem.neutral.test.story.tsx +1 -1
  48. package/src/listItem/_stories/variants/ListItem.personal.test.story.tsx +1 -1
  49. package/src/listItem/_stories/variants/ListItem.rtl.test.story.tsx +1 -1
  50. package/src/listItem/_stories/variants/ListItem.small.test.story.tsx +1 -1
  51. package/src/main.css +97 -77
  52. package/src/main.less +2 -1
  53. package/src/prompt/InlinePrompt/InlinePrompt.css +2 -23
  54. package/src/prompt/InlinePrompt/InlinePrompt.less +3 -18
  55. package/src/prompt/InlinePrompt/InlinePrompt.spec.tsx +6 -6
  56. package/src/prompt/InlinePrompt/InlinePrompt.tsx +6 -7
  57. package/src/prompt/PrimitivePrompt/PrimitivePrompt.css +41 -0
  58. package/src/prompt/PrimitivePrompt/PrimitivePrompt.less +37 -0
  59. package/src/prompt/PrimitivePrompt/PrimitivePrompt.tsx +70 -0
  60. package/src/prompt/PrimitivePrompt/index.ts +2 -0
  61. package/src/radioGroup/RadioGroup.test.story.tsx +1 -1
  62. package/src/sentimentSurface/SentimentSurface.tests.story.tsx +1 -1
  63. package/src/snackbar/Snackbar.tests.story.tsx +1 -1
  64. package/src/stepper/Stepper.tests.story.tsx +1 -0
  65. package/src/summary/Summary.tests.story.tsx +1 -0
  66. package/src/title/Title.test.story.tsx +1 -0
  67. package/src/upload/Upload.tests.story.tsx +1 -0
  68. package/src/uploadInput/UploadInput.tests.story.tsx +1 -0
  69. package/src/withId/withId.story.tsx +1 -1
@@ -4,7 +4,7 @@ import { allModes } from '../../.storybook/modes';
4
4
 
5
5
  const meta: Meta<typeof Header> = {
6
6
  component: Header,
7
- tags: ['!autodocs'],
7
+ tags: ['!autodocs', '!manifest'],
8
8
  title: 'Typography/Header/tests',
9
9
  };
10
10
  export default meta;
@@ -263,6 +263,183 @@ describe('SelectInput', () => {
263
263
  expect(screen.getByLabelText(/Currency/)).toHaveAttribute('aria-haspopup');
264
264
  });
265
265
 
266
+ it('deduplicates search results across groups using compareValues as key', async () => {
267
+ interface Currency {
268
+ code: string;
269
+ name: string;
270
+ }
271
+
272
+ const usdInGroup1: Currency = { code: 'USD', name: 'US Dollar' };
273
+ const usdInGroup2: Currency = { code: 'USD', name: 'US Dollar' };
274
+ const eur: Currency = { code: 'EUR', name: 'Euro' };
275
+ const gbp: Currency = { code: 'GBP', name: 'British Pound' };
276
+
277
+ render(
278
+ <SelectInput<Currency>
279
+ items={[
280
+ {
281
+ type: 'group',
282
+ label: 'Popular',
283
+ options: [
284
+ { type: 'option', value: usdInGroup1 },
285
+ { type: 'option', value: eur },
286
+ ],
287
+ },
288
+ {
289
+ type: 'group',
290
+ label: 'All currencies',
291
+ options: [
292
+ { type: 'option', value: usdInGroup2 },
293
+ { type: 'option', value: gbp },
294
+ ],
295
+ },
296
+ ]}
297
+ compareValues="code"
298
+ renderValue={(currency) => currency.name}
299
+ filterable
300
+ />,
301
+ );
302
+
303
+ const trigger = screen.getByRole('combobox');
304
+ await userEvent.click(trigger);
305
+
306
+ const listbox = screen.getByRole('listbox');
307
+
308
+ // Before filtering, should show all 4 options (no deduplication yet)
309
+ let options = within(listbox).getAllByRole('option');
310
+ expect(options).toHaveLength(4);
311
+
312
+ const usdOptions = within(listbox).getAllByText('US Dollar');
313
+ expect(usdOptions).toHaveLength(2);
314
+
315
+ // Start filtering - type a search query to trigger deduplication
316
+ const searchInput = screen.getByRole('combobox', { expanded: true });
317
+ await userEvent.type(searchInput, 'u');
318
+
319
+ // After filtering with 'u', should show 3 unique options (USD deduplicated, EUR, GBP)
320
+ options = within(listbox).getAllByRole('option');
321
+ expect(options).toHaveLength(3);
322
+ expect(within(listbox).getByText('Euro')).toBeInTheDocument();
323
+ expect(within(listbox).getByText('British Pound')).toBeInTheDocument();
324
+
325
+ // Filter more specifically for 'dollar'
326
+ await userEvent.clear(searchInput);
327
+ await userEvent.type(searchInput, 'dollar');
328
+
329
+ const filteredOptions = within(listbox).getAllByRole('option');
330
+ // Should only show 1 USD option, not 2
331
+ expect(filteredOptions).toHaveLength(1);
332
+ expect(within(listbox).getByText('US Dollar')).toBeInTheDocument();
333
+ });
334
+
335
+ it('deduplicates search results across groups using compareValues as function', async () => {
336
+ interface Item {
337
+ id: number;
338
+ label: string;
339
+ }
340
+
341
+ const item1Group1: Item = { id: 1, label: 'Item One' };
342
+ const item2Group1: Item = { id: 2, label: 'Item Two' };
343
+
344
+ const item1Group2: Item = { id: 1, label: 'Item One' };
345
+
346
+ render(
347
+ <SelectInput<Item>
348
+ items={[
349
+ {
350
+ type: 'group',
351
+ label: 'Group A',
352
+ options: [
353
+ { type: 'option', value: item1Group1 },
354
+ { type: 'option', value: item2Group1 },
355
+ ],
356
+ },
357
+ {
358
+ type: 'group',
359
+ label: 'Group B',
360
+ options: [{ type: 'option', value: item1Group2 }],
361
+ },
362
+ ]}
363
+ compareValues={(a, b) => a?.id === b?.id}
364
+ renderValue={(item) => item.label}
365
+ filterable
366
+ />,
367
+ );
368
+
369
+ const trigger = screen.getByRole('combobox');
370
+ await userEvent.click(trigger);
371
+
372
+ const listbox = screen.getByRole('listbox');
373
+
374
+ // Before filtering, should show all 3 options (no deduplication yet)
375
+ let options = within(listbox).getAllByRole('option');
376
+ expect(options).toHaveLength(3);
377
+
378
+ // Start filtering - type a search query to trigger deduplication
379
+ const searchInput = screen.getByRole('combobox', { expanded: true });
380
+ await userEvent.type(searchInput, 'item');
381
+
382
+ // After filtering, should show 2 unique options (item with id:1 deduplicated, item with id:2)
383
+ options = within(listbox).getAllByRole('option');
384
+ expect(options).toHaveLength(2);
385
+ expect(within(listbox).getByText('Item One')).toBeInTheDocument();
386
+ expect(within(listbox).getByText('Item Two')).toBeInTheDocument();
387
+ });
388
+
389
+ it('sorts filtered options using sortFilteredOptions prop', async () => {
390
+ interface Country {
391
+ code: string;
392
+ name: string;
393
+ keywords: string[];
394
+ }
395
+
396
+ const countries: Country[] = [
397
+ { code: 'AD', name: 'Andorra', keywords: ['united states dollar'] },
398
+ { code: 'DE', name: 'Germany', keywords: ['EUR'] },
399
+ { code: 'US', name: 'United States', keywords: ['United States dollar', 'USD'] },
400
+ { code: 'ZM', name: 'Zambia', keywords: ['USD', 'united states dollar'] },
401
+ ];
402
+
403
+ render(
404
+ <SelectInput<Country>
405
+ items={countries.map((country) => ({
406
+ type: 'option',
407
+ value: country,
408
+ filterMatchers: country.keywords,
409
+ }))}
410
+ renderValue={(country) => country.name}
411
+ filterable
412
+ sortFilteredOptions={(a, b, searchQuery) => {
413
+ const query = searchQuery.toLowerCase();
414
+ const nameA = a.value.name.toLowerCase();
415
+ const nameB = b.value.name.toLowerCase();
416
+
417
+ const aMatch = nameA.includes(query);
418
+ const bMatch = nameB.includes(query);
419
+
420
+ if (aMatch && !bMatch) return -1;
421
+ if (!aMatch && bMatch) return 1;
422
+
423
+ return nameA.localeCompare(nameB);
424
+ }}
425
+ />,
426
+ );
427
+
428
+ const trigger = screen.getByRole('combobox');
429
+ await userEvent.click(trigger);
430
+
431
+ const searchInput = screen.getByRole('combobox', { expanded: true });
432
+ await userEvent.type(searchInput, 'united');
433
+
434
+ const listbox = screen.getByRole('listbox');
435
+ const options = within(listbox).getAllByRole('option');
436
+
437
+ expect(options).toHaveLength(3);
438
+ expect(options[0]).toHaveTextContent('United States');
439
+ expect(options[1]).toHaveTextContent('Andorra');
440
+ expect(options[2]).toHaveTextContent('Zambia');
441
+ });
442
+
266
443
  describe('listbox label', () => {
267
444
  const fieldLabel = 'Fruits';
268
445
  const triggerLabel = 'Select fruit';
@@ -616,3 +616,71 @@ export const WithAutocomplete: Story<string> = {
616
616
  );
617
617
  },
618
618
  };
619
+
620
+ interface CountryWithCurrency extends Country {
621
+ keywords: string[];
622
+ }
623
+
624
+ const countriesWithCurrency: CountryWithCurrency[] = [
625
+ { code: 'AD', name: 'Andorra', keywords: ['united states dollar'] },
626
+ { code: 'DE', name: 'Germany', keywords: ['EUR'] },
627
+ { code: 'US', name: 'United States', keywords: ['USD'] },
628
+ { code: 'ZM', name: 'Zambia', keywords: ['USD', 'united states dollar'] },
629
+ ];
630
+
631
+ function countryWithCurrencyOption(country: CountryWithCurrency) {
632
+ return {
633
+ type: 'option',
634
+ value: country,
635
+ filterMatchers: [country.name, country.code, ...country.keywords],
636
+ } satisfies SelectInputItem<CountryWithCurrency>;
637
+ }
638
+
639
+ export const WithCustomSearchResultSorting: Story<CountryWithCurrency> = {
640
+ args: {
641
+ items: countriesWithCurrency.map(countryWithCurrencyOption),
642
+ compareValues: 'code',
643
+ renderValue: (country) => (
644
+ <SelectInputOptionContent
645
+ title={country.name}
646
+ icon={<Flag code={country.code} intrinsicSize={24} />}
647
+ />
648
+ ),
649
+ value: countriesWithCurrency[0],
650
+ filterable: true,
651
+ filterPlaceholder: 'Type a currency / country',
652
+ sortFilteredOptions: (a, b, searchQuery) => {
653
+ const normalizedQuery = searchQuery.toLowerCase();
654
+ const nameA = a.value.name.toLowerCase();
655
+ const nameB = b.value.name.toLowerCase();
656
+
657
+ // Prioritize countries where name contains the search query
658
+ const aNameMatch = nameA.includes(normalizedQuery);
659
+ const bNameMatch = nameB.includes(normalizedQuery);
660
+
661
+ if (aNameMatch && !bNameMatch) return -1;
662
+ if (!aNameMatch && bNameMatch) return 1;
663
+
664
+ // Then sort alphabetically
665
+ return nameA.localeCompare(nameB);
666
+ },
667
+ size: 'lg',
668
+ } satisfies Story<CountryWithCurrency>['args'],
669
+ decorators: [
670
+ (Story) => (
671
+ <div>
672
+ <p className="m-b-3 np-text-body-default" style={{ maxWidth: '600px' }}>
673
+ This example demonstrates custom sorting with the <code>sortFilteredOptions</code> prop.
674
+ <br />
675
+ <br />
676
+ Try searching for &quot;united&quot; - Without the custom sorter, it would just be the
677
+ provided options order.
678
+ <br />
679
+ With a customer sorter function in this story, the countries with &quot;United&quot; in
680
+ their name appear first, followed by countries that only match via keywords.
681
+ </p>
682
+ <Story />
683
+ </div>
684
+ ),
685
+ ],
686
+ };
@@ -93,8 +93,13 @@ export type SelectInputItem<T = string> =
93
93
  function dedupeSelectInputOptionItem<T>(
94
94
  item: SelectInputOptionItem<T>,
95
95
  existingValues: Set<T>,
96
+ compareValues?: (a: T, b: T) => boolean,
96
97
  ): SelectInputOptionItem<T | undefined> {
97
- if (!existingValues.has(item.value)) {
98
+ const isDuplicate = compareValues
99
+ ? Array.from(existingValues).some((existingValue) => compareValues(item.value, existingValue))
100
+ : existingValues.has(item.value);
101
+
102
+ if (!isDuplicate) {
98
103
  existingValues.add(item.value);
99
104
  return item;
100
105
  }
@@ -108,18 +113,20 @@ function dedupeSelectInputOptionItem<T>(
108
113
  */
109
114
  function dedupeSelectInputItems<T>(
110
115
  items: readonly SelectInputItem<T>[],
116
+ compareValues?: (a: T, b: T) => boolean,
111
117
  ): SelectInputItem<T | undefined>[] {
112
118
  const existingValues = new Set<T>();
119
+
113
120
  return items.map((item) => {
114
121
  switch (item.type) {
115
122
  case 'option': {
116
- return dedupeSelectInputOptionItem(item, existingValues);
123
+ return dedupeSelectInputOptionItem(item, existingValues, compareValues);
117
124
  }
118
125
  case 'group': {
119
126
  return {
120
127
  ...item,
121
128
  options: item.options.map((option) =>
122
- dedupeSelectInputOptionItem(option, existingValues),
129
+ dedupeSelectInputOptionItem(option, existingValues, compareValues),
123
130
  ),
124
131
  };
125
132
  }
@@ -153,6 +160,38 @@ function filterSelectInputItems<T>(
153
160
  });
154
161
  }
155
162
 
163
+ /**
164
+ * Flattens and sorts filtered options using the provided comparator.
165
+ * Extracts all options from groups, filters out undefined values (deduplicated items),
166
+ * sorts them, and returns as a flat list of option items.
167
+ */
168
+ function sortSelectInputItems<T>(
169
+ items: readonly SelectInputItem<T | undefined>[],
170
+ compareFn: (
171
+ a: SelectInputOptionItem<NonNullable<T>>,
172
+ b: SelectInputOptionItem<NonNullable<T>>,
173
+ searchQuery: string,
174
+ ) => number,
175
+ searchQuery: string,
176
+ ): SelectInputItem<NonNullable<T>>[] {
177
+ const flattenedOption = items.flatMap((item) => {
178
+ if (item.type === 'option') {
179
+ return item.value !== undefined ? [item as SelectInputOptionItem<NonNullable<T>>] : [];
180
+ }
181
+
182
+ if (item.type === 'group') {
183
+ return item.options.filter(
184
+ (option): option is SelectInputOptionItem<NonNullable<T>> => option.value !== undefined,
185
+ );
186
+ }
187
+
188
+ return [];
189
+ });
190
+
191
+ // eslint-disable-next-line functional/immutable-data
192
+ return flattenedOption.sort((a, b) => compareFn(a, b, searchQuery));
193
+ }
194
+
156
195
  export interface SelectInputProps<T = string, M extends boolean = false> {
157
196
  id?: string;
158
197
  /**
@@ -203,6 +242,11 @@ export interface SelectInputProps<T = string, M extends boolean = false> {
203
242
  }) => React.ReactNode;
204
243
  filterable?: boolean;
205
244
  filterPlaceholder?: string;
245
+ sortFilteredOptions?: (
246
+ a: SelectInputOptionItem<NonNullable<T>>,
247
+ b: SelectInputOptionItem<NonNullable<T>>,
248
+ searchQuery: string,
249
+ ) => number;
206
250
  disabled?: boolean;
207
251
  size?: 'sm' | 'md' | 'lg';
208
252
  className?: string;
@@ -297,6 +341,7 @@ export function SelectInput<T = string, M extends boolean = false>({
297
341
  renderTrigger = defaultRenderTrigger,
298
342
  filterable,
299
343
  filterPlaceholder,
344
+ sortFilteredOptions,
300
345
  disabled,
301
346
  size = 'md',
302
347
  className,
@@ -481,10 +526,12 @@ export function SelectInput<T = string, M extends boolean = false>({
481
526
  id={id ? `${id}Search` : undefined}
482
527
  parentId={parentId}
483
528
  items={items}
529
+ compareValues={compareValues}
484
530
  renderValue={renderValue}
485
531
  renderFooter={renderFooter}
486
532
  filterable={filterable}
487
533
  filterPlaceholder={filterPlaceholder}
534
+ sortFilteredOptions={sortFilteredOptions}
488
535
  searchInputRef={searchInputRef}
489
536
  listboxRef={listboxRef}
490
537
  filterQuery={deferredFilterQuery}
@@ -589,7 +636,15 @@ const SelectInputOptionsContainer = forwardRef(function SelectInputOptionsContai
589
636
 
590
637
  interface SelectInputOptionsProps<T = string> extends Pick<
591
638
  SelectInputProps<T>,
592
- 'items' | 'renderValue' | 'renderFooter' | 'filterable' | 'filterPlaceholder' | 'id' | 'parentId'
639
+ | 'items'
640
+ | 'renderValue'
641
+ | 'renderFooter'
642
+ | 'filterable'
643
+ | 'filterPlaceholder'
644
+ | 'id'
645
+ | 'parentId'
646
+ | 'compareValues'
647
+ | 'sortFilteredOptions'
593
648
  > {
594
649
  searchInputRef: React.MutableRefObject<HTMLInputElement | null>;
595
650
  listboxRef: React.MutableRefObject<HTMLDivElement | null>;
@@ -606,10 +661,12 @@ function SelectInputOptions<T = string>({
606
661
  id,
607
662
  parentId,
608
663
  items,
664
+ compareValues: compareValuesProp,
609
665
  renderValue = String,
610
666
  renderFooter,
611
667
  filterable = false,
612
668
  filterPlaceholder,
669
+ sortFilteredOptions,
613
670
  searchInputRef,
614
671
  listboxRef,
615
672
  filterQuery,
@@ -651,12 +708,40 @@ function SelectInputOptions<T = string>({
651
708
  }
652
709
  }, [controllerRef, needle]);
653
710
 
654
- const filteredItems: readonly SelectInputItem<NonNullable<T> | undefined>[] =
655
- needle != null
656
- ? filterSelectInputItems(dedupeSelectInputItems(items), (item) =>
657
- selectInputOptionItemIncludesNeedle(item, needle),
658
- )
659
- : items;
711
+ const compareValues = useMemo(() => {
712
+ if (!compareValuesProp) {
713
+ return undefined;
714
+ }
715
+
716
+ if (typeof compareValuesProp === 'function') {
717
+ return (a: NonNullable<T>, b: NonNullable<T>) => compareValuesProp(a, b);
718
+ }
719
+
720
+ const key = compareValuesProp;
721
+ return (a: NonNullable<T>, b: NonNullable<T>) => {
722
+ if (typeof a === 'object' && a != null && typeof b === 'object' && b != null) {
723
+ return (a as Record<string, unknown>)[key] === (b as Record<string, unknown>)[key];
724
+ }
725
+ return a === b;
726
+ };
727
+ }, [compareValuesProp]);
728
+
729
+ const filteredItems: readonly SelectInputItem<NonNullable<T> | undefined>[] = useMemo(() => {
730
+ if (needle == null) {
731
+ return items;
732
+ }
733
+
734
+ const filtered = filterSelectInputItems(dedupeSelectInputItems(items, compareValues), (item) =>
735
+ selectInputOptionItemIncludesNeedle(item, needle),
736
+ );
737
+
738
+ if (sortFilteredOptions) {
739
+ return sortSelectInputItems(filtered, sortFilteredOptions, filterQuery);
740
+ }
741
+
742
+ return filtered;
743
+ // eslint-disable-next-line react-hooks/exhaustive-deps
744
+ }, [needle, items, compareValues]);
660
745
  const resultsEmpty = needle != null && filteredItems.length === 0;
661
746
 
662
747
  const virtualized = filteredItems.length > MAX_ITEMS_WITHOUT_VIRTUALIZATION;
@@ -6,7 +6,7 @@ import List from '../list';
6
6
 
7
7
  export default {
8
8
  component: LegacyListItem,
9
- tags: ['!autodocs'],
9
+ tags: ['!autodocs', 'deprecated', '!manifest'],
10
10
  title: 'Content/LegacyListItem/tests',
11
11
  };
12
12
 
@@ -1526,10 +1526,9 @@ describe('ListItem', () => {
1526
1526
  );
1527
1527
  expect(screen.queryByText(prompt)).not.toBeInTheDocument();
1528
1528
  expect(screen.getByText(disabledPromptMessage)).toBeInTheDocument();
1529
- expect(screen.getByTestId('InlinePrompt_Muted').parentNode?.parentNode).toHaveAttribute(
1530
- 'id',
1531
- expect.stringMatching(/_prompt$/),
1532
- );
1529
+ expect(
1530
+ screen.getByTestId('InlinePrompt_Muted').parentNode?.parentNode?.parentNode,
1531
+ ).toHaveAttribute('id', expect.stringMatching(/_prompt$/));
1533
1532
  });
1534
1533
 
1535
1534
  it('should render muted prompt if disabled and disabledPromptMessage are set', () => {
@@ -1537,10 +1536,9 @@ describe('ListItem', () => {
1537
1536
  <ListItem title="Test Title" disabled disabledPromptMessage={disabledPromptMessage} />,
1538
1537
  );
1539
1538
  expect(screen.getByText(disabledPromptMessage)).toBeInTheDocument();
1540
- expect(screen.getByTestId('InlinePrompt_Muted').parentNode?.parentNode).toHaveAttribute(
1541
- 'id',
1542
- expect.stringMatching(/_prompt$/),
1543
- );
1539
+ expect(
1540
+ screen.getByTestId('InlinePrompt_Muted').parentNode?.parentNode?.parentNode,
1541
+ ).toHaveAttribute('id', expect.stringMatching(/_prompt$/));
1544
1542
  });
1545
1543
  });
1546
1544
  });
@@ -21,7 +21,7 @@ const waitForListItem = async (canvas: ReturnType<typeof within>, timeout = 3000
21
21
  export default {
22
22
  component: ListItem,
23
23
  title: 'Content/ListItem/tests/focus',
24
- tags: ['!autodocs'],
24
+ tags: ['!autodocs', '!manifest'],
25
25
  parameters: {
26
26
  controls: { disable: true },
27
27
  actions: { disable: true },
@@ -12,7 +12,7 @@ import { LISTITEM_CQ } from '../constants';
12
12
 
13
13
  export default {
14
14
  title: 'Content/ListItem/tests/layout',
15
- tags: ['!autodocs'],
15
+ tags: ['!autodocs', '!manifest'],
16
16
  parameters: {
17
17
  controls: { disable: true },
18
18
  actions: { disable: true },
@@ -6,7 +6,7 @@ import { allModes } from '../../../../.storybook/modes';
6
6
  export default {
7
7
  component: ListItem,
8
8
  title: 'Content/ListItem/tests/variants/theme: bright-green',
9
- tags: ['!autodocs'],
9
+ tags: ['!autodocs', '!manifest'],
10
10
  parameters: {
11
11
  controls: { disable: true },
12
12
  actions: { disable: true },
@@ -6,7 +6,7 @@ import { allModes } from '../../../../.storybook/modes';
6
6
  export default {
7
7
  component: ListItem,
8
8
  title: 'Content/ListItem/tests/variants/theme: dark',
9
- tags: ['!autodocs'],
9
+ tags: ['!autodocs', '!manifest'],
10
10
  parameters: {
11
11
  controls: { disable: true },
12
12
  actions: { disable: true },
@@ -6,7 +6,7 @@ import { allModes } from '../../../../.storybook/modes';
6
6
  export default {
7
7
  component: ListItem,
8
8
  title: 'Content/ListItem/tests/variants/theme: forest-green',
9
- tags: ['!autodocs'],
9
+ tags: ['!autodocs', '!manifest'],
10
10
  parameters: {
11
11
  controls: { disable: true },
12
12
  actions: { disable: true },
@@ -6,7 +6,7 @@ import { withSizedContainer } from '../helpers';
6
6
  export default {
7
7
  component: ListItem,
8
8
  title: 'Content/ListItem/tests/variants/cq: medium',
9
- tags: ['!autodocs'],
9
+ tags: ['!autodocs', '!manifest'],
10
10
  parameters: {
11
11
  controls: { disable: true },
12
12
  actions: { disable: true },
@@ -12,7 +12,7 @@ import {
12
12
  export default {
13
13
  component: ListItem,
14
14
  title: 'Content/ListItem/tests/variants/surface: neutral',
15
- tags: ['!autodocs'],
15
+ tags: ['!autodocs', '!manifest'],
16
16
  parameters: {
17
17
  controls: { disable: true },
18
18
  actions: { disable: true },
@@ -11,7 +11,7 @@ import { VariantStory } from './helpers';
11
11
  export default {
12
12
  component: ListItem,
13
13
  title: 'Content/ListItem/tests/variants/theme: personal',
14
- tags: ['!autodocs'],
14
+ tags: ['!autodocs', '!manifest'],
15
15
  parameters: {
16
16
  controls: { disable: true },
17
17
  actions: { disable: true },
@@ -6,7 +6,7 @@ import { allModes } from '../../../../.storybook/modes';
6
6
  export default {
7
7
  component: ListItem,
8
8
  title: 'Content/ListItem/tests/variants/rtl',
9
- tags: ['!autodocs'],
9
+ tags: ['!autodocs', '!manifest'],
10
10
  parameters: {
11
11
  controls: { disable: true },
12
12
  actions: { disable: true },
@@ -6,7 +6,7 @@ import { withSizedContainer } from '../helpers';
6
6
  export default {
7
7
  component: ListItem,
8
8
  title: 'Content/ListItem/tests/variants/cq: small',
9
- tags: ['!autodocs'],
9
+ tags: ['!autodocs', '!manifest'],
10
10
  parameters: {
11
11
  controls: { disable: true },
12
12
  actions: { disable: true },