@transferwise/components 46.49.0 → 46.50.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 (174) hide show
  1. package/build/avatar/Avatar.js +3 -1
  2. package/build/avatar/Avatar.js.map +1 -1
  3. package/build/avatar/Avatar.mjs +3 -1
  4. package/build/avatar/Avatar.mjs.map +1 -1
  5. package/build/avatarWrapper/AvatarWrapper.js +16 -4
  6. package/build/avatarWrapper/AvatarWrapper.js.map +1 -1
  7. package/build/avatarWrapper/AvatarWrapper.mjs +16 -4
  8. package/build/avatarWrapper/AvatarWrapper.mjs.map +1 -1
  9. package/build/badge/Badge.js +2 -0
  10. package/build/badge/Badge.js.map +1 -1
  11. package/build/badge/Badge.mjs +2 -0
  12. package/build/badge/Badge.mjs.map +1 -1
  13. package/build/card/Card.js.map +1 -1
  14. package/build/card/Card.mjs.map +1 -1
  15. package/build/common/bottomSheet/BottomSheet.js +3 -0
  16. package/build/common/bottomSheet/BottomSheet.js.map +1 -1
  17. package/build/common/bottomSheet/BottomSheet.mjs +4 -1
  18. package/build/common/bottomSheet/BottomSheet.mjs.map +1 -1
  19. package/build/common/panel/Panel.js +3 -0
  20. package/build/common/panel/Panel.js.map +1 -1
  21. package/build/common/panel/Panel.mjs +4 -1
  22. package/build/common/panel/Panel.mjs.map +1 -1
  23. package/build/dateLookup/DateLookup.js +21 -17
  24. package/build/dateLookup/DateLookup.js.map +1 -1
  25. package/build/dateLookup/DateLookup.mjs +21 -17
  26. package/build/dateLookup/DateLookup.mjs.map +1 -1
  27. package/build/dateLookup/dateTrigger/DateTrigger.js +6 -0
  28. package/build/dateLookup/dateTrigger/DateTrigger.js.map +1 -1
  29. package/build/dateLookup/dateTrigger/DateTrigger.mjs +6 -0
  30. package/build/dateLookup/dateTrigger/DateTrigger.mjs.map +1 -1
  31. package/build/drawer/Drawer.js +3 -0
  32. package/build/drawer/Drawer.js.map +1 -1
  33. package/build/drawer/Drawer.mjs +4 -1
  34. package/build/drawer/Drawer.mjs.map +1 -1
  35. package/build/i18n/de.json +2 -0
  36. package/build/i18n/de.json.js +2 -0
  37. package/build/i18n/de.json.js.map +1 -1
  38. package/build/i18n/de.json.mjs +2 -0
  39. package/build/i18n/de.json.mjs.map +1 -1
  40. package/build/i18n/es.json +2 -0
  41. package/build/i18n/es.json.js +2 -0
  42. package/build/i18n/es.json.js.map +1 -1
  43. package/build/i18n/es.json.mjs +2 -0
  44. package/build/i18n/es.json.mjs.map +1 -1
  45. package/build/i18n/fr.json +2 -0
  46. package/build/i18n/fr.json.js +2 -0
  47. package/build/i18n/fr.json.js.map +1 -1
  48. package/build/i18n/fr.json.mjs +2 -0
  49. package/build/i18n/fr.json.mjs.map +1 -1
  50. package/build/i18n/hu.json +2 -0
  51. package/build/i18n/hu.json.js +2 -0
  52. package/build/i18n/hu.json.js.map +1 -1
  53. package/build/i18n/hu.json.mjs +2 -0
  54. package/build/i18n/hu.json.mjs.map +1 -1
  55. package/build/i18n/id.json +2 -0
  56. package/build/i18n/id.json.js +2 -0
  57. package/build/i18n/id.json.js.map +1 -1
  58. package/build/i18n/id.json.mjs +2 -0
  59. package/build/i18n/id.json.mjs.map +1 -1
  60. package/build/i18n/it.json +2 -0
  61. package/build/i18n/it.json.js +2 -0
  62. package/build/i18n/it.json.js.map +1 -1
  63. package/build/i18n/it.json.mjs +2 -0
  64. package/build/i18n/it.json.mjs.map +1 -1
  65. package/build/i18n/ja.json +2 -0
  66. package/build/i18n/ja.json.js +2 -0
  67. package/build/i18n/ja.json.js.map +1 -1
  68. package/build/i18n/ja.json.mjs +2 -0
  69. package/build/i18n/ja.json.mjs.map +1 -1
  70. package/build/i18n/pl.json +2 -0
  71. package/build/i18n/pl.json.js +2 -0
  72. package/build/i18n/pl.json.js.map +1 -1
  73. package/build/i18n/pl.json.mjs +2 -0
  74. package/build/i18n/pl.json.mjs.map +1 -1
  75. package/build/i18n/ro.json +2 -0
  76. package/build/i18n/ro.json.js +2 -0
  77. package/build/i18n/ro.json.js.map +1 -1
  78. package/build/i18n/ro.json.mjs +2 -0
  79. package/build/i18n/ro.json.mjs.map +1 -1
  80. package/build/i18n/ru.json +2 -0
  81. package/build/i18n/ru.json.js +2 -0
  82. package/build/i18n/ru.json.js.map +1 -1
  83. package/build/i18n/ru.json.mjs +2 -0
  84. package/build/i18n/ru.json.mjs.map +1 -1
  85. package/build/i18n/th.json +2 -0
  86. package/build/i18n/th.json.js +2 -0
  87. package/build/i18n/th.json.js.map +1 -1
  88. package/build/i18n/th.json.mjs +2 -0
  89. package/build/i18n/th.json.mjs.map +1 -1
  90. package/build/i18n/tr.json +2 -0
  91. package/build/i18n/tr.json.js +2 -0
  92. package/build/i18n/tr.json.js.map +1 -1
  93. package/build/i18n/tr.json.mjs +2 -0
  94. package/build/i18n/tr.json.mjs.map +1 -1
  95. package/build/main.css +28 -20
  96. package/build/modal/Modal.js +3 -0
  97. package/build/modal/Modal.js.map +1 -1
  98. package/build/modal/Modal.mjs +4 -1
  99. package/build/modal/Modal.mjs.map +1 -1
  100. package/build/provider/overlay/OverlayIdProvider.js +20 -0
  101. package/build/provider/overlay/OverlayIdProvider.js.map +1 -0
  102. package/build/provider/overlay/OverlayIdProvider.mjs +17 -0
  103. package/build/provider/overlay/OverlayIdProvider.mjs.map +1 -0
  104. package/build/styles/main.css +28 -20
  105. package/build/styles/uploadInput/uploadButton/UploadButton.css +5 -0
  106. package/build/styles/uploadInput/uploadItem/UploadItem.css +23 -20
  107. package/build/types/avatar/Avatar.d.ts +1 -0
  108. package/build/types/avatar/Avatar.d.ts.map +1 -1
  109. package/build/types/avatarWrapper/AvatarWrapper.d.ts +2 -1
  110. package/build/types/avatarWrapper/AvatarWrapper.d.ts.map +1 -1
  111. package/build/types/badge/Badge.d.ts +2 -1
  112. package/build/types/badge/Badge.d.ts.map +1 -1
  113. package/build/types/card/Card.d.ts +4 -0
  114. package/build/types/card/Card.d.ts.map +1 -1
  115. package/build/types/common/bottomSheet/BottomSheet.d.ts.map +1 -1
  116. package/build/types/common/panel/Panel.d.ts.map +1 -1
  117. package/build/types/dateLookup/DateLookup.d.ts.map +1 -1
  118. package/build/types/dateLookup/dateTrigger/DateTrigger.d.ts.map +1 -1
  119. package/build/types/drawer/Drawer.d.ts.map +1 -1
  120. package/build/types/modal/Modal.d.ts.map +1 -1
  121. package/build/types/provider/overlay/OverlayIdProvider.d.ts +7 -0
  122. package/build/types/provider/overlay/OverlayIdProvider.d.ts.map +1 -0
  123. package/build/types/uploadInput/uploadItem/UploadItem.d.ts.map +1 -1
  124. package/build/types/uploadInput/uploadItem/{UploadItemBody.d.ts → UploadItemLink.d.ts} +2 -2
  125. package/build/types/uploadInput/uploadItem/UploadItemLink.d.ts.map +1 -0
  126. package/build/uploadInput/uploadItem/UploadItem.js +14 -11
  127. package/build/uploadInput/uploadItem/UploadItem.js.map +1 -1
  128. package/build/uploadInput/uploadItem/UploadItem.mjs +15 -12
  129. package/build/uploadInput/uploadItem/UploadItem.mjs.map +1 -1
  130. package/build/uploadInput/uploadItem/UploadItemLink.js +32 -0
  131. package/build/uploadInput/uploadItem/UploadItemLink.js.map +1 -0
  132. package/build/uploadInput/uploadItem/{UploadItemBody.mjs → UploadItemLink.mjs} +5 -4
  133. package/build/uploadInput/uploadItem/UploadItemLink.mjs.map +1 -0
  134. package/package.json +18 -18
  135. package/src/avatar/Avatar.spec.tsx +10 -0
  136. package/src/avatar/Avatar.tsx +3 -0
  137. package/src/avatarWrapper/AvatarWrapper.spec.tsx +15 -4
  138. package/src/avatarWrapper/AvatarWrapper.story.tsx +2 -0
  139. package/src/avatarWrapper/AvatarWrapper.tsx +26 -7
  140. package/src/avatarWrapper/__snapshots__/AvatarWrapper.spec.tsx.snap +0 -64
  141. package/src/badge/Badge.spec.tsx +8 -0
  142. package/src/badge/Badge.tsx +3 -1
  143. package/src/card/Card.tsx +4 -0
  144. package/src/common/bottomSheet/BottomSheet.tsx +12 -1
  145. package/src/common/panel/Panel.tsx +5 -0
  146. package/src/dateLookup/DateLookup.tsx +22 -19
  147. package/src/dateLookup/dateTrigger/DateTrigger.spec.js +1 -0
  148. package/src/dateLookup/dateTrigger/DateTrigger.tsx +7 -0
  149. package/src/drawer/Drawer.tsx +5 -1
  150. package/src/i18n/de.json +2 -0
  151. package/src/i18n/es.json +2 -0
  152. package/src/i18n/fr.json +2 -0
  153. package/src/i18n/hu.json +2 -0
  154. package/src/i18n/id.json +2 -0
  155. package/src/i18n/it.json +2 -0
  156. package/src/i18n/ja.json +2 -0
  157. package/src/i18n/pl.json +2 -0
  158. package/src/i18n/ro.json +2 -0
  159. package/src/i18n/ru.json +2 -0
  160. package/src/i18n/th.json +2 -0
  161. package/src/i18n/tr.json +2 -0
  162. package/src/main.css +28 -20
  163. package/src/modal/Modal.tsx +5 -1
  164. package/src/provider/overlay/OverlayIdProvider.tsx +15 -0
  165. package/src/uploadInput/uploadButton/UploadButton.css +5 -0
  166. package/src/uploadInput/uploadButton/UploadButton.less +6 -0
  167. package/src/uploadInput/uploadItem/UploadItem.css +23 -20
  168. package/src/uploadInput/uploadItem/UploadItem.less +16 -12
  169. package/src/uploadInput/uploadItem/UploadItem.tsx +12 -7
  170. package/src/uploadInput/uploadItem/{UploadItemBody.tsx → UploadItemLink.tsx} +6 -2
  171. package/build/types/uploadInput/uploadItem/UploadItemBody.d.ts.map +0 -1
  172. package/build/uploadInput/uploadItem/UploadItemBody.js +0 -27
  173. package/build/uploadInput/uploadItem/UploadItemBody.js.map +0 -1
  174. package/build/uploadInput/uploadItem/UploadItemBody.mjs.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@transferwise/components",
