@transferwise/components 46.17.2 → 46.18.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 (99) hide show
  1. package/build/index.esm.js +28 -20
  2. package/build/index.esm.js.map +1 -1
  3. package/build/index.js +27 -19
  4. package/build/index.js.map +1 -1
  5. package/build/main.css +4 -0
  6. package/build/styles/instructionsList/InstructionsList.css +4 -0
  7. package/build/styles/main.css +4 -0
  8. package/build/types/accordion/Accordion.d.ts +3 -7
  9. package/build/types/accordion/Accordion.d.ts.map +1 -1
  10. package/build/types/accordion/index.d.ts +1 -0
  11. package/build/types/accordion/index.d.ts.map +1 -1
  12. package/build/types/body/Body.d.ts +1 -1
  13. package/build/types/chips/Chips.d.ts +2 -2
  14. package/build/types/chips/Chips.d.ts.map +1 -1
  15. package/build/types/dateLookup/DateLookup.d.ts +1 -0
  16. package/build/types/dateLookup/DateLookup.d.ts.map +1 -1
  17. package/build/types/dateLookup/getFocusableTime/getFocusableTime.d.ts +1 -1
  18. package/build/types/dateLookup/getFocusableTime/getFocusableTime.d.ts.map +1 -1
  19. package/build/types/decision/Decision.d.ts +1 -1
  20. package/build/types/decision/Decision.d.ts.map +1 -1
  21. package/build/types/flowNavigation/FlowNavigation.d.ts +1 -1
  22. package/build/types/flowNavigation/FlowNavigation.d.ts.map +1 -1
  23. package/build/types/flowNavigation/animatedLabel/AnimatedLabel.d.ts +1 -1
  24. package/build/types/flowNavigation/animatedLabel/AnimatedLabel.d.ts.map +1 -1
  25. package/build/types/index.d.ts +2 -1
  26. package/build/types/index.d.ts.map +1 -1
  27. package/build/types/instructionsList/InstructionsList.d.ts +4 -4
  28. package/build/types/instructionsList/InstructionsList.d.ts.map +1 -1
  29. package/build/types/markdown/Markdown.d.ts +2 -2
  30. package/build/types/markdown/Markdown.d.ts.map +1 -1
  31. package/build/types/phoneNumberInput/PhoneNumberInput.d.ts +1 -1
  32. package/build/types/phoneNumberInput/PhoneNumberInput.d.ts.map +1 -1
  33. package/build/types/phoneNumberInput/utils/excludeCountries/excludeCountries.d.ts +1 -1
  34. package/build/types/phoneNumberInput/utils/excludeCountries/excludeCountries.d.ts.map +1 -1
  35. package/build/types/phoneNumberInput/utils/groupCountriesByPrefix/groupCountriesByPrefix.d.ts +1 -1
  36. package/build/types/phoneNumberInput/utils/groupCountriesByPrefix/groupCountriesByPrefix.d.ts.map +1 -1
  37. package/build/types/phoneNumberInput/utils/longestMatchingPrefix/index.d.ts +1 -1
  38. package/build/types/phoneNumberInput/utils/longestMatchingPrefix/index.d.ts.map +1 -1
  39. package/build/types/phoneNumberInput/utils/sortArrayByProperty/sortArrayByProperty.d.ts +1 -1
  40. package/build/types/phoneNumberInput/utils/sortArrayByProperty/sortArrayByProperty.d.ts.map +1 -1
  41. package/build/types/radioGroup/RadioGroup.d.ts +2 -1
  42. package/build/types/radioGroup/RadioGroup.d.ts.map +1 -1
  43. package/build/types/radioGroup/index.d.ts +1 -1
  44. package/build/types/radioGroup/index.d.ts.map +1 -1
  45. package/build/types/segmentedControl/SegmentedControl.d.ts +3 -3
  46. package/build/types/segmentedControl/SegmentedControl.d.ts.map +1 -1
  47. package/build/types/slidingPanel/SlidingPanel.d.ts.map +1 -1
  48. package/build/types/stepper/Stepper.d.ts +1 -1
  49. package/build/types/stepper/Stepper.d.ts.map +1 -1
  50. package/build/types/typeahead/Typeahead.d.ts +8 -6
  51. package/build/types/typeahead/Typeahead.d.ts.map +1 -1
  52. package/build/types/typeahead/typeaheadInput/TypeaheadInput.d.ts +1 -1
  53. package/build/types/typeahead/typeaheadInput/TypeaheadInput.d.ts.map +1 -1
  54. package/build/types/uploadInput/UploadInput.d.ts +1 -1
  55. package/build/types/uploadInput/UploadInput.d.ts.map +1 -1
  56. package/build/types/uploadInput/uploadButton/UploadButton.d.ts +1 -1
  57. package/build/types/uploadInput/uploadButton/UploadButton.d.ts.map +1 -1
  58. package/build/types/uploadInput/uploadButton/getAllowedFileTypes.d.ts +1 -1
  59. package/build/types/uploadInput/uploadButton/getAllowedFileTypes.d.ts.map +1 -1
  60. package/package.json +1 -1
  61. package/src/accordion/Accordion.tsx +6 -7
  62. package/src/accordion/index.ts +1 -0
  63. package/src/chips/Chips.story.tsx +2 -2
  64. package/src/chips/Chips.tsx +2 -2
  65. package/src/dateLookup/DateLookup.js +2 -0
  66. package/src/dateLookup/DateLookup.story.js +3 -0
  67. package/src/dateLookup/DateLookup.view.spec.js +5 -0
  68. package/src/dateLookup/dateTrigger/DateTrigger.js +1 -1
  69. package/src/dateLookup/dateTrigger/DateTrigger.spec.js +11 -3
  70. package/src/dateLookup/getFocusableTime/getFocusableTime.tsx +1 -1
  71. package/src/decision/Decision.tsx +1 -1
  72. package/src/flowNavigation/FlowNavigation.tsx +1 -1
  73. package/src/flowNavigation/__snapshots__/FlowNavigation.spec.js.snap +6 -0
  74. package/src/flowNavigation/animatedLabel/AnimatedLabel.tsx +1 -1
  75. package/src/index.ts +2 -1
  76. package/src/instructionsList/InstructionsList.css +4 -0
  77. package/src/instructionsList/InstructionsList.less +5 -0
  78. package/src/instructionsList/InstructionsList.tsx +7 -7
  79. package/src/main.css +4 -0
  80. package/src/markdown/Markdown.tsx +3 -3
  81. package/src/moneyInput/MoneyInput.tsx +3 -3
  82. package/src/phoneNumberInput/PhoneNumberInput.tsx +1 -1
  83. package/src/phoneNumberInput/utils/excludeCountries/excludeCountries.ts +5 -2
  84. package/src/phoneNumberInput/utils/groupCountriesByPrefix/groupCountriesByPrefix.ts +1 -1
  85. package/src/phoneNumberInput/utils/longestMatchingPrefix/index.ts +1 -1
  86. package/src/phoneNumberInput/utils/sortArrayByProperty/sortArrayByProperty.ts +1 -1
  87. package/src/promoCard/PromoCardGroup.tsx +1 -1
  88. package/src/radioGroup/RadioGroup.tsx +6 -1
  89. package/src/radioGroup/index.ts +1 -1
  90. package/src/segmentedControl/SegmentedControl.tsx +3 -3
  91. package/src/slidingPanel/SlidingPanel.js +1 -0
  92. package/src/stepper/Stepper.spec.js +16 -0
  93. package/src/stepper/Stepper.tsx +2 -1
  94. package/src/typeahead/Typeahead.story.tsx +109 -0
  95. package/src/typeahead/Typeahead.tsx +18 -9
  96. package/src/typeahead/typeaheadInput/TypeaheadInput.tsx +1 -1
  97. package/src/uploadInput/UploadInput.tsx +6 -6
  98. package/src/uploadInput/uploadButton/UploadButton.tsx +5 -7
  99. package/src/uploadInput/uploadButton/getAllowedFileTypes.ts +1 -1
