@transferwise/components 46.30.2 → 46.32.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 (80) hide show
  1. package/build/index.js +931 -523
  2. package/build/index.js.map +1 -1
  3. package/build/index.mjs +928 -523
  4. package/build/index.mjs.map +1 -1
  5. package/build/main.css +135 -0
  6. package/build/styles/carousel/Carousel.css +135 -0
  7. package/build/styles/main.css +135 -0
  8. package/build/types/carousel/Carousel.d.ts +26 -0
  9. package/build/types/carousel/Carousel.d.ts.map +1 -0
  10. package/build/types/carousel/index.d.ts +3 -0
  11. package/build/types/carousel/index.d.ts.map +1 -0
  12. package/build/types/common/card/Card.d.ts +2 -2
  13. package/build/types/common/card/Card.d.ts.map +1 -1
  14. package/build/types/dateInput/DateInput.d.ts +5 -4
  15. package/build/types/dateInput/DateInput.d.ts.map +1 -1
  16. package/build/types/dateLookup/DateLookup.d.ts +11 -4
  17. package/build/types/dateLookup/DateLookup.d.ts.map +1 -1
  18. package/build/types/field/Field.d.ts +12 -0
  19. package/build/types/field/Field.d.ts.map +1 -0
  20. package/build/types/index.d.ts +6 -0
  21. package/build/types/index.d.ts.map +1 -1
  22. package/build/types/inputs/Input.d.ts.map +1 -1
  23. package/build/types/inputs/SelectInput.d.ts +1 -1
  24. package/build/types/inputs/SelectInput.d.ts.map +1 -1
  25. package/build/types/inputs/TextArea.d.ts.map +1 -1
  26. package/build/types/inputs/_common.d.ts +2 -2
  27. package/build/types/inputs/_common.d.ts.map +1 -1
  28. package/build/types/inputs/contexts.d.ts +24 -0
  29. package/build/types/inputs/contexts.d.ts.map +1 -0
  30. package/build/types/label/Label.d.ts +9 -0
  31. package/build/types/label/Label.d.ts.map +1 -0
  32. package/build/types/phoneNumberInput/PhoneNumberInput.d.ts +1 -1
  33. package/build/types/phoneNumberInput/PhoneNumberInput.d.ts.map +1 -1
  34. package/build/types/promoCard/PromoCard.d.ts +16 -5
  35. package/build/types/promoCard/PromoCard.d.ts.map +1 -1
  36. package/build/types/radioGroup/RadioGroup.d.ts.map +1 -1
  37. package/build/types/switch/Switch.d.ts +6 -3
  38. package/build/types/switch/Switch.d.ts.map +1 -1
  39. package/package.json +3 -3
  40. package/src/carousel/Carousel.css +135 -0
  41. package/src/carousel/Carousel.less +133 -0
  42. package/src/carousel/Carousel.spec.tsx +221 -0
  43. package/src/carousel/Carousel.story.tsx +63 -0
  44. package/src/carousel/Carousel.tsx +345 -0
  45. package/src/carousel/index.ts +3 -0
  46. package/src/common/card/Card.tsx +51 -43
  47. package/src/dateInput/DateInput.rtl.spec.tsx +17 -0
  48. package/src/dateInput/DateInput.tsx +28 -22
  49. package/src/dateLookup/DateLookup.keyboardEvents.spec.js +2 -2
  50. package/src/dateLookup/DateLookup.rtl.spec.tsx +21 -0
  51. package/src/dateLookup/DateLookup.state.spec.js +5 -5
  52. package/src/dateLookup/DateLookup.tests.story.tsx +4 -11
  53. package/src/dateLookup/DateLookup.tsx +24 -9
  54. package/src/dateLookup/DateLookup.view.spec.js +11 -11
  55. package/src/field/Field.spec.tsx +95 -0
  56. package/src/field/Field.story.tsx +59 -0
  57. package/src/field/Field.tsx +70 -0
  58. package/src/index.ts +6 -0
  59. package/src/inputs/Input.tsx +5 -3
  60. package/src/inputs/SelectInput.spec.tsx +10 -0
  61. package/src/inputs/SelectInput.tsx +9 -4
  62. package/src/inputs/TextArea.tsx +6 -3
  63. package/src/inputs/_ButtonInput.tsx +2 -2
  64. package/src/inputs/_common.ts +2 -2
  65. package/src/inputs/contexts.tsx +45 -0
  66. package/src/label/Label.spec.tsx +26 -0
  67. package/src/label/Label.story.tsx +37 -0
  68. package/src/label/Label.tsx +20 -0
  69. package/src/main.css +135 -0
  70. package/src/main.less +1 -0
  71. package/src/phoneNumberInput/PhoneNumberInput.story.tsx +16 -22
  72. package/src/phoneNumberInput/PhoneNumberInput.tsx +14 -2
  73. package/src/promoCard/PromoCard.story.tsx +2 -2
  74. package/src/promoCard/PromoCard.tsx +30 -9
  75. package/src/radioGroup/RadioGroup.rtl.spec.tsx +14 -0
  76. package/src/radioGroup/RadioGroup.story.tsx +26 -0
  77. package/src/radioGroup/RadioGroup.tsx +4 -1
  78. package/src/switch/Switch.spec.tsx +10 -0
  79. package/src/switch/Switch.tsx +22 -13
  80. package/src/utilities/logActionRequired.js +1 -1
