@transferwise/components 0.0.0-experimental-333df2c → 0.0.0-experimental-e4e09f5

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 (70) hide show
  1. package/build/dateLookup/dateTrigger/DateTrigger.js +8 -4
  2. package/build/dateLookup/dateTrigger/DateTrigger.js.map +1 -1
  3. package/build/dateLookup/dateTrigger/DateTrigger.mjs +8 -4
  4. package/build/dateLookup/dateTrigger/DateTrigger.mjs.map +1 -1
  5. package/build/field/Field.js +9 -2
  6. package/build/field/Field.js.map +1 -1
  7. package/build/field/Field.mjs +9 -2
  8. package/build/field/Field.mjs.map +1 -1
  9. package/build/i18n/en.json +3 -1
  10. package/build/i18n/en.json.js +3 -1
  11. package/build/i18n/en.json.js.map +1 -1
  12. package/build/i18n/en.json.mjs +3 -1
  13. package/build/i18n/en.json.mjs.map +1 -1
  14. package/build/inputs/SelectInput.js +34 -86
  15. package/build/inputs/SelectInput.js.map +1 -1
  16. package/build/inputs/SelectInput.mjs +36 -88
  17. package/build/inputs/SelectInput.mjs.map +1 -1
  18. package/build/label/Label.js +29 -1
  19. package/build/label/Label.js.map +1 -1
  20. package/build/label/Label.messages.js +15 -0
  21. package/build/label/Label.messages.js.map +1 -0
  22. package/build/label/Label.messages.mjs +13 -0
  23. package/build/label/Label.messages.mjs.map +1 -0
  24. package/build/label/Label.mjs +30 -2
  25. package/build/label/Label.mjs.map +1 -1
  26. package/build/main.css +0 -18
  27. package/build/styles/dateLookup/dateTrigger/DateTrigger.css +0 -8
  28. package/build/styles/inputs/SelectInput.css +0 -10
  29. package/build/styles/main.css +0 -18
  30. package/build/types/dateLookup/dateTrigger/DateTrigger.d.ts.map +1 -1
  31. package/build/types/field/Field.d.ts +4 -2
  32. package/build/types/field/Field.d.ts.map +1 -1
  33. package/build/types/index.d.ts +1 -1
  34. package/build/types/index.d.ts.map +1 -1
  35. package/build/types/inputs/SelectInput.d.ts.map +1 -1
  36. package/build/types/label/Label.d.ts +10 -1
  37. package/build/types/label/Label.d.ts.map +1 -1
  38. package/build/types/label/Label.messages.d.ts +12 -0
  39. package/build/types/label/Label.messages.d.ts.map +1 -0
  40. package/build/types/label/index.d.ts +3 -0
  41. package/build/types/label/index.d.ts.map +1 -0
  42. package/package.json +4 -5
  43. package/src/dateInput/DateInput.tests.story.tsx +8 -32
  44. package/src/dateLookup/DateLookup.rtl.spec.tsx +1 -1
  45. package/src/dateLookup/dateTrigger/DateTrigger.css +0 -8
  46. package/src/dateLookup/dateTrigger/DateTrigger.less +0 -8
  47. package/src/dateLookup/dateTrigger/DateTrigger.spec.js +1 -1
  48. package/src/dateLookup/dateTrigger/DateTrigger.tsx +9 -4
  49. package/src/field/Field.spec.tsx +3 -3
  50. package/src/field/Field.story.tsx +81 -3
  51. package/src/field/Field.tsx +10 -4
  52. package/src/i18n/en.json +3 -1
  53. package/src/index.ts +1 -1
  54. package/src/inlineAlert/InlineAlert.story.tsx +8 -21
  55. package/src/inputs/InputGroup.spec.tsx +1 -1
  56. package/src/inputs/SearchInput.spec.tsx +1 -1
  57. package/src/inputs/SelectInput.css +0 -10
  58. package/src/inputs/SelectInput.less +0 -12
  59. package/src/inputs/SelectInput.spec.tsx +1 -1
  60. package/src/inputs/SelectInput.story.tsx +0 -20
  61. package/src/inputs/SelectInput.tsx +37 -116
  62. package/src/label/Label.messages.tsx +12 -0
  63. package/src/label/Label.story.tsx +30 -21
  64. package/src/label/Label.tsx +43 -2
  65. package/src/label/index.ts +2 -0
  66. package/src/main.css +0 -18
  67. package/src/radioGroup/RadioGroup.rtl.spec.tsx +1 -1
  68. package/src/select/Select.rtl.spec.tsx +1 -1
  69. package/src/switch/Switch.spec.tsx +1 -1
  70. package/src/field/Field.tests.story.tsx +0 -33
