@transferwise/components 45.14.2 → 45.15.1

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 (160) hide show
  1. package/build/i18n/en.json +8 -0
  2. package/build/index.esm.js +1001 -367
  3. package/build/index.esm.js.map +1 -1
  4. package/build/index.js +1003 -366
  5. package/build/index.js.map +1 -1
  6. package/build/main.css +1 -1
  7. package/build/styles/common/closeButton/CloseButton.css +1 -1
  8. package/build/styles/dateLookup/DateLookup.css +1 -1
  9. package/build/styles/inputs/Input.css +1 -1
  10. package/build/styles/inputs/InputGroup.css +1 -1
  11. package/build/styles/inputs/SelectInput.css +1 -0
  12. package/build/styles/inputs/TextArea.css +1 -1
  13. package/build/styles/main.css +1 -1
  14. package/build/styles/promoCard/PromoCard.css +1 -1
  15. package/build/styles/stepper/Stepper.css +1 -1
  16. package/build/types/avatarWrapper/AvatarWrapper.d.ts +14 -5
  17. package/build/types/avatarWrapper/AvatarWrapper.d.ts.map +1 -1
  18. package/build/types/common/Option/Option.d.ts.map +1 -1
  19. package/build/types/common/focusBoundary/FocusBoundary.d.ts +2 -2
  20. package/build/types/common/focusBoundary/FocusBoundary.d.ts.map +1 -1
  21. package/build/types/common/hooks/useMedia.d.ts +2 -0
  22. package/build/types/common/hooks/useMedia.d.ts.map +1 -0
  23. package/build/types/common/hooks/useScreenSize.d.ts +3 -0
  24. package/build/types/common/hooks/useScreenSize.d.ts.map +1 -0
  25. package/build/types/common/preventScroll/PreventScroll.d.ts +2 -0
  26. package/build/types/common/preventScroll/PreventScroll.d.ts.map +1 -0
  27. package/build/types/dateInput/DateInput.d.ts +2 -0
  28. package/build/types/dateInput/DateInput.d.ts.map +1 -1
  29. package/build/types/dateLookup/DateLookup.messages.d.ts +65 -0
  30. package/build/types/dateLookup/DateLookup.messages.d.ts.map +1 -0
  31. package/build/types/dateLookup/dateHeader/DateHeader.d.ts +3 -1
  32. package/build/types/dateLookup/dateHeader/DateHeader.d.ts.map +1 -1
  33. package/build/types/dateLookup/dateTrigger/DateTrigger.messages.d.ts +7 -7
  34. package/build/types/dateLookup/dateTrigger/DateTrigger.messages.d.ts.map +1 -1
  35. package/build/types/dateLookup/tableLink/TableLink.d.ts +4 -26
  36. package/build/types/dateLookup/tableLink/TableLink.d.ts.map +1 -1
  37. package/build/types/dateLookup/yearCalendar/YearCalendar.d.ts +4 -29
  38. package/build/types/dateLookup/yearCalendar/YearCalendar.d.ts.map +1 -1
  39. package/build/types/index.d.ts +4 -0
  40. package/build/types/index.d.ts.map +1 -1
  41. package/build/types/inputs/Input.d.ts +1 -0
  42. package/build/types/inputs/Input.d.ts.map +1 -1
  43. package/build/types/inputs/SearchInput.d.ts +10 -0
  44. package/build/types/inputs/SearchInput.d.ts.map +1 -0
  45. package/build/types/inputs/SelectInput.d.ts +41 -0
  46. package/build/types/inputs/SelectInput.d.ts.map +1 -0
  47. package/build/types/inputs/_BottomSheet.d.ts +17 -0
  48. package/build/types/inputs/_BottomSheet.d.ts.map +1 -0
  49. package/build/types/inputs/_ButtonInput.d.ts +6 -0
  50. package/build/types/inputs/_ButtonInput.d.ts.map +1 -0
  51. package/build/types/inputs/_Popover.d.ts +18 -0
  52. package/build/types/inputs/_Popover.d.ts.map +1 -0
  53. package/build/types/inputs/_common.d.ts.map +1 -1
  54. package/build/types/logo/Logo.d.ts.map +1 -1
  55. package/build/types/phoneNumberInput/PhoneNumberInput.d.ts.map +1 -1
  56. package/build/types/snackbar/Snackbar.d.ts.map +1 -1
  57. package/build/types/stepper/Stepper.d.ts.map +1 -1
  58. package/build/types/tabs/Tabs.d.ts.map +1 -1
  59. package/build/types/tile/Tile.d.ts.map +1 -1
  60. package/build/types/upload/steps/completeStep/completeStep.d.ts.map +1 -1
  61. package/build/types/upload/steps/processingStep/processingStep.d.ts.map +1 -1
  62. package/build/types/utilities/wrapInFragment.d.ts +3 -0
  63. package/build/types/utilities/wrapInFragment.d.ts.map +1 -0
  64. package/package.json +27 -20
  65. package/src/accordion/AccordionItem/__snapshots__/AccordionItem.spec.js.snap +6 -6
  66. package/src/avatarWrapper/AvatarWrapper.tsx +20 -8
  67. package/src/avatarWrapper/__snapshots__/AvatarWrapper.spec.tsx.snap +1 -1
  68. package/src/card/Card.spec.js +2 -2
  69. package/src/common/Option/Option.tsx +1 -7
  70. package/src/common/bottomSheet/__snapshots__/BottomSheet.spec.tsx.snap +8 -1
  71. package/src/common/closeButton/CloseButton.css +1 -1
  72. package/src/common/focusBoundary/FocusBoundary.tsx +9 -32
  73. package/src/common/hooks/useMedia.spec.ts +39 -0
  74. package/src/common/hooks/useMedia.ts +15 -0
  75. package/src/common/hooks/useScreenSize.ts +7 -0
  76. package/src/common/preventScroll/PreventScroll.tsx +6 -0
  77. package/src/dateInput/DateInput.js +6 -0
  78. package/src/dateInput/DateInput.story.tsx +2 -0
  79. package/src/dateLookup/DateLookup.css +1 -1
  80. package/src/dateLookup/DateLookup.keyboardEvents.spec.js +3 -3
  81. package/src/dateLookup/DateLookup.less +4 -0
  82. package/src/dateLookup/DateLookup.messages.js +44 -0
  83. package/src/dateLookup/DateLookup.testingLibrary.spec.js +39 -0
  84. package/src/dateLookup/dateHeader/DateHeader.js +48 -26
  85. package/src/dateLookup/dateHeader/DateHeader.spec.js +37 -0
  86. package/src/dateLookup/dayCalendar/DayCalendar.js +3 -1
  87. package/src/dateLookup/dayCalendar/DayCalendar.spec.js +7 -1
  88. package/src/dateLookup/dayCalendar/table/DayCalendarTable.js +7 -3
  89. package/src/dateLookup/dayCalendar/table/DayCalendarTable.spec.js +1 -0
  90. package/src/dateLookup/monthCalendar/MonthCalendar.js +3 -1
  91. package/src/dateLookup/monthCalendar/MonthCalendar.spec.js +7 -1
  92. package/src/dateLookup/monthCalendar/table/MonthCalendarTable.spec.js +4 -5
  93. package/src/dateLookup/tableLink/TableLink.js +24 -3
  94. package/src/dateLookup/tableLink/TableLink.spec.js +60 -4
  95. package/src/dateLookup/yearCalendar/YearCalendar.js +16 -3
  96. package/src/dateLookup/yearCalendar/YearCalendar.spec.js +14 -1
  97. package/src/dateLookup/yearCalendar/table/YearCalendarTable.spec.js +4 -5
  98. package/src/decision/Decision.story.js +11 -11
  99. package/src/flowNavigation/__snapshots__/FlowNavigation.spec.js.snap +12 -12
  100. package/src/i18n/en.json +9 -0
  101. package/src/index.ts +8 -0
  102. package/src/inputs/Input.css +1 -1
  103. package/src/inputs/Input.less +14 -0
  104. package/src/inputs/Input.tsx +6 -2
  105. package/src/inputs/InputGroup.css +1 -1
  106. package/src/inputs/InputGroup.less +5 -0
  107. package/src/inputs/SearchInput.story.tsx +40 -0
  108. package/src/inputs/SearchInput.tsx +35 -0
  109. package/src/inputs/SelectInput.css +1 -0
  110. package/src/inputs/SelectInput.less +183 -0
  111. package/src/inputs/SelectInput.spec.tsx +120 -0
  112. package/src/inputs/SelectInput.story.tsx +264 -0
  113. package/src/inputs/SelectInput.tsx +565 -0
  114. package/src/inputs/TextArea.css +1 -1
  115. package/src/inputs/TextArea.less +5 -0
  116. package/src/inputs/_BottomSheet.less +107 -0
  117. package/src/inputs/_BottomSheet.tsx +128 -0
  118. package/src/inputs/_ButtonInput.less +7 -0
  119. package/src/inputs/_ButtonInput.tsx +27 -0
  120. package/src/inputs/_Popover.less +38 -0
  121. package/src/inputs/_Popover.tsx +118 -0
  122. package/src/inputs/_common.less +0 -4
  123. package/src/inputs/_common.ts +0 -1
  124. package/src/logo/Logo.js +3 -21
  125. package/src/logo/__snapshots__/Logo.spec.js.snap +78 -30
  126. package/src/main.css +1 -1
  127. package/src/main.less +4 -0
  128. package/src/phoneNumberInput/PhoneNumberInput.js +1 -0
  129. package/src/promoCard/PromoCard.css +1 -1
  130. package/src/select/searchBox/__snapshots__/SearchBox.spec.js.snap +1 -1
  131. package/src/snackbar/Snackbar.js +6 -1
  132. package/src/snackbar/Snackbar.spec.js +1 -3
  133. package/src/ssr.spec.js +7 -0
  134. package/src/stepper/Stepper.css +1 -1
  135. package/src/stepper/Stepper.less +1 -9
  136. package/src/stepper/Stepper.spec.js +4 -4
  137. package/src/stepper/Stepper.tsx +2 -5
  138. package/src/tabs/Tabs.js +2 -1
  139. package/src/tile/Tile.js +5 -11
  140. package/src/tile/__snapshots__/Tile.spec.js.snap +7 -9
  141. package/src/upload/Upload.js +1 -1
  142. package/src/upload/steps/completeStep/completeStep.js +4 -1
  143. package/src/upload/steps/processingStep/processingStep.js +1 -0
  144. package/src/uploadInput/uploadItem/UploadItem.tsx +1 -1
  145. package/src/utilities/wrapInFragment.tsx +3 -0
  146. package/build/types/common/focusBoundary/utils/getFocusableElements.d.ts +0 -2
  147. package/build/types/common/focusBoundary/utils/getFocusableElements.d.ts.map +0 -1
  148. package/build/types/common/focusBoundary/utils/index.d.ts +0 -3
  149. package/build/types/common/focusBoundary/utils/index.d.ts.map +0 -1
  150. package/build/types/common/focusBoundary/utils/resetFocus.d.ts +0 -2
  151. package/build/types/common/focusBoundary/utils/resetFocus.d.ts.map +0 -1
  152. package/src/common/focusBoundary/FocusBoundary.spec.tsx +0 -66
  153. package/src/common/focusBoundary/__snapshots__/FocusBoundary.spec.tsx.snap +0 -16
  154. package/src/common/focusBoundary/utils/getFocusableElements.js +0 -25
  155. package/src/common/focusBoundary/utils/getFocusableElements.spec.js +0 -51
  156. package/src/common/focusBoundary/utils/index.js +0 -2
  157. package/src/common/focusBoundary/utils/resetFocus.js +0 -23
  158. package/src/common/focusBoundary/utils/resetFocus.spec.js +0 -103
  159. package/src/snackbar/__snapshots__/Snackbar.spec.js.snap +0 -5
  160. /package/src/dateLookup/dateTrigger/{DateTrigger.messages.js → DateTrigger.messages.ts} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@transferwise/components",