@@ -1,19 +1,19 @@
1
1
  import { useId } from '@radix-ui/react-id';
2
2
  import { Check } from '@transferwise/icons';
3
3
  import classNames from 'classnames';
4
- import React, { forwardRef, FunctionComponent, useEffect, useState } from 'react';
4
+ import React, { forwardRef, type FunctionComponent, useEffect, useState } from 'react';
5
5
 
6
6
  import Body from '../body';
7
7
  import { Typography } from '../common';
8
- import Card, { CardProps } from '../common/card';
8
+ import Card, { type CardProps } from '../common/card';
9
9
  import Display from '../display';
10
10
  import Image from '../image/Image';
11
11
  import Title from '../title';
12
12
 
13
13
  import { usePromoCardContext } from './PromoCardContext';
14
- import PromoCardIndicator, { PromoCardIndicatorProps } from './PromoCardIndicator';
14
+ import PromoCardIndicator, { type PromoCardIndicatorProps } from './PromoCardIndicator';
15
15
 
16
- export type ReferenceType = React.Ref<HTMLInputElement>;
16
+ export type ReferenceType = React.Ref<HTMLInputElement> | React.Ref<HTMLDivElement>;
17
17
  export type RelatedTypes =
18
18
  | ''
19
19
  | 'alternate'