@@ -2,17 +2,16 @@ import { useState, FC } from 'react';
2
2
 
3
3
  import AccordionItem, { AccordionItemProps } from './AccordionItem';
4
4
 
5
+ export type AccordionItem = Pick<
6
+ AccordionItemProps,
7
+ 'id' | 'title' | 'subtitle' | 'content' | 'icon'
8
+ >;
9
+
5
10
  export interface AccordionProps {
6
11
  /** The index of the item that should be open by default. */
7
12
  indexOpen?: number;
8
13
  /** An array of items to display in the accordion. */
9
- items: {
10
- id?: string;
11
- title: React.ReactNode;
12
- subtitle?: React.ReactNode;
13
- content: React.ReactNode;
14
- icon?: React.ReactNode;
15
- }[];
14
+ items: readonly AccordionItem[];
16
15
  /** A callback function that is called when an item is clicked. */
17
16
  onClick?: (index: number) => void;
18
17
  /** @deprecated */
@@ -1 +1,2 @@
1
1
  export { default } from './Accordion';
2
+ export type { AccordionProps, AccordionItem } from './Accordion';
@@ -11,8 +11,8 @@ const meta: Meta<typeof Chips> = {
11
11
  export default meta;
12
12
 
13
13
  const FilterTemplate: Story<ChipsProps> = (args: ChipsProps) => {
14
- const [selected, setSelected] = useState<ChipValue[]>(
15
- args.selected ? (args.selected as ChipValue[]) : [],
14
+ const [selected, setSelected] = useState<readonly ChipValue[]>(
15
+ args.selected == null || Array.isArray(args.selected) ? args.selected : [args.selected],
16
16
  );
17
17
  return (
18
18
  <Chips
@@ -17,7 +17,7 @@ export type Chip = {
17
17
  export type ChipsProps = CommonProps &
18
18
  AriaLabelProperty & {
19
19
  /** List of chips with string labels and string/number values */
20
- chips: Chip[];
20
+ chips: readonly Chip[];
21
21
  /** Callback which is invoked when a chip is selected or deselected */
22
22
  onChange: ({
23
23
  isEnabled,
@@ -27,7 +27,7 @@ export type ChipsProps = CommonProps &
27
27
  selectedValue: ChipValue;
28
28
  }) => void;
29
29
  /** Used to manage which chips are selected */
30
- selected: ChipValue | ChipValue[];
30
+ selected: ChipValue | readonly ChipValue[];
31
31
  /** Used to activate multi-selection */
32
32
  multiple?: boolean;
33
33
  };
@@ -253,6 +253,7 @@ class DateLookup extends PureComponent {
253
253
  return (
254
254
  <div // eslint-disable-line jsx-a11y/no-static-element-interactions
255
255
  ref={this.element}
256
+ id={this.props.id}
256
257
  aria-labelledby={ariaLabelledBy}
257
258
  className="input-group"
258
259
  onKeyDown={this.handleKeyDown}
@@ -277,6 +278,7 @@ class DateLookup extends PureComponent {
277
278
  }
278
279
 
279
280
  DateLookup.propTypes = {
281
+ id: PropTypes.string,
280
282
  value: PropTypes.instanceOf(Date),
281
283
  min: PropTypes.instanceOf(Date),
282
284
  max: PropTypes.instanceOf(Date),
@@ -25,12 +25,14 @@ export const Basic = () => {
25
25
  const monthFormat = select('monthFormat', ['long', 'short']);
26
26
  const placeholder = text('placeholder', 'placeholder');
27
27
  const size = select('size', Object.values(Size), Size.MEDIUM);
28
+ const id = text('id', 'date-lookup');
28
29
 
29
30
  const clearable = boolean('clearable', false);
30
31
 
31
32
  return (
32
33
  <DateLookup
33
34
  disabled={disabled}
35
+ id={id}
34
36
  label={label}
35
37
  min={thePast}
36
38
  monthFormat={monthFormat}
@@ -81,6 +83,7 @@ export const RightAligned = () => {
81
83
  </div>
82
84
  <div className="col-xs-6">
83
85
  <DateLookup
86
+ id="right-aligned"
84
87
  disabled={disabled}
85
88
  label={label}
86
89
  min={new Date(minvalue)}
@@ -37,6 +37,7 @@ describe('DateLookup view', () => {
37
37
  placeholder: 'Asd..',
38
38
  label: 'Date..',
39
39
  onChange: jest.fn(),
40
+ id: '123456',
40
41
  };
41
42
  component = shallow(<DateLookup {...props} />);
42
43
  });
@@ -45,6 +46,10 @@ describe('DateLookup view', () => {
45
46
  expect(dateTrigger()).toHaveLength(1);
46
47
  });
47
48
 
49
+ it('passes id to the component', () => {
50
+ expect(component.prop('id')).toBe('123456');
51
+ });
52
+
48
53
  it('passes props forward to open button', () => {
49
54
  expect(+dateTrigger().prop('selectedDate')).toBe(+date);
50
55
  expect(dateTrigger().prop('size')).toBe('lg');
@@ -62,7 +62,7 @@ const DateTrigger = ({
62
62
  <span className="input-group-addon">
63
63
  <CloseButton
64
64
  className={`clear-btn clear-btn--${size}`}
65
- aria-label={formatMessage(messages.ariaLabel)}
65
+ aria-label={`${formatMessage(messages.ariaLabel)} ${label}`}
66
66
  size={Size.SMALL}
67
67
  onClick={(event) => {
68
68
  event.stopPropagation();
@@ -6,10 +6,12 @@ import { fakeKeyDownEventForKey } from '../../common/fakeEvents';
6
6
 
7
7
  import DateTrigger from '.';
8
8
 
9
- const defaultLocale = 'en-GB';
9
+ const locale = 'en-GB';
10
+ const formatMessage = (message) => message.id;
11
+
10
12
  jest.mock('react-intl', () => ({
11
- useIntl: () => ({ locale: defaultLocale, formatMessage: (id) => `${id}` }),
12
- defineMessages: (translations) => translations,
13
+ ...jest.requireActual('react-intl'),
14
+ useIntl: jest.fn(() => ({ locale, formatMessage })),
13
15
  }));
14
16
 
15
17
  jest.mock('@transferwise/formatting', () => ({
@@ -73,6 +75,12 @@ describe('DateTrigger', () => {
73
75
  expect(label().text()).toBe('hello');
74
76
  });
75
77
 
78
+ it('adds label to clear button aria-label', () => {
79
+ const onClear = jest.fn();
80
+ component.setProps({ label: 'hello', onClear });
81
+ expect(clearButton().prop('aria-label')).toBe('neptune.ClearButton.ariaLabel hello');
82
+ });
83
+
76
84
  it('calls on click handler on button click', () => {
77
85
  button().simulate('click');
78
86
  expect(props.onClick).toHaveBeenCalledTimes(1);
@@ -2,7 +2,7 @@ interface FocusableTimeProps {
2
2
  isActive: (time: number) => boolean;
3
3
  isNow: (time: number) => boolean;
4
4
  isDisabled: (time: number) => boolean;
5
- timeSpan: number[];
5
+ timeSpan: readonly number[];
6
6
  }
7
7
 
8
8
  export function getFocusableTime(props: FocusableTimeProps): number | undefined {
@@ -30,7 +30,7 @@ export enum DecisionType {
30
30
 
31
31
  export interface DecisionProps {
32
32
  /** A list of elements to be rendered */
33
- options: DecisionOption[];
33
+ options: readonly DecisionOption[];
34
34
  /** Handles the display mode of the component */
35
35
  presentation?: `${DecisionPresentation}`;
36
36
  /** Size currently affects only Tile dimension */
@@ -24,7 +24,7 @@ export interface FlowNavigationProps {
24
24
  /** Called when the back button is clicked. If not provided the back button won't show. The back button only shows on small screens */
25
25
  onGoBack?: () => void;
26
26
  /** Steps to be displayed in stepper. If you don't need the stepper, please use OverlayHeader instead */
27
- steps: Step[];
27
+ steps: readonly Step[];
28
28
  }
29
29
 
30
30
  const FlowNavigation = ({
@@ -75,6 +75,7 @@ exports[`FlowNavigation on mobile renders as expected 1`] = `
75
75
  class="tw-stepper-steps p-t-1 m-b-0"
76
76
  >
77
77
  <li
78
+ aria-current="false"
78
79
  class="hidden-xs tw-stepper__step np-text-body-default"
79
80
  style="left: 0%;"
80
81
  >
@@ -85,6 +86,7 @@ exports[`FlowNavigation on mobile renders as expected 1`] = `
85
86
  </span>
86
87
  </li>
87
88
  <li
89
+ aria-current="step"
88
90
  class="hidden-xs tw-stepper__step np-text-body-default-bold tw-stepper__step--active"
89
91
  style="left: 50%;"
90
92
  >
@@ -95,6 +97,7 @@ exports[`FlowNavigation on mobile renders as expected 1`] = `
95
97
  </span>
96
98
  </li>
97
99
  <li
100
+ aria-current="false"
98
101
  class="hidden-xs tw-stepper__step np-text-body-default"
99
102
  style="left: 100%;"
100
103
  >
@@ -188,6 +191,7 @@ exports[`FlowNavigation renders as expected 1`] = `
188
191
  class="tw-stepper-steps p-t-1 m-b-0"
189
192
  >
190
193
  <li
194
+ aria-current="step"
191
195
  class="hidden-xs tw-stepper__step np-text-body-default-bold tw-stepper__step--active"
192
196
  style="left: 0%;"
193
197
  >
@@ -198,6 +202,7 @@ exports[`FlowNavigation renders as expected 1`] = `
198
202
  </span>
199
203
  </li>
200
204
  <li
205
+ aria-current="false"
201
206
  class="hidden-xs tw-stepper__step np-text-body-default"
202
207
  style="left: 50%;"
203
208
  >
@@ -208,6 +213,7 @@ exports[`FlowNavigation renders as expected 1`] = `
208
213
  </span>
209
214
  </li>
210
215
  <li
216
+ aria-current="false"
211
217
  class="hidden-xs tw-stepper__step np-text-body-default"
212
218
  style="left: 100%;"
213
219
  >
@@ -7,7 +7,7 @@ import { Typography } from '../../common';
7
7
  export interface AnimatedLabelProps {
8
8
  activeLabel: number;
9
9
  className?: string;
10
- labels: React.ReactNode[];
10
+ labels: readonly React.ReactNode[];
11
11
  }
12
12
 
13
13
  const AnimatedLabel = ({ activeLabel, className, labels }: AnimatedLabelProps) => {
package/src/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Types
3
3
  */
4
+ export type { AccordionProps, AccordionItem } from './accordion';
4
5
  export type { DecisionProps } from './decision/Decision';
5
6
  export type { InfoProps } from './info';
6
7
  export type { SummaryProps } from './summary';
@@ -21,7 +22,7 @@ export type {
21
22
  export type { TextAreaProps } from './inputs/TextArea';
22
23
  export type { PhoneNumberInputProps } from './phoneNumberInput/PhoneNumberInput';
23
24
  export type { RadioProps } from './radio';
24
- export type { RadioGroupProps } from './radioGroup';
25
+ export type { RadioGroupProps, RadioGroupRadio } from './radioGroup';
25
26
  export type { RadioOptionProps } from './radioOption';
26
27
  export type { SnackbarProps } from './snackbar/Snackbar';
27
28
  export type { SnackbarContextType } from './snackbar/SnackbarContext';
@@ -1,4 +1,8 @@
1
1
  .tw-instructions {
2
+ /* Reset */
3
+ margin: 0;
4
+ padding: 0;
5
+ list-style: none;
2
6
  display: flex;
3
7
  flex-direction: column;
4
8
  }
@@ -1,4 +1,9 @@
1
1
  .tw-instructions {
2
+ /* Reset */
3
+ margin: 0;
4
+ padding: 0;
5
+ list-style: none;
6
+
2
7
  display: flex;
3
8
  flex-direction: column;
4
9
 
@@ -12,13 +12,13 @@ type InstructionNode = {
12
12
  export type InstructionsListProps = CommonProps &
13
13
  (
14
14
  | {
15
- dos?: ReactNode[];
16
- donts?: ReactNode[];
15
+ dos?: readonly ReactNode[];
16
+ donts?: readonly ReactNode[];
17
17
  sort?: 'dosFirst' | 'dontsFirst';
18
18
  }
19
19
  | {
20
- dos?: InstructionNode[];
21
- donts?: InstructionNode[];
20
+ dos?: readonly InstructionNode[];
21
+ donts?: readonly InstructionNode[];
22
22
  sort?: 'dosFirst' | 'dontsFirst';
23
23
  }
24
24
  );
@@ -49,14 +49,14 @@ const InstructionsList = ({ dos, donts, sort = 'dosFirst' }: InstructionsListPro
49
49
  </>
50
50
  );
51
51
 
52
- return <div className="tw-instructions">{orderedInstructions}</div>;
52
+ return <ul className="tw-instructions">{orderedInstructions}</ul>;
53
53
  };
54
54
 
55
55
  function Instruction({ item, type }: { item: ReactNode | InstructionNode; type: 'do' | 'dont' }) {
56
56
  const isInstructionNode =
57
57
  typeof item === 'object' && item !== null && 'content' in item && 'aria-label' in item;
58
58
  return (
59
- <div
59
+ <li
60
60
  className="instruction"
61
61
  aria-label={isInstructionNode ? (item['aria-label'] as string) : undefined}
62
62
  >
@@ -68,7 +68,7 @@ function Instruction({ item, type }: { item: ReactNode | InstructionNode; type:
68
68
  <Body className="text-primary" type={Typography.BODY_LARGE}>
69
69
  {isInstructionNode ? item.content : item}
70
70
  </Body>
71
- </div>
71
+ </li>
72
72
  );
73
73
  }
74
74
 
package/src/main.css CHANGED
@@ -2665,6 +2665,10 @@ html:not([dir="rtl"]) .np-flow-navigation--sm .np-flow-navigation__stepper {
2665
2665
  color: var(--color-content-tertiary);
2666
2666
  }
2667
2667
  .tw-instructions {
2668
+ /* Reset */
2669
+ margin: 0;
2670
+ padding: 0;
2671
+ list-style: none;
2668
2672
  display: flex;
2669
2673
  flex-direction: column;
2670
2674
  }
@@ -17,12 +17,12 @@ export type MarkdownProps = {
17
17
  children?: string;
18
18
  } & (
19
19
  | {
20
- allowList?: `${MarkdownNodeType}`[];
20
+ allowList?: readonly `${MarkdownNodeType}`[];
21
21
  blockList?: never;
22
22
  }
23
23
  | {
24
24
  allowList?: never;
25
- blockList?: `${MarkdownNodeType}`[];
25
+ blockList?: readonly `${MarkdownNodeType}`[];
26
26
  }
27
27
  );
28
28
 
@@ -61,7 +61,7 @@ export default function Markdown({
61
61
  return <Element className={className} dangerouslySetInnerHTML={{ __html: createMarkup() }} />;
62
62
  }
63
63
 
64
- function stripNodes({ blockList, parsed }: { blockList: string[]; parsed: MarkdownNode }) {
64
+ function stripNodes({ blockList, parsed }: { blockList: readonly string[]; parsed: MarkdownNode }) {
65
65
  if (!parsed) {
66
66
  return parsed;
67
67
  }
@@ -462,7 +462,7 @@ function filterCurrenciesForQuery(
462
462
  return sortOptionsLabelsToFirst(filteredOptions, query);
463
463
  }
464
464
 
465
- function removeDuplicateValueOptions(options: CurrencyOptionItem[]) {
465
+ function removeDuplicateValueOptions(options: readonly CurrencyOptionItem[]) {
466
466
  const uniqueValues = new Set<string>();
467
467
  return options.filter((option) => {
468
468
  if (!uniqueValues.has(option.value)) {
@@ -489,8 +489,8 @@ function contains(property: string | undefined, query: string) {
489
489
  return property && property.toLowerCase().includes(query.toLowerCase());
490
490
  }
491
491
 
492
- function sortOptionsLabelsToFirst(options: CurrencyOptionItem[], query: string) {
493
- return options.sort((first, second) => {
492
+ function sortOptionsLabelsToFirst(options: readonly CurrencyOptionItem[], query: string) {
493
+ return [...options].sort((first, second) => {
494
494
  const firstContains = contains(first.label, query);
495
495
  const secondContains = contains(second.label, query);
496
496
 
@@ -34,7 +34,7 @@ export interface PhoneNumberInputProps {
34
34
  placeholder?: string;
35
35
  selectProps?: Partial<SelectInputProps<string | null>>;
36
36
  /** List of iso3 codes of countries to remove from the list */
37
- disabledCountries?: string[];
37
+ disabledCountries?: readonly string[];
38
38
  }
39
39
 
40
40
  const defaultSelectProps = {} satisfies PhoneNumberInputProps['selectProps'];
@@ -1,7 +1,7 @@
1
1
  import type { Country } from '../../data/countries';
2
2
 
3
3
  // Reference fro localeCompare : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare
4
- const filterCountriesByIso3 = (countries: Country[], iso3Codes: string[]) => {
4
+ const filterCountriesByIso3 = (countries: readonly Country[], iso3Codes: readonly string[]) => {
5
5
  const iso3CodesSet = new Set(iso3Codes);
6
6
  return countries.filter((country) => !iso3CodesSet.has(country.iso3));
7
7
  };
@@ -12,7 +12,10 @@ const filterCountriesByIso3 = (countries: Country[], iso3Codes: string[]) => {
12
12
  * @param countries list of country metadata objects
13
13
  * @param disabledCountries list of iso3 country codes to remove from the list
14
14
  */
15
- export const excludeCountries = (countries: Country[], disabledCountries: string[]) => {
15
+ export const excludeCountries = (
16
+ countries: readonly Country[],
17
+ disabledCountries: readonly string[],
18
+ ) => {
16
19
  return disabledCountries.length > 0
17
20
  ? filterCountriesByIso3(countries, disabledCountries)
18
21
  : countries;
@@ -1,6 +1,6 @@
1
1
  import { Country } from '../../data/countries';
2
2
 
3
- export const groupCountriesByPrefix = (countries: Country[]) => {
3
+ export const groupCountriesByPrefix = (countries: readonly Country[]) => {
4
4
  const countriesByPrefix = new Map<string, Country[]>();
5
5
  countries.forEach((country) => {
6
6
  countriesByPrefix.set(country.phone, [
@@ -1,4 +1,4 @@
1
1
  import { Country } from '../../data/countries';
2
2
 
3
- export const longestMatchingPrefix = (matchingCodes: Country[]) =>
3
+ export const longestMatchingPrefix = (matchingCodes: readonly Country[]) =>
4
4
  matchingCodes.reduce((a, b) => (a.phone.length > b.phone.length ? a : b));
@@ -1,5 +1,5 @@
1
1
  export function sortArrayByProperty<T extends Record<PropertyKey, string>>(
2
- arrayToSort: T[],
2
+ arrayToSort: readonly T[],
3
3
  property: keyof T,
4
4
  ) {
5
5
  return [...arrayToSort].sort((a, b) => a[property].localeCompare(b[property]));
@@ -106,7 +106,7 @@ const PromoCardGroup: FunctionComponent<PromoCardGroupProps> = ({
106
106
  setState(defaultChecked);
107
107
 
108
108
  // Collect an array of types from the children PromoCard components
109
- const types: ('radio' | 'checkbox')[] =
109
+ const types =
110
110
  React.Children.map(children, (child) => {
111
111
  if (React.isValidElement<PromoCardProps>(child) && child.props.type) {
112
112
  return child.props.type;
@@ -3,9 +3,14 @@ import { useState } from 'react';
3
3
  import Radio from '../radio';
4
4
  import { RadioProps } from '../radio/Radio';
5
5
 
6
+ export type RadioGroupRadio<T extends string | number = string> = Omit<
7
+ RadioProps<T>,
8
+ 'name' | 'checked' | 'onChange' | 'className'
9
+ >;
10
+
6
11
  export interface RadioGroupProps<T extends string | number = string> {
7
12
  name: string;
8
- radios: Omit<RadioProps<T>, 'name' | 'checked' | 'onChange' | 'className'>[];
13
+ radios: readonly RadioGroupRadio<T>[];
9
14
  selectedValue?: T; // TODO: `NoInfer<T>` from TypeScript 5.4
10
15
  onChange: NonNullable<RadioProps<T>['onChange']>;
11
16
  }
@@ -1,2 +1,2 @@
1
1
  export { default } from './RadioGroup';
2
- export type { RadioGroupProps } from './RadioGroup';
2
+ export type { RadioGroupProps, RadioGroupRadio } from './RadioGroup';
@@ -9,7 +9,7 @@ type SegmentBase = { id: string; label: string; value: string };
9
9
  type Segment = SegmentBase & { controls?: never };
10
10
  type SegmentWithControls = SegmentBase & { controls: string };
11
11
 
12
- export type Segments = Segment[] | SegmentWithControls[];
12
+ export type Segments = readonly Segment[] | readonly SegmentWithControls[];
13
13
 
14
14
  type SegmentedControlPropsBase = {
15
15
  name: string;
@@ -20,12 +20,12 @@ type SegmentedControlPropsBase = {
20
20
 
21
21
  type SegmentedControlViewProps = {
22
22
  mode: 'view';
23
- segments: SegmentWithControls[];
23
+ segments: readonly SegmentWithControls[];
24
24
  };
25
25
 
26
26
  type SegmentedControlInputProps = {
27
27
  mode: 'input';
28
- segments: Segment[];
28
+ segments: readonly Segment[];
29
29
  };
30
30
 
31
31
  export type SegmentedControlProps = SegmentedControlPropsBase &
@@ -25,6 +25,7 @@ const SlidingPanel = forwardRef(
25
25
  return (
26
26
  <CSSTransition
27
27
  {...rest}
28
+ key={`sliding-panel--open-${position}`}
28
29
  nodeRef={localReference}
29
30
  in={open}
30
31
  // Wait for animation to finish before unmount.
@@ -162,6 +162,22 @@ describe('Stepper', () => {
162
162
  expect(step(1).clickable).toBe(true);
163
163
  expect(step(2).clickable).toBe(true);
164
164
  });
165
+
166
+ it('are aria-current=step when active', () => {
167
+ const stepCurrent = (index) =>
168
+ component.find('.tw-stepper__step').at(index).props()['aria-current'];
169
+ steps(4);
170
+ activeStep(1);
171
+ expect(stepCurrent(0)).toBe(false);
172
+ expect(stepCurrent(1)).toBe('step');
173
+ expect(stepCurrent(2)).toBe(false);
174
+ expect(stepCurrent(3)).toBe(false);
175
+ activeStep(2);
176
+ expect(stepCurrent(0)).toBe(false);
177
+ expect(stepCurrent(1)).toBe(false);
178
+ expect(stepCurrent(2)).toBe('step');
179
+ expect(stepCurrent(3)).toBe(false);
180
+ });
165
181
  });
166
182
 
167
183
  describe('hover labels', () => {
@@ -18,7 +18,7 @@ export interface Step {
18
18
  }
19
19
 
20
20
  export interface StepperProps {
21
- steps: Step[];
21
+ steps: readonly Step[];
22
22
  activeStep?: number;
23
23
  className?: string;
24
24
  }
@@ -60,6 +60,7 @@ const Stepper = ({ steps, activeStep = 0, className }: StepperProps) => {
60
60
  clickable && 'tw-stepper__step--clickable',
61
61
  step.hoverLabel && 'tw-stepper__step--has-tooltip',
62
62
  )}
63
+ aria-current={active ? 'step' : false}
63
64
  style={
64
65
  isRTL
65
66
  ? { right: `${index * stepPercentage * 100}%` }