3
- "version": "46.49.0",
3
+ "version": "46.50.0",
4
4
  "description": "Neptune React components",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -38,9 +38,9 @@
38
38
  "!**/*.tsbuildinfo"
39
39
  ],
40
40
  "devDependencies": {
41
- "@babel/core": "^7.24.7",
41
+ "@babel/core": "^7.24.8",
42
42
  "@babel/plugin-transform-runtime": "^7.24.7",
43
- "@babel/preset-env": "^7.24.7",
43
+ "@babel/preset-env": "^7.24.8",
44
44
  "@babel/preset-react": "^7.24.7",
45
45
  "@babel/preset-typescript": "^7.24.7",
46
46
  "@cfaester/enzyme-adapter-react-18": "0.8.0",
@@ -50,20 +50,20 @@
50
50
  "@rollup/plugin-node-resolve": "^15.2.3",
51
51
  "@rollup/plugin-typescript": "^11.1.6",
52
52
  "@rollup/plugin-url": "^8.0.2",
53
- "@storybook/addon-a11y": "^8.2.1",
54
- "@storybook/addon-actions": "^8.2.1",
55
- "@storybook/addon-essentials": "^8.2.1",
56
- "@storybook/addon-interactions": "^8.2.1",
53
+ "@storybook/addon-a11y": "^8.2.2",
54
+ "@storybook/addon-actions": "^8.2.2",
55
+ "@storybook/addon-essentials": "^8.2.2",
56
+ "@storybook/addon-interactions": "^8.2.2",
57
57
  "@storybook/addon-knobs": "^8.0.1",
58
- "@storybook/addon-viewport": "^8.2.1",
58
+ "@storybook/addon-viewport": "^8.2.2",
59
59
  "@storybook/addon-webpack5-compiler-babel": "^3.0.3",
60
- "@storybook/components": "^8.2.1",
61
- "@storybook/core-events": "^8.2.1",
62
- "@storybook/manager-api": "^8.2.1",
63
- "@storybook/react": "^8.2.1",
64
- "@storybook/react-webpack5": "^8.2.1",
65
- "@storybook/test": "^8.2.1",
66
- "@storybook/theming": "^8.2.1",
60
+ "@storybook/components": "^8.2.2",
61
+ "@storybook/core-events": "^8.2.2",
62
+ "@storybook/manager-api": "^8.2.2",
63
+ "@storybook/react": "^8.2.2",
64
+ "@storybook/react-webpack5": "^8.2.2",
65
+ "@storybook/test": "^8.2.2",
66
+ "@storybook/theming": "^8.2.2",
67
67
  "@testing-library/dom": "^10.3.1",
68
68
  "@testing-library/jest-dom": "^6.4.6",
69
69
  "@testing-library/react": "^16.0.0",
@@ -91,9 +91,9 @@
91
91
  "react-intl": "^6.6.8",
92
92
  "rollup": "^4.18.1",
93
93
  "rollup-preserve-directives": "^1.1.1",
94
- "storybook": "^8.2.1",
94
+ "storybook": "^8.2.2",
95
95
  "@transferwise/less-config": "3.1.0",
96
- "@transferwise/neptune-css": "14.12.0",
96
+ "@transferwise/neptune-css": "14.12.1",
97
97
  "@wise/components-theming": "1.4.0"
98
98
  },
