@transferwise/components 46.76.0 → 46.78.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 (142) hide show
  1. package/build/alert/Alert.js +17 -13
  2. package/build/alert/Alert.js.map +1 -1
  3. package/build/alert/Alert.mjs +17 -13
  4. package/build/alert/Alert.mjs.map +1 -1
  5. package/build/avatar/Avatar.js +2 -7
  6. package/build/avatar/Avatar.js.map +1 -1
  7. package/build/avatar/Avatar.mjs +2 -7
  8. package/build/avatar/Avatar.mjs.map +1 -1
  9. package/build/badge/Badge.js +1 -10
  10. package/build/badge/Badge.js.map +1 -1
  11. package/build/badge/Badge.mjs +1 -10
  12. package/build/badge/Badge.mjs.map +1 -1
  13. package/build/circularButton/CircularButton.js +1 -1
  14. package/build/circularButton/CircularButton.js.map +1 -1
  15. package/build/circularButton/CircularButton.mjs +1 -1
  16. package/build/circularButton/CircularButton.mjs.map +1 -1
  17. package/build/common/circle/Circle.js +15 -2
  18. package/build/common/circle/Circle.js.map +1 -1
  19. package/build/common/circle/Circle.mjs +15 -2
  20. package/build/common/circle/Circle.mjs.map +1 -1
  21. package/build/i18n/de.json +5 -0
  22. package/build/i18n/de.json.js +5 -0
  23. package/build/i18n/de.json.js.map +1 -1
  24. package/build/i18n/de.json.mjs +5 -0
  25. package/build/i18n/de.json.mjs.map +1 -1
  26. package/build/i18n/es.json +5 -0
  27. package/build/i18n/es.json.js +5 -0
  28. package/build/i18n/es.json.js.map +1 -1
  29. package/build/i18n/es.json.mjs +5 -0
  30. package/build/i18n/es.json.mjs.map +1 -1
  31. package/build/i18n/fr.json +5 -0
  32. package/build/i18n/fr.json.js +5 -0
  33. package/build/i18n/fr.json.js.map +1 -1
  34. package/build/i18n/fr.json.mjs +5 -0
  35. package/build/i18n/fr.json.mjs.map +1 -1
  36. package/build/i18n/hu.json +5 -0
  37. package/build/i18n/hu.json.js +5 -0
  38. package/build/i18n/hu.json.js.map +1 -1
  39. package/build/i18n/hu.json.mjs +5 -0
  40. package/build/i18n/hu.json.mjs.map +1 -1
  41. package/build/i18n/id.json +5 -0
  42. package/build/i18n/id.json.js +5 -0
  43. package/build/i18n/id.json.js.map +1 -1
  44. package/build/i18n/id.json.mjs +5 -0
  45. package/build/i18n/id.json.mjs.map +1 -1
  46. package/build/i18n/it.json +5 -0
  47. package/build/i18n/it.json.js +5 -0
  48. package/build/i18n/it.json.js.map +1 -1
  49. package/build/i18n/it.json.mjs +5 -0
  50. package/build/i18n/it.json.mjs.map +1 -1
  51. package/build/i18n/ja.json +5 -0
  52. package/build/i18n/ja.json.js +5 -0
  53. package/build/i18n/ja.json.js.map +1 -1
  54. package/build/i18n/ja.json.mjs +5 -0
  55. package/build/i18n/ja.json.mjs.map +1 -1
  56. package/build/i18n/pl.json +5 -0
  57. package/build/i18n/pl.json.js +5 -0
  58. package/build/i18n/pl.json.js.map +1 -1
  59. package/build/i18n/pl.json.mjs +5 -0
  60. package/build/i18n/pl.json.mjs.map +1 -1
  61. package/build/i18n/pt.json +5 -0
  62. package/build/i18n/pt.json.js +5 -0
  63. package/build/i18n/pt.json.js.map +1 -1
  64. package/build/i18n/pt.json.mjs +5 -0
  65. package/build/i18n/pt.json.mjs.map +1 -1
  66. package/build/i18n/ro.json +5 -0
  67. package/build/i18n/ro.json.js +5 -0
  68. package/build/i18n/ro.json.js.map +1 -1
  69. package/build/i18n/ro.json.mjs +5 -0
  70. package/build/i18n/ro.json.mjs.map +1 -1
  71. package/build/i18n/ru.json +5 -0
  72. package/build/i18n/ru.json.js +5 -0
  73. package/build/i18n/ru.json.js.map +1 -1
  74. package/build/i18n/ru.json.mjs +5 -0
  75. package/build/i18n/ru.json.mjs.map +1 -1
  76. package/build/i18n/tr.json +5 -0
  77. package/build/i18n/tr.json.js +5 -0
  78. package/build/i18n/tr.json.js.map +1 -1
  79. package/build/i18n/tr.json.mjs +5 -0
  80. package/build/i18n/tr.json.mjs.map +1 -1
  81. package/build/main.css +46 -22
  82. package/build/statusIcon/StatusIcon.js +4 -4
  83. package/build/statusIcon/StatusIcon.js.map +1 -1
  84. package/build/statusIcon/StatusIcon.mjs +4 -4
  85. package/build/statusIcon/StatusIcon.mjs.map +1 -1
  86. package/build/styles/avatar/Avatar.css +29 -0
  87. package/build/styles/badge/Badge.css +6 -0
  88. package/build/styles/circularButton/CircularButton.css +2 -2
  89. package/build/styles/common/circle/Circle.css +4 -0
  90. package/build/styles/main.css +46 -22
  91. package/build/styles/statusIcon/StatusIcon.css +0 -20
  92. package/build/types/alert/Alert.d.ts +3 -11
  93. package/build/types/alert/Alert.d.ts.map +1 -1
  94. package/build/types/avatar/Avatar.d.ts.map +1 -1
  95. package/build/types/badge/Badge.d.ts.map +1 -1
  96. package/build/types/common/circle/Circle.d.ts +1 -1
  97. package/build/types/common/circle/Circle.d.ts.map +1 -1
  98. package/build/types/statusIcon/StatusIcon.d.ts +7 -3
  99. package/build/types/statusIcon/StatusIcon.d.ts.map +1 -1
  100. package/package.json +3 -3
  101. package/src/alert/Alert.spec.story.tsx +85 -4
  102. package/src/alert/Alert.spec.tsx +36 -14
  103. package/src/alert/Alert.story.tsx +50 -35
  104. package/src/alert/Alert.tsx +22 -23
  105. package/src/avatar/Avatar.css +29 -0
  106. package/src/avatar/Avatar.less +12 -0
  107. package/src/avatar/Avatar.tsx +4 -8
  108. package/src/avatarWrapper/__snapshots__/AvatarWrapper.spec.tsx.snap +11 -22
  109. package/src/badge/Badge.css +6 -0
  110. package/src/badge/Badge.less +6 -0
  111. package/src/badge/Badge.tsx +1 -11
  112. package/src/circularButton/CircularButton.css +2 -2
  113. package/src/circularButton/CircularButton.less +1 -1
  114. package/src/circularButton/CircularButton.story.tsx +3 -0
  115. package/src/circularButton/CircularButton.tsx +1 -1
  116. package/src/circularButton/__snapshots__/CircularButton.spec.tsx.snap +10 -10
  117. package/src/common/circle/Circle.css +4 -0
  118. package/src/common/circle/Circle.less +6 -0
  119. package/src/common/circle/Circle.story.tsx +2 -2
  120. package/src/common/circle/Circle.tsx +25 -2
  121. package/src/flowNavigation/__snapshots__/FlowNavigation.spec.js.snap +4 -8
  122. package/src/i18n/de.json +5 -0
  123. package/src/i18n/es.json +5 -0
  124. package/src/i18n/fr.json +5 -0
  125. package/src/i18n/hu.json +5 -0
  126. package/src/i18n/id.json +5 -0
  127. package/src/i18n/it.json +5 -0
  128. package/src/i18n/ja.json +5 -0
  129. package/src/i18n/pl.json +5 -0
  130. package/src/i18n/pt.json +5 -0
  131. package/src/i18n/ro.json +5 -0
  132. package/src/i18n/ru.json +5 -0
  133. package/src/i18n/tr.json +5 -0
  134. package/src/main.css +46 -22
  135. package/src/overlayHeader/__snapshots__/OverlayHeader.spec.tsx.snap +2 -4
  136. package/src/promoCard/PromoCard.spec.tsx +1 -1
  137. package/src/radio/__snapshots__/Radio.rtl.spec.tsx.snap +2 -4
  138. package/src/statusIcon/StatusIcon.css +0 -20
  139. package/src/statusIcon/StatusIcon.less +0 -17
  140. package/src/statusIcon/StatusIcon.spec.tsx +2 -21
  141. package/src/statusIcon/StatusIcon.story.tsx +32 -1
  142. package/src/statusIcon/StatusIcon.tsx +11 -10