3
- "version": "45.14.2",
3
+ "version": "45.15.1",
4
4
  "description": "Neptune React components",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -32,28 +32,28 @@
32
32
  "@rollup/plugin-json": "^6.0.0",
33
33
  "@rollup/plugin-node-resolve": "^15.2.1",
34
34
  "@rollup/plugin-typescript": "^11.1.3",
35
- "@storybook/addon-a11y": "^7.0.6",
36
- "@storybook/addon-actions": "^7.0.6",
37
- "@storybook/addon-essentials": "^7.0.6",
38
- "@storybook/addon-interactions": "^7.0.6",
39
- "@storybook/addon-knobs": "^7.0.0",
40
- "@storybook/addon-viewport": "^7.0.6",
41
- "@storybook/addons": "^7.0.0",
42
- "@storybook/api": "^7.0.0",
43
- "@storybook/components": "^7.0.0",
44
- "@storybook/core-events": "^7.0.0",
45
- "@storybook/react": "^7.0.6",
46
- "@storybook/react-webpack5": "^7.0.6",
35
+ "@storybook/addon-a11y": "^7.4.5",
36
+ "@storybook/addon-actions": "^7.4.5",
37
+ "@storybook/addon-essentials": "^7.4.5",
38
+ "@storybook/addon-interactions": "^7.4.5",
39
+ "@storybook/addon-knobs": "^7.0.2",
40
+ "@storybook/addon-viewport": "^7.4.5",
41
+ "@storybook/addons": "^7.4.5",
42
+ "@storybook/api": "^7.4.5",
43
+ "@storybook/components": "^7.4.5",
44
+ "@storybook/core-events": "^7.4.5",
45
+ "@storybook/react": "^7.4.5",
46
+ "@storybook/react-webpack5": "^7.4.5",
47
47
  "@storybook/testing-library": "^0.1.0",