99
99
  "peerDependencies": {
@@ -106,7 +106,7 @@
106
106
  "react-intl": "^5.10.0 || ^6"
107
107
  },
108
108
  "dependencies": {
109
- "@babel/runtime": "^7.24.7",
109
+ "@babel/runtime": "^7.24.8",
110
110
  "@floating-ui/react": "^0.25.4",
111
111
  "@formatjs/intl-locale": "^3.4.6",
112
112
  "@headlessui/react": "^1.7.19",
@@ -41,4 +41,14 @@ describe('Avatar', () => {
41
41
  expect(screen.getByText('JS')).toHaveStyle(`background-color: ${item.color.token}`);
42
42
  });
43
43
  });
44
+
45
+ it('adds aria-label to component if it is passed in', () => {
46
+ render(
47
+ <Avatar size={Size.MEDIUM} aria-label="Japanese flag avatar">
48
+ <img src="https://wise.com/web-art/assets/flags/jpy.svg" alt="" />
49
+ </Avatar>,
50
+ );
51
+
52
+ expect(screen.getByLabelText('Japanese flag avatar')).toBeInTheDocument();
53
+ });
44
54
  });
@@ -24,6 +24,7 @@ export interface AvatarProps {
24
24
  size?: AvatarSize;
25
25
  theme?: AvatarTheme;
26
26
  type?: AvatarType;
27
+ 'aria-label'?: string;
27
28
  }