@@ -6,7 +6,6 @@ import {
6
6
  createContext,
7
7
  forwardRef,
8
8
  useContext,
9
- useDeferredValue,
10
9
  useEffect,
11
10
  useId,
12
11
  useMemo,
@@ -14,7 +13,6 @@ import {
14
13
  useState,
15
14
  } from 'react';
16
15
  import { useIntl } from 'react-intl';
17
- import { Virtualizer } from 'virtua';
18
16
 
19
17
  import { useEffectEvent } from '../common/hooks/useEffectEvent';
20
18
  import { useScreenSize } from '../common/hooks/useScreenSize';
@@ -31,8 +29,6 @@ import { InputGroup } from './InputGroup';
31
29
  import { SearchInput } from './SearchInput';
32
30
  import messages from './SelectInput.messages';
33
31
 
34
- const MAX_ITEMS_WITHOUT_VIRTUALIZATION = 50;
35
-
36
32
  function searchableString(value: string) {
37
33
  return value.trim().replace(/\s+/gu, ' ').normalize('NFKC').toLowerCase();
38
34
  }
@@ -44,7 +40,7 @@ function inferSearchableStrings(value: unknown) {
44
40
 
45
41
  if (typeof value === 'object' && value != null) {
46
42
  return Object.values(value)
47
- .filter((innerValue) => typeof innerValue === 'string')
43
+ .filter((innerValue): innerValue is string => typeof innerValue === 'string')
48
44
  .map((innerValue) => searchableString(innerValue));
49
45
  }
50
46
 
@@ -93,11 +89,6 @@ function dedupeSelectInputOptionItem<T>(
93
89
  return { ...item, value: undefined };
94
90
  }
95
91
 
96
- /**
97
- * Sets the `value` of duplicate option items to `undefined`, hiding them when
98
- * rendered. Indexes are kept intact within groups to preserve the active item
99
- * between filter changes when possible.
100
- */
101
92
  function dedupeSelectInputItems<T>(
102
93
  items: readonly SelectInputItem<T>[],
103
94
  ): SelectInputItem<T | undefined>[] {
@@ -121,23 +112,20 @@ function dedupeSelectInputItems<T>(
121
112
  });
122
113
  }
123
114
 
124
- function selectInputOptionItemIncludesNeedle<T>(item: SelectInputOptionItem<T>, needle: string) {
115
+ function filterSelectInputOptionItem<T>(item: SelectInputOptionItem<T>, needle: string) {
125
116
  return inferSearchableStrings(item.filterMatchers ?? item.value).some((haystack) =>
126
117
  haystack.includes(needle),
127
118
  );
128
119
  }
129
120
 
130
- function filterSelectInputItems<T>(
131
- items: readonly SelectInputItem<T>[],
132
- predicate: (item: SelectInputOptionItem<T>) => boolean,
133
- ) {
121
+ function filterSelectInputItems<T>(items: readonly SelectInputItem<T>[], needle: string) {
134
122
  return items.filter((item) => {
135
123
  switch (item.type) {
136
124
  case 'option': {
137
- return predicate(item);
125
+ return filterSelectInputOptionItem(item, needle);
138
126
  }
139
127
  case 'group': {
140
- return item.options.some((option) => predicate(option));
128
+ return item.options.some((option) => filterSelectInputOptionItem(option, needle));
141
129
  }
142
130
  default:
143
131
  }
@@ -283,15 +271,12 @@ export function SelectInput<T = string, M extends boolean = false>({
283
271
  }, [handleClose, open]);
284
272
 
285
273
  const [filterQuery, _setFilterQuery] = useState('');
286
- const deferredFilterQuery = useDeferredValue(filterQuery);
287
274
  const setFilterQuery = useEffectEvent((query: string) => {
288
275
  _setFilterQuery(query);
289
- if (query !== filterQuery) {
290
- onFilterChange({
291
- query,
292
- queryNormalized: query ? searchableString(query) : null,
293
- });
294
- }
276
+ onFilterChange({
277
+ query,
278
+ queryNormalized: query ? searchableString(query) : null,
279
+ });
295
280
  });
296
281
 
297
282
  const triggerRef = useRef<HTMLButtonElement | null>(null);
@@ -309,7 +294,9 @@ export function SelectInput<T = string, M extends boolean = false>({
309
294
  multiple={multiple}
310
295
  defaultValue={defaultValue}
311
296
  value={controlledValue}
312
- by={compareValues}
297
+ // TODO: Remove assertion when upgrading TypeScript to v5
298
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
299
+ by={compareValues as any}
313
300
  disabled={disabled}
314
301
  onChange={
315
302
  ((value) => {
@@ -362,8 +349,8 @@ export function SelectInput<T = string, M extends boolean = false>({
362
349
  content: !placeholderShown ? (
363
350
  <SelectInputOptionContentWithinTriggerContext.Provider value>
364
351
  {multiple && Array.isArray(value)
365
- ? (value as readonly NonNullable<T>[])
366
- .map((option) => renderValue(option, true))
352
+ ? value
353
+ .map((option: NonNullable<T>) => renderValue(option, true))
367
354
  .filter((node) => node != null)
368
355
  .join(', ')
369
356
  : renderValue(value as NonNullable<T>, true)}
@@ -392,7 +379,9 @@ export function SelectInput<T = string, M extends boolean = false>({
392
379
  setOpen(false);
393
380
  }}
394
381
  onCloseEnd={() => {
395
- setFilterQuery('');
382
+ if (filterQuery !== '') {
383
+ setFilterQuery('');
384
+ }
396
385
  }}
397
386
  >
398
387
  <SelectInputOptions
@@ -403,7 +392,7 @@ export function SelectInput<T = string, M extends boolean = false>({
403
392
  filterPlaceholder={filterPlaceholder}
404
393
  searchInputRef={searchInputRef}
405
394
  listboxRef={listboxRef}
406
- filterQuery={deferredFilterQuery}
395
+ filterQuery={filterQuery}
407
396
  onFilterChange={setFilterQuery}
408
397
  />
409
398
  </OptionsOverlay>
@@ -517,48 +506,7 @@ function SelectInputOptions<T = string>({
517
506
  }
518
507
  return undefined;
519
508
  }, [filterQuery, filterable]);
520
- useEffect(() => {
521
- if (needle) {
522
- // Ensure having an active option while filtering
523
- requestAnimationFrame(() => {
524
- if (
525
- controllerRef.current != null &&
526
- !controllerRef.current.hasAttribute('aria-activedescendant')
527
- ) {
528
- // Activate first option via synthetic key press
529
- controllerRef.current.dispatchEvent(
530
- new KeyboardEvent('keydown', { key: 'Home', bubbles: true }),
531
- );
532
- }
533
- });
534
- }
535
- }, [controllerRef, needle]);
536
-
537
- const filteredItems: readonly SelectInputItem<NonNullable<T> | undefined>[] =
538
- needle != null
539
- ? filterSelectInputItems(dedupeSelectInputItems(items), (item) =>
540
- selectInputOptionItemIncludesNeedle(item, needle),
541
- )
542
- : items;
543
- const resultsEmpty = needle != null && filteredItems.length === 0;
544
-
545
- const virtualized = filteredItems.length > MAX_ITEMS_WITHOUT_VIRTUALIZATION;
546
-
547
- // Items shown once shall be kept mounted until the needle changes, otherwise
548
- // the scroll position may jump around inadvertently. Pattern adopted from:
549
- // https://inokawa.github.io/virtua/?path=/story/advanced-keep-offscreen-items--append-only
550
- const [mountedIndexes, setMountedIndexes] = useState<number[]>([]);
551
- useEffect(() => {
552
- // Ensure the 'End' key works as intended by keeping the last item mounted
553
- setMountedIndexes((prevMountedIndexes) => {
554
- const indexes = new Set(prevMountedIndexes);
555
- indexes.add(filteredItems.length - 1);
556
- return [...indexes]; // Sorting is redundant by nature here
557
- });
558
- }, [
559
- needle, // Needed as `filteredItems.length` may be equal between two updates
560
- filteredItems.length,
561
- ]);
509
+ const resultsEmpty = needle != null && filterSelectInputItems(items, needle).length === 0;
562
510
 
563
511
  const listboxContainerRef = useRef<HTMLDivElement>(null);
564
512
  useEffect(() => {
@@ -574,19 +522,6 @@ function SelectInputOptions<T = string>({
574
522
  const statusId = useId();
575
523
  const listboxId = useId();
576
524
 
577
- const getItemNode = (index: number) => {
578
- const item = filteredItems[index];
579
- return (
580
- <SelectInputItemView
581
- // eslint-disable-next-line react/no-array-index-key
582
- key={index}
583
- item={item}
584
- renderValue={renderValue}
585
- needle={needle}
586
- />
587
- );
588
- };
589
-
590
525
  return (
591
526
  <ListboxBase.Options
592
527
  as={SelectInputOptionsContainer}
@@ -598,6 +533,12 @@ function SelectInputOptions<T = string>({
598
533
  controllerRef.current.setAttribute('aria-activedescendant', value);
599
534
  } else {
600
535
  controllerRef.current.removeAttribute('aria-activedescendant');
536
+ if (filterQuery) {
537
+ // Ensure having an active option while filtering
538
+ controllerRef.current.dispatchEvent(
539
+ new KeyboardEvent('keydown', { key: 'Home', bubbles: true }),
540
+ );
541
+ }
601
542
  }
602
543
  }
603
544
  }}
@@ -608,7 +549,7 @@ function SelectInputOptions<T = string>({
608
549
  ref={searchInputRef}
609
550
  shape="rectangle"
610
551
  placeholder={filterPlaceholder}
611
- defaultValue={filterQuery}
552
+ value={filterQuery}
612
553
  aria-controls={listboxId}
613
554
  aria-describedby={showStatus ? statusId : undefined}
614
555
  onKeyDown={(event) => {
@@ -619,9 +560,6 @@ function SelectInputOptions<T = string>({
619
560
  }
620
561
  }}
621
562
  onChange={(event) => {
622
- // Free up resources and ensure not to go out of bounds when the
623
- // resulting item count is less than before
624
- setMountedIndexes([]);
625
563
  onFilterChange(event.currentTarget.value);
626
564
  }}
627
565
  />
@@ -633,9 +571,7 @@ function SelectInputOptions<T = string>({
633
571
  tabIndex={-1}
634
572
  className={clsx(
635
573
  'np-select-input-listbox-container',
636
- virtualized && 'np-select-input-listbox-container--virtualized',
637
- needle == null && // Groups aren't shown when filtering
638
- items.some((item) => item.type === 'group') &&
574
+ items.some((item) => item.type === 'group') &&
639
575
  'np-select-input-listbox-container--has-group',
640
576
  )}
641
577
  >
@@ -654,27 +590,15 @@ function SelectInputOptions<T = string>({
654
590
  tabIndex={0}
655
591
  className="np-select-input-listbox"
656
592
  >
657
- {!virtualized ? (
658
- filteredItems.map((_, index) => getItemNode(index))
659
- ) : (
660
- <Virtualizer
661
- key={needle}
662
- count={filteredItems.length}
663
- keepMounted={mountedIndexes}
664
- scrollRef={listboxRef} // `VList` doesn't expose this
665
- onRangeChange={(startIndex, endIndex) => {
666
- setMountedIndexes((prevMountedIndexes) => {
667
- const indexes = new Set(prevMountedIndexes);
668
- for (let index = startIndex; index <= endIndex; index += 1) {
669
- indexes.add(index);
670
- }
671
- return [...indexes].sort((a, b) => a - b);
672
- });
673
- }}
674
- >
675
- {(index) => getItemNode(index)}
676
- </Virtualizer>
677
- )}
593
+ {(needle != null ? dedupeSelectInputItems(items) : items).map((item, index) => (
594
+ <SelectInputItemView
595
+ // eslint-disable-next-line react/no-array-index-key
596
+ key={index}
597
+ item={item}
598
+ renderValue={renderValue}
599
+ needle={needle}
600
+ />
601
+ ))}
678
602
  </div>
679
603
 
680
604
  {renderFooter != null ? (
@@ -715,10 +639,7 @@ function SelectInputItemView<T = string>({
715
639
  }: SelectInputItemViewProps<T>) {
716
640
  switch (item.type) {
717
641
  case 'option': {
718
- if (
719
- item.value != null &&
720
- (needle == null || selectInputOptionItemIncludesNeedle(item, needle))
721
- ) {
642
+ if (item.value != null && (needle == null || filterSelectInputOptionItem(item, needle))) {
722
643
  return (
723
644
  <SelectInputOption value={item.value} disabled={item.disabled}>
724
645
  {renderValue(item.value, false)}
@@ -0,0 +1,12 @@
1
+ import { defineMessages } from 'react-intl';
2
+
3
+ export default defineMessages({
4
+ optionalLabel: {
5
+ id: 'neptune.Label.optional',
6
+ defaultMessage: '(Optional)',
7
+ },
8
+ optionalAriaLabel: {
9
+ id: 'neptune.aria.Label.optional',
10
+ defaultMessage: 'This field is optional',
11
+ },
12
+ });
@@ -1,37 +1,46 @@
1
1
  import { useState } from 'react';
2
2
 
3
- import Info from '../info/Info';
4
3
  import { Input } from '../inputs/Input';
5
4
  import { Label } from './Label';
5
+ import InlineAlert from '../inlineAlert/InlineAlert';
6
+ import { lorem10 } from '../test-utils';
6
7
 
7
8
  export default {
8
9
  component: Label,
9
10
  title: 'Label',
11
+ tags: ['autodocs'],
10
12
  };
11
13
 
12
14
  export const Basic = () => {
13
15
  const [value, setValue] = useState<string | undefined>('This is some text');
14
16
  return (
15
- <Label>
16
- Phone number
17
- <Input value={value} id="input" onChange={({ target }) => setValue(target.value)} />
18
- </Label>
19
- );
20
- };
17
+ <>
18
+ <Label className="m-b-2">
19
+ Phone number
20
+ <Input value={value} id="input" onChange={({ target }) => setValue(target.value)} />
21
+ </Label>
21
22
 
22
- export const WithInfo = () => {
23
- const [value, setValue] = useState<string | undefined>('This is some text');
24
- return (
25
- <Label>
26
- <span className="d-flex">
27
- Phone number{' '}
28
- <Info
29
- content="This is some help in popover"
30
- aria-label="The aria label"
31
- className="m-l-1"
32
- />
33
- </span>
34
- <Input value={value} id="input" onChange={({ target }) => setValue(target.value)} />
35
- </Label>
23
+ <Label className="m-b-2">
24
+ <Label.Optional>Phone number</Label.Optional>
25
+ <Input value={value} id="input" onChange={({ target }) => setValue(target.value)} />
26
+ </Label>
27
+
28
+ <Label className="m-b-2">
29
+ <Label.Optional>Phone number</Label.Optional>
30
+ <Label.Description>This an field Description</Label.Description>
31
+ <Input value={value} id="input" onChange={({ target }) => setValue(target.value)} />
32
+ </Label>
33
+
34
+ <Label htmlFor="phone-number-1">
35
+ <Label.Optional>Phone number</Label.Optional>
36
+ <Label.Description>This an field Description</Label.Description>
37
+ </Label>
38
+ <Input
39
+ id="phone-number-1"
40
+ className="m-b-2"
41
+ value={value}
42
+ onChange={({ target }) => setValue(target.value)}
43
+ />
44
+ </>
36
45
  );
37
46
  };
@@ -1,4 +1,9 @@
1
1
  import { clsx } from 'clsx';
2
+ import messages from './Label.messages';
3
+ import { useIntl } from 'react-intl';
4
+ import Body from '../body';
5
+ import { CommonProps } from '../common';
6
+ import { PropsWithChildren } from 'react';
2
7
 
3
8
  export type LabelProps = {
4
9
  id?: string;
@@ -7,14 +12,50 @@ export type LabelProps = {
7
12
  children?: React.ReactNode;
8
13
  };
9
14
 
10
- export const Label = ({ id, htmlFor, className, children }: LabelProps) => {
15
+ const Label = ({ id, htmlFor, className, children }: LabelProps) => {
11
16
  return (
12
17
  <label
13
18
  id={id}
14
19
  htmlFor={htmlFor}
15
- className={clsx('control-label d-flex flex-column gap-y-1 m-b-0', className)}
20
+ className={clsx(
21
+ 'd-flex',
22
+ 'flex-column',
23
+ 'gap-y-4',
24
+ 'm-b-0',
25
+ 'np-text-body-default-bold',
26
+ 'text-primary',
27
+ className,
28
+ )}
16
29
  >
17
30
  {children}
18
31
  </label>
19
32
  );
20
33
  };
34
+
35
+ export type LabelOptionalProps = PropsWithChildren<CommonProps>;
36
+
37
+ const Optional = ({ children, className }: LabelOptionalProps) => {
38
+ const { formatMessage } = useIntl();
39
+ return (
40
+ <div>
41
+ {children}
42
+ <Body
43
+ as="span"
44
+ aria-label={formatMessage(messages.optionalAriaLabel)}
45
+ className={clsx('text-secondary', 'm-l-1', className)}
46
+ >
47
+ {formatMessage(messages.optionalLabel)}
48
+ </Body>
49
+ </div>
50
+ );
51
+ };
52
+
53
+ export type LabelDescriptionProps = PropsWithChildren<CommonProps>;
54
+
55
+ const Description = ({ children, className }: LabelDescriptionProps) =>
56
+ children ? <Body className={clsx('text-secondary', className)}>{children}</Body> : null;
57
+
58
+ Label.Optional = Optional;
59
+ Label.Description = Description;
60
+
61
+ export { Label };
@@ -0,0 +1,2 @@
1
+ export { Label } from './Label';
2
+ export type { LabelProps, LabelOptionalProps, LabelDescriptionProps } from './Label';
package/src/main.css CHANGED
@@ -1719,18 +1719,10 @@ button.np-option {
1719
1719
  white-space: nowrap;
1720
1720
  width: 100%;
1721
1721
  }
1722
- .np-date-trigger .control-label {
1723
- font-weight: 400;
1724
- font-weight: var(--font-weight-regular);
1725
- }
1726
1722
  .np-theme-personal .np-date-trigger {
1727
1723
  padding-left: 16px;
1728
1724
  padding-left: var(--size-16);
1729
1725
  }
1730
- .np-theme-personal .np-date-trigger .control-label + span {
1731
- font-weight: 400;
1732
- font-weight: var(--font-weight-regular);
1733
- }
1734
1726
  .clear-btn {
1735
1727
  transition: color 0.15s ease-in-out;
1736
1728
  color: #c9cbce;
@@ -2655,10 +2647,6 @@ html:not([dir="rtl"]) .np-flow-navigation--sm .np-flow-navigation__stepper {
2655
2647
  height: auto;
2656
2648
  }
2657
2649
  }
2658
- .np-select-input-listbox-container--virtualized {
2659
- /* The wrapping element shrinks this as needed */
2660
- height: 100vh;
2661
- }
2662
2650
  .np-select-input-listbox-container--has-group {
2663
2651
  scroll-padding-top: 32px;
2664
2652
  scroll-padding-top: var(--size-32);
@@ -2677,12 +2665,6 @@ html:not([dir="rtl"]) .np-flow-navigation--sm .np-flow-navigation__stepper {
2677
2665
  outline: var(--ring-outline-color) solid var(--ring-outline-width);
2678
2666
  outline-offset: var(--ring-outline-offset);
2679
2667
  }
2680
- .np-select-input-listbox-container--virtualized .np-select-input-listbox {
2681
- /* Adopted from `VList` in virtua: https://github.com/inokawa/virtua/blob/7f6ed5b37df6b480d4ff350f3960067c5b3519d2/src/react/VList.tsx#L113-L116 */
2682
- overflow-y: auto;
2683
- contain: strict;
2684
- height: 100%;
2685
- }
2686
2668
  .np-select-input-separator-item {
2687
2669
  margin: 8px;
2688
2670
  margin: var(--size-8);
@@ -1,4 +1,4 @@
1
- import { render, screen } from '@testing-library/react';
1
+ import { render, screen } from '../test-utils';
2
2
 
3
3
  import RadioGroup from '.';
4
4
  import { Field } from '../field/Field';
@@ -12,6 +12,6 @@ describe('Select', () => {
12
12
  <Select options={options} selected={options[0]} onChange={() => {}} />
13
13
  </Field>,
14
14
  );
15
- expect(screen.getByLabelText('Currency')).toHaveTextContent('USD');
15
+ expect(screen.getByLabelText(/Currency/)).toHaveTextContent('USD');
16
16
  });
17
17
  });
@@ -85,7 +85,7 @@ describe('Switch', () => {
85
85
 
86
86
  it('supports `Field` for labeling', () => {
87
87
  render(
88
- <Field label="Dark mode">
88
+ <Field label="Dark mode" required>
89
89
  <Switch checked onClick={props.onClick} />
90
90
  </Field>,
91
91
  );
@@ -1,33 +0,0 @@
1
- import { useState } from 'react';
2
-
3
- import { Input } from '../inputs/Input';
4
- import { Field } from './Field';
5
- import { Sentiment } from '../common';
6
-
7
- export default {
8
- component: Field,
9
- title: 'Field/Tests',
10
- };
11
-
12
- export const WithHelpAndErrorOnBlur = () => {
13
- const [value, setValue] = useState('This is some text');
14
- const [error, setError] = useState<string | undefined>(undefined);
15
- return (
16
- <Field
17
- label="Phone number"
18
- sentiment={error ? Sentiment.NEGATIVE : Sentiment.NEUTRAL}
19
- message={error || 'Please include country code'}
20
- >
21
- <Input
22
- value={value}
23
- onChange={({ target }) => {
24
- setValue(target.value);
25
- setError(undefined);
26
- }}
27
- onBlur={() => {
28
- setError('Something went wrong');
29
- }}
30
- />
31
- </Field>
32
- );
33
- };