48
- "@storybook/theming": "^7.0.0",
49
- "@storybook/types": "^7.0.6",
48
+ "@storybook/theming": "^7.4.5",
49
+ "@storybook/types": "^7.4.5",
50
50
  "@testing-library/dom": "^7.21.4",
51
51
  "@testing-library/jest-dom": "^5.14.1",
52
52
  "@testing-library/react": "^12.0.0",
53
53
  "@testing-library/react-hooks": "^8.0.0",
54
54
  "@testing-library/user-event": "^13.2.1",
55
55
  "@transferwise/icons": "^3.7.0",
56
- "@transferwise/neptune-tokens": "^8.5.0",
56
+ "@transferwise/neptune-tokens": "^8.5.1",
57
57
  "@tsconfig/recommended": "^1.0.2",
58
58
  "@types/babel__core": "^7.20.1",
59
59
  "@types/jest": "^26.0.20",
@@ -62,6 +62,7 @@
62
62
  "@types/react-dom": "^17.0.20",
63
63
  "@types/react-transition-group": "4.4.5",
64
64
  "@types/testing-library__jest-dom": "^5.14.5",
65
+ "@types/use-sync-external-store": "^0.0.4",
65
66
  "@wise/art": "^2.7.0",
66
67
  "@wojtekmaj/enzyme-adapter-react-17": "^0.6.3",
67
68
  "babel-plugin-formatjs": "^10.5.3",
@@ -75,7 +76,7 @@
75
76
  "npm-watch": "^0.11.0",
76
77
  "react-intl": "^5.10.0",
77
78
  "rollup": "^3.28.1",
78
- "storybook": "^7.0.6",
79
+ "storybook": "^7.4.5",
79
80
  "@transferwise/less-config": "3.0.6",
80
81
  "@transferwise/neptune-css": "14.6.0",
81
82
  "@wise/components-theming": "0.8.4"
@@ -83,7 +84,7 @@
83
84
  "peerDependencies": {
84
85
  "@transferwise/icons": "^3.7.0",
85
86
  "@transferwise/neptune-css": "^14.5.2",
86
- "@wise/art": "^2",
87
+ "@wise/art": "^2.7.0",
87
88
  "@wise/components-theming": "^0.8.4",
88
89
  "currency-flags": "^4.0.2",
89
90
  "react": ">=16.14",
@@ -97,8 +98,13 @@
97
98
  },
98
99
  "dependencies": {
99
100
  "@babel/runtime": "^7.22.15",
101
+ "@floating-ui/react": "^0.25.4",
100
102
  "@formatjs/intl-locale": "^2.4.14",
103
+ "@headlessui/react": "^1.7.17",
101
104
  "@popperjs/core": "^2.6.0",
105
+ "@radix-ui/react-id": "^1.0.0",
106
+ "@react-aria/focus": "^3.14.2",
107
+ "@react-aria/overlays": "^3.17.0",
102
108
  "@react-spring/web": "~9.6.1",
103
109
  "@transferwise/formatting": "^2.1.0",
104
110
  "@transferwise/neptune-validation": "^3.1.0",
@@ -115,7 +121,8 @@
115
121
  "react-merge-refs": "^1.1.0",
116
122
  "react-popper": "^2.2.4",
117
123
  "react-required-if": "^1.0.3",
118
- "react-transition-group": "^4.4.2"
124
+ "react-transition-group": "^4.4.2",
125
+ "use-sync-external-store": "^1.2.0"
119
126
  },