28
29
 
29
30
  const backwardsCompatibleSize = (size: AvatarSize): NumericAvatarSize => {
@@ -51,6 +52,7 @@ const Avatar: React.FC<AvatarProps> = ({
51
52
  size: sizeFromProps = 48,
52
53
  theme = Theme.LIGHT,
53
54
  type = 'thumbnail',
55
+ 'aria-label': ariaLabel,
54
56
  }) => {
55
57
  const backgroundColorFromSeed = useMemo<string | undefined>(
56
58
  () =>
@@ -69,6 +71,7 @@ const Avatar: React.FC<AvatarProps> = ({
69
71
  'tw-avatar--branded': Boolean(backgroundColorFromSeed),
70
72
  'np-text-title-body': type === 'initials',
71
73
  })}
74
+ aria-label={ariaLabel}
72
75
  >
73
76
  <div
74
77
  className="tw-avatar__content"
@@ -1,6 +1,5 @@
1
1
  import { BadgeProps } from '../badge';
2
- import { Sentiment } from '../common';
3
- import { Size, ProfileType } from '../common';
2
+ import { Sentiment, Size, ProfileType } from '../common';
4
3
  import { render, screen } from '../test-utils';
5
4
 
6
5
  import AvatarWrapper from '.';
@@ -77,6 +76,12 @@ describe('FlowNavigationAvatar', () => {
77
76
 
78
77
  expect(container.firstChild).toMatchSnapshot();
79
78
  });
79
+
80
+ it('renders aria-label', () => {
81
+ render(<AvatarWrapper aria-label="test" />);
82
+
83
+ expect(screen.getByLabelText('test')).toBeInTheDocument();
84
+ });
80
85
  });
81
86
 