@@ -1 +1 @@
1
- {"version":3,"file":"Avatar.d.ts","sourceRoot":"","sources":["../../../src/avatar/Avatar.tsx"],"names":[],"mappings":"AAOA,KAAK,iBAAiB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAEhD,KAAK,gBAAgB,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE3C,MAAM,MAAM,UAAU,GAAG,iBAAiB,GAAG,gBAAgB,CAAC;AAE9D,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,MAAM,CAAC;AAE3C,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,UAAU,CAAC;AAErE,MAAM,WAAW,WAAW;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAkBD,QAAA,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CA4CjC,CAAC;AAEF,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"Avatar.d.ts","sourceRoot":"","sources":["../../../src/avatar/Avatar.tsx"],"names":[],"mappings":"AAOA,KAAK,iBAAiB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAEhD,KAAK,gBAAgB,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE3C,MAAM,MAAM,UAAU,GAAG,iBAAiB,GAAG,gBAAgB,CAAC;AAE9D,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,MAAM,CAAC;AAE3C,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,UAAU,CAAC;AAErE,MAAM,WAAW,WAAW;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAkBD,QAAA,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CAwCjC,CAAC;AAEF,eAAe,MAAM,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"Badge.d.ts","sourceRoot":"","sources":["../../../src/badge/Badge.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAElC,OAAO,EAGL,SAAS,EACT,UAAU,EACV,SAAS,EACT,SAAS,EACT,UAAU,EACV,WAAW,EACZ,MAAM,WAAW,CAAC;AAGnB;;GAEG;AACH,KAAK,eAAe,GAAG,UAAU,CAAC;AAElC,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,SAAS,CAAC;IACjB,QAAQ,EAAE,SAAS,CAAC;IACpB;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,GAAG,eAAe,GAAG,SAAS,CAAC;IAC/C,MAAM,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,WAAW,CAAC;AAShB,QAAA,MAAM,KAAK,qFAOR,UAAU,gCAoBZ,CAAC;AAEF,eAAe,KAAK,CAAC"}
1
+ {"version":3,"file":"Badge.d.ts","sourceRoot":"","sources":["../../../src/badge/Badge.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAElC,OAAO,EAGL,SAAS,EACT,UAAU,EACV,SAAS,EACT,SAAS,EACT,UAAU,EACV,WAAW,EACZ,MAAM,WAAW,CAAC;AAEnB;;GAEG;AACH,KAAK,eAAe,GAAG,UAAU,CAAC;AAElC,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,SAAS,CAAC;IACjB,QAAQ,EAAE,SAAS,CAAC;IACpB;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,GAAG,eAAe,GAAG,SAAS,CAAC;IAC/C,MAAM,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,WAAW,CAAC;AAEhB,QAAA,MAAM,KAAK,qFAOR,UAAU,gCAkBZ,CAAC;AAEF,eAAe,KAAK,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { HTMLAttributes } from 'react';
2
- export type ShapeSize = 16 | 24 | 32 | 40 | 48 | 56 | 64 | 72;
2
+ export type ShapeSize = 16 | 24 | 32 | 40 | 48 | 56 | 72;
3
3
  export type Props = {
4
4
  /**
5
5
  * Modify underlying element, `div` by default
@@ -1 +1 @@
1
- {"version":3,"file":"Circle.d.ts","sourceRoot":"","sources":["../../../../src/common/circle/Circle.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAc,MAAM,OAAO,CAAC;AAGnD,MAAM,MAAM,SAAS,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAE9D,MAAM,MAAM,KAAK,GAAG;IAClB;;OAEG;IACH,EAAE,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC;IACvB;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,GAAG,cAAc,CAAC,cAAc,CAAC,CAAC;AAEnC,QAAA,MAAM,MAAM;IAfV;;OAEG;SACE,KAAK,CAAC,WAAW;IACtB;;OAEG;WACI,SAAS;IAChB;;;OAGG;gBACS,OAAO;4EAyBnB,CAAC;AAEH,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"Circle.d.ts","sourceRoot":"","sources":["../../../../src/common/circle/Circle.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAc,MAAM,OAAO,CAAC;AAKnD,MAAM,MAAM,SAAS,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAEzD,MAAM,MAAM,KAAK,GAAG;IAClB;;OAEG;IACH,EAAE,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC;IACvB;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,GAAG,cAAc,CAAC,cAAc,CAAC,CAAC;AAenC,QAAA,MAAM,MAAM;IA5BV;;OAEG;SACE,KAAK,CAAC,WAAW;IACtB;;OAEG;WACI,SAAS;IAChB;;;OAGG;gBACS,OAAO;4EA8CnB,CAAC;AAEH,eAAe,MAAM,CAAC"}
@@ -1,7 +1,11 @@
1
1
  import { SizeSmall, SizeMedium, SizeLarge, Sentiment } from '../common';
2
+ /**
3
+ * @deprecated use `16 | 24 | 32 | 40 | 48 | 56 | 72` component instead
4
+ */
5
+ type LegacySizes = SizeSmall | SizeMedium | SizeLarge;
2
6
  export type StatusIconProps = {
3
- sentiment: `${Sentiment}`;
4
- size: SizeSmall | SizeMedium | SizeLarge;
7
+ sentiment?: `${Sentiment}`;
8
+ size?: LegacySizes | 16 | 24 | 32 | 40 | 48 | 56 | 72;
5
9
  /**
6
10
  * Override for the sentiment's-derived, default, accessible
7
11
  * name announced by the screen readers. <br />
@@ -9,6 +13,6 @@ export type StatusIconProps = {
9
13
  * */
10
14
  iconLabel?: string | null;
11
15
  };
12
- declare const StatusIcon: ({ sentiment, size: sizeProp, iconLabel, }: StatusIconProps) => import("react").JSX.Element;
16
+ declare const StatusIcon: ({ sentiment, size: sizeProp, iconLabel }: StatusIconProps) => import("react").JSX.Element;
13
17
  export default StatusIcon;
14
18
  //# sourceMappingURL=StatusIcon.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"StatusIcon.d.ts","sourceRoot":"","sources":["../../../src/statusIcon/StatusIcon.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAoB,MAAM,WAAW,CAAC;AAM1F,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,GAAG,SAAS,EAAE,CAAC;IAC1B,IAAI,EAAE,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;IACzC;;;;SAIK;IACL,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAAC;AAQF,QAAA,MAAM,UAAU,8CAIb,eAAe,gCA8DjB,CAAC;AAEF,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"StatusIcon.d.ts","sourceRoot":"","sources":["../../../src/statusIcon/StatusIcon.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAoB,MAAM,WAAW,CAAC;AAM1F;;GAEG;AACH,KAAK,WAAW,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;AAEtD,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,CAAC,EAAE,GAAG,SAAS,EAAE,CAAC;IAC3B,IAAI,CAAC,EAAE,WAAW,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;IACtD;;;;SAIK;IACL,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAAC;AAQF,QAAA,MAAM,UAAU,6CAA+D,eAAe,gCA8D7F,CAAC;AAEF,eAAe,UAAU,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@transferwise/components",
3
- "version": "46.76.0",
3
+ "version": "46.78.0",
4
4
  "description": "Neptune React components",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -91,9 +91,9 @@
91
91
  "rollup": "^4.18.1",
92
92
  "rollup-preserve-directives": "^1.1.1",
93
93
  "storybook": "^8.2.2",
94
+ "@transferwise/less-config": "3.1.0",
94
95
  "@transferwise/neptune-css": "14.19.1",
95
- "@wise/components-theming": "1.6.1",
96
- "@transferwise/less-config": "3.1.0"
96
+ "@wise/components-theming": "1.6.1"
97
97
  },
98
98
  "peerDependencies": {
99
99
  "@transferwise/icons": "^3.13.1",
@@ -42,7 +42,7 @@ export const SimpleTrigger: Story = {
42
42
  Trigger Alert
43
43
  </Button>
44
44
 
45
- {isActive && <Alert {...args} dynamicRender className="m-t-4" />}
45
+ {isActive && <Alert {...args} className="m-t-4" />}
46
46
  </>
47
47
  );
48
48
  },
@@ -79,13 +79,13 @@ export const ComplexTrigger: Story = {
79
79
  />
80
80
  </Field>
81
81
 
82
- {isActive && <Alert dynamicRender {...args} className="m-t-2" />}
82
+ {isActive && <Alert {...args} className="m-t-2" />}
83
83
  </>
84
84
  );
85
85
  },
86
86
  };
87
87
 
88
- export const BackendForFrontend: Story = {
88
+ export const MultipleDynamicAlerts: Story = {
89
89
  play: async ({ canvasElement }) => {
90
90
  const canvas = within(canvasElement);
91
91
  await expect(canvas.queryAllByRole('status')).toHaveLength(0);
@@ -125,9 +125,90 @@ export const BackendForFrontend: Story = {
125
125
  </Button>
126
126
 
127
127
  {alerts.map((props) => (
128
- <Alert dynamicRender {...props} key={props.title} className="m-t-3" />
128
+ <Alert {...props} key={props.title} className="m-t-3" />
129
129
  ))}
130
130
  </>
131
131
  );
132
132
  },
133
133
  };
134
+
135
+ /** Making sure using `active` is non-breaking **/
136
+ export const SimpleTriggerDeprecated: Story = {
137
+ name: 'Deprecated: Simple Trigger',
138
+ play: async ({ args, canvasElement }) => {
139
+ const canvas = within(canvasElement);
140
+ await wait();
141
+ await userEvent.tab();
142
+ await wait();
143
+ await userEvent.keyboard('{Enter}');
144
+
145
+ await waitFor(async () => expect(canvas.getByText(args.message || '')).toBeInTheDocument());
146
+ await wait(1000);
147
+ await userEvent.keyboard('{Enter}');
148
+ await wait();
149
+ await waitFor(async () =>
150
+ expect(canvas.queryByText(args.message || '')).not.toBeInTheDocument(),
151
+ );
152
+ },
153
+ render: function Render(args) {
154
+ const [isActive, setIsActive] = useState(false);
155
+
156
+ return (
157
+ <>
158
+ <Button htmlType="button" onClick={() => setIsActive((current) => !current)}>
159
+ Trigger Alert
160
+ </Button>
161
+
162
+ <Alert {...args} active={isActive} className="m-t-4" />
163
+ </>
164
+ );
165
+ },
166
+ };
167
+
168
+ /** Making sure using `active` is non-breaking **/
169
+ export const ComplexTriggerDeprecated: Story = {
170
+ name: 'Deprecated: Complex Trigger',
171
+ play: async ({ args, canvasElement }) => {
172
+ const canvas = within(canvasElement);
173
+ await wait();
174
+ await userEvent.tab();
175
+ await wait();
176
+ await userEvent.keyboard('{ArrowDown}');
177
+ await wait();
178
+ await userEvent.keyboard('{ArrowDown}');
179
+ await wait();
180
+ await userEvent.keyboard('{ArrowDown}');
181
+ await wait();
182
+ await userEvent.keyboard('{Enter}');
183
+
184
+ await waitFor(async () => expect(canvas.getByText(args.message || '')).toBeInTheDocument());
185
+ await wait(1000);
186
+ await userEvent.keyboard('{Enter}');
187
+ await wait();
188
+ await userEvent.keyboard('{ArrowUp}');
189
+ await wait();
190
+ await userEvent.keyboard('{Enter}');
191
+ // await waitFor(async () => expect(canvas.queryByText(args.message || '')).not.toBeInTheDocument());
192
+ },
193
+ render: function Render(args) {
194
+ const [isActive, setIsActive] = useState(false);
195
+ const [value, setValue] = useState<string>();
196
+
197
+ return (
198
+ <>
199
+ <Field label="Select option to triger Alert">
200
+ <SelectInput
201
+ items={[
202
+ { type: 'option', value: 'one' },
203
+ { type: 'option', value: 'two' },
204
+ ]}
205
+ onChange={setValue}
206
+ onClose={() => setIsActive(Boolean(value === 'two'))}
207
+ />
208
+ </Field>
209
+
210
+ <Alert {...args} active={isActive} className="m-t-2" />
211
+ </>
212
+ );
213
+ },
214
+ };
@@ -4,7 +4,14 @@ import { ThemeProvider } from '@wise/components-theming';
4
4
  import React from 'react';
5
5
 
6
6
  import { Sentiment, Size, Theme, Variant } from '../common';
7
- import { render, cleanup, screen, userEvent, fireEvent, mockMatchMedia } from '../test-utils';
7
+ import {
8
+ render,
9
+ cleanup,
10
+ screen,
11
+ userEvent as user,
12
+ fireEvent,
13
+ mockMatchMedia,
14
+ } from '../test-utils';
8
15
 
9
16
  import Alert, { AlertAction, AlertArrowPosition, AlertType } from './Alert';
10
17
 
@@ -25,6 +32,9 @@ describe('Alert', () => {
25
32
  let alert: HTMLElement;
26
33
  let closeButton: HTMLElement;
27
34
  let action: AlertAction;
35
+ const userEvent = user.setup({
36
+ advanceTimers: jest.advanceTimersByTimeAsync,
37
+ });
28
38
 
29
39
  const classForType = (type: AlertType) => `alert-${type}`;
30
40
 
@@ -39,7 +49,6 @@ describe('Alert', () => {
39
49
  });
40
50
 
41
51
  afterEach(() => {
42
- cleanup();
43
52
  jest.clearAllMocks();
44
53
  });
45
54
 
@@ -100,6 +109,24 @@ describe('Alert', () => {
100
109
  );
101
110
  });
102
111
 
112
+ it('active is ignored and a warning is logged', () => {
113
+ render(<Alert active message={message} />);
114
+
115
+ expect(screen.getByText(message)).toBeInTheDocument();
116
+ expect(mockedWarn).toHaveBeenCalledWith(
117
+ expect.stringMatching(/Alert component doesn't support `active`/),
118
+ );
119
+ });
120
+
121
+ it('dynamicRender is ignored and a warning is logged', () => {
122
+ render(<Alert dynamicRender message={message} />);
123
+
124
+ expect(screen.getByText(message)).toBeInTheDocument();
125
+ expect(mockedWarn).toHaveBeenCalledWith(
126
+ expect.stringMatching(/Alert component doesn't support.*?`dynamicRender`/),
127
+ );
128
+ });
129
+
103
130
  it('dismissible is ignored and a warning is logged', () => {
104
131
  ({ container } = render(<Alert dismissible message={message} />));
105
132
 
@@ -252,18 +279,17 @@ describe('Alert', () => {
252
279
  });
253
280
 
254
281
  describe('onDismiss', () => {
255
- it('renders the close button if onDismiss is provided', () => {
282
+ it('renders the close button if onDismiss is provided', async () => {
256
283
  render(<Alert message={message} onDismiss={jest.fn()} />);
257
-
258
- expect(screen.getByRole('button')).toBeInTheDocument();
284
+ const button = await screen.findByRole('button', { name: 'Close' });
285
+ expect(button).toBeEnabled();
259
286
  });
260
287
 
261
288
  it('calls onDismiss when the close button is clicked', async () => {
262
289
  const onDismiss = jest.fn();
263
-
264
290
  render(<Alert message={message} onDismiss={onDismiss} />);
265
-
266
- await userEvent.click(screen.getByRole('button'));
291
+ const button = await screen.findByRole('button', { name: 'Close' });
292
+ await userEvent.click(button);
267
293
 
268
294
  expect(onDismiss).toHaveBeenCalledTimes(1);
269
295
  });
@@ -478,7 +504,7 @@ describe('Alert', () => {
478
504
  });
479
505
  });
480
506
 
481
- describe('`active` prop', () => {
507
+ describe('`active` prop backward compatibility', () => {
482
508
  it('should render wrapper and alert if `active` is unset', () => {
483
509
  render(<Alert message={message} />);
484
510
  expect(screen.getByRole('status')).toBeInTheDocument();
@@ -492,13 +518,9 @@ describe('Alert', () => {
492
518
  });
493
519
 
494
520
  it('should render wrapper and alert if `active` is set', () => {
495
- render(<Alert message={message} />);
521
+ render(<Alert message={message} active />);
496
522
  expect(screen.getByRole('status')).toBeInTheDocument();
497
523
  expect(screen.getByText(message)).toBeInTheDocument();
498
524
  });
499
525
  });
500
-
501
- describe('dynamicRender', () => {
502
- it('should', () => {});
503
- });
504
526
  });
@@ -12,10 +12,8 @@ export default {
12
12
  title: 'Feedback/Alert',
13
13
  args: {
14
14
  type: Sentiment.POSITIVE,
15
- dynamicRender: false,
16
15
  message:
17
16
  'Payments sent to your bank details **today** might not arrive in time for the holidays.',
18
- active: true,
19
17
  },
20
18
  argTypes: {
21
19
  arrow: {
@@ -107,35 +105,43 @@ export const WithTitle: Story = {
107
105
  };
108
106
 
109
107
  /**
110
- * For ARIA live region to function correctly with screen readers,
108
+ * For ARIA live region to function correctly with screen readers (SR),
111
109
  * the container with an appropriate ARIA role (in the case of this
112
110
  * component, it's `status` or `alert`) must be rendered first.
113
111
  * Once present in the accessibility tree (AT), its dynamic contents
114
112
  * will be announced correctly.
115
113
  *
116
114
  * It's not a problem if your page includes an Alert that is initially
117
- * visible upon the very first render, but if it appears as a result
118
- * of some interaction or is async, e.g. BE-driven then the screen
119
- * reader might not announce it correctly.
115
+ * visible at the very first render, but if it appears as a result of
116
+ * user interaction or is async, e.g. BE-driven, then the screen reader
117
+ * might not announce it correctly. It becomes even more tricky if an
118
+ * Alert is triggerred by a component that causes refocusing or introduces
119
+ * many modifications to the AT, such as MoneyInput, for example, as that
120
+ * entirely hijacks screen-reader's attention and prevents it from
121
+ * announcing live region changes.
120
122
  *
121
- * In order for this to work, we've introduced a small delay (175ms)
122
- * between mounting the Alert's live region wrapper and actual DOM
123
- * content injection to allow screen readers to process it correctly.
124
- * It also to compensates for focus and AT manipulations commonly
125
- * introduced by more complex triggers like e.g. `SelectInput`,
126
- * which prevent the screen readers from announcing simultaneous
127
- * changes on the page.
123
+ * In order for this to work, we've introduced a simple mechanism which
124
+ * overcomes all of the above:
125
+ * 1. On the component mount, we render the live region wrapper.
126
+ * 2. We also render the actual Alert but set `aria-hidden` on it.
127
+ * 3. After 175ms, which has been confirmed as sufficient delay for
128
+ * the SRs to become responsive after trigger's activity, we remove the
129
+ * `aria-hidden` attribute, which, in turn, initiates the announcements.
128
130
  *
129
- * In those cases you should enable the `dynamicRender` prop.
131
+ * **NB:** While this approach has no visual side-effects (e.g. layout
132
+ * shift), it makes the Alert hidden from the assistive tech for 175ms.
133
+ * It's our belief that such short pause between announcements is
134
+ * negligible for the user.
130
135
  */
131
136
  export const DynamicRender: Story = {
132
137
  render: function Render(args) {
133
- const [isActive, setIsActive] = useState(false);
138
+ const [isOneActive, setIsOneActive] = useState(false);
139
+ const [isTwoActive, setIsTwoActive] = useState(false);
134
140
  const [value, setValue] = useState<string>();
135
141
 
136
142
  return (
137
143
  <>
138
- <Button htmlType="button" onClick={() => setIsActive((current) => !current)}>
144
+ <Button htmlType="button" onClick={() => setIsOneActive(true)}>
139
145
  Trigger Alert
140
146
  </Button>
141
147
 
@@ -146,11 +152,16 @@ export const DynamicRender: Story = {
146
152
  { type: 'option', value: 'two' },
147
153
  ]}
148
154
  onChange={setValue}
149
- onClose={() => setIsActive(value === 'two')}
155
+ onClose={() => setIsTwoActive(value === 'two')}
150
156
  />
151
157
  </Field>
152
158
 
153
- {isActive && <Alert {...args} dynamicRender className="m-t-5" />}
159
+ {isOneActive && <Alert {...args}>This Alert has a simple trigger.</Alert>}
160
+ {isTwoActive && (
161
+ <Alert {...args} type={Sentiment.WARNING}>
162
+ This Alert has a complex trigger.
163
+ </Alert>
164
+ )}
154
165
  </>
155
166
  );
156
167
  },
@@ -159,27 +170,33 @@ export const DynamicRender: Story = {
159
170
  source: {
160
171
  code: `
161
172
  function Render(args) {
162
- const [isActive, setIsActive] = useState(false);
163
- const [value, setValue] = useState<string>();
173
+ const [isOneActive, setIsOneActive] = useState(false);
174
+ const [isTwoActive, setIsTwoActive] = useState(false);
175
+ const [value, setValue] = useState<string>();
164
176
 
165
177
  return (
166
178
  <>
167
- <Button htmlType="button" onClick={() => setIsActive((current) => !current)}>
179
+ <Button htmlType="button" onClick={() => setIsOneActive(true)}>
168
180
  Trigger Alert
169
181
  </Button>
170
182
 
171
- <Field label="Select 'two' to triger Alert" className="m-t-3">
183
+ <Field label="Select \`two\` to triger Alert" className="m-t-3">
172
184
  <SelectInput
173
185
  items={[
174
186
  { type: 'option', value: 'one' },
175
187
  { type: 'option', value: 'two' },
176
188
  ]}
177
189
  onChange={setValue}
178
- onClose={() => setIsActive(value === 'two')}
190
+ onClose={() => setIsTwoActive(value === 'two')}
179
191
  />
180
192
  </Field>
181
-
182
- {isActive && <Alert {...args} dynamicRender className="m-t-5" />}
193
+
194
+ {isOneActive && <Alert {...args}>This Alert has a simple trigger.</Alert>}
195
+ {isTwoActive && (
196
+ <Alert {...args} type={Sentiment.WARNING}>
197
+ This Alert has a complex trigger.
198
+ </Alert>
199
+ )}
183
200
  </>
184
201
  );
185
202
  }`,
@@ -210,16 +227,15 @@ const getAlerts = () => {
210
227
  };
211
228
 
212
229
  /**
213
- * When dealing with Backend for Frontend (BFF), sometimes it's not
214
- * just about conditional rendering but also the fact you don't
215
- * know how many `Alert`s you're going to get and when.
216
- *
217
- * `dynamicRender` prop will work with those scenarios as well.
230
+ * Sometimes, especially when dealing with Backend for Frontend (BFF),
231
+ * it's not just about conditional rendering but also the fact that
232
+ * you don't know how many `Alert`s you'll need to render.
218
233
  *
219
- * **NB:** Screen readers do not guarantee the order in which they
220
- * read out multiple injected sibling nodes.
234
+ * If you have to inject multiple Alerts simultaneously into the page,
235
+ * please note that we currently have no means of guaranteeing the order
236
+ * in which they screen reader will read them out.
221
237
  */
222
- export const BackendForFrontend: Story = {
238
+ export const MultipleDynamicAlerts: Story = {
223
239
  render: function Render() {
224
240
  const [isActive, setIsActive] = useState(false);
225
241
  const [alerts, setAlerts] = useState<AlertProps[]>([]);
@@ -237,7 +253,7 @@ export const BackendForFrontend: Story = {
237
253
  </Button>
238
254
 
239
255
  {alerts.map((props) => (
240
- <Alert {...props} key={props.title} dynamicRender className="m-t-3" />
256
+ <Alert {...props} key={props.title} className="m-t-3" />
241
257
  ))}
242
258
  </>
243
259
  );
@@ -267,7 +283,6 @@ function Render() {
267
283
  {...props}
268
284
  key={props.title}
269
285
  className="m-t-3"
270
- dynamicRender
271
286
  />
272
287
  ))}
273
288
  </>
@@ -64,17 +64,9 @@ export interface AlertProps {
64
64
  /** The type dictates which icon and colour will be used */
65
65
  type?: AlertType;
66
66
  variant?: `${Variant}`;
67
- /**
68
- * Controls rendering of the Alert component. <br />
69
- * Toggle this prop instead using conditional rendering and logical AND (&&)
70
- * operator, to make the component work with screen readers.
71
- * @deprecated use `dynamicRender`
72
- * */
67
+ /** @deprecated Safe to remove */
73
68
  active?: boolean;
74
- /**
75
- * Toggle this prop when dealing with multiple, dynamic Alerts, to make them
76
- * work with screen readers. This is especially helpful for the BFF use cases.
77
- * */
69
+ /** @deprecated Safe to remove */
78
70
  dynamicRender?: boolean;
79
71
  /** @deprecated Use `InlineAlert` instead. */
80
72
  arrow?: `${AlertArrowPosition}`;
@@ -100,22 +92,30 @@ function resolveType(type: AlertType): AlertTypeResolved {
100
92
  }
101
93
 
102
94
  export default function Alert({
103
- arrow,
104
95
  action,
105
- children,
106
96
  className,
107
- dismissible,
108
97
  icon,
109
98
  statusIconLabel,
110
99
  onDismiss,
111
100
  message,
112
- size,
113
101
  title,
114
102
  type = 'neutral',
115
103
  variant = 'desktop',
104
+ arrow,
105
+ children,
106
+ size,
107
+ dismissible,
116
108
  active = true,
117
- dynamicRender = false,
109
+ dynamicRender,
118
110
  }: AlertProps) {
111
+ useEffect(() => {
112
+ if (active !== undefined || dynamicRender !== undefined) {
113
+ logActionRequired(
114
+ "Alert component doesn't support `active` or `dynamicRender` anymore, please remove that prop.",
115
+ );
116
+ }
117
+ }, [active, dynamicRender]);
118
+
119
119
  useEffect(() => {
120
120
  if (arrow !== undefined) {
121
121
  logActionRequired(
@@ -157,14 +157,12 @@ export default function Alert({
157
157
 
158
158
  const [shouldFire, setShouldFire] = useState<boolean>();
159
159
 
160
- const [shouldShow, setShouldShow] = useState<boolean>();
160
+ const [shouldAnnounce, setShouldAnnounce] = useState<boolean>(false);
161
161
  useEffect(() => {
162
- if ((shouldShow === undefined && !dynamicRender) || !active) {
163
- setShouldShow(active);
164
- } else {
165
- setTimeout(() => setShouldShow(active), WDS_LIVE_REGION_DELAY_MS);
166
- }
167
- }, [active, shouldShow, dynamicRender]);
162
+ setTimeout(() => {
163
+ setShouldAnnounce(true);
164
+ }, WDS_LIVE_REGION_DELAY_MS);
165
+ }, []);
168
166
 
169
167
  const closeButtonReference = useRef<HTMLButtonElement>(null);
170
168
 
@@ -173,8 +171,9 @@ export default function Alert({
173
171
  role={resolvedType === Sentiment.NEGATIVE ? 'alert' : 'status'}
174
172
  className="wds-alert__liveRegion"
175
173
  >
176
- {shouldShow && (
174
+ {active && (
177
175
  <div
176
+ aria-hidden={shouldAnnounce ? undefined : 'true'}
178
177
  className={clsx(
179
178
  'alert d-flex',
180
179
  `alert-${resolvedType}`,
@@ -2,17 +2,26 @@
2
2
  }@media (min-width: 768px) {
3
3
  }.tw-avatar {
4
4
  position: relative;
5
+ border-radius: 50%;
5
6
  -webkit-user-select: none;
6
7
  -moz-user-select: none;
7
8
  user-select: none;
8
9
  box-sizing: border-box;
9
10
  }.tw-avatar .tw-avatar__content {
11
+ align-items: center;
10
12
  background-color: rgba(134,167,189,0.10196);
11
13
  background-color: #86a7bd1a;
12
14
  background-color: var(--color-background-neutral);
15
+ border-radius: 50%;
16
+ color: #37517e;
17
+ color: var(--color-content-primary);
18
+ display: flex;
19
+ height: 100%;
20
+ justify-content: center;
13
21
  max-height: 100%;
14
22
  max-width: 100%;
15
23
  overflow: hidden;
24
+ width: 100%;
16
25
  }.tw-avatar--outlined {
17
26
  border: 1px solid #00a2dd;
18
27
  border: 1px solid var(--color-interactive-accent);
@@ -46,6 +55,10 @@
46
55
  font-feature-settings: "ss01";
47
56
  color: var(--color-dark-charcoal);
48
57
  line-height: 1;
58
+ }.tw-avatar--24 {
59
+ min-width: 24px;
60
+ width: 24px;
61
+ height: 24px;
49
62
  }.tw-avatar--24.tw-avatar--emoji,
50
63
  .tw-avatar--24.tw-avatar--icon {
51
64
  font-size: 12px;
@@ -70,6 +83,10 @@
70
83
  border: 1px solid var(--color-border-overlay);
71
84
  content: "";
72
85
  border-radius: 50%;
86
+ }.tw-avatar--40 {
87
+ min-width: 40px;
88
+ width: 40px;
89
+ height: 40px;
73
90
  }.tw-avatar--40.tw-avatar--emoji,
74
91
  .tw-avatar--40.tw-avatar--icon {
75
92
  font-size: 20px;
@@ -94,6 +111,10 @@
94
111
  border: 1px solid var(--color-border-overlay);
95
112
  content: "";
96
113
  border-radius: 50%;
114
+ }.tw-avatar--48 {
115
+ min-width: 48px;
116
+ width: 48px;
117
+ height: 48px;
97
118
  }.tw-avatar--48.tw-avatar--emoji,
98
119
  .tw-avatar--48.tw-avatar--icon {
99
120
  font-size: 24px;
@@ -118,6 +139,10 @@
118
139
  border: 1px solid var(--color-border-overlay);
119
140
  content: "";
120
141
  border-radius: 50%;
142
+ }.tw-avatar--56 {
143
+ min-width: 56px;
144
+ width: 56px;
145
+ height: 56px;
121
146
  }.tw-avatar--56.tw-avatar--emoji,
122
147
  .tw-avatar--56.tw-avatar--icon {
123
148
  font-size: 28px;
@@ -142,6 +167,10 @@
142
167
  border: 1px solid var(--color-border-overlay);
143
168
  content: "";
144
169
  border-radius: 50%;
170
+ }.tw-avatar--72 {
171
+ min-width: 72px;
172
+ width: 72px;
173
+ height: 72px;
145
174
  }.tw-avatar--72.tw-avatar--emoji,
146
175
  .tw-avatar--72.tw-avatar--icon {
147
176
  font-size: 36px;
@@ -10,15 +10,23 @@
10
10
 
11
11
  .tw-avatar {
12
12
  position: relative;
13
+ border-radius: 50%;
13
14
  user-select: none;
14
15
  box-sizing: border-box;
15
16
 
16
17
  & &__content {
18
+ align-items: center;
17
19
  background-color: #86a7bd1a;
18
20
  background-color: var(--color-background-neutral);
21
+ border-radius: 50%;
22
+ color: var(--color-content-primary);
23
+ display: flex;
24
+ height: 100%;
25
+ justify-content: center;
19
26
  max-height: 100%;
20
27
  max-width: 100%;
21
28
  overflow: hidden;
29
+ width: 100%;
22
30
  }
23
31
 
24
32
  &--outlined {
@@ -109,6 +117,10 @@
109
117
  .avatar-dimension(@avatar-size) {
110
118
  @mask-size: 2px;
111
119
 
120
+ min-width: @avatar-size;
121
+ width: @avatar-size;
122
+ height: @avatar-size;
123
+
112
124
  &.tw-avatar--emoji,
113
125
  &.tw-avatar--icon {
114
126
  font-size: @avatar-size * 0.5;