@@ -68,6 +68,9 @@ export interface PromoCardCommonProps {
68
68
  /** Specify an onClick event handler */
69
69
  onClick?: () => void;
70
70
 
71
+ /** Specify an onKeyDown event handler */
72
+ onKeyDown?: (event: React.KeyboardEvent<HTMLInputElement>) => void;
73
+
71
74
  /** Optional prop to specify the ID used for testing */
72
75
  testId?: string;
73
76
 
@@ -76,6 +79,8 @@ export interface PromoCardCommonProps {
76
79
 
77
80
  /** Set to false to use body font style for the title */
78
81
  useDisplayFont?: boolean;
82
+
83
+ ref?: ReferenceType;
79
84
  }
80
85
 
81
86
  export interface PromoCardLinkProps extends PromoCardCommonProps, Omit<CardProps, 'children'> {
@@ -91,6 +96,14 @@ export interface PromoCardLinkProps extends PromoCardCommonProps, Omit<CardProps
91
96
  /** Optionally specify the language of the linked URL */
92
97
  hrefLang?: string;
93
98
 
99
+ /** Optional property that can be pass a ref for the anchor. */
100
+ anchorRef?: React.Ref<HTMLAnchorElement>;
101
+
102
+ /**
103
+ * Optional prop to specify the ID of the anchor element which can be useful when using a ref.
104
+ */
105
+ anchorId?: string;
106
+
94
107
  /**
95
108
  * Relationship between the PromoCard href URL and the current page. See
96
109
  * [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel).
@@ -105,7 +118,7 @@ export interface PromoCardLinkProps extends PromoCardCommonProps, Omit<CardProps
105
118
  isChecked?: never;
106
119
  tabIndex?: never;
107
120
  type?: never;
108
- reference?: never;
121
+ ref?: ReferenceType;
109
122
  value?: never;
110
123
  }
111
124
 
@@ -120,7 +133,7 @@ export interface PromoCardCheckedProps extends PromoCardCommonProps, Omit<CardPr
120
133
  tabIndex?: number;
121
134
 
122
135
  /** Optional property to provide component Ref */
123
- reference?: ReferenceType;
136
+ ref?: ReferenceType;
124
137
 
125
138
  /** Optional prop to specify the input type of the PromoCard */
126
139
  type?: 'checkbox' | 'radio';
@@ -131,6 +144,8 @@ export interface PromoCardCheckedProps extends PromoCardCommonProps, Omit<CardPr
131
144
  /** Only applies to <a />s */
132
145
  download?: never;
133
146
  href?: never;
147
+ anchorRef?: never;
148
+ anchorId?: never;
134
149
  hrefLang?: never;
135
150
  rel?: never;
136
151
  target?: never;
@@ -202,6 +217,7 @@ const PromoCard: FunctionComponent<PromoCardProps> = forwardRef(
202
217
  isChecked,
203
218
  isDisabled,
204
219
  onClick,
220
+ onKeyDown,
205
221
  rel,
206
222
  tabIndex,
207
223
  target,
@@ -211,9 +227,11 @@ const PromoCard: FunctionComponent<PromoCardProps> = forwardRef(
211
227
  value,
212
228
  isSmall,
213
229
  useDisplayFont = true,
230
+ anchorRef,
231
+ anchorId,
214
232
  ...props
215
233
  },
216
- reference,
234
+ ref: ReferenceType,
217
235
  ) => {
218
236
  // Set the `checked` state to the value of `defaultChecked` if it is truthy,
219
237
  // or the value of `isChecked` if it is truthy, or `false` if neither
@@ -276,7 +294,8 @@ const PromoCard: FunctionComponent<PromoCardProps> = forwardRef(
276
294
  id: componentId,
277
295
  isDisabled: isDisabled || contextIsDisabled,
278
296
  onClick,
279
- ref: reference,
297
+ onKeyDown,
298
+ ref,
280
299
  'data-testid': testId,
281
300
  isSmall,
282
301
  };
@@ -291,6 +310,8 @@ const PromoCard: FunctionComponent<PromoCardProps> = forwardRef(
291
310
  hrefLang,
292
311
  rel,
293
312
  target,
313
+ ref: anchorRef,
314
+ id: anchorId,
294
315
  }
295
316
  : {};
296
317
 
@@ -311,7 +332,7 @@ const PromoCard: FunctionComponent<PromoCardProps> = forwardRef(
311
332
  handleClick();
312
333
  }
313
334
  },
314
- ref: reference,
335
+ ref,
315
336
  tabIndex: 0,
316
337
  }
317
338
  : {};
@@ -1,6 +1,7 @@
1
1
  import { render, screen } from '@testing-library/react';
2
2
 
3
3
  import RadioGroup from '.';
4
+ import { Field } from '../field/Field';
4
5
 
5
6
  describe('RadioGroup', () => {
6
7
  it('has accessible role', () => {
@@ -13,4 +14,17 @@ describe('RadioGroup', () => {
13
14
  );
14
15
  expect(screen.getByRole('radiogroup')).toBeInTheDocument();
15
16
  });
17
+
18
+ it('supports `Field` for labeling', () => {
19
+ render(
20
+ <Field label="Currency">
21
+ <RadioGroup
22
+ name="currency"
23
+ radios={[{ label: 'USD' }, { label: 'EUR' }]}
24
+ onChange={() => {}}
25
+ />
26
+ </Field>,
27
+ );
28
+ expect(screen.getByRole('radiogroup')).toHaveAccessibleName(/^Currency/);
29
+ });
16
30
  });
@@ -5,6 +5,7 @@ import { Flag } from '@wise/art';
5
5
  import Avatar, { AvatarType } from '../avatar';
6
6
 
7
7
  import RadioGroup from './RadioGroup';
8
+ import { Field } from '../field/Field';
8
9
 
9
10
  export default {
10
11
  component: RadioGroup,
@@ -53,3 +54,28 @@ export const Basic = () => {
53
54
  </div>
54
55
  );
55
56
  };
57
+
58
+ export const Labeled = () => {
59
+ return (
60
+ <Field label="Do you like our product?">
61
+ <RadioGroup
62
+ name="radio-group"
63
+ radios={[
64
+ {
65
+ value: 'yes',
66
+ label: 'Yes',
67
+ },
68
+ {
69
+ value: 'definitely',
70
+ label: 'Definitely',
71
+ },
72
+ {
73
+ value: 'absolutely',
74
+ label: 'Absolutely',
75
+ },
76
+ ]}
77
+ onChange={(v) => action(v)}
78
+ />
79
+ </Field>
80
+ );
81
+ };
@@ -2,6 +2,7 @@ import { useState } from 'react';
2
2
 
3
3
  import Radio from '../radio';
4
4
  import { RadioProps } from '../radio/Radio';
5
+ import { useInputAttributes } from '../inputs/contexts';
5
6
 
6
7
  export type RadioGroupRadio<T extends string | number = string> = Omit<
7
8
  RadioProps<T>,
@@ -21,10 +22,12 @@ export default function RadioGroup<T extends string | number = never>({
21
22
  selectedValue: controlledValue,
22
23
  onChange,
23
24
  }: RadioGroupProps<T>) {
25
+ const inputAttributes = useInputAttributes({ nonLabelable: true });
26
+
24
27
  const [uncontrolledValue, setUncontrolledValue] = useState(controlledValue);
25
28
 
26
29
  return radios.length > 0 ? (
27
- <div role="radiogroup">
30
+ <div role="radiogroup" {...inputAttributes}>
28
31
  {radios.map(({ value = '' as T, ...restProps }, index) => (
29
32
  <Radio
30
33
  // eslint-disable-next-line react/no-array-index-key
@@ -1,3 +1,4 @@
1
+ import { Field } from '../field/Field';
1
2
  import { render, fireEvent, screen } from '../test-utils';
2
3
 
3
4
  import Switch from './Switch';
@@ -81,4 +82,13 @@ describe('Switch', () => {
81
82
  fireEvent.click(input);
82
83
  expect(mockCallback).not.toHaveBeenCalled();
83
84
  });
85
+
86
+ it('supports `Field` for labeling', () => {
87
+ render(
88
+ <Field label="Dark mode">
89
+ <Switch checked onClick={props.onClick} />
90
+ </Field>,
91
+ );
92
+ expect(screen.getByLabelText('Dark mode')).toHaveAttribute('role', 'switch');
93
+ });
84
94
  });
@@ -1,13 +1,16 @@
1
1
  import { CheckCircleFill, CrossCircleFill } from '@transferwise/icons';
2
2
  import { useTheme } from '@wise/components-theming';
3
3
  import classnames from 'classnames';
4
- import { KeyboardEventHandler, MouseEvent } from 'react';
4
+ import type { KeyboardEventHandler, MouseEvent } from 'react';
5
5
 
6
- import { CommonProps } from '../common';
7
- import { logActionRequiredIf } from '../utilities';
6
+ import type { CommonProps } from '../common';
7
+ import { useInputAttributes } from '../inputs/contexts';
8
8
 
9
9
  export type SwitchProps = CommonProps & {
10
- /** Used to describe the purpose of the switch. To be used if there is no external label (i.e. aria-labelledby is null) */
10
+ /**
11
+ * Used to describe the purpose of the switch. To be used if there is no external label (i.e. aria-labelledby is null)
12
+ * @deprecated Use `Field` wrapper or the `aria-labelledby` attribute instead.
13
+ */
11
14
  'aria-label'?: string;
12
15
  /** A reference to a label that describes the purpose of the switch. Ignored if aria-label is provided */
13
16
  'aria-labelledby'?: string;
@@ -21,8 +24,18 @@ export type SwitchProps = CommonProps & {
21
24
  };
22
25
 
23
26
  const Switch = (props: SwitchProps) => {
27
+ const inputAttributes = useInputAttributes({ nonLabelable: true });
28
+
24
29
  const { isModern } = useTheme();
25
- const { checked, className, id, onClick, disabled } = props;
30
+ const {
31
+ checked,
32
+ className,
33
+ id = inputAttributes.id,
34
+ 'aria-label': ariaLabel,
35
+ 'aria-labelledby': ariaLabelledbyProp,
36
+ onClick,
37
+ disabled,
38
+ } = props;
26
39
 
27
40
  const handleKeyDown: KeyboardEventHandler = (event) => {
28
41
  if (event.key === ' ') {
@@ -50,13 +63,8 @@ const Switch = (props: SwitchProps) => {
50
63
  );
51
64
  };
52
65
 
53
- const ariaLabel = props['aria-label'];
54
- const ariaLabelledby = ariaLabel ? undefined : props['aria-labelledby'];
55
-
56
- logActionRequiredIf(
57
- 'Switch now expects either `aria-label` or `aria-labelledby`, and will soon make these props required. Please update your usage to provide one or the other.',
58
- !ariaLabel && !ariaLabelledby,
59
- );
66
+ const ariaLabelledby =
67
+ (ariaLabel ? undefined : ariaLabelledbyProp) ?? inputAttributes['aria-labelledby'];
60
68
 
61
69
  return (
62
70
  <span
@@ -66,7 +74,7 @@ const Switch = (props: SwitchProps) => {
66
74
  {
67
75
  'np-switch--unchecked': !checked,
68
76
  'np-switch--checked': checked,
69
- disabled: disabled,
77
+ disabled,
70
78
  },
71
79
  className,
72
80
  )}
@@ -74,6 +82,7 @@ const Switch = (props: SwitchProps) => {
74
82
  role="switch"
75
83
  aria-checked={checked}
76
84
  aria-label={ariaLabel}
85
+ {...inputAttributes}
77
86
  aria-labelledby={ariaLabelledby}
78
87
  id={id}
79
88
  aria-disabled={disabled}
@@ -1,5 +1,5 @@
1
1
  export function logActionRequired(message) {
2
- if (['development', 'test'].includes(process?.env?.NODE_ENV)) {
2
+ if (typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {
3
3
  // eslint-disable-next-line no-console
4
4
  console.warn(message);
5
5
  }