82
87
  describe('with a badge url passed', () => {
@@ -95,9 +100,15 @@ describe('FlowNavigationAvatar', () => {
95
100
 
96
101
  describe('with a badge status icon passed', () => {
97
102
  it('renders the badge', () => {
98
- const { container } = render(<AvatarWrapper badgeStatusIcon={Sentiment.POSITIVE} />);
103
+ render(<AvatarWrapper badgeStatusIcon={Sentiment.POSITIVE} />);
99
104
 
100
- expect(container.firstChild).toMatchSnapshot();
105
+ expect(screen.getByTestId('check-icon')).toBeInTheDocument();
106
+ });
107
+
108
+ it('renders aria-label', () => {
109
+ render(<AvatarWrapper badgeStatusIcon={Sentiment.POSITIVE} aria-label="test" />);
110
+
111
+ expect(screen.getByLabelText('test')).toBeInTheDocument();
101
112
  });
102
113
  });
103
114
  });
@@ -37,6 +37,7 @@ export const All: Story = {
37
37
  <div>
38
38
  <AvatarWrapper
39
39
  url="https://wise.com/web-art/assets/flags/jpy.svg"
40
+ aria-label="Japanese flag avatar"
40
41
  profileType={ProfileType.BUSINESS}
41
42
  avatarProps={avatarProps}
42
43
  />
@@ -59,6 +60,7 @@ export const All: Story = {
59
60
  <AvatarWrapper
60
61
  badgeUrl={badgeUrl}
61
62
  badgeAltText="wise fast flag badge"
63
+ aria-label="Person avatar with wise fast flag badge"
62
64
  profileType={ProfileType.PERSONAL}
63
65
  avatarProps={avatarProps}
64
66
  />
@@ -8,21 +8,33 @@ import StatusIcon from '../statusIcon/StatusIcon';
8
8
 
9
9
  interface OptionalBadgeProps extends Omit<BadgeProps, 'badge'> {
10
10
  url?: string;
11
+ ariaLabel?: string;
11
12
  altText?: string;
12
13
  statusIcon?: Sentiment;
13
14
  }
14
15
 
15
- const OptionalBadge = ({ url, altText, statusIcon, children, ...rest }: OptionalBadgeProps) => {
16
+ const OptionalBadge = ({
17
+ url,
18
+ altText,
19
+ statusIcon,
20
+ children,
21
+ ariaLabel,
22
+ ...rest
23
+ }: OptionalBadgeProps) => {
16
24
  if (url) {
17
25
  return (
18
- <Badge badge={<img src={url} alt={altText} />} {...rest}>
26
+ <Badge aria-label={ariaLabel} badge={<img src={url} alt={altText} />} {...rest}>
19
27
  {children}
20
28
  </Badge>
21
29
  );
22
30
  }
23
31
  if (statusIcon) {
24
32
  return (
25
- <Badge badge={<StatusIcon sentiment={statusIcon} size={Size.SMALL} />} {...rest}>
33
+ <Badge
34
+ aria-label={ariaLabel}
35
+ badge={<StatusIcon sentiment={statusIcon} size={Size.SMALL} />}
36
+ {...rest}
37
+ >
26
38
  {children}
27
39
  </Badge>
28
40
  );
@@ -32,6 +44,7 @@ const OptionalBadge = ({ url, altText, statusIcon, children, ...rest }: Optional
32
44
 
33
45
  export type AvatarWrapperProps = {
34
46
  url?: string;
47
+ 'aria-label'?: string;
35
48
  profileType?: ProfileTypeBusiness | ProfileTypePersonal;
36
49
  profileId?: string;
37
50
  name?: string;
@@ -57,6 +70,7 @@ export type AvatarWrapperProps = {
57
70
 
58
71
  const AvatarWrapper = ({
59
72
  url,
73
+ 'aria-label': ariaLabel,
60
74
  profileType,
61
75
  profileId,
62
76
  badgeUrl,
@@ -73,18 +87,22 @@ const AvatarWrapper = ({
73
87
  useEffect(() => setImageLoadError(false), [url]);
74
88
 
75
89
  const getAvatarProps = () => {
90
+ let updatedAvatarProps = avatarProps;
91
+ if (!badgeUrl && !badgeStatusIcon && ariaLabel) {
92
+ updatedAvatarProps = { ...updatedAvatarProps, 'aria-label': ariaLabel };
93
+ }
76
94
  if (url && !hasImageLoadError) {
77
95
  return {
78
96
  type: AvatarType.THUMBNAIL,
79
97
  children: <img src={url} alt="" onError={() => setImageLoadError(true)} />,
80
- ...avatarProps,
98
+ ...updatedAvatarProps,
81
99
  };
82
100
  }
83
101
  if (profileType) {
84
102
  return {
85
103
  type: AvatarType.ICON,
86
104
  children: isBusinessProfile ? <BriefcaseIcon size="24" /> : <ProfileIcon size="24" />,
87
- ...avatarProps,
105
+ ...updatedAvatarProps,
88
106
  };
89
107
  }
90
108
  if (name) {
@@ -92,19 +110,20 @@ const AvatarWrapper = ({
92
110
  type: AvatarType.INITIALS,
93
111
  children: <>{getInitials(name)}</>,
94
112
  backgroundColorSeed: profileId?.toString(),
95
- ...avatarProps,
113
+ ...updatedAvatarProps,
96
114
  };
97
115
  }
98
116
  return {
99
117
  type: AvatarType.ICON,
100
118
  children: <ProfileIcon size="24" />,
101
- ...avatarProps,
119
+ ...updatedAvatarProps,
102
120
  };
103
121
  };
104
122
 
105
123
  return (
106
124
  <OptionalBadge
107
125
  url={badgeUrl}
126
+ ariaLabel={ariaLabel}
108
127
  altText={badgeAltText}
109
128
  statusIcon={badgeStatusIcon}
110
129
  {...badgeProps}
@@ -73,70 +73,6 @@ exports[`FlowNavigationAvatar with a name AND profileType FlowNavigationAvatar w
73
73
  </div>
74
74
  `;
75
75
 
76
- exports[`FlowNavigationAvatar with a name AND profileType with a badge status icon passed renders the badge 1`] = `
77
- <div
78
- class="tw-badge tw-badge-border-light tw-badge-sm"
79
- >
80
- <div
81
- class="tw-badge__children"
82
- >
83
- <div
84
- class="tw-avatar tw-avatar--48 tw-avatar--icon"
85
- >
86
- <div
87
- class="tw-avatar__content"
88
- >
89
- <span
90
- aria-hidden="true"
91
- class="tw-icon tw-icon-person "
92
- data-testid="person-icon"
93
- role="presentation"
94
- >
95
- <svg
96
- fill="currentColor"
97
- focusable="false"
98
- height="24"
99
- viewBox="0 0 24 24"
100
- width="24"
101
- >
102
- <path
103
- d="M15.257 14.014A5.922 5.922 0 0 0 18 9c0-3.3-2.7-6-6-6S6 5.7 6 9c0 2.1 1.071 3.943 2.743 5.014-2.614 1.243-4.457 3.9-4.457 6.986H6c0-3.3 2.7-6 6-6s6 2.7 6 6h1.714c0-3.086-1.842-5.743-4.457-6.986ZM7.714 9A4.298 4.298 0 0 1 12 4.714 4.298 4.298 0 0 1 16.286 9 4.298 4.298 0 0 1 12 13.286 4.298 4.298 0 0 1 7.714 9Z"
104
- />
105
- </svg>
106
- </span>
107
- </div>
108
- </div>
109
- </div>
110
- <div
111
- class="tw-badge__content"
112
- >
113
- <span
114
- class="status-circle status-circle-sm positive"
115
- data-testid="status-icon"
116
- >
117
- <span
118
- aria-hidden="true"
119
- class="tw-icon tw-icon-check status-icon light"
120
- data-testid="check-icon"
121
- role="presentation"
122
- >
123
- <svg
124
- fill="currentColor"
125
- focusable="false"
126
- height="16"
127
- viewBox="0 0 24 24"
128
- width="16"
129
- >
130
- <path
131
- d="M20.143 6.427 9.557 16.97 4.2 11.57 3 12.77l5.957 6a.846.846 0 0 0 .6.257.846.846 0 0 0 .6-.257L21.343 7.627l-1.2-1.2Z"
132
- />
133
- </svg>
134
- </span>
135
- </span>
136
- </div>
137
- </div>
138
- `;
139
-
140
76
  exports[`FlowNavigationAvatar with a name AND profileType with a badge url passed renders the badge 1`] = `
141
77
  <div
142
78
  class="tw-badge tw-badge-border-light tw-badge-lg"
@@ -33,4 +33,12 @@ describe('Badge', () => {
33
33
 
34
34
  expect(screen.getByText(childText).parentElement).toHaveClass('tw-badge__children');
35
35
  });
36
+
37
+ it('adds aria-label to badge if it is passed in', () => {
38
+ const ariaLabel = 'badge-aria-label';
39
+
40
+ renderBadge({ 'aria-label': ariaLabel });
41
+
42
+ expect(screen.getByLabelText(ariaLabel)).toBeInTheDocument();
43
+ });
36
44
  });
@@ -17,6 +17,7 @@ export type BadgeProps = {
17
17
  children: ReactNode;
18
18
  size?: SizeSmall | SizeMedium | SizeLarge;
19
19
  border?: ThemeDark | ThemeLight;
20
+ 'aria-label'?: string;
20
21
  } & CommonProps;
21
22
 
22
23
  const Badge = ({
@@ -24,6 +25,7 @@ const Badge = ({
24
25
  className = undefined,
25
26
  size = Size.SMALL,
26
27
  border = Theme.LIGHT,
28
+ 'aria-label': ariaLabel,
27
29
  children,
28
30
  }: BadgeProps) => {
29
31
  const classes: string = classNames(
@@ -36,7 +38,7 @@ const Badge = ({
36
38
  );
37
39
 
38
40
  return (
39
- <div className={classes}>
41
+ <div aria-label={ariaLabel} className={classes}>
40
42
  <div className="tw-badge__children">{children}</div>
41
43
  <div className="tw-badge__content">{badge}</div>
42
44
  </div>
package/src/card/Card.tsx CHANGED
@@ -20,6 +20,10 @@ export interface CardProps {
20
20
  onClick?: (nextIsExpanded: boolean) => void;
21
21
  }
22
22
 
23
+ /**
24
+ * @deprecated use Navigation pattern (via `NavigationOption` component)
25
+ * @see https://transferwise.atlassian.net/wiki/spaces/DS/pages/2387314550/Instructions+for+killing+expanding+cards+on+web+design+pattern
26
+ */
23
27
  const Card = forwardRef(function Card(
24
28
  {
25
29
  'aria-label': ariaLabel,
@@ -1,8 +1,16 @@
1
1
  import classNames from 'classnames';
2
- import { useRef, useState, PropsWithChildren, CSSProperties, SyntheticEvent } from 'react';
2
+ import {
3
+ CSSProperties,
4
+ PropsWithChildren,
5
+ SyntheticEvent,
6
+ useContext,
7
+ useRef,
8
+ useState,
9
+ } from 'react';
3
10
 
4
11
  import Dimmer from '../../dimmer';
5
12
  import Drawer from '../../drawer';
13
+ import { OverlayIdContext } from '../../provider/overlay/OverlayIdProvider';
6
14
  import SlidingPanel from '../../slidingPanel';
7
15
  import { CloseButton } from '../closeButton';
8
16
  import { CommonProps } from '../commonProps';
@@ -170,6 +178,8 @@ const BottomSheet = (props: BottomSheetProps) => {
170
178
 
171
179
  const is400Zoom = useMedia(`(max-width: ${Breakpoint.ZOOM_400}px)`);
172
180
 
181
+ const overlayId = useContext(OverlayIdContext);
182
+
173
183
  return is400Zoom ? (
174
184
  <Drawer open={props.open} className={props.className} onClose={close}>
175
185
  {props.children}
@@ -184,6 +194,7 @@ const BottomSheet = (props: BottomSheetProps) => {
184
194
  >
185
195
  {/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */}
186
196
  <div
197
+ id={overlayId}
187
198
  role="dialog"
188
199
  aria-modal
189
200
  onTouchStart={onSwipeStart}
@@ -6,6 +6,7 @@ import {
6
6
  PropsWithChildren,
7
7
  SyntheticEvent,
8
8
  forwardRef,
9
+ useContext,
9
10
  useEffect,
10
11
  useState,
11
12
  } from 'react';
@@ -13,6 +14,7 @@ import { usePopper } from 'react-popper';
13
14
 
14
15
  import { Position, PositionBottom, PositionLeft, PositionRight, PositionTop } from '..';
15
16
  import Dimmer from '../../dimmer';
17
+ import { OverlayIdContext } from '../../provider/overlay/OverlayIdProvider';
16
18
 
17
19
  const POPOVER_OFFSET = [0, 16];
18
20
 
@@ -108,9 +110,12 @@ const Panel = forwardRef<HTMLDivElement, PanelProps>(function Panel(
108
110
  ...(anchorWidth ? { width: anchorRef.current?.clientWidth } : undefined),
109
111
  };
110
112
 
113
+ const overlayId = useContext(OverlayIdContext);
114
+
111
115
  return (
112
116
  <Dimmer open={open} transparent fadeContentOnEnter fadeContentOnExit onClose={onClose}>
113
117
  <div
118
+ id={overlayId}
114
119
  {...rest}
115
120
  ref={setPopperElement}
116
121
  role="dialog"
@@ -13,6 +13,7 @@ import {
13
13
  import { isWithinRange, moveToWithinRange } from '../common/dateUtils';
14
14
  import ResponsivePanel from '../common/responsivePanel';
15
15
  import { WithInputAttributesProps, withInputAttributes } from '../inputs/contexts';
16
+ import { OverlayIdContext, OverlayIdProvider } from '../provider/overlay/OverlayIdProvider';
16
17
  import DateTrigger from './dateTrigger';
17
18
  import DayCalendar from './dayCalendar';
18
19
  import { getStartOfDay } from './getStartOfDay';
@@ -326,25 +327,27 @@ class DateLookup extends PureComponent<DateLookupPropsWithInputAttributes, DateL
326
327
  className="input-group"
327
328
  onKeyDown={this.handleKeyDown}
328
329
  >
329
- <DateTrigger
330
- selectedDate={selectedDate}
331
- size={size}
332
- placeholder={placeholder}
333
- label={label}
334
- monthFormat={monthFormat}
335
- disabled={disabled || false}
336
- onClick={this.open}
337
- onClear={!disabled && clearable && value ? this.handleClear : undefined}
338
- />
339
- <ResponsivePanel
340
- anchorRef={this.element}
341
- open={open}
342
- className="tw-date-lookup-menu"
343
- position={Position.BOTTOM}
344
- onClose={this.discard}
345
- >
346
- {this.getCalendar()}
347
- </ResponsivePanel>
330
+ <OverlayIdProvider open={open}>
331
+ <DateTrigger
332
+ selectedDate={selectedDate}
333
+ size={size}
334
+ placeholder={placeholder}
335
+ label={label}
336
+ monthFormat={monthFormat}
337
+ disabled={disabled || false}
338
+ onClick={this.open}
339
+ onClear={!disabled && clearable && value ? this.handleClear : undefined}
340
+ />
341
+ <ResponsivePanel
342
+ anchorRef={this.element}
343
+ open={open}
344
+ className="tw-date-lookup-menu"
345
+ position={Position.BOTTOM}
346
+ onClose={this.discard}
347
+ >
348
+ {this.getCalendar()}
349
+ </ResponsivePanel>
350
+ </OverlayIdProvider>
348
351
  </div>
349
352
  );
350
353
  }
@@ -25,6 +25,7 @@ describe('DateTrigger', () => {
25
25
 
26
26
  beforeEach(() => {
27
27
  props = {
28
+ 'aria-expanded': false,
28
29
  size: 'md',
29
30
  placeholder: 'Enter date..',
30
31
  label: '',
@@ -6,6 +6,8 @@ import { Size, Position, SizeSmall, SizeMedium, SizeLarge } from '../../common';
6
6
  import { CloseButton } from '../../common/closeButton/CloseButton';
7
7
 
8
8
  import messages from './DateTrigger.messages';
9
+ import { useContext } from 'react';
10
+ import { OverlayIdContext } from '../../provider/overlay/OverlayIdProvider';
9
11
 
10
12
  interface DateTriggerProps {
11
13
  selectedDate: Date | null;
@@ -30,9 +32,14 @@ const DateTrigger: React.FC<DateTriggerProps> = ({
30
32
  }) => {
31
33
  const { locale, formatMessage } = useIntl();
32
34
 
35
+ const overlayId = useContext(OverlayIdContext);
36
+
33
37
  return (
34
38
  <>
35
39
  <button
40
+ aria-haspopup="dialog"
41
+ aria-expanded={overlayId != null}
42
+ aria-controls={overlayId}
36
43
  className={`btn btn-${size} btn-input np-date-trigger`}
37
44
  disabled={disabled}
38
45
  type="button"
@@ -1,10 +1,11 @@
1
1
  import classNames from 'classnames';
2
- import { useId } from 'react';
2
+ import { useContext, useId } from 'react';
3
3
 
4
4
  import { Position, Typography } from '../common';
5
5
  import { CloseButton } from '../common/closeButton';
6
6
  import { useLayout } from '../common/hooks';
7
7
  import Dimmer from '../dimmer';
8
+ import { OverlayIdContext } from '../provider/overlay/OverlayIdProvider';
8
9
  import SlidingPanel from '../slidingPanel';
9
10
  import Title from '../title';
10
11
  import { logActionRequiredIf } from '../utilities';
@@ -42,10 +43,13 @@ export default function Drawer({
42
43
  const { isMobile } = useLayout();
43
44
  const titleId = useId();
44
45
 
46
+ const overlayId = useContext(OverlayIdContext);
47
+
45
48
  return (
46
49
  <Dimmer open={open} onClose={onClose}>
47
50
  <SlidingPanel open={open} position={isMobile ? Position.BOTTOM : position}>
48
51
  <div
52
+ id={overlayId}
49
53
  role="dialog"
50
54
  aria-modal
51
55
  aria-labelledby={headerTitle ? titleId : undefined}
package/src/i18n/de.json CHANGED
@@ -23,6 +23,8 @@
23
23
  "neptune.PhoneNumberInput.SelectInput.placeholder": "Wähle eine Option...",
24
24
  "neptune.Select.searchPlaceholder": "Wird gesucht...",
25
25
  "neptune.SelectInput.noResultsFound": "Keine Ergebnisse gefunden",
26
+ "neptune.SelectOption.action.label": "Auswählen",
27
+ "neptune.SelectOption.selected.action.label": "Ausgewählte Option ändern",
26
28
  "neptune.Summary.statusDone": "Schritt erledigt",
27
29
  "neptune.Summary.statusNotDone": "Schritt noch zu erledigen",
28
30
  "neptune.Summary.statusPending": "Schritt ausstehend",
package/src/i18n/es.json CHANGED
@@ -23,6 +23,8 @@
23
23
  "neptune.PhoneNumberInput.SelectInput.placeholder": "Selecciona una opción...",
24
24
  "neptune.Select.searchPlaceholder": "Buscar...",
25
25
  "neptune.SelectInput.noResultsFound": "No se han encontrado resultados",
26
+ "neptune.SelectOption.action.label": "Elegir",
27
+ "neptune.SelectOption.selected.action.label": "Cambiar opción elegida",
26
28
  "neptune.Summary.statusDone": "Apartado listo",
27
29
  "neptune.Summary.statusNotDone": "Apartado a completar",
28
30
  "neptune.Summary.statusPending": "Apartado pendiente",
package/src/i18n/fr.json CHANGED
@@ -23,6 +23,8 @@
23
23
  "neptune.PhoneNumberInput.SelectInput.placeholder": "Sélectionnez une option…",
24
24
  "neptune.Select.searchPlaceholder": "Recherche...",
25
25
  "neptune.SelectInput.noResultsFound": "Aucun résultat trouvé",
26
+ "neptune.SelectOption.action.label": "Sélectionner",
27
+ "neptune.SelectOption.selected.action.label": "Modifier l'option choisie",
26
28
  "neptune.Summary.statusDone": "Validé",
27
29
  "neptune.Summary.statusNotDone": "À compléter",
28
30
  "neptune.Summary.statusPending": "En attente",
package/src/i18n/hu.json CHANGED
@@ -23,6 +23,8 @@
23
23
  "neptune.PhoneNumberInput.SelectInput.placeholder": "Válassz ki egy lehetőséget...",
24
24
  "neptune.Select.searchPlaceholder": "Keresés...",
25
25
  "neptune.SelectInput.noResultsFound": "Nincs találat",
26
+ "neptune.SelectOption.action.label": "Kiválasztás",
27
+ "neptune.SelectOption.selected.action.label": "Kiválasztott opció módosítása",
26
28
  "neptune.Summary.statusDone": "Kész",
27
29
  "neptune.Summary.statusNotDone": "Hátravan",
28
30
  "neptune.Summary.statusPending": "Függőben",
package/src/i18n/id.json CHANGED
@@ -23,6 +23,8 @@
23
23
  "neptune.PhoneNumberInput.SelectInput.placeholder": "Pilih opsi...",
24
24
  "neptune.Select.searchPlaceholder": "Cari...",
25
25
  "neptune.SelectInput.noResultsFound": "Hasil tidak ditemukan",
26
+ "neptune.SelectOption.action.label": "Pilih",
27
+ "neptune.SelectOption.selected.action.label": "Ubah opsi yang dipilih",
26
28
  "neptune.Summary.statusDone": "Item selesai",
27
29
  "neptune.Summary.statusNotDone": "Item yang harus dilakukan",
28
30
  "neptune.Summary.statusPending": "Item tertunda",
package/src/i18n/it.json CHANGED
@@ -23,6 +23,8 @@
23
23
  "neptune.PhoneNumberInput.SelectInput.placeholder": "Seleziona un'opzione...",
24
24
  "neptune.Select.searchPlaceholder": "Cerca...",
25
25
  "neptune.SelectInput.noResultsFound": "Nessun risultato trovato",
26
+ "neptune.SelectOption.action.label": "Scegli",
27
+ "neptune.SelectOption.selected.action.label": "Modifica l'opzione scelta",
26
28
  "neptune.Summary.statusDone": "Completato",
27
29
  "neptune.Summary.statusNotDone": "Da completare",
28
30
  "neptune.Summary.statusPending": "In corso",
package/src/i18n/ja.json CHANGED
@@ -23,6 +23,8 @@
23
23
  "neptune.PhoneNumberInput.SelectInput.placeholder": "選択してください…",
24
24
  "neptune.Select.searchPlaceholder": "検索する... ",
25
25
  "neptune.SelectInput.noResultsFound": "結果が見つかりませんでした",
26
+ "neptune.SelectOption.action.label": "選択してください",
27
+ "neptune.SelectOption.selected.action.label": "選択したオプションを変更する",
26
28
  "neptune.Summary.statusDone": "完了",
27
29
  "neptune.Summary.statusNotDone": "未完了",
28
30
  "neptune.Summary.statusPending": "保留中",
package/src/i18n/pl.json CHANGED
@@ -23,6 +23,8 @@
23
23
  "neptune.PhoneNumberInput.SelectInput.placeholder": "Wybierz opcję...",
24
24
  "neptune.Select.searchPlaceholder": "Wyszukaj...",
25
25
  "neptune.SelectInput.noResultsFound": "Nie znaleziono wyników",
26
+ "neptune.SelectOption.action.label": "Wybierz",
27
+ "neptune.SelectOption.selected.action.label": "Zmień wybraną opcję",
26
28
  "neptune.Summary.statusDone": "Czynność wykonana",
27
29
  "neptune.Summary.statusNotDone": "Czynność do wykonania",
28
30
  "neptune.Summary.statusPending": "Czynność oczekująca",