120
127
  "watch": {
121
128
  "build:crowdin-source-file": "src/**/*.messages.js"
@@ -139,7 +146,7 @@
139
146
  "build:clean": "rm -rf lib build",
140
147
  "build:crowdin-source-file": "formatjs extract 'src/**/*.messages.+(js|ts|tsx)' --out-file src/i18n/en.json --format simple && prettier --find-config-path --write src/i18n/*.json",
141
148
  "build:js": "rollup --config --sourcemap",
142
- "build:generate-types": "tsc && node ./scripts/generate-type-declarations.js",
149
+ "build:generate-types": "node ./scripts/generate-type-declarations.js",
143
150
  "build:copy-files": "cpx 'src/**/!(db)/*.{json,svg}' build",
144
151
  "build:copy-css": "cpx 'src/main.css' build/ & cpx 'src/**/*.css' build/styles/",
145
152
  "build:copy-lang": "cpx 'src/i18n/*.json' build/i18n && cpx 'src/i18n/index.js' build/i18n",
@@ -14,11 +14,11 @@ exports[`AccordionItem open / close renders an item closed 1`] = `
14
14
  <div
15
15
  class="media-body"
16
16
  >
17
- <span
18
- class="np-text-body-large-bold text-primary np-option__title d-block"
17
+ <h4
18
+ class="np-text-body-large-bold text-primary np-option__title"
19
19
  >
20
20
  This is title number one
21
- </span>
21
+ </h4>
22
22
  </div>
23
23
  <div
24
24
  class="media-right"
@@ -62,11 +62,11 @@ exports[`AccordionItem open / close renders an item open 1`] = `
62
62
  <div
63
63
  class="media-body"
64
64
  >
65
- <span
66
- class="np-text-body-large-bold text-primary np-option__title d-block"
65
+ <h4
66
+ class="np-text-body-large-bold text-primary np-option__title"
67
67
  >
68
68
  This is title number one
69
- </span>
69
+ </h4>
70
70
  </div>
71
71
  <div
72
72
  class="media-right"
@@ -6,11 +6,10 @@ import Badge, { BadgeProps } from '../badge';
6
6
  import { ProfileType, ProfileTypePersonal, ProfileTypeBusiness, Size, Sentiment } from '../common';
7
7
  import StatusIcon from '../statusIcon/StatusIcon';
8
8
 
9
- interface OptionalBadgeProps {
9
+ interface OptionalBadgeProps extends Omit<BadgeProps, 'badge'> {
10
10
  url?: string;
11
11
  altText?: string;
12
12
  statusIcon?: Sentiment;
13
- children: React.ReactNode;
14
13
  }
15
14
 
16
15
  const OptionalBadge = ({ url, altText, statusIcon, children, ...rest }: OptionalBadgeProps) => {
@@ -31,17 +30,30 @@ const OptionalBadge = ({ url, altText, statusIcon, children, ...rest }: Optional
31
30
  return <>{children}</>;
32
31
  };
33
32
 
34
- export interface AvatarWrapperProps {
33
+ export type AvatarWrapperProps = {
35
34
  url?: string;
36
35
  profileType?: ProfileTypeBusiness | ProfileTypePersonal;
37
36
  profileId?: string;
38
- badgeUrl?: string;
39
- badgeAltText?: string;
40
- badgeStatusIcon?: Sentiment;
41
37
  name?: string;
42
38
  avatarProps?: AvatarProps;
43
39
  badgeProps?: BadgeProps;
44
- }
40
+ } & (
41
+ | {
42
+ badgeUrl: string;
43
+ badgeAltText: string;
44
+ badgeStatusIcon?: never;
45
+ }
46
+ | {
47
+ badgeUrl?: never;
48
+ badgeAltText?: never;
49
+ badgeStatusIcon: Sentiment;
50
+ }
51
+ | {
52
+ badgeUrl?: never;
53
+ badgeAltText?: never;
54
+ badgeStatusIcon?: never;
55
+ }
56
+ );
45
57
 
46
58
  const AvatarWrapper = ({
47
59
  url,
@@ -64,7 +76,7 @@ const AvatarWrapper = ({
64
76
  if (url && !hasImageLoadError) {
65
77
  return {
66
78
  type: AvatarType.THUMBNAIL,
67
- children: <img src={url} alt="avatar" onError={() => setImageLoadError(true)} />,
79
+ children: <img src={url} alt="" onError={() => setImageLoadError(true)} />,
68
80
  ...avatarProps,
69
81
  };
70
82
  }
@@ -8,7 +8,7 @@ exports[`FlowNavigationAvatar with a name AND profileType FlowNavigationAvatar w
8
8
  class="tw-avatar__content"
9
9
  >
10
10
  <img
11
- alt="avatar"
11
+ alt=""
12
12
  src="https://wise.com"
13
13
  />
14
14
  </div>
@@ -56,12 +56,12 @@ describe('Card', () => {
56
56
  });
57
57
 
58
58
  describe('when there is no children prop', () => {
59
- it('redners Option title as a heading 4', () => {
59
+ it('renders Option title as a heading 4', () => {
60
60
  const onClick = jest.fn();
61
61
  renderCard(onClick);
62
62
 
63
63
  expect(screen.queryByRole('button')).not.toBeInTheDocument();
64
- expect(screen.getByText(defaultProps.title).tagName).toBe('SPAN');
64
+ expect(screen.getByText(defaultProps.title).tagName).toBe('H4');
65
65
  });
66
66
 
67
67
  it('has an inactive class', () => {
@@ -85,13 +85,7 @@ const Option = forwardRef<ReferenceType, OptionProps>(
85
85
  </div>
86
86
  )}
87
87
  <div className="media-body">
88
- <Body
89
- as="span"
90
- type={Typography.BODY_LARGE_BOLD}
91
- className="text-primary np-option__title d-block"
92
- >
93
- {title}
94
- </Body>
88
+ <h4 className="np-text-body-large-bold text-primary np-option__title">{title}</h4>
95
89
  {content && <Body className="d-block np-option__body">{content}</Body>}
96
90
  </div>
97
91
  <div className="media-right">{button}</div>
@@ -7,9 +7,12 @@ exports[`BottomSheet renders content when open 1`] = `
7
7
  class="np-theme-light"
8
8
  >
9
9
  <div
10
- class="np-focus-boundary outline-none"
11
10
  tabindex="-1"
12
11
  >
12
+ <span
13
+ data-focus-scope-start="true"
14
+ hidden=""
15
+ />
13
16
  <div
14
17
  class="dimmer"
15
18
  role="presentation"
@@ -63,6 +66,10 @@ exports[`BottomSheet renders content when open 1`] = `
63
66
  </div>
64
67
  </div>
65
68
  </div>
69
+ <span
70
+ data-focus-scope-end="true"
71
+ hidden=""
72
+ />
66
73
  </div>
67
74
  </div>
68
75
  </body>
@@ -1 +1 @@
1
- .np-close-button{--ring-outline-offset:0;background:none;border:none;color:#00a2dd;color:var(--color-interactive-accent);height:16px;height:var(--size-16);padding:0;transition:color .15s ease-in-out}.np-close-button--large,.np-close-button--x-large{height:24px;height:var(--size-24)}.np-close-button:hover{color:#008fc9;color:var(--color-interactive-accent-hover)}.np-theme-personal .np-close-button{align-items:center;border-radius:50%;border-radius:var(--radius-full);color:var(--color-interactive-primary);display:inline-flex;height:32px;height:var(--size-32);justify-content:center;transition:background-color .1s ease;width:32px;width:var(--size-32)}.np-theme-personal .np-close-button--large{flex-shrink:0;height:40px;height:var(--size-40);width:40px;width:var(--size-40)}.np-theme-personal .np-close-button--x-large{flex-shrink:0;height:48px;height:var(--size-48);width:48px;width:var(--size-48)}.np-theme-personal .np-close-button:not(.disabled,:disabled):hover{background-color:var(--color-background-neutral-hover);color:var(--color-interactive-primary-hover)}.np-theme-personal .np-close-button:not(.disabled,:disabled):active{background-color:var(--color-background-neutral-active);color:var(--color-interactive-primary-active)}
1
+ .np-close-button{--ring-outline-offset:0;background:none;border:none;color:#00a2dd;color:var(--color-interactive-accent);height:16px;height:var(--size-16);padding:0;transition:color .15s ease-in-out}.np-close-button--large,.np-close-button--x-large{height:24px;height:var(--size-24)}.np-close-button:hover{color:#008fc9;color:var(--color-interactive-accent-hover)}.np-theme-personal .np-close-button{align-items:center;border-radius:9999px;border-radius:var(--radius-full);color:var(--color-interactive-primary);display:inline-flex;height:32px;height:var(--size-32);justify-content:center;transition:background-color .1s ease;width:32px;width:var(--size-32)}.np-theme-personal .np-close-button--large{flex-shrink:0;height:40px;height:var(--size-40);width:40px;width:var(--size-40)}.np-theme-personal .np-close-button--x-large{flex-shrink:0;height:48px;height:var(--size-48);width:48px;width:var(--size-48)}.np-theme-personal .np-close-button:not(.disabled,:disabled):hover{background-color:var(--color-background-neutral-hover);color:var(--color-interactive-primary-hover)}.np-theme-personal .np-close-button:not(.disabled,:disabled):active{background-color:var(--color-background-neutral-active);color:var(--color-interactive-primary-active)}
@@ -1,44 +1,21 @@
1
- import { isKey, isUndefined } from '@transferwise/neptune-validation';
2
- import { ReactNode, useEffect, useRef, useState } from 'react';
3
-
4
- import { useConditionalListener } from '../hooks';
5
- import { Key } from '../key';
6
-
7
- import { getFocusableElements, resetFocus } from './utils';
8
-
9
- const { TAB } = Key;
1
+ import { FocusScope } from '@react-aria/focus';
2
+ import { useEffect, useRef } from 'react';
10
3
 
11
4
  type FocusBoundaryProps = {
12
- children: ReactNode;
5
+ children: React.ReactNode;
13
6
  };
14
7
 
15
8
  const FocusBoundary = ({ children }: FocusBoundaryProps) => {
16
- const boundaryReference = useRef<HTMLDivElement | null>(null);
17
- const parent = isUndefined(document) ? undefined : document;
18
- const [focusableEls, setFocusableEls] = useState({});
19
-
9
+ const wrapperReference = useRef<HTMLDivElement>(null);
20
10
  useEffect(() => {
21
- if (boundaryReference?.current) {
22
- boundaryReference.current.focus({ preventScroll: true });
23
- setFocusableEls(getFocusableElements(boundaryReference.current));
24
- }
11
+ wrapperReference.current?.focus({ preventScroll: true });
25
12
  }, []);
26
13
 
27
- // If event type is Tab the resetFocus will force the focus to either the first focusable or last in boundaryRef .
28
- useConditionalListener({
29
- eventType: 'keydown',
30
- callback: (event) => {
31
- if (isKey({ keyType: TAB, event })) {
32
- resetFocus({ event, focusableEls });
33
- }
34
- },
35
- attachListener: true,
36
- parent,
37
- });
38
-
39
14
  return (
40
- <div ref={boundaryReference} tabIndex={-1} className="np-focus-boundary outline-none">
41
- {children}
15
+ <div ref={wrapperReference} tabIndex={-1}>
16
+ <FocusScope contain restoreFocus>
17
+ {children}
18
+ </FocusScope>
42
19
  </div>
43
20
  );
44
21
  };
@@ -0,0 +1,39 @@
1
+ import { renderHook } from '@testing-library/react-hooks';
2
+
3
+ import { useMedia } from './useMedia';
4
+
5
+ Object.defineProperty(window, 'matchMedia', {
6
+ writable: true,
7
+ value: jest.fn().mockImplementation((query: string) => {
8
+ const matches = /^\(min-width: ([0-9]+)px\)$/.exec(query);
9
+ const minWidth = matches != null ? Number(matches[1]) : undefined;
10
+ return {
11
+ matches: minWidth != null ? window.innerWidth >= minWidth : false,
12
+ media: query,
13
+ onchange: null,
14
+ addListener: jest.fn(), // deprecated
15
+ removeListener: jest.fn(), // deprecated
16
+ addEventListener: jest.fn(),
17
+ removeEventListener: jest.fn(),
18
+ dispatchEvent: jest.fn(),
19
+ };
20
+ }),
21
+ });
22
+
23
+ describe('useMedia', () => {
24
+ it('tracks changes of window size', () => {
25
+ const { result, rerender } = renderHook(() => useMedia('(min-width: 1024px)'));
26
+
27
+ window.innerWidth = 1920;
28
+ rerender();
29
+ expect(result.current).toBeTruthy();
30
+
31
+ window.innerWidth = 1024;
32
+ rerender();
33
+ expect(result.current).toBeTruthy();
34
+
35
+ window.innerWidth = 800;
36
+ rerender();
37
+ expect(result.current).toBeFalsy();
38
+ });
39
+ });
@@ -0,0 +1,15 @@
1
+ import { useSyncExternalStore } from 'use-sync-external-store/shim';
2
+
3
+ export function useMedia(query: string) {
4
+ return useSyncExternalStore(
5
+ (onStoreChange) => {
6
+ const mediaQueryList = window.matchMedia(query);
7
+ mediaQueryList.addEventListener('change', onStoreChange);
8
+ return () => {
9
+ mediaQueryList.removeEventListener('change', onStoreChange);
10
+ };
11
+ },
12
+ () => (typeof window !== 'undefined' ? window.matchMedia(query).matches : undefined),
13
+ () => undefined,
14
+ );
15
+ }
@@ -0,0 +1,7 @@
1
+ import { Breakpoint } from '../propsValues/breakpoint';
2
+
3
+ import { useMedia } from './useMedia';
4
+
5
+ export function useScreenSize(size: Breakpoint) {
6
+ return useMedia(`(min-width: ${size}px)`);
7
+ }
@@ -0,0 +1,6 @@
1
+ import { usePreventScroll } from '@react-aria/overlays';
2
+
3
+ export function PreventScroll() {
4
+ usePreventScroll();
5
+ return null;
6
+ }
@@ -18,8 +18,10 @@ const DateInput = ({
18
18
  size,
19
19
  value,
20
20
  dayLabel,
21
+ dayAutoComplete,
21
22
  monthLabel,
22
23
  yearLabel,
24
+ yearAutoComplete,
23
25
  monthFormat,
24
26
  mode,
25
27
  onChange,
@@ -239,6 +241,7 @@ const DateInput = ({
239
241
  <Input
240
242
  type="number"
241
243
  name="day"
244
+ autoComplete={dayAutoComplete}
242
245
  value={day || ''}
243
246
  placeholder={placeholders.day}
244
247
  disabled={disabled}
@@ -259,6 +262,7 @@ const DateInput = ({
259
262
  <Input
260
263
  type="number"
261
264
  name="year"
265
+ autoComplete={yearAutoComplete}
262
266
  placeholder={placeholders.year}
263
267
  value={year || ''}
264
268
  disabled={disabled}
@@ -303,8 +307,10 @@ DateInput.propTypes = {
303
307
  onFocus: PropTypes.func,
304
308
  onBlur: PropTypes.func,
305
309
  dayLabel: PropTypes.string,
310
+ dayAutoComplete: PropTypes.string,
306
311
  monthLabel: PropTypes.string,
307
312
  yearLabel: PropTypes.string,
313
+ yearAutoComplete: PropTypes.string,
308
314
  monthFormat: PropTypes.oneOf(['long', 'short']),
309
315
  mode: PropTypes.oneOf(['day-month-year', 'month-year']),
310
316
  placeholders: PropTypes.shape({
@@ -8,8 +8,10 @@ export default {
8
8
  title: 'Forms/DateInput',
9
9
  args: {
10
10
  dayLabel: 'Day input',
11
+ dayAutoComplete: 'bday-day',
11
12
  monthLabel: 'Month select',
12
13
  yearLabel: 'Year input',
14
+ yearAutoComplete: 'bday-year',
13
15
  selectProps: {
14
16
  buttonProps: {
15
17
  'aria-label': 'Select month',
@@ -1 +1 @@
1
- .tw-date-lookup-menu{width:400px}.tw-date-lookup-calendar{min-width:300px;table-layout:fixed;text-align:center}.tw-date-lookup-calendar>tbody>tr>td{padding:4px;padding:var(--size-4)}.tw-date-lookup-calendar>tbody>tr>td button{background-color:transparent;border:transparent;border-radius:10px;border-radius:var(--radius-small);color:#0097c7;color:var(--color-content-accent);font-weight:600;font-weight:var(--font-weight-semi-bold);padding:4px 0;padding:var(--size-4) 0;width:100%}.tw-date-lookup-calendar>tbody>tr>td button:not(.disabled,:disabled).active{background-color:#0081ba;background-color:var(--color-interactive-accent-active);color:#fff}.tw-date-lookup-calendar>tbody>tr>td button:not(.disabled,:disabled):hover{background-color:#008fc9;background-color:var(--color-interactive-accent-hover);color:#fff}.np-theme-personal .tw-date-lookup-calendar>tbody>tr>td button{color:#37517e;color:var(--color-content-primary)}.np-theme-personal .tw-date-lookup-calendar>tbody>tr>td button:not(.disabled,:disabled).active,.np-theme-personal .tw-date-lookup-calendar>tbody>tr>td button:not(.disabled,:disabled):hover{color:var(--color-interactive-control)}.np-theme-personal .tw-date-lookup-calendar>tbody>tr>td:nth-child(7n+6) button,.np-theme-personal .tw-date-lookup-calendar>tbody>tr>td:nth-child(7n+7) button,.np-theme-personal .tw-date-lookup-calendar>thead>tr>th:nth-child(7n+6),.np-theme-personal .tw-date-lookup-calendar>thead>tr>th:nth-child(7n+7){color:#5d7079;color:var(--color-content-secondary);font-weight:400;font-weight:var(--font-weight-regular)}.np-theme-personal .tw-date-lookup-calendar{background-color:inherit}.tw-date-lookup-header-current{font-weight:800;font-weight:var(--font-weight-bold)}.np-theme-personal .tw-date-lookup-menu .table-bordered,.np-theme-personal.tw-date-lookup-menu .table-bordered{border:none}.np-theme-personal .tw-date-lookup-menu thead,.np-theme-personal.tw-date-lookup-menu thead{background-color:unset}.np-theme-personal .tw-date-lookup-menu td,.np-theme-personal.tw-date-lookup-menu td{border:none}.np-theme-personal .tw-date-lookup-menu .tw-date-lookup-header-current,.np-theme-personal.tw-date-lookup-menu .tw-date-lookup-header-current{color:#37517e;color:var(--color-content-primary)}.np-theme-personal .tw-date-lookup-menu .tw-date-lookup-day-option,.np-theme-personal.tw-date-lookup-menu .tw-date-lookup-day-option{align-items:center;color:#37517e;color:var(--color-content-primary);display:inline-flex;height:32px;height:var(--size-32);justify-content:center;line-height:32px;line-height:var(--size-32)}.np-theme-personal .tw-date-lookup-menu .tw-date-lookup-day-option.active,.np-theme-personal.tw-date-lookup-menu .tw-date-lookup-day-option.active{background-color:#00a2dd;background-color:var(--color-interactive-accent);color:var(--color-interactive-primary)}
1
+ .tw-date-lookup-menu{width:400px}.tw-date-lookup-calendar{min-width:300px;table-layout:fixed;text-align:center}.tw-date-lookup-calendar>tbody>tr>td{padding:4px;padding:var(--size-4)}.tw-date-lookup-calendar>tbody>tr>td button{background-color:transparent;border:transparent;border-radius:10px;border-radius:var(--radius-small);color:#0097c7;color:var(--color-content-accent);font-weight:600;font-weight:var(--font-weight-semi-bold);padding:4px 0;padding:var(--size-4) 0;width:100%}.tw-date-lookup-calendar>tbody>tr>td button:not(.disabled,:disabled).active{background-color:#0081ba;background-color:var(--color-interactive-accent-active);color:#fff}.tw-date-lookup-calendar>tbody>tr>td button:not(.disabled,:disabled):hover{background-color:#008fc9;background-color:var(--color-interactive-accent-hover);color:#fff}.np-theme-personal .tw-date-lookup-calendar>tbody>tr>td button{color:#37517e;color:var(--color-content-primary)}.np-theme-personal .tw-date-lookup-calendar>tbody>tr>td button:not(.disabled,:disabled).active,.np-theme-personal .tw-date-lookup-calendar>tbody>tr>td button:not(.disabled,:disabled):hover{color:var(--color-interactive-control)}.np-theme-personal .tw-date-lookup-calendar>tbody>tr>td:nth-child(7n+6) button,.np-theme-personal .tw-date-lookup-calendar>tbody>tr>td:nth-child(7n+7) button,.np-theme-personal .tw-date-lookup-calendar>thead>tr>th:nth-child(7n+6),.np-theme-personal .tw-date-lookup-calendar>thead>tr>th:nth-child(7n+7){color:#5d7079;color:var(--color-content-secondary);font-weight:400;font-weight:var(--font-weight-regular)}.tw-date-lookup-calendar abbr{text-decoration:none}.np-theme-personal .tw-date-lookup-calendar{background-color:inherit}.tw-date-lookup-header-current{font-weight:800;font-weight:var(--font-weight-bold)}.np-theme-personal .tw-date-lookup-menu .table-bordered,.np-theme-personal.tw-date-lookup-menu .table-bordered{border:none}.np-theme-personal .tw-date-lookup-menu thead,.np-theme-personal.tw-date-lookup-menu thead{background-color:unset}.np-theme-personal .tw-date-lookup-menu td,.np-theme-personal.tw-date-lookup-menu td{border:none}.np-theme-personal .tw-date-lookup-menu .tw-date-lookup-header-current,.np-theme-personal.tw-date-lookup-menu .tw-date-lookup-header-current{color:#37517e;color:var(--color-content-primary)}.np-theme-personal .tw-date-lookup-menu .tw-date-lookup-day-option,.np-theme-personal.tw-date-lookup-menu .tw-date-lookup-day-option{align-items:center;color:#37517e;color:var(--color-content-primary);display:inline-flex;height:32px;height:var(--size-32);justify-content:center;line-height:32px;line-height:var(--size-32)}.np-theme-personal .tw-date-lookup-menu .tw-date-lookup-day-option.active,.np-theme-personal.tw-date-lookup-menu .tw-date-lookup-day-option.active{background-color:#00a2dd;background-color:var(--color-interactive-accent);color:var(--color-interactive-primary)}
@@ -7,13 +7,13 @@ import KEY_CODES from '../common/keyCodes';
7
7
  import DateLookup from '.';
8
8
 
9
9
  const defaultLocale = 'en-GB';
10
-
10
+ const formatMessage = (id) => `${id}`;
11
11
  jest.mock('react-intl', () => ({
12
12
  injectIntl: (Component) =>
13
13
  function (props) {
14
- return <Component {...props} intl={{ locale: defaultLocale }} />;
14
+ return <Component {...props} intl={{ locale: defaultLocale, formatMessage }} />;
15
15
  },
16
- useIntl: () => ({ locale: defaultLocale }),
16
+ useIntl: () => ({ locale: defaultLocale, formatMessage }),
17
17
  defineMessages: (translations) => translations,
18
18
  }));
19
19
 
@@ -54,6 +54,10 @@
54
54
  }
55
55
  }
56
56
 
57
+ abbr {
58
+ text-decoration: none;
59
+ }
60
+
57
61
  .np-theme-personal & {
58
62
  background-color: inherit;
59
63
  }
@@ -0,0 +1,44 @@
1
+ import { defineMessages } from 'react-intl';
2
+
3
+ export default defineMessages({
4
+ next: {
5
+ id: 'neptune.DateLookup.next',
6
+ defaultMessage: 'next',
7
+ description: 'Description of next button',
8
+ },
9
+ previous: {
10
+ id: 'neptune.DateLookup.previous',
11
+ defaultMessage: 'previous',
12
+ description: 'Description of previous button',
13
+ },
14
+ day: {
15
+ id: 'neptune.DateLookup.day',
16
+ defaultMessage: 'day',
17
+ description: 'Description of next/previous/selected day button',
18
+ },
19
+ month: {
20
+ id: 'neptune.DateLookup.month',
21
+ defaultMessage: 'month',
22
+ description: 'Description of next/previous/selected month button',
23
+ },
24
+ year: {
25
+ id: 'neptune.DateLookup.year',
26
+ defaultMessage: 'year',
27
+ description: 'Description of next/previous/selected year button',
28
+ },
29
+ twentyYears: {
30
+ id: 'neptune.DateLookup.twentyYears',
31
+ defaultMessage: '20 years',
32
+ description: 'Description of next/previous 20 years button',
33
+ },
34
+ selected: {
35
+ id: 'neptune.DateLookup.selected',
36
+ defaultMessage: 'selected',
37
+ description: 'Description of the selected day/month/year',
38
+ },
39
+ goTo20YearView: {
40
+ id: 'neptune.DateLookup.goTo20YearView',
41
+ defaultMessage: 'Go to 20 year view',
42
+ description: 'Description of the button to go to 20 year view',
43
+ },
44
+ });
@@ -54,6 +54,14 @@ describe('DateLookup (events)', () => {
54
54
  expect(getActiveYearButton()).toHaveFocus();
55
55
  });
56
56
 
57
+ it('has aria-label for 20 years', () => {
58
+ openDateLookup();
59
+ clickDateButton();
60
+
61
+ expect(getButtonByAriaLabel('next 20 years')).toBeInTheDocument();
62
+ expect(getButtonByAriaLabel('previous 20 years')).toBeInTheDocument();
63
+ });
64
+
57
65
  it('switches to months', () => {
58
66
  openDateLookup();
59
67
  clickDateButton();
@@ -62,6 +70,15 @@ describe('DateLookup (events)', () => {
62
70
  expect(getActiveMonthButton()).toHaveFocus();
63
71
  });
64
72
 
73
+ it('has aria label for year', () => {
74
+ openDateLookup();
75
+ clickDateButton();
76
+ user.click(getActiveYearButton());
77
+
78
+ expect(getButtonByAriaLabel('next year')).toBeInTheDocument();
79
+ expect(getButtonByAriaLabel('previous year')).toBeInTheDocument();
80
+ });
81
+
65
82
  it('switches to days', () => {
66
83
  openDateLookup();
67
84
  clickDateButton();
@@ -71,6 +88,16 @@ describe('DateLookup (events)', () => {
71
88
  expect(getActiveDayButton()).toHaveFocus();
72
89
  });
73
90
 
91
+ it('has aria label for month', () => {
92
+ openDateLookup();
93
+ clickDateButton();
94
+ user.click(getActiveYearButton());
95
+ user.click(getActiveMonthButton());
96
+
97
+ expect(getButtonByAriaLabel('next month')).toBeInTheDocument();
98
+ expect(getButtonByAriaLabel('previous month')).toBeInTheDocument();
99
+ });
100
+
74
101
  it('updates selected date and closes', () => {
75
102
  openDateLookup();
76
103
  const d = new Date(2018, 11, 28);
@@ -81,6 +108,15 @@ describe('DateLookup (events)', () => {
81
108
  expect(getOpenButton()).toHaveFocus();
82
109
  });
83
110
 
111
+ it('has aria label on selected date', () => {
112
+ openDateLookup();
113
+ const d = new Date(2018, 11, 28);
114
+ const newDay = screen.getByText(`${d.getDate()}`);
115
+ user.click(newDay);
116
+ openDateLookup();
117
+ expect(screen.getByRole('button', { name: /selected day/i })).toBeInTheDocument();
118
+ });
119
+
84
120
  describe('at extra small breakpoints', () => {
85
121
  it('opens dateLookup using slider on small + 1 width', () => {
86
122
  winWidth(Breakpoint.SMALL + 1);
@@ -148,6 +184,9 @@ describe('DateLookup (events)', () => {
148
184
  return container.querySelector('button.tw-date-lookup-day-option.active');
149
185
  };
150
186
 
187
+ const getButtonByAriaLabel = (ariaLabel) => {
188
+ return screen.getByRole('button', { name: ariaLabel });
189
+ };
151
190
  const getClearButton = () => container.querySelector('.clear-btn');
152
191
  const getOpenButton = () => container.querySelector('button.np-date-trigger');
153
192
  const getDateButton = () => container.querySelector('button.tw-date-lookup-header-current');