@transferwise/components 0.0.0-experimental-537d2bc → 0.0.0-experimental-e093446
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.
- package/build/alert/Alert.js +1 -1
- package/build/alert/Alert.mjs +1 -1
- package/build/common/liveRegion/LiveRegion.js +42 -0
- package/build/common/liveRegion/LiveRegion.js.map +1 -0
- package/build/common/liveRegion/LiveRegion.mjs +40 -0
- package/build/common/liveRegion/LiveRegion.mjs.map +1 -0
- package/build/flowNavigation/FlowNavigation.js +1 -1
- package/build/flowNavigation/FlowNavigation.mjs +1 -1
- package/build/index.js +4 -4
- package/build/inputs/SelectInput.mjs +1 -1
- package/build/overlayHeader/OverlayHeader.js +1 -1
- package/build/overlayHeader/OverlayHeader.mjs +1 -1
- package/build/popover/Popover.mjs +1 -1
- package/build/promoCard/PromoCardContext.mjs +1 -1
- package/build/prompt/ActionPrompt/ActionPrompt.js +3 -0
- package/build/prompt/ActionPrompt/ActionPrompt.js.map +1 -1
- package/build/prompt/ActionPrompt/ActionPrompt.mjs +1 -1
- package/build/prompt/InfoPrompt/InfoPrompt.js +35 -29
- package/build/prompt/InfoPrompt/InfoPrompt.js.map +1 -1
- package/build/prompt/InfoPrompt/InfoPrompt.mjs +35 -29
- package/build/prompt/InfoPrompt/InfoPrompt.mjs.map +1 -1
- package/build/types/common/index.d.ts +2 -0
- package/build/types/common/index.d.ts.map +1 -1
- package/build/types/common/liveRegion/LiveRegion.d.ts +23 -0
- package/build/types/common/liveRegion/LiveRegion.d.ts.map +1 -0
- package/build/types/common/liveRegion/index.d.ts +3 -0
- package/build/types/common/liveRegion/index.d.ts.map +1 -0
- package/build/types/prompt/InfoPrompt/InfoPrompt.d.ts +11 -2
- package/build/types/prompt/InfoPrompt/InfoPrompt.d.ts.map +1 -1
- package/package.json +27 -27
- package/src/common/index.ts +2 -0
- package/src/common/liveRegion/LiveRegion.test.tsx +56 -0
- package/src/common/liveRegion/LiveRegion.tsx +49 -0
- package/src/common/liveRegion/index.ts +2 -0
- package/src/prompt/InfoPrompt/InfoPrompt.test.story.tsx +119 -0
- package/src/prompt/InfoPrompt/InfoPrompt.tsx +47 -34
package/build/alert/Alert.js
CHANGED
|
@@ -26,13 +26,13 @@ require('../common/propsValues/markdownNodeType.js');
|
|
|
26
26
|
require('../common/fileType.js');
|
|
27
27
|
var constants = require('../common/constants.js');
|
|
28
28
|
var CloseButton = require('../common/closeButton/CloseButton.js');
|
|
29
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
29
30
|
var StatusIcon = require('../statusIcon/StatusIcon.js');
|
|
30
31
|
var Title = require('../title/Title.js');
|
|
31
32
|
var logActionRequired = require('../utilities/logActionRequired.js');
|
|
32
33
|
var InlineMarkdown = require('./inlineMarkdown/InlineMarkdown.js');
|
|
33
34
|
var Button_resolver = require('../button/Button.resolver.js');
|
|
34
35
|
var Link = require('../link/Link.js');
|
|
35
|
-
var jsxRuntime = require('react/jsx-runtime');
|
|
36
36
|
|
|
37
37
|
exports.AlertArrowPosition = void 0;
|
|
38
38
|
(function (AlertArrowPosition) {
|
package/build/alert/Alert.mjs
CHANGED
|
@@ -22,13 +22,13 @@ import '../common/propsValues/markdownNodeType.mjs';
|
|
|
22
22
|
import '../common/fileType.mjs';
|
|
23
23
|
import { WDS_LIVE_REGION_DELAY_MS } from '../common/constants.mjs';
|
|
24
24
|
import { CloseButton } from '../common/closeButton/CloseButton.mjs';
|
|
25
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
25
26
|
import StatusIcon from '../statusIcon/StatusIcon.mjs';
|
|
26
27
|
import Title from '../title/Title.mjs';
|
|
27
28
|
import { logActionRequired } from '../utilities/logActionRequired.mjs';
|
|
28
29
|
import InlineMarkdown from './inlineMarkdown/InlineMarkdown.mjs';
|
|
29
30
|
import Button from '../button/Button.resolver.mjs';
|
|
30
31
|
import Link from '../link/Link.mjs';
|
|
31
|
-
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
32
32
|
|
|
33
33
|
var AlertArrowPosition;
|
|
34
34
|
(function (AlertArrowPosition) {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
|
|
5
|
+
const ARIA_LIVE_ROLE_MAP = {
|
|
6
|
+
assertive: 'alert',
|
|
7
|
+
polite: 'status'
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Renders an ARIA live region with the correct implicit role.
|
|
11
|
+
*
|
|
12
|
+
* - `aria-live="polite"` → `role="status"`
|
|
13
|
+
* - `aria-live="assertive"` → `role="alert"`
|
|
14
|
+
* - `aria-live="off"` → no live region
|
|
15
|
+
*
|
|
16
|
+
* The `role` prop is intentionally excluded from the public API
|
|
17
|
+
* to prevent mismatches between `aria-live` and `role`.
|
|
18
|
+
*/
|
|
19
|
+
const LiveRegion = ({
|
|
20
|
+
'aria-live': ariaLive,
|
|
21
|
+
children,
|
|
22
|
+
...props
|
|
23
|
+
}) => {
|
|
24
|
+
if (ariaLive === 'off') {
|
|
25
|
+
return /*#__PURE__*/jsxRuntime.jsx(jsxRuntime.Fragment, {
|
|
26
|
+
children: children
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return /*#__PURE__*/jsxRuntime.jsx("div", {
|
|
30
|
+
role: ARIA_LIVE_ROLE_MAP[ariaLive],
|
|
31
|
+
"aria-live": ariaLive,
|
|
32
|
+
"aria-atomic": "true",
|
|
33
|
+
style: {
|
|
34
|
+
display: 'contents'
|
|
35
|
+
},
|
|
36
|
+
...props,
|
|
37
|
+
children: children
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
exports.LiveRegion = LiveRegion;
|
|
42
|
+
//# sourceMappingURL=LiveRegion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LiveRegion.js","sources":["../../../src/common/liveRegion/LiveRegion.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from 'react';\n\nconst ARIA_LIVE_ROLE_MAP = {\n assertive: 'alert',\n polite: 'status',\n} as const;\n\nexport type AriaLive = 'off' | 'polite' | 'assertive';\n\nexport interface LiveRegionProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n 'role' | 'aria-live' | 'aria-atomic'\n> {\n /**\n * Determines urgency: 'assertive' interrupts, 'polite' waits for idle, 'off' disables live region.\n */\n 'aria-live': AriaLive;\n /** Test ID for testing tools */\n 'data-testid'?: string;\n children?: ReactNode;\n}\n\n/**\n * Renders an ARIA live region with the correct implicit role.\n *\n * - `aria-live=\"polite\"` → `role=\"status\"`\n * - `aria-live=\"assertive\"` → `role=\"alert\"`\n * - `aria-live=\"off\"` → no live region\n *\n * The `role` prop is intentionally excluded from the public API\n * to prevent mismatches between `aria-live` and `role`.\n */\nexport const LiveRegion = ({ 'aria-live': ariaLive, children, ...props }: LiveRegionProps) => {\n if (ariaLive === 'off') {\n return <>{children}</>;\n }\n\n return (\n <div\n role={ARIA_LIVE_ROLE_MAP[ariaLive]}\n aria-live={ariaLive}\n aria-atomic=\"true\"\n style={{ display: 'contents' }}\n {...props}\n >\n {children}\n </div>\n );\n};\n"],"names":["ARIA_LIVE_ROLE_MAP","assertive","polite","LiveRegion","ariaLive","children","props","_jsx","_Fragment","role","style","display"],"mappings":";;;;AAEA,MAAMA,kBAAkB,GAAG;AACzBC,EAAAA,SAAS,EAAE,OAAO;AAClBC,EAAAA,MAAM,EAAE;CACA;AAiBV;;;;;;;;;AASG;AACI,MAAMC,UAAU,GAAGA,CAAC;AAAE,EAAA,WAAW,EAAEC,QAAQ;EAAEC,QAAQ;EAAE,GAAGC;AAAK,CAAmB,KAAI;EAC3F,IAAIF,QAAQ,KAAK,KAAK,EAAE;IACtB,oBAAOG,cAAA,CAAAC,mBAAA,EAAA;AAAAH,MAAAA,QAAA,EAAGA;AAAQ,MAAI;AACxB,EAAA;AAEA,EAAA,oBACEE,cAAA,CAAA,KAAA,EAAA;AACEE,IAAAA,IAAI,EAAET,kBAAkB,CAACI,QAAQ,CAAE;AACnC,IAAA,WAAA,EAAWA,QAAS;AACpB,IAAA,aAAA,EAAY,MAAM;AAClBM,IAAAA,KAAK,EAAE;AAAEC,MAAAA,OAAO,EAAE;KAAa;AAAA,IAAA,GAC3BL,KAAK;AAAAD,IAAAA,QAAA,EAERA;AAAQ,GACN,CAAC;AAEV;;;;"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
const ARIA_LIVE_ROLE_MAP = {
|
|
4
|
+
assertive: 'alert',
|
|
5
|
+
polite: 'status'
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Renders an ARIA live region with the correct implicit role.
|
|
9
|
+
*
|
|
10
|
+
* - `aria-live="polite"` → `role="status"`
|
|
11
|
+
* - `aria-live="assertive"` → `role="alert"`
|
|
12
|
+
* - `aria-live="off"` → no live region
|
|
13
|
+
*
|
|
14
|
+
* The `role` prop is intentionally excluded from the public API
|
|
15
|
+
* to prevent mismatches between `aria-live` and `role`.
|
|
16
|
+
*/
|
|
17
|
+
const LiveRegion = ({
|
|
18
|
+
'aria-live': ariaLive,
|
|
19
|
+
children,
|
|
20
|
+
...props
|
|
21
|
+
}) => {
|
|
22
|
+
if (ariaLive === 'off') {
|
|
23
|
+
return /*#__PURE__*/jsx(Fragment, {
|
|
24
|
+
children: children
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return /*#__PURE__*/jsx("div", {
|
|
28
|
+
role: ARIA_LIVE_ROLE_MAP[ariaLive],
|
|
29
|
+
"aria-live": ariaLive,
|
|
30
|
+
"aria-atomic": "true",
|
|
31
|
+
style: {
|
|
32
|
+
display: 'contents'
|
|
33
|
+
},
|
|
34
|
+
...props,
|
|
35
|
+
children: children
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export { LiveRegion };
|
|
40
|
+
//# sourceMappingURL=LiveRegion.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LiveRegion.mjs","sources":["../../../src/common/liveRegion/LiveRegion.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from 'react';\n\nconst ARIA_LIVE_ROLE_MAP = {\n assertive: 'alert',\n polite: 'status',\n} as const;\n\nexport type AriaLive = 'off' | 'polite' | 'assertive';\n\nexport interface LiveRegionProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n 'role' | 'aria-live' | 'aria-atomic'\n> {\n /**\n * Determines urgency: 'assertive' interrupts, 'polite' waits for idle, 'off' disables live region.\n */\n 'aria-live': AriaLive;\n /** Test ID for testing tools */\n 'data-testid'?: string;\n children?: ReactNode;\n}\n\n/**\n * Renders an ARIA live region with the correct implicit role.\n *\n * - `aria-live=\"polite\"` → `role=\"status\"`\n * - `aria-live=\"assertive\"` → `role=\"alert\"`\n * - `aria-live=\"off\"` → no live region\n *\n * The `role` prop is intentionally excluded from the public API\n * to prevent mismatches between `aria-live` and `role`.\n */\nexport const LiveRegion = ({ 'aria-live': ariaLive, children, ...props }: LiveRegionProps) => {\n if (ariaLive === 'off') {\n return <>{children}</>;\n }\n\n return (\n <div\n role={ARIA_LIVE_ROLE_MAP[ariaLive]}\n aria-live={ariaLive}\n aria-atomic=\"true\"\n style={{ display: 'contents' }}\n {...props}\n >\n {children}\n </div>\n );\n};\n"],"names":["ARIA_LIVE_ROLE_MAP","assertive","polite","LiveRegion","ariaLive","children","props","_jsx","_Fragment","role","style","display"],"mappings":";;AAEA,MAAMA,kBAAkB,GAAG;AACzBC,EAAAA,SAAS,EAAE,OAAO;AAClBC,EAAAA,MAAM,EAAE;CACA;AAiBV;;;;;;;;;AASG;AACI,MAAMC,UAAU,GAAGA,CAAC;AAAE,EAAA,WAAW,EAAEC,QAAQ;EAAEC,QAAQ;EAAE,GAAGC;AAAK,CAAmB,KAAI;EAC3F,IAAIF,QAAQ,KAAK,KAAK,EAAE;IACtB,oBAAOG,GAAA,CAAAC,QAAA,EAAA;AAAAH,MAAAA,QAAA,EAAGA;AAAQ,MAAI;AACxB,EAAA;AAEA,EAAA,oBACEE,GAAA,CAAA,KAAA,EAAA;AACEE,IAAAA,IAAI,EAAET,kBAAkB,CAACI,QAAQ,CAAE;AACnC,IAAA,WAAA,EAAWA,QAAS;AACpB,IAAA,aAAA,EAAY,MAAM;AAClBM,IAAAA,KAAK,EAAE;AAAEC,MAAAA,OAAO,EAAE;KAAa;AAAA,IAAA,GAC3BL,KAAK;AAAAD,IAAAA,QAAA,EAERA;AAAQ,GACN,CAAC;AAEV;;;;"}
|
|
@@ -24,6 +24,7 @@ require('../common/propsValues/scroll.js');
|
|
|
24
24
|
require('../common/propsValues/markdownNodeType.js');
|
|
25
25
|
require('../common/fileType.js');
|
|
26
26
|
var CloseButton = require('../common/closeButton/CloseButton.js');
|
|
27
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
27
28
|
var FlowHeader = require('../common/flowHeader/FlowHeader.js');
|
|
28
29
|
var Logo = require('../logo/Logo.js');
|
|
29
30
|
var Stepper = require('../stepper/Stepper.js');
|
|
@@ -32,7 +33,6 @@ var FlowNavigation_messages = require('./FlowNavigation.messages.js');
|
|
|
32
33
|
var AnimatedLabel = require('./animatedLabel/AnimatedLabel.js');
|
|
33
34
|
var IconButton = require('../iconButton/IconButton.js');
|
|
34
35
|
var icons = require('@transferwise/icons');
|
|
35
|
-
var jsxRuntime = require('react/jsx-runtime');
|
|
36
36
|
|
|
37
37
|
const FlowNavigation = ({
|
|
38
38
|
activeStep = 0,
|
|
@@ -20,6 +20,7 @@ import '../common/propsValues/scroll.mjs';
|
|
|
20
20
|
import '../common/propsValues/markdownNodeType.mjs';
|
|
21
21
|
import '../common/fileType.mjs';
|
|
22
22
|
import { CloseButton } from '../common/closeButton/CloseButton.mjs';
|
|
23
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
23
24
|
import FlowHeader from '../common/flowHeader/FlowHeader.mjs';
|
|
24
25
|
import Logo from '../logo/Logo.mjs';
|
|
25
26
|
import Stepper from '../stepper/Stepper.mjs';
|
|
@@ -28,7 +29,6 @@ import messages from './FlowNavigation.messages.mjs';
|
|
|
28
29
|
import AnimatedLabel from './animatedLabel/AnimatedLabel.mjs';
|
|
29
30
|
import IconButton from '../iconButton/IconButton.mjs';
|
|
30
31
|
import { ArrowLeft } from '@transferwise/icons';
|
|
31
|
-
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
32
32
|
|
|
33
33
|
const FlowNavigation = ({
|
|
34
34
|
activeStep = 0,
|
package/build/index.js
CHANGED
|
@@ -23,7 +23,7 @@ var Body = require('./body/Body.js');
|
|
|
23
23
|
var Button_resolver = require('./button/Button.resolver.js');
|
|
24
24
|
var IconButton = require('./iconButton/IconButton.js');
|
|
25
25
|
var Carousel = require('./carousel/Carousel.js');
|
|
26
|
-
var Card
|
|
26
|
+
var Card = require('./card/Card.js');
|
|
27
27
|
var Checkbox = require('./checkbox/Checkbox.js');
|
|
28
28
|
var CheckboxButton = require('./checkboxButton/CheckboxButton.js');
|
|
29
29
|
var CheckboxOption = require('./checkboxOption/CheckboxOption.js');
|
|
@@ -33,7 +33,7 @@ var Chip = require('./chips/Chip.js');
|
|
|
33
33
|
var CircularButton = require('./circularButton/CircularButton.js');
|
|
34
34
|
var Option = require('./common/Option/Option.js');
|
|
35
35
|
var BottomSheet = require('./common/bottomSheet/BottomSheet.js');
|
|
36
|
-
var Card = require('./common/card/Card.js');
|
|
36
|
+
var Card$1 = require('./common/card/Card.js');
|
|
37
37
|
var CriticalCommsBanner = require('./criticalBanner/CriticalCommsBanner.js');
|
|
38
38
|
var DateInput = require('./dateInput/DateInput.js');
|
|
39
39
|
var DateLookup = require('./dateLookup/DateLookup.js');
|
|
@@ -173,7 +173,7 @@ exports.Body = Body.default;
|
|
|
173
173
|
exports.Button = Button_resolver.default;
|
|
174
174
|
exports.IconButton = IconButton.default;
|
|
175
175
|
exports.Carousel = Carousel.default;
|
|
176
|
-
exports.Card = Card
|
|
176
|
+
exports.Card = Card.default;
|
|
177
177
|
exports.Checkbox = Checkbox.default;
|
|
178
178
|
exports.CheckboxButton = CheckboxButton.default;
|
|
179
179
|
exports.CheckboxOption = CheckboxOption.default;
|
|
@@ -183,7 +183,7 @@ exports.Chip = Chip.default;
|
|
|
183
183
|
exports.CircularButton = CircularButton.default;
|
|
184
184
|
exports.Option = Option.default;
|
|
185
185
|
exports.BottomSheet = BottomSheet.default;
|
|
186
|
-
exports.BaseCard = Card.default;
|
|
186
|
+
exports.BaseCard = Card$1.default;
|
|
187
187
|
exports.CriticalCommsBanner = CriticalCommsBanner.default;
|
|
188
188
|
exports.DateInput = DateInput.default;
|
|
189
189
|
exports.DateLookup = DateLookup.default;
|
|
@@ -2,7 +2,7 @@ import { Listbox, ListboxOptions, ListboxButton, ListboxOption } from '@headless
|
|
|
2
2
|
import { CrossCircle, ChevronDown, Cross, Check } from '@transferwise/icons';
|
|
3
3
|
import { clsx } from 'clsx';
|
|
4
4
|
import mergeProps from 'merge-props';
|
|
5
|
-
import { useState, useRef, useEffect, useDeferredValue, useContext, useMemo, useId,
|
|
5
|
+
import { useState, useRef, useEffect, useDeferredValue, createContext, useContext, useMemo, useId, forwardRef } from 'react';
|
|
6
6
|
import { useIntl } from 'react-intl';
|
|
7
7
|
import { Virtualizer } from 'virtua';
|
|
8
8
|
import { useEffectEvent } from '../common/hooks/useEffectEvent.mjs';
|
|
@@ -23,9 +23,9 @@ require('../common/propsValues/scroll.js');
|
|
|
23
23
|
require('../common/propsValues/markdownNodeType.js');
|
|
24
24
|
require('../common/fileType.js');
|
|
25
25
|
var CloseButton = require('../common/closeButton/CloseButton.js');
|
|
26
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
26
27
|
var FlowHeader = require('../common/flowHeader/FlowHeader.js');
|
|
27
28
|
var Logo = require('../logo/Logo.js');
|
|
28
|
-
var jsxRuntime = require('react/jsx-runtime');
|
|
29
29
|
|
|
30
30
|
const defaultLogo = /*#__PURE__*/jsxRuntime.jsx(Logo.default, {});
|
|
31
31
|
function OverlayHeader({
|
|
@@ -19,9 +19,9 @@ import '../common/propsValues/scroll.mjs';
|
|
|
19
19
|
import '../common/propsValues/markdownNodeType.mjs';
|
|
20
20
|
import '../common/fileType.mjs';
|
|
21
21
|
import { CloseButton } from '../common/closeButton/CloseButton.mjs';
|
|
22
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
22
23
|
import FlowHeader from '../common/flowHeader/FlowHeader.mjs';
|
|
23
24
|
import Logo from '../logo/Logo.mjs';
|
|
24
|
-
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
25
25
|
|
|
26
26
|
const defaultLogo = /*#__PURE__*/jsx(Logo, {});
|
|
27
27
|
function OverlayHeader({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { clsx } from 'clsx';
|
|
2
|
-
import { useId, useEffect, useRef, useState,
|
|
2
|
+
import { useId, useEffect, useRef, useState, cloneElement, isValidElement } from 'react';
|
|
3
3
|
import '../common/theme.mjs';
|
|
4
4
|
import '../common/direction.mjs';
|
|
5
5
|
import '../common/propsValues/control.mjs';
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
3
5
|
var React = require('react');
|
|
4
6
|
var clsx = require('clsx');
|
|
5
7
|
var StatusIcon = require('../../statusIcon/StatusIcon.js');
|
|
@@ -142,4 +144,5 @@ const ActionPrompt = ({
|
|
|
142
144
|
};
|
|
143
145
|
|
|
144
146
|
exports.ActionPrompt = ActionPrompt;
|
|
147
|
+
exports.default = ActionPrompt;
|
|
145
148
|
//# sourceMappingURL=ActionPrompt.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActionPrompt.js","sources":["../../../src/prompt/ActionPrompt/ActionPrompt.tsx"],"sourcesContent":["import { AriaAttributes, ReactNode, useId } from 'react';\nimport { clsx } from 'clsx';\n\nimport StatusIcon from '../../statusIcon';\nimport Body from '../../body';\nimport Button from '../../button';\nimport { Breakpoint, Typography } from '../../common';\nimport AvatarView, { AvatarViewProps } from '../../avatarView';\nimport Image from '../../image';\nimport { ButtonProps } from '../../button/Button.types';\nimport { PrimitivePrompt, PrimitivePromptProps } from '../PrimitivePrompt';\nimport { BadgeAssetsProps } from '../../badge';\nimport { GiftBox } from '@transferwise/icons';\nimport { useScreenSize } from '../../common/hooks/useScreenSize';\n\nexport type ActionPromptProps = {\n title: ReactNode;\n description?: ReactNode;\n media?: {\n imgSrc?: string;\n avatar?: Pick<AvatarViewProps, 'imgSrc' | 'profileName' | 'profileType'> & {\n asset?: AvatarViewProps['children'];\n badge?: Pick<BadgeAssetsProps, 'flagCode'>;\n };\n 'aria-label'?: string;\n 'aria-hidden'?: boolean;\n };\n action: Pick<ButtonProps, 'onClick' | 'href' | 'target'> & {\n label: ButtonProps['children'];\n };\n actionSecondary?: Pick<ButtonProps, 'onClick' | 'href' | 'target'> & {\n label: ButtonProps['children'];\n };\n 'aria-label'?: AriaAttributes['aria-label'];\n} & Pick<PrimitivePromptProps, 'id' | 'className' | 'data-testid' | 'sentiment' | 'onDismiss'>;\n\n/**\n * Use an action prompt for optional feedback that doesn't require immediate action, such as feature upsells, warnings, or suggestions. These prompts are typically used outside of core product flows (e.g., Launchpad, Recipient, or Transaction screens) and can be addressed at the user's convenience.\n *\n * If your message is about immediate user feedback (e.g., form submission errors, download failures, missing data warnings), use an [info prompt](https://storybook.wise.design/?path=/docs/prompts-infoprompt--docs) instead.\n *\n * Guidance can be found in the [design system](https://wise.design/components/action-prompt).\n */\nexport const ActionPrompt = ({\n sentiment = 'neutral',\n title,\n description,\n onDismiss,\n media = {},\n action,\n actionSecondary,\n id,\n className,\n 'data-testid': testId,\n 'aria-label': ariaLabel,\n}: ActionPromptProps) => {\n const isMobile = !useScreenSize(Breakpoint.MEDIUM);\n\n const mediaId = useId();\n const titleId = useId();\n const descId = useId();\n\n const ariaLabelledByIds = [\n media['aria-hidden'] ? undefined : mediaId,\n ariaLabel ? undefined : titleId,\n ]\n .filter(Boolean)\n .join(' ');\n\n const renderMedia = () => {\n if (media?.imgSrc) {\n return (\n <Image\n id={mediaId}\n src={media.imgSrc}\n className=\"wds-action-prompt--media-image\"\n alt={media['aria-label'] ?? ''}\n />\n );\n }\n if (media?.avatar) {\n const badge = media.avatar.badge\n ? media.avatar.badge\n : sentiment === 'proposition'\n ? {}\n : { status: sentiment };\n return (\n <AvatarView\n {...media.avatar}\n badge={badge}\n aria-label={media['aria-label']}\n aria-hidden={media['aria-hidden']}\n id={mediaId}\n size={48}\n >\n {media.avatar.asset}\n </AvatarView>\n );\n }\n return sentiment === 'proposition' ? (\n <AvatarView\n id={mediaId}\n size={48}\n aria-label={media['aria-label']}\n aria-hidden={media['aria-hidden']}\n >\n <GiftBox />\n </AvatarView>\n ) : (\n <StatusIcon\n id={mediaId}\n size={48}\n sentiment={sentiment}\n iconLabel={media['aria-hidden'] ? null : media['aria-label']}\n />\n );\n };\n\n return (\n <PrimitivePrompt\n id={id}\n sentiment={sentiment}\n data-testid={testId}\n className={clsx(\n 'wds-action-prompt',\n { 'wds-action-prompt--with-two-actions': !!actionSecondary },\n className,\n )}\n media={renderMedia()}\n actions={\n <>\n {actionSecondary && (\n // @ts-expect-error onClick type mismatch\n <Button\n v2\n size=\"md\"\n priority=\"secondary\"\n href={actionSecondary.href}\n block={isMobile}\n onClick={actionSecondary?.onClick}\n >\n {actionSecondary.label}\n </Button>\n )}\n {/* @ts-expect-error onClick type mismatch */}\n <Button\n v2\n size=\"md\"\n priority=\"primary\"\n href={action.href}\n block={isMobile}\n onClick={action.onClick}\n >\n {action.label}\n </Button>\n </>\n }\n role=\"region\"\n onDismiss={onDismiss}\n {...(ariaLabel\n ? { 'aria-label': ariaLabel }\n : {\n 'aria-labelledby': ariaLabelledByIds,\n 'aria-describedby': descId,\n })}\n >\n <div className={clsx('d-flex', 'flex-column', 'justify-content-center')}>\n <Body id={titleId} type={Typography.BODY_LARGE_BOLD} className=\"wds-action-prompt__content\">\n {title}\n </Body>\n {description && (\n <Body id={descId} className=\"wds-action-prompt__content\">\n {description}\n </Body>\n )}\n </div>\n </PrimitivePrompt>\n );\n};\n\nexport default ActionPrompt;\n"],"names":["ActionPrompt","sentiment","title","description","onDismiss","media","action","actionSecondary","id","className","testId","ariaLabel","isMobile","useScreenSize","Breakpoint","MEDIUM","mediaId","useId","titleId","descId","ariaLabelledByIds","undefined","filter","Boolean","join","renderMedia","imgSrc","_jsx","Image","src","alt","avatar","badge","status","AvatarView","size","children","asset","GiftBox","StatusIcon","iconLabel","PrimitivePrompt","clsx","actions","_jsxs","_Fragment","Button","v2","priority","href","block","onClick","label","role","Body","type","Typography","BODY_LARGE_BOLD"],"mappings":"
|
|
1
|
+
{"version":3,"file":"ActionPrompt.js","sources":["../../../src/prompt/ActionPrompt/ActionPrompt.tsx"],"sourcesContent":["import { AriaAttributes, ReactNode, useId } from 'react';\nimport { clsx } from 'clsx';\n\nimport StatusIcon from '../../statusIcon';\nimport Body from '../../body';\nimport Button from '../../button';\nimport { Breakpoint, Typography } from '../../common';\nimport AvatarView, { AvatarViewProps } from '../../avatarView';\nimport Image from '../../image';\nimport { ButtonProps } from '../../button/Button.types';\nimport { PrimitivePrompt, PrimitivePromptProps } from '../PrimitivePrompt';\nimport { BadgeAssetsProps } from '../../badge';\nimport { GiftBox } from '@transferwise/icons';\nimport { useScreenSize } from '../../common/hooks/useScreenSize';\n\nexport type ActionPromptProps = {\n title: ReactNode;\n description?: ReactNode;\n media?: {\n imgSrc?: string;\n avatar?: Pick<AvatarViewProps, 'imgSrc' | 'profileName' | 'profileType'> & {\n asset?: AvatarViewProps['children'];\n badge?: Pick<BadgeAssetsProps, 'flagCode'>;\n };\n 'aria-label'?: string;\n 'aria-hidden'?: boolean;\n };\n action: Pick<ButtonProps, 'onClick' | 'href' | 'target'> & {\n label: ButtonProps['children'];\n };\n actionSecondary?: Pick<ButtonProps, 'onClick' | 'href' | 'target'> & {\n label: ButtonProps['children'];\n };\n 'aria-label'?: AriaAttributes['aria-label'];\n} & Pick<PrimitivePromptProps, 'id' | 'className' | 'data-testid' | 'sentiment' | 'onDismiss'>;\n\n/**\n * Use an action prompt for optional feedback that doesn't require immediate action, such as feature upsells, warnings, or suggestions. These prompts are typically used outside of core product flows (e.g., Launchpad, Recipient, or Transaction screens) and can be addressed at the user's convenience.\n *\n * If your message is about immediate user feedback (e.g., form submission errors, download failures, missing data warnings), use an [info prompt](https://storybook.wise.design/?path=/docs/prompts-infoprompt--docs) instead.\n *\n * Guidance can be found in the [design system](https://wise.design/components/action-prompt).\n */\nexport const ActionPrompt = ({\n sentiment = 'neutral',\n title,\n description,\n onDismiss,\n media = {},\n action,\n actionSecondary,\n id,\n className,\n 'data-testid': testId,\n 'aria-label': ariaLabel,\n}: ActionPromptProps) => {\n const isMobile = !useScreenSize(Breakpoint.MEDIUM);\n\n const mediaId = useId();\n const titleId = useId();\n const descId = useId();\n\n const ariaLabelledByIds = [\n media['aria-hidden'] ? undefined : mediaId,\n ariaLabel ? undefined : titleId,\n ]\n .filter(Boolean)\n .join(' ');\n\n const renderMedia = () => {\n if (media?.imgSrc) {\n return (\n <Image\n id={mediaId}\n src={media.imgSrc}\n className=\"wds-action-prompt--media-image\"\n alt={media['aria-label'] ?? ''}\n />\n );\n }\n if (media?.avatar) {\n const badge = media.avatar.badge\n ? media.avatar.badge\n : sentiment === 'proposition'\n ? {}\n : { status: sentiment };\n return (\n <AvatarView\n {...media.avatar}\n badge={badge}\n aria-label={media['aria-label']}\n aria-hidden={media['aria-hidden']}\n id={mediaId}\n size={48}\n >\n {media.avatar.asset}\n </AvatarView>\n );\n }\n return sentiment === 'proposition' ? (\n <AvatarView\n id={mediaId}\n size={48}\n aria-label={media['aria-label']}\n aria-hidden={media['aria-hidden']}\n >\n <GiftBox />\n </AvatarView>\n ) : (\n <StatusIcon\n id={mediaId}\n size={48}\n sentiment={sentiment}\n iconLabel={media['aria-hidden'] ? null : media['aria-label']}\n />\n );\n };\n\n return (\n <PrimitivePrompt\n id={id}\n sentiment={sentiment}\n data-testid={testId}\n className={clsx(\n 'wds-action-prompt',\n { 'wds-action-prompt--with-two-actions': !!actionSecondary },\n className,\n )}\n media={renderMedia()}\n actions={\n <>\n {actionSecondary && (\n // @ts-expect-error onClick type mismatch\n <Button\n v2\n size=\"md\"\n priority=\"secondary\"\n href={actionSecondary.href}\n block={isMobile}\n onClick={actionSecondary?.onClick}\n >\n {actionSecondary.label}\n </Button>\n )}\n {/* @ts-expect-error onClick type mismatch */}\n <Button\n v2\n size=\"md\"\n priority=\"primary\"\n href={action.href}\n block={isMobile}\n onClick={action.onClick}\n >\n {action.label}\n </Button>\n </>\n }\n role=\"region\"\n onDismiss={onDismiss}\n {...(ariaLabel\n ? { 'aria-label': ariaLabel }\n : {\n 'aria-labelledby': ariaLabelledByIds,\n 'aria-describedby': descId,\n })}\n >\n <div className={clsx('d-flex', 'flex-column', 'justify-content-center')}>\n <Body id={titleId} type={Typography.BODY_LARGE_BOLD} className=\"wds-action-prompt__content\">\n {title}\n </Body>\n {description && (\n <Body id={descId} className=\"wds-action-prompt__content\">\n {description}\n </Body>\n )}\n </div>\n </PrimitivePrompt>\n );\n};\n\nexport default ActionPrompt;\n"],"names":["ActionPrompt","sentiment","title","description","onDismiss","media","action","actionSecondary","id","className","testId","ariaLabel","isMobile","useScreenSize","Breakpoint","MEDIUM","mediaId","useId","titleId","descId","ariaLabelledByIds","undefined","filter","Boolean","join","renderMedia","imgSrc","_jsx","Image","src","alt","avatar","badge","status","AvatarView","size","children","asset","GiftBox","StatusIcon","iconLabel","PrimitivePrompt","clsx","actions","_jsxs","_Fragment","Button","v2","priority","href","block","onClick","label","role","Body","type","Typography","BODY_LARGE_BOLD"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CO,MAAMA,YAAY,GAAGA,CAAC;AAC3BC,EAAAA,SAAS,GAAG,SAAS;EACrBC,KAAK;EACLC,WAAW;EACXC,SAAS;EACTC,KAAK,GAAG,EAAE;EACVC,MAAM;EACNC,eAAe;EACfC,EAAE;EACFC,SAAS;AACT,EAAA,aAAa,EAAEC,MAAM;AACrB,EAAA,YAAY,EAAEC;AAAS,CACL,KAAI;EACtB,MAAMC,QAAQ,GAAG,CAACC,2BAAa,CAACC,qBAAU,CAACC,MAAM,CAAC;AAElD,EAAA,MAAMC,OAAO,GAAGC,WAAK,EAAE;AACvB,EAAA,MAAMC,OAAO,GAAGD,WAAK,EAAE;AACvB,EAAA,MAAME,MAAM,GAAGF,WAAK,EAAE;EAEtB,MAAMG,iBAAiB,GAAG,CACxBf,KAAK,CAAC,aAAa,CAAC,GAAGgB,SAAS,GAAGL,OAAO,EAC1CL,SAAS,GAAGU,SAAS,GAAGH,OAAO,CAChC,CACEI,MAAM,CAACC,OAAO,CAAC,CACfC,IAAI,CAAC,GAAG,CAAC;EAEZ,MAAMC,WAAW,GAAGA,MAAK;IACvB,IAAIpB,KAAK,EAAEqB,MAAM,EAAE;MACjB,oBACEC,cAAA,CAACC,aAAK,EAAA;AACJpB,QAAAA,EAAE,EAAEQ,OAAQ;QACZa,GAAG,EAAExB,KAAK,CAACqB,MAAO;AAClBjB,QAAAA,SAAS,EAAC,gCAAgC;AAC1CqB,QAAAA,GAAG,EAAEzB,KAAK,CAAC,YAAY,CAAC,IAAI;AAAG,OAAA,CAC/B;AAEN,IAAA;IACA,IAAIA,KAAK,EAAE0B,MAAM,EAAE;MACjB,MAAMC,KAAK,GAAG3B,KAAK,CAAC0B,MAAM,CAACC,KAAK,GAC5B3B,KAAK,CAAC0B,MAAM,CAACC,KAAK,GAClB/B,SAAS,KAAK,aAAa,GACzB,EAAE,GACF;AAAEgC,QAAAA,MAAM,EAAEhC;OAAW;MAC3B,oBACE0B,cAAA,CAACO,kBAAU,EAAA;QAAA,GACL7B,KAAK,CAAC0B,MAAM;AAChBC,QAAAA,KAAK,EAAEA,KAAM;QACb,YAAA,EAAY3B,KAAK,CAAC,YAAY,CAAE;QAChC,aAAA,EAAaA,KAAK,CAAC,aAAa,CAAE;AAClCG,QAAAA,EAAE,EAAEQ,OAAQ;AACZmB,QAAAA,IAAI,EAAE,EAAG;AAAAC,QAAAA,QAAA,EAER/B,KAAK,CAAC0B,MAAM,CAACM;AAAK,OACT,CAAC;AAEjB,IAAA;AACA,IAAA,OAAOpC,SAAS,KAAK,aAAa,gBAChC0B,cAAA,CAACO,kBAAU,EAAA;AACT1B,MAAAA,EAAE,EAAEQ,OAAQ;AACZmB,MAAAA,IAAI,EAAE,EAAG;MACT,YAAA,EAAY9B,KAAK,CAAC,YAAY,CAAE;MAChC,aAAA,EAAaA,KAAK,CAAC,aAAa,CAAE;AAAA+B,MAAAA,QAAA,eAElCT,cAAA,CAACW,aAAO,EAAA,EAAA;AACV,KAAY,CAAC,gBAEbX,cAAA,CAACY,kBAAU,EAAA;AACT/B,MAAAA,EAAE,EAAEQ,OAAQ;AACZmB,MAAAA,IAAI,EAAE,EAAG;AACTlC,MAAAA,SAAS,EAAEA,SAAU;MACrBuC,SAAS,EAAEnC,KAAK,CAAC,aAAa,CAAC,GAAG,IAAI,GAAGA,KAAK,CAAC,YAAY;AAAE,KAAA,CAEhE;EACH,CAAC;EAED,oBACEsB,cAAA,CAACc,+BAAe,EAAA;AACdjC,IAAAA,EAAE,EAAEA,EAAG;AACPP,IAAAA,SAAS,EAAEA,SAAU;AACrB,IAAA,aAAA,EAAaS,MAAO;AACpBD,IAAAA,SAAS,EAAEiC,SAAI,CACb,mBAAmB,EACnB;MAAE,qCAAqC,EAAE,CAAC,CAACnC;KAAiB,EAC5DE,SAAS,CACT;IACFJ,KAAK,EAAEoB,WAAW,EAAG;IACrBkB,OAAO,eACLC,eAAA,CAAAC,mBAAA,EAAA;AAAAT,MAAAA,QAAA,GACG7B,eAAe;AAAA;AACd;AACAoB,MAAAA,cAAA,CAACmB,uBAAM,EAAA;QACLC,EAAE,EAAA,IAAA;AACFZ,QAAAA,IAAI,EAAC,IAAI;AACTa,QAAAA,QAAQ,EAAC,WAAW;QACpBC,IAAI,EAAE1C,eAAe,CAAC0C,IAAK;AAC3BC,QAAAA,KAAK,EAAEtC,QAAS;QAChBuC,OAAO,EAAE5C,eAAe,EAAE4C,OAAQ;QAAAf,QAAA,EAEjC7B,eAAe,CAAC6C;AAAK,OAChB,CACT,eAEDzB,cAAA,CAACmB,uBAAM,EAAA;QACLC,EAAE,EAAA,IAAA;AACFZ,QAAAA,IAAI,EAAC,IAAI;AACTa,QAAAA,QAAQ,EAAC,SAAS;QAClBC,IAAI,EAAE3C,MAAM,CAAC2C,IAAK;AAClBC,QAAAA,KAAK,EAAEtC,QAAS;QAChBuC,OAAO,EAAE7C,MAAM,CAAC6C,OAAQ;QAAAf,QAAA,EAEvB9B,MAAM,CAAC8C;AAAK,OACP,CACV;AAAA,KAAA,CACD;AACDC,IAAAA,IAAI,EAAC,QAAQ;AACbjD,IAAAA,SAAS,EAAEA,SAAU;AAAA,IAAA,IAChBO,SAAS,GACV;AAAE,MAAA,YAAY,EAAEA;AAAS,KAAE,GAC3B;AACE,MAAA,iBAAiB,EAAES,iBAAiB;AACpC,MAAA,kBAAkB,EAAED;KACrB,CAAA;AAAAiB,IAAAA,QAAA,eAELQ,eAAA,CAAA,KAAA,EAAA;MAAKnC,SAAS,EAAEiC,SAAI,CAAC,QAAQ,EAAE,aAAa,EAAE,wBAAwB,CAAE;MAAAN,QAAA,EAAA,cACtET,cAAA,CAAC2B,YAAI,EAAA;AAAC9C,QAAAA,EAAE,EAAEU,OAAQ;QAACqC,IAAI,EAAEC,qBAAU,CAACC,eAAgB;AAAChD,QAAAA,SAAS,EAAC,4BAA4B;AAAA2B,QAAAA,QAAA,EACxFlC;AAAK,OACF,CACN,EAACC,WAAW,iBACVwB,cAAA,CAAC2B,YAAI,EAAA;AAAC9C,QAAAA,EAAE,EAAEW,MAAO;AAACV,QAAAA,SAAS,EAAC,4BAA4B;AAAA2B,QAAAA,QAAA,EACrDjC;AAAW,OACR,CACP;KACE;AACP,GAAiB,CAAC;AAEtB;;;;;"}
|
|
@@ -25,6 +25,7 @@ var clsx = require('clsx');
|
|
|
25
25
|
require('react-intl');
|
|
26
26
|
require('../../common/closeButton/CloseButton.messages.js');
|
|
27
27
|
var jsxRuntime = require('react/jsx-runtime');
|
|
28
|
+
var LiveRegion = require('../../common/liveRegion/LiveRegion.js');
|
|
28
29
|
var StatusIcon = require('../../statusIcon/StatusIcon.js');
|
|
29
30
|
var Body = require('../../body/Body.js');
|
|
30
31
|
var Link = require('../../link/Link.js');
|
|
@@ -38,6 +39,7 @@ const InfoPrompt = ({
|
|
|
38
39
|
title,
|
|
39
40
|
description,
|
|
40
41
|
className,
|
|
42
|
+
'aria-live': ariaLive = 'polite',
|
|
41
43
|
'data-testid': dataTestId,
|
|
42
44
|
...restProps
|
|
43
45
|
}) => {
|
|
@@ -76,35 +78,39 @@ const InfoPrompt = ({
|
|
|
76
78
|
sentiment: statusIconSentiment
|
|
77
79
|
});
|
|
78
80
|
};
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
81
|
+
// Render content directly in LiveRegion
|
|
82
|
+
return /*#__PURE__*/jsxRuntime.jsx(LiveRegion.LiveRegion, {
|
|
83
|
+
"aria-live": ariaLive,
|
|
84
|
+
children: /*#__PURE__*/jsxRuntime.jsx(PrimitivePrompt.PrimitivePrompt, {
|
|
85
|
+
sentiment: sentiment$1,
|
|
86
|
+
media: renderMedia(),
|
|
87
|
+
"data-testid": dataTestId,
|
|
88
|
+
className: clsx.clsx('wds-info-prompt', className),
|
|
89
|
+
...restProps,
|
|
90
|
+
onTouchStart: handleTouchStart,
|
|
91
|
+
onTouchEnd: handleTouchEnd,
|
|
92
|
+
onTouchMove: handleTouchMove,
|
|
93
|
+
onDismiss: onDismiss,
|
|
94
|
+
children: /*#__PURE__*/jsxRuntime.jsxs("div", {
|
|
95
|
+
className: "wds-info-prompt__content",
|
|
96
|
+
children: [title && /*#__PURE__*/jsxRuntime.jsx(Body.default, {
|
|
97
|
+
className: "wds-info-prompt__title",
|
|
98
|
+
type: typography.Typography.BODY_LARGE_BOLD,
|
|
99
|
+
as: "span",
|
|
100
|
+
children: title
|
|
101
|
+
}), /*#__PURE__*/jsxRuntime.jsx(Body.default, {
|
|
102
|
+
as: "span",
|
|
103
|
+
className: "wds-info-prompt__description",
|
|
104
|
+
children: description
|
|
105
|
+
}), action && /*#__PURE__*/jsxRuntime.jsx(Link.default, {
|
|
106
|
+
href: action.href,
|
|
107
|
+
target: action.target,
|
|
108
|
+
type: typography.Typography.LINK_DEFAULT,
|
|
109
|
+
className: "wds-info-prompt__action",
|
|
110
|
+
onClick: action.onClick,
|
|
111
|
+
children: action.label
|
|
112
|
+
})]
|
|
113
|
+
})
|
|
108
114
|
})
|
|
109
115
|
});
|
|
110
116
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InfoPrompt.js","sources":["../../../src/prompt/InfoPrompt/InfoPrompt.tsx"],"sourcesContent":["import { HTMLAttributes, ReactNode, useState } from 'react';\nimport { Sentiment, Typography } from '../../common';\nimport { GiftBox } from '@transferwise/icons';\nimport type { Sentiment as SurfaceSentiment } from '../../sentimentSurface';\nimport StatusIcon from '../../statusIcon';\nimport { clsx } from 'clsx';\nimport Body from '../../body';\nimport Link, { LinkProps } from '../../link';\nimport { PrimitivePrompt, PrimitivePromptProps } from '../PrimitivePrompt';\n\nexport type InfoPromptAction = Pick<LinkProps, 'href' | 'target' | 'onClick'> & {\n /**\n * The label text for the action link\n */\n label: string;\n};\n\nexport type InfoPromptMedia = {\n /**\n * The icon/image asset to display.\n * The asset should include its own accessibility attributes (e.g. title, aria-label) if it conveys meaning.\n */\n asset: ReactNode;\n};\n\nexport type InfoPromptProps = Omit<HTMLAttributes<HTMLDivElement>, 'title'> &\n Pick<PrimitivePromptProps, 'data-testid'> & {\n /**\n * The sentiment determines the colour scheme\n * @default 'neutral'\n */\n sentiment?: SurfaceSentiment;\n /**\n * Handler called when the close button is clicked.\n * If not provided, the close button is hidden.\n */\n onDismiss?: () => void;\n /**\n * Custom media to override the default status icon.\n * Success and proposition sentiments support 2 status variations: standard checkmark & confetti.\n */\n media?: InfoPromptMedia;\n /**\n * Action link to be displayed below the description\n */\n action?: InfoPromptAction;\n /**\n * Title content for the prompt\n */\n title?: string;\n /**\n * Description text for the prompt (required)\n */\n description: string;\n className?: string;\n };\n\n/**\n * InfoPrompt displays important contextual messages to users within a screen.\n * It provides a visually distinct way to communicate information, warnings, errors,\n * or positive feedback with optional actions and dismissal capabilities.\n *\n * Use this component to replace the deprecated Alert component.\n *\n * Guidance can be found in the [design system](https://wise.design/components/info-prompt).\n */\nexport const InfoPrompt = ({\n sentiment = 'neutral',\n onDismiss,\n media,\n action,\n title,\n description,\n className,\n 'data-testid': dataTestId,\n ...restProps\n}: InfoPromptProps) => {\n const [shouldFire, setShouldFire] = useState<boolean>();\n const statusIconSentiment =\n sentiment === 'success'\n ? Sentiment.POSITIVE\n : (sentiment as Exclude<SurfaceSentiment, 'proposition'>);\n\n const handleTouchStart = () => {\n setShouldFire(true);\n };\n\n const handleTouchEnd = () => {\n if (shouldFire && action?.href) {\n if (action.target === '_blank') {\n window.top?.open(action.href, '_blank', 'noopener, noreferrer');\n } else {\n window.top?.location.assign(action.href);\n }\n }\n setShouldFire(false);\n };\n\n const handleTouchMove = () => {\n setShouldFire(false);\n };\n\n const renderMedia = () => {\n if (media) {\n return <span className=\"wds-info-prompt__media\">{media.asset}</span>;\n }\n\n if (sentiment === 'proposition') {\n return <GiftBox size={24} />;\n }\n\n return <StatusIcon size={24} sentiment={statusIconSentiment} />;\n };\n\n return (\n <PrimitivePrompt\n
|
|
1
|
+
{"version":3,"file":"InfoPrompt.js","sources":["../../../src/prompt/InfoPrompt/InfoPrompt.tsx"],"sourcesContent":["import { HTMLAttributes, ReactNode, useState } from 'react';\nimport { LiveRegion, Sentiment, Typography } from '../../common';\nimport type { AriaLive } from '../../common';\nimport { GiftBox } from '@transferwise/icons';\nimport type { Sentiment as SurfaceSentiment } from '../../sentimentSurface';\nimport StatusIcon from '../../statusIcon';\nimport { clsx } from 'clsx';\nimport Body from '../../body';\nimport Link, { LinkProps } from '../../link';\nimport { PrimitivePrompt, PrimitivePromptProps } from '../PrimitivePrompt';\n\nexport type InfoPromptAction = Pick<LinkProps, 'href' | 'target' | 'onClick'> & {\n /**\n * The label text for the action link\n */\n label: string;\n};\n\nexport type InfoPromptMedia = {\n /**\n * The icon/image asset to display.\n * The asset should include its own accessibility attributes (e.g. title, aria-label) if it conveys meaning.\n */\n asset: ReactNode;\n};\n\nexport type InfoPromptProps = Omit<HTMLAttributes<HTMLDivElement>, 'title' | 'aria-live' | 'role'> &\n Pick<PrimitivePromptProps, 'data-testid'> & {\n /**\n * The sentiment determines the colour scheme\n * @default 'neutral'\n */\n sentiment?: SurfaceSentiment;\n /**\n * Handler called when the close button is clicked.\n * If not provided, the close button is hidden.\n */\n onDismiss?: () => void;\n /**\n * Custom media to override the default status icon.\n * Success and proposition sentiments support 2 status variations: standard checkmark & confetti.\n */\n media?: InfoPromptMedia;\n /**\n * Action link to be displayed below the description\n */\n action?: InfoPromptAction;\n /**\n * Title content for the prompt\n */\n title?: string;\n /**\n * Description text for the prompt (required)\n */\n description: string;\n className?: string;\n /**\n * Sets the ARIA live region politeness level.\n * - `'polite'` — announced after the current speech (default)\n * - `'assertive'` — interrupts the current speech immediately\n * - `'off'` — disables the live region entirely\n * @default 'polite'\n */\n 'aria-live'?: AriaLive;\n };\n\n/**\n * InfoPrompt displays important contextual messages to users within a screen.\n * It provides a visually distinct way to communicate information, warnings, errors,\n * or positive feedback with optional actions and dismissal capabilities.\n *\n * Use this component to replace the deprecated Alert component.\n *\n * Guidance can be found in the [design system](https://wise.design/components/info-prompt).\n */\nexport const InfoPrompt = ({\n sentiment = 'neutral',\n onDismiss,\n media,\n action,\n title,\n description,\n className,\n 'aria-live': ariaLive = 'polite',\n 'data-testid': dataTestId,\n ...restProps\n}: InfoPromptProps) => {\n const [shouldFire, setShouldFire] = useState<boolean>();\n const statusIconSentiment =\n sentiment === 'success'\n ? Sentiment.POSITIVE\n : (sentiment as Exclude<SurfaceSentiment, 'proposition'>);\n\n const handleTouchStart = () => {\n setShouldFire(true);\n };\n\n const handleTouchEnd = () => {\n if (shouldFire && action?.href) {\n if (action.target === '_blank') {\n window.top?.open(action.href, '_blank', 'noopener, noreferrer');\n } else {\n window.top?.location.assign(action.href);\n }\n }\n setShouldFire(false);\n };\n\n const handleTouchMove = () => {\n setShouldFire(false);\n };\n\n const renderMedia = () => {\n if (media) {\n return <span className=\"wds-info-prompt__media\">{media.asset}</span>;\n }\n\n if (sentiment === 'proposition') {\n return <GiftBox size={24} />;\n }\n\n return <StatusIcon size={24} sentiment={statusIconSentiment} />;\n };\n\n // Render content directly in LiveRegion\n return (\n <LiveRegion aria-live={ariaLive}>\n <PrimitivePrompt\n sentiment={sentiment}\n media={renderMedia()}\n data-testid={dataTestId}\n className={clsx('wds-info-prompt', className)}\n {...restProps}\n onTouchStart={handleTouchStart}\n onTouchEnd={handleTouchEnd}\n onTouchMove={handleTouchMove}\n onDismiss={onDismiss}\n >\n <div className=\"wds-info-prompt__content\">\n {title && (\n <Body className=\"wds-info-prompt__title\" type={Typography.BODY_LARGE_BOLD} as=\"span\">\n {title}\n </Body>\n )}\n <Body as=\"span\" className=\"wds-info-prompt__description\">\n {description}\n </Body>\n {action && (\n <Link\n href={action.href}\n target={action.target}\n type={Typography.LINK_DEFAULT}\n className=\"wds-info-prompt__action\"\n onClick={action.onClick}\n >\n {action.label}\n </Link>\n )}\n </div>\n </PrimitivePrompt>\n </LiveRegion>\n );\n};\n"],"names":["InfoPrompt","sentiment","onDismiss","media","action","title","description","className","ariaLive","dataTestId","restProps","shouldFire","setShouldFire","useState","statusIconSentiment","Sentiment","POSITIVE","handleTouchStart","handleTouchEnd","href","target","window","top","open","location","assign","handleTouchMove","renderMedia","_jsx","children","asset","GiftBox","size","StatusIcon","LiveRegion","PrimitivePrompt","clsx","onTouchStart","onTouchEnd","onTouchMove","_jsxs","Body","type","Typography","BODY_LARGE_BOLD","as","Link","LINK_DEFAULT","onClick","label"],"mappings":"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"}
|
|
@@ -23,6 +23,7 @@ import { clsx } from 'clsx';
|
|
|
23
23
|
import 'react-intl';
|
|
24
24
|
import '../../common/closeButton/CloseButton.messages.mjs';
|
|
25
25
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
26
|
+
import { LiveRegion } from '../../common/liveRegion/LiveRegion.mjs';
|
|
26
27
|
import StatusIcon from '../../statusIcon/StatusIcon.mjs';
|
|
27
28
|
import Body from '../../body/Body.mjs';
|
|
28
29
|
import Link from '../../link/Link.mjs';
|
|
@@ -36,6 +37,7 @@ const InfoPrompt = ({
|
|
|
36
37
|
title,
|
|
37
38
|
description,
|
|
38
39
|
className,
|
|
40
|
+
'aria-live': ariaLive = 'polite',
|
|
39
41
|
'data-testid': dataTestId,
|
|
40
42
|
...restProps
|
|
41
43
|
}) => {
|
|
@@ -74,35 +76,39 @@ const InfoPrompt = ({
|
|
|
74
76
|
sentiment: statusIconSentiment
|
|
75
77
|
});
|
|
76
78
|
};
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
79
|
+
// Render content directly in LiveRegion
|
|
80
|
+
return /*#__PURE__*/jsx(LiveRegion, {
|
|
81
|
+
"aria-live": ariaLive,
|
|
82
|
+
children: /*#__PURE__*/jsx(PrimitivePrompt, {
|
|
83
|
+
sentiment: sentiment,
|
|
84
|
+
media: renderMedia(),
|
|
85
|
+
"data-testid": dataTestId,
|
|
86
|
+
className: clsx('wds-info-prompt', className),
|
|
87
|
+
...restProps,
|
|
88
|
+
onTouchStart: handleTouchStart,
|
|
89
|
+
onTouchEnd: handleTouchEnd,
|
|
90
|
+
onTouchMove: handleTouchMove,
|
|
91
|
+
onDismiss: onDismiss,
|
|
92
|
+
children: /*#__PURE__*/jsxs("div", {
|
|
93
|
+
className: "wds-info-prompt__content",
|
|
94
|
+
children: [title && /*#__PURE__*/jsx(Body, {
|
|
95
|
+
className: "wds-info-prompt__title",
|
|
96
|
+
type: Typography.BODY_LARGE_BOLD,
|
|
97
|
+
as: "span",
|
|
98
|
+
children: title
|
|
99
|
+
}), /*#__PURE__*/jsx(Body, {
|
|
100
|
+
as: "span",
|
|
101
|
+
className: "wds-info-prompt__description",
|
|
102
|
+
children: description
|
|
103
|
+
}), action && /*#__PURE__*/jsx(Link, {
|
|
104
|
+
href: action.href,
|
|
105
|
+
target: action.target,
|
|
106
|
+
type: Typography.LINK_DEFAULT,
|
|
107
|
+
className: "wds-info-prompt__action",
|
|
108
|
+
onClick: action.onClick,
|
|
109
|
+
children: action.label
|
|
110
|
+
})]
|
|
111
|
+
})
|
|
106
112
|
})
|
|
107
113
|
});
|
|
108
114
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InfoPrompt.mjs","sources":["../../../src/prompt/InfoPrompt/InfoPrompt.tsx"],"sourcesContent":["import { HTMLAttributes, ReactNode, useState } from 'react';\nimport { Sentiment, Typography } from '../../common';\nimport { GiftBox } from '@transferwise/icons';\nimport type { Sentiment as SurfaceSentiment } from '../../sentimentSurface';\nimport StatusIcon from '../../statusIcon';\nimport { clsx } from 'clsx';\nimport Body from '../../body';\nimport Link, { LinkProps } from '../../link';\nimport { PrimitivePrompt, PrimitivePromptProps } from '../PrimitivePrompt';\n\nexport type InfoPromptAction = Pick<LinkProps, 'href' | 'target' | 'onClick'> & {\n /**\n * The label text for the action link\n */\n label: string;\n};\n\nexport type InfoPromptMedia = {\n /**\n * The icon/image asset to display.\n * The asset should include its own accessibility attributes (e.g. title, aria-label) if it conveys meaning.\n */\n asset: ReactNode;\n};\n\nexport type InfoPromptProps = Omit<HTMLAttributes<HTMLDivElement>, 'title'> &\n Pick<PrimitivePromptProps, 'data-testid'> & {\n /**\n * The sentiment determines the colour scheme\n * @default 'neutral'\n */\n sentiment?: SurfaceSentiment;\n /**\n * Handler called when the close button is clicked.\n * If not provided, the close button is hidden.\n */\n onDismiss?: () => void;\n /**\n * Custom media to override the default status icon.\n * Success and proposition sentiments support 2 status variations: standard checkmark & confetti.\n */\n media?: InfoPromptMedia;\n /**\n * Action link to be displayed below the description\n */\n action?: InfoPromptAction;\n /**\n * Title content for the prompt\n */\n title?: string;\n /**\n * Description text for the prompt (required)\n */\n description: string;\n className?: string;\n };\n\n/**\n * InfoPrompt displays important contextual messages to users within a screen.\n * It provides a visually distinct way to communicate information, warnings, errors,\n * or positive feedback with optional actions and dismissal capabilities.\n *\n * Use this component to replace the deprecated Alert component.\n *\n * Guidance can be found in the [design system](https://wise.design/components/info-prompt).\n */\nexport const InfoPrompt = ({\n sentiment = 'neutral',\n onDismiss,\n media,\n action,\n title,\n description,\n className,\n 'data-testid': dataTestId,\n ...restProps\n}: InfoPromptProps) => {\n const [shouldFire, setShouldFire] = useState<boolean>();\n const statusIconSentiment =\n sentiment === 'success'\n ? Sentiment.POSITIVE\n : (sentiment as Exclude<SurfaceSentiment, 'proposition'>);\n\n const handleTouchStart = () => {\n setShouldFire(true);\n };\n\n const handleTouchEnd = () => {\n if (shouldFire && action?.href) {\n if (action.target === '_blank') {\n window.top?.open(action.href, '_blank', 'noopener, noreferrer');\n } else {\n window.top?.location.assign(action.href);\n }\n }\n setShouldFire(false);\n };\n\n const handleTouchMove = () => {\n setShouldFire(false);\n };\n\n const renderMedia = () => {\n if (media) {\n return <span className=\"wds-info-prompt__media\">{media.asset}</span>;\n }\n\n if (sentiment === 'proposition') {\n return <GiftBox size={24} />;\n }\n\n return <StatusIcon size={24} sentiment={statusIconSentiment} />;\n };\n\n return (\n <PrimitivePrompt\n
|
|
1
|
+
{"version":3,"file":"InfoPrompt.mjs","sources":["../../../src/prompt/InfoPrompt/InfoPrompt.tsx"],"sourcesContent":["import { HTMLAttributes, ReactNode, useState } from 'react';\nimport { LiveRegion, Sentiment, Typography } from '../../common';\nimport type { AriaLive } from '../../common';\nimport { GiftBox } from '@transferwise/icons';\nimport type { Sentiment as SurfaceSentiment } from '../../sentimentSurface';\nimport StatusIcon from '../../statusIcon';\nimport { clsx } from 'clsx';\nimport Body from '../../body';\nimport Link, { LinkProps } from '../../link';\nimport { PrimitivePrompt, PrimitivePromptProps } from '../PrimitivePrompt';\n\nexport type InfoPromptAction = Pick<LinkProps, 'href' | 'target' | 'onClick'> & {\n /**\n * The label text for the action link\n */\n label: string;\n};\n\nexport type InfoPromptMedia = {\n /**\n * The icon/image asset to display.\n * The asset should include its own accessibility attributes (e.g. title, aria-label) if it conveys meaning.\n */\n asset: ReactNode;\n};\n\nexport type InfoPromptProps = Omit<HTMLAttributes<HTMLDivElement>, 'title' | 'aria-live' | 'role'> &\n Pick<PrimitivePromptProps, 'data-testid'> & {\n /**\n * The sentiment determines the colour scheme\n * @default 'neutral'\n */\n sentiment?: SurfaceSentiment;\n /**\n * Handler called when the close button is clicked.\n * If not provided, the close button is hidden.\n */\n onDismiss?: () => void;\n /**\n * Custom media to override the default status icon.\n * Success and proposition sentiments support 2 status variations: standard checkmark & confetti.\n */\n media?: InfoPromptMedia;\n /**\n * Action link to be displayed below the description\n */\n action?: InfoPromptAction;\n /**\n * Title content for the prompt\n */\n title?: string;\n /**\n * Description text for the prompt (required)\n */\n description: string;\n className?: string;\n /**\n * Sets the ARIA live region politeness level.\n * - `'polite'` — announced after the current speech (default)\n * - `'assertive'` — interrupts the current speech immediately\n * - `'off'` — disables the live region entirely\n * @default 'polite'\n */\n 'aria-live'?: AriaLive;\n };\n\n/**\n * InfoPrompt displays important contextual messages to users within a screen.\n * It provides a visually distinct way to communicate information, warnings, errors,\n * or positive feedback with optional actions and dismissal capabilities.\n *\n * Use this component to replace the deprecated Alert component.\n *\n * Guidance can be found in the [design system](https://wise.design/components/info-prompt).\n */\nexport const InfoPrompt = ({\n sentiment = 'neutral',\n onDismiss,\n media,\n action,\n title,\n description,\n className,\n 'aria-live': ariaLive = 'polite',\n 'data-testid': dataTestId,\n ...restProps\n}: InfoPromptProps) => {\n const [shouldFire, setShouldFire] = useState<boolean>();\n const statusIconSentiment =\n sentiment === 'success'\n ? Sentiment.POSITIVE\n : (sentiment as Exclude<SurfaceSentiment, 'proposition'>);\n\n const handleTouchStart = () => {\n setShouldFire(true);\n };\n\n const handleTouchEnd = () => {\n if (shouldFire && action?.href) {\n if (action.target === '_blank') {\n window.top?.open(action.href, '_blank', 'noopener, noreferrer');\n } else {\n window.top?.location.assign(action.href);\n }\n }\n setShouldFire(false);\n };\n\n const handleTouchMove = () => {\n setShouldFire(false);\n };\n\n const renderMedia = () => {\n if (media) {\n return <span className=\"wds-info-prompt__media\">{media.asset}</span>;\n }\n\n if (sentiment === 'proposition') {\n return <GiftBox size={24} />;\n }\n\n return <StatusIcon size={24} sentiment={statusIconSentiment} />;\n };\n\n // Render content directly in LiveRegion\n return (\n <LiveRegion aria-live={ariaLive}>\n <PrimitivePrompt\n sentiment={sentiment}\n media={renderMedia()}\n data-testid={dataTestId}\n className={clsx('wds-info-prompt', className)}\n {...restProps}\n onTouchStart={handleTouchStart}\n onTouchEnd={handleTouchEnd}\n onTouchMove={handleTouchMove}\n onDismiss={onDismiss}\n >\n <div className=\"wds-info-prompt__content\">\n {title && (\n <Body className=\"wds-info-prompt__title\" type={Typography.BODY_LARGE_BOLD} as=\"span\">\n {title}\n </Body>\n )}\n <Body as=\"span\" className=\"wds-info-prompt__description\">\n {description}\n </Body>\n {action && (\n <Link\n href={action.href}\n target={action.target}\n type={Typography.LINK_DEFAULT}\n className=\"wds-info-prompt__action\"\n onClick={action.onClick}\n >\n {action.label}\n </Link>\n )}\n </div>\n </PrimitivePrompt>\n </LiveRegion>\n );\n};\n"],"names":["InfoPrompt","sentiment","onDismiss","media","action","title","description","className","ariaLive","dataTestId","restProps","shouldFire","setShouldFire","useState","statusIconSentiment","Sentiment","POSITIVE","handleTouchStart","handleTouchEnd","href","target","window","top","open","location","assign","handleTouchMove","renderMedia","_jsx","children","asset","GiftBox","size","StatusIcon","LiveRegion","PrimitivePrompt","clsx","onTouchStart","onTouchEnd","onTouchMove","_jsxs","Body","type","Typography","BODY_LARGE_BOLD","as","Link","LINK_DEFAULT","onClick","label"],"mappings":"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"}
|
|
@@ -26,4 +26,6 @@ export * from './initials';
|
|
|
26
26
|
export * from './colors';
|
|
27
27
|
export * from './constants';
|
|
28
28
|
export { CloseButton } from './closeButton';
|
|
29
|
+
export { LiveRegion } from './liveRegion';
|
|
30
|
+
export type { AriaLive, LiveRegionProps } from './liveRegion';
|
|
29
31
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/common/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACjE,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACxE,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC;AAE5B,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,cAAc,wBAAwB,CAAC;AACvC,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,cAAc,2BAA2B,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/common/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACjE,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACxE,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC;AAE5B,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,cAAc,wBAAwB,CAAC;AACvC,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,cAAc,2BAA2B,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
export type AriaLive = 'off' | 'polite' | 'assertive';
|
|
3
|
+
export interface LiveRegionProps extends Omit<HTMLAttributes<HTMLDivElement>, 'role' | 'aria-live' | 'aria-atomic'> {
|
|
4
|
+
/**
|
|
5
|
+
* Determines urgency: 'assertive' interrupts, 'polite' waits for idle, 'off' disables live region.
|
|
6
|
+
*/
|
|
7
|
+
'aria-live': AriaLive;
|
|
8
|
+
/** Test ID for testing tools */
|
|
9
|
+
'data-testid'?: string;
|
|
10
|
+
children?: ReactNode;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Renders an ARIA live region with the correct implicit role.
|
|
14
|
+
*
|
|
15
|
+
* - `aria-live="polite"` → `role="status"`
|
|
16
|
+
* - `aria-live="assertive"` → `role="alert"`
|
|
17
|
+
* - `aria-live="off"` → no live region
|
|
18
|
+
*
|
|
19
|
+
* The `role` prop is intentionally excluded from the public API
|
|
20
|
+
* to prevent mismatches between `aria-live` and `role`.
|
|
21
|
+
*/
|
|
22
|
+
export declare const LiveRegion: ({ "aria-live": ariaLive, children, ...props }: LiveRegionProps) => import("react").JSX.Element;
|
|
23
|
+
//# sourceMappingURL=LiveRegion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LiveRegion.d.ts","sourceRoot":"","sources":["../../../../src/common/liveRegion/LiveRegion.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAOvD,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,QAAQ,GAAG,WAAW,CAAC;AAEtD,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAC3C,cAAc,CAAC,cAAc,CAAC,EAC9B,MAAM,GAAG,WAAW,GAAG,aAAa,CACrC;IACC;;OAEG;IACH,WAAW,EAAE,QAAQ,CAAC;IACtB,gCAAgC;IAChC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,UAAU,GAAI,+CAA+C,eAAe,gCAgBxF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/common/liveRegion/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
import type { AriaLive } from '../../common';
|
|
2
3
|
import type { Sentiment as SurfaceSentiment } from '../../sentimentSurface';
|
|
3
4
|
import { LinkProps } from '../../link';
|
|
4
5
|
import { PrimitivePromptProps } from '../PrimitivePrompt';
|
|
@@ -15,7 +16,7 @@ export type InfoPromptMedia = {
|
|
|
15
16
|
*/
|
|
16
17
|
asset: ReactNode;
|
|
17
18
|
};
|
|
18
|
-
export type InfoPromptProps = Omit<HTMLAttributes<HTMLDivElement>, 'title'> & Pick<PrimitivePromptProps, 'data-testid'> & {
|
|
19
|
+
export type InfoPromptProps = Omit<HTMLAttributes<HTMLDivElement>, 'title' | 'aria-live' | 'role'> & Pick<PrimitivePromptProps, 'data-testid'> & {
|
|
19
20
|
/**
|
|
20
21
|
* The sentiment determines the colour scheme
|
|
21
22
|
* @default 'neutral'
|
|
@@ -44,6 +45,14 @@ export type InfoPromptProps = Omit<HTMLAttributes<HTMLDivElement>, 'title'> & Pi
|
|
|
44
45
|
*/
|
|
45
46
|
description: string;
|
|
46
47
|
className?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Sets the ARIA live region politeness level.
|
|
50
|
+
* - `'polite'` — announced after the current speech (default)
|
|
51
|
+
* - `'assertive'` — interrupts the current speech immediately
|
|
52
|
+
* - `'off'` — disables the live region entirely
|
|
53
|
+
* @default 'polite'
|
|
54
|
+
*/
|
|
55
|
+
'aria-live'?: AriaLive;
|
|
47
56
|
};
|
|
48
57
|
/**
|
|
49
58
|
* InfoPrompt displays important contextual messages to users within a screen.
|
|
@@ -54,5 +63,5 @@ export type InfoPromptProps = Omit<HTMLAttributes<HTMLDivElement>, 'title'> & Pi
|
|
|
54
63
|
*
|
|
55
64
|
* Guidance can be found in the [design system](https://wise.design/components/info-prompt).
|
|
56
65
|
*/
|
|
57
|
-
export declare const InfoPrompt: ({ sentiment, onDismiss, media, action, title, description, className, "data-testid": dataTestId, ...restProps }: InfoPromptProps) => import("react").JSX.Element;
|
|
66
|
+
export declare const InfoPrompt: ({ sentiment, onDismiss, media, action, title, description, className, "aria-live": ariaLive, "data-testid": dataTestId, ...restProps }: InfoPromptProps) => import("react").JSX.Element;
|
|
58
67
|
//# sourceMappingURL=InfoPrompt.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InfoPrompt.d.ts","sourceRoot":"","sources":["../../../../src/prompt/InfoPrompt/InfoPrompt.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,SAAS,EAAY,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"InfoPrompt.d.ts","sourceRoot":"","sources":["../../../../src/prompt/InfoPrompt/InfoPrompt.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,SAAS,EAAY,MAAM,OAAO,CAAC;AAE5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,KAAK,EAAE,SAAS,IAAI,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAI5E,OAAa,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAmB,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE3E,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC,GAAG;IAC9E;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B;;;OAGG;IACH,KAAK,EAAE,SAAS,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,GAAG,WAAW,GAAG,MAAM,CAAC,GAChG,IAAI,CAAC,oBAAoB,EAAE,aAAa,CAAC,GAAG;IAC1C;;;OAGG;IACH,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB;;;OAGG;IACH,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB;;OAEG;IACH,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,QAAQ,CAAC;CACxB,CAAC;AAEJ;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,GAAI,wIAWxB,eAAe,gCA4EjB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@transferwise/components",
|
|
3
|
-
"version": "0.0.0-experimental-
|
|
3
|
+
"version": "0.0.0-experimental-e093446",
|
|
4
4
|
"description": "Neptune React components",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -38,22 +38,22 @@
|
|
|
38
38
|
"!**/*.tsbuildinfo"
|
|
39
39
|
],
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@babel/core": "^7.
|
|
42
|
-
"@babel/plugin-transform-runtime": "^7.
|
|
43
|
-
"@babel/preset-env": "^7.
|
|
41
|
+
"@babel/core": "^7.28.5",
|
|
42
|
+
"@babel/plugin-transform-runtime": "^7.28.5",
|
|
43
|
+
"@babel/preset-env": "^7.28.5",
|
|
44
44
|
"@babel/preset-react": "^7.28.5",
|
|
45
45
|
"@babel/preset-typescript": "^7.28.5",
|
|
46
|
-
"@formatjs/cli": "^6.
|
|
46
|
+
"@formatjs/cli": "^6.8.4",
|
|
47
47
|
"@rollup/plugin-babel": "^6.1.0",
|
|
48
48
|
"@rollup/plugin-json": "^6.1.0",
|
|
49
49
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
50
50
|
"@rollup/plugin-typescript": "^12.3.0",
|
|
51
51
|
"@rollup/plugin-url": "^8.0.2",
|
|
52
|
-
"@storybook/addon-a11y": "^10.3.0-alpha.
|
|
53
|
-
"@storybook/addon-docs": "^10.3.0-alpha.
|
|
52
|
+
"@storybook/addon-a11y": "^10.3.0-alpha.0",
|
|
53
|
+
"@storybook/addon-docs": "^10.3.0-alpha.0",
|
|
54
54
|
"@storybook/addon-mcp": "^0.2.2",
|
|
55
55
|
"@storybook/addon-webpack5-compiler-babel": "^4.0.0",
|
|
56
|
-
"@storybook/react-webpack5": "
|
|
56
|
+
"@storybook/react-webpack5": "10.3.0-alpha.0",
|
|
57
57
|
"@testing-library/dom": "^10.4.1",
|
|
58
58
|
"@testing-library/jest-dom": "^6.9.1",
|
|
59
59
|
"@testing-library/react": "^16.3.2",
|
|
@@ -63,37 +63,37 @@
|
|
|
63
63
|
"@types/babel__core": "^7.20.5",
|
|
64
64
|
"@types/commonmark": "^0.27.10",
|
|
65
65
|
"@types/jest": "^30.0.0",
|
|
66
|
-
"@types/lodash": "4.17.
|
|
66
|
+
"@types/lodash": "4.17.21",
|
|
67
67
|
"@types/lodash.clamp": "^4.0.9",
|
|
68
68
|
"@types/lodash.debounce": "^4.0.9",
|
|
69
|
-
"@types/react": "^18.3.
|
|
69
|
+
"@types/react": "^18.3.23",
|
|
70
70
|
"@types/react-dom": "^18.3.7",
|
|
71
71
|
"@types/react-transition-group": "4.4.12",
|
|
72
72
|
"@wise/art": "^2.26.0",
|
|
73
73
|
"@wise/eslint-config": "^13.3.0",
|
|
74
|
-
"babel-plugin-formatjs": "^10.5.
|
|
74
|
+
"babel-plugin-formatjs": "^10.5.39",
|
|
75
75
|
"eslint": "^9.39.2",
|
|
76
|
-
"eslint-plugin-storybook": "^10.3.0-alpha.
|
|
76
|
+
"eslint-plugin-storybook": "^10.3.0-alpha.0",
|
|
77
77
|
"gulp": "^5.0.1",
|
|
78
78
|
"jest": "^30.2.0",
|
|
79
79
|
"jest-environment-jsdom": "^29.7.0",
|
|
80
80
|
"jest-fetch-mock": "^3.0.3",
|
|
81
81
|
"jsdom-testing-mocks": "^1.16.0",
|
|
82
82
|
"lodash.times": "^4.3.2",
|
|
83
|
-
"react-intl": "^7.1.
|
|
84
|
-
"rollup": "^4.
|
|
83
|
+
"react-intl": "^7.1.11",
|
|
84
|
+
"rollup": "^4.54.0",
|
|
85
85
|
"rollup-preserve-directives": "^1.1.3",
|
|
86
|
-
"storybook": "^10.3.0-alpha.
|
|
87
|
-
"storybook-addon-tag-badges": "^3.0.
|
|
86
|
+
"storybook": "^10.3.0-alpha.0",
|
|
87
|
+
"storybook-addon-tag-badges": "^3.0.4",
|
|
88
88
|
"storybook-addon-test-codegen": "^3.0.1",
|
|
89
89
|
"@transferwise/less-config": "3.1.2",
|
|
90
|
-
"@transferwise/neptune-css": "
|
|
90
|
+
"@transferwise/neptune-css": "14.26.2",
|
|
91
91
|
"@wise/components-theming": "1.10.1",
|
|
92
92
|
"@wise/wds-configs": "0.0.0"
|
|
93
93
|
},
|
|
94
94
|
"peerDependencies": {
|
|
95
95
|
"@transferwise/icons": "^3 || ^4",
|
|
96
|
-
"@transferwise/neptune-css": "
|
|
96
|
+
"@transferwise/neptune-css": "^14.26",
|
|
97
97
|
"@wise/art": "^2.26",
|
|
98
98
|
"@wise/components-theming": "^1.9.1",
|
|
99
99
|
"framer-motion": "^12.23.26",
|
|
@@ -102,24 +102,24 @@
|
|
|
102
102
|
"react-intl": "^5.10.0 || ^6 || ^7"
|
|
103
103
|
},
|
|
104
104
|
"dependencies": {
|
|
105
|
-
"@babel/runtime": "^7.28.
|
|
106
|
-
"@floating-ui/react": "^0.
|
|
105
|
+
"@babel/runtime": "^7.28.4",
|
|
106
|
+
"@floating-ui/react": "^0.26.28",
|
|
107
107
|
"@headlessui/react": "^2.2.9",
|
|
108
108
|
"@popperjs/core": "^2.11.8",
|
|
109
|
-
"@react-aria/focus": "^3.21.
|
|
110
|
-
"@react-aria/overlays": "^3.31.
|
|
111
|
-
"@transferwise/formatting": "^2.13.
|
|
112
|
-
"@transferwise/neptune-validation": "^3.3.
|
|
109
|
+
"@react-aria/focus": "^3.21.3",
|
|
110
|
+
"@react-aria/overlays": "^3.31.0",
|
|
111
|
+
"@transferwise/formatting": "^2.13.4",
|
|
112
|
+
"@transferwise/neptune-validation": "^3.3.1",
|
|
113
113
|
"clsx": "^2.1.1",
|
|
114
114
|
"commonmark": "^0.31.2",
|
|
115
|
-
"core-js": "^3.
|
|
116
|
-
"framer-motion": "^12.
|
|
115
|
+
"core-js": "^3.47.0",
|
|
116
|
+
"framer-motion": "^12.23.26",
|
|
117
117
|
"lodash.clamp": "^4.0.3",
|
|
118
118
|
"lodash.debounce": "^4.0.8",
|
|
119
119
|
"merge-props": "^6.0.0",
|
|
120
120
|
"react-popper": "^2.3.0",
|
|
121
121
|
"react-transition-group": "^4.4.5",
|
|
122
|
-
"virtua": "^0.48.
|
|
122
|
+
"virtua": "^0.48.3"
|
|
123
123
|
},
|
|
124
124
|
"publishConfig": {
|
|
125
125
|
"access": "public",
|
package/src/common/index.ts
CHANGED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import { LiveRegion, LiveRegionProps } from './LiveRegion';
|
|
3
|
+
|
|
4
|
+
describe('LiveRegion', () => {
|
|
5
|
+
const renderLiveRegion = (props: Partial<LiveRegionProps> & Pick<LiveRegionProps, 'aria-live'>) =>
|
|
6
|
+
render(<LiveRegion {...props}>{props.children ?? 'Live content'}</LiveRegion>);
|
|
7
|
+
|
|
8
|
+
describe('when aria-live is "polite"', () => {
|
|
9
|
+
it('renders with role="status"', () => {
|
|
10
|
+
renderLiveRegion({ 'aria-live': 'polite' });
|
|
11
|
+
expect(screen.getByRole('status')).toBeInTheDocument();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('sets aria-live="polite"', () => {
|
|
15
|
+
renderLiveRegion({ 'aria-live': 'polite' });
|
|
16
|
+
expect(screen.getByRole('status')).toHaveAttribute('aria-live', 'polite');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('sets aria-atomic="true"', () => {
|
|
20
|
+
renderLiveRegion({ 'aria-live': 'polite' });
|
|
21
|
+
expect(screen.getByRole('status')).toHaveAttribute('aria-atomic', 'true');
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('when aria-live is "assertive"', () => {
|
|
26
|
+
it('renders with role="alert"', () => {
|
|
27
|
+
renderLiveRegion({ 'aria-live': 'assertive' });
|
|
28
|
+
expect(screen.getByRole('alert')).toBeInTheDocument();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('sets aria-live="assertive"', () => {
|
|
32
|
+
renderLiveRegion({ 'aria-live': 'assertive' });
|
|
33
|
+
expect(screen.getByRole('alert')).toHaveAttribute('aria-live', 'assertive');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('sets aria-atomic="true"', () => {
|
|
37
|
+
renderLiveRegion({ 'aria-live': 'assertive' });
|
|
38
|
+
expect(screen.getByRole('alert')).toHaveAttribute('aria-atomic', 'true');
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('renders children', () => {
|
|
43
|
+
renderLiveRegion({ 'aria-live': 'polite', children: 'Transfer sent' });
|
|
44
|
+
expect(screen.getByText('Transfer sent')).toBeInTheDocument();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('passes additional HTML attributes to the wrapper div', () => {
|
|
48
|
+
renderLiveRegion({ 'aria-live': 'polite', className: 'custom' });
|
|
49
|
+
expect(screen.getByRole('status')).toHaveClass('custom');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('supports data-testid prop', () => {
|
|
53
|
+
renderLiveRegion({ 'aria-live': 'polite', 'data-testid': 'live-region' });
|
|
54
|
+
expect(screen.getByTestId('live-region')).toBeInTheDocument();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
const ARIA_LIVE_ROLE_MAP = {
|
|
4
|
+
assertive: 'alert',
|
|
5
|
+
polite: 'status',
|
|
6
|
+
} as const;
|
|
7
|
+
|
|
8
|
+
export type AriaLive = 'off' | 'polite' | 'assertive';
|
|
9
|
+
|
|
10
|
+
export interface LiveRegionProps extends Omit<
|
|
11
|
+
HTMLAttributes<HTMLDivElement>,
|
|
12
|
+
'role' | 'aria-live' | 'aria-atomic'
|
|
13
|
+
> {
|
|
14
|
+
/**
|
|
15
|
+
* Determines urgency: 'assertive' interrupts, 'polite' waits for idle, 'off' disables live region.
|
|
16
|
+
*/
|
|
17
|
+
'aria-live': AriaLive;
|
|
18
|
+
/** Test ID for testing tools */
|
|
19
|
+
'data-testid'?: string;
|
|
20
|
+
children?: ReactNode;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Renders an ARIA live region with the correct implicit role.
|
|
25
|
+
*
|
|
26
|
+
* - `aria-live="polite"` → `role="status"`
|
|
27
|
+
* - `aria-live="assertive"` → `role="alert"`
|
|
28
|
+
* - `aria-live="off"` → no live region
|
|
29
|
+
*
|
|
30
|
+
* The `role` prop is intentionally excluded from the public API
|
|
31
|
+
* to prevent mismatches between `aria-live` and `role`.
|
|
32
|
+
*/
|
|
33
|
+
export const LiveRegion = ({ 'aria-live': ariaLive, children, ...props }: LiveRegionProps) => {
|
|
34
|
+
if (ariaLive === 'off') {
|
|
35
|
+
return <>{children}</>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<div
|
|
40
|
+
role={ARIA_LIVE_ROLE_MAP[ariaLive]}
|
|
41
|
+
aria-live={ariaLive}
|
|
42
|
+
aria-atomic="true"
|
|
43
|
+
style={{ display: 'contents' }}
|
|
44
|
+
{...props}
|
|
45
|
+
>
|
|
46
|
+
{children}
|
|
47
|
+
</div>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
@@ -297,3 +297,122 @@ export const TinyScreen: Story = {
|
|
|
297
297
|
),
|
|
298
298
|
...withVariantConfig(['400%']),
|
|
299
299
|
};
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Test that the default LiveRegion renders with role="status" and aria-live="polite".
|
|
303
|
+
*/
|
|
304
|
+
export const LiveRegionPoliteDefault: Story = {
|
|
305
|
+
play: async ({ canvasElement, step }) => {
|
|
306
|
+
const canvas = within(canvasElement);
|
|
307
|
+
|
|
308
|
+
await step('Verify live region with role="status" exists', async () => {
|
|
309
|
+
await waitFor(async () => {
|
|
310
|
+
const liveRegion = canvas.getByRole('status');
|
|
311
|
+
await expect(liveRegion).toBeInTheDocument();
|
|
312
|
+
await waitFor(async () => expect(liveRegion).toHaveAttribute('aria-live', 'polite'));
|
|
313
|
+
await expect(liveRegion).toHaveAttribute('aria-atomic', 'true');
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
await step('Verify prompt content is inside the live region', async () => {
|
|
318
|
+
const liveRegion = canvas.getByRole('status');
|
|
319
|
+
await expect(within(liveRegion).getByText('Polite announcement')).toBeInTheDocument();
|
|
320
|
+
});
|
|
321
|
+
},
|
|
322
|
+
args: {
|
|
323
|
+
description: 'Polite announcement',
|
|
324
|
+
},
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Test that aria-live="assertive" renders with role="alert".
|
|
329
|
+
*/
|
|
330
|
+
export const LiveRegionAssertive: Story = {
|
|
331
|
+
play: async ({ canvasElement, step }) => {
|
|
332
|
+
const canvas = within(canvasElement);
|
|
333
|
+
|
|
334
|
+
await step('Verify live region with role="alert" exists', async () => {
|
|
335
|
+
await waitFor(async () => {
|
|
336
|
+
const liveRegion = canvas.getByRole('alert');
|
|
337
|
+
await expect(liveRegion).toBeInTheDocument();
|
|
338
|
+
await waitFor(async () => expect(liveRegion).toHaveAttribute('aria-live', 'assertive'));
|
|
339
|
+
await expect(liveRegion).toHaveAttribute('aria-atomic', 'true');
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
await step('Verify prompt content is inside the live region', async () => {
|
|
344
|
+
const liveRegion = canvas.getByRole('alert');
|
|
345
|
+
await expect(within(liveRegion).getByText('Payment failed')).toBeInTheDocument();
|
|
346
|
+
});
|
|
347
|
+
},
|
|
348
|
+
args: {
|
|
349
|
+
sentiment: 'negative',
|
|
350
|
+
description: 'Payment failed',
|
|
351
|
+
'aria-live': 'assertive',
|
|
352
|
+
},
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Test that aria-live="off" renders without a live region wrapper.
|
|
357
|
+
*/
|
|
358
|
+
export const LiveRegionOff: Story = {
|
|
359
|
+
play: async ({ canvasElement, step }) => {
|
|
360
|
+
const canvas = within(canvasElement);
|
|
361
|
+
|
|
362
|
+
await step('Verify no live region wrapper exists', async () => {
|
|
363
|
+
await waitFor(async () => {
|
|
364
|
+
await expect(canvas.queryByRole('status')).not.toBeInTheDocument();
|
|
365
|
+
await expect(canvas.queryByRole('alert')).not.toBeInTheDocument();
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
await step('Verify prompt content still renders', async () => {
|
|
370
|
+
await expect(canvas.getByText('Static info')).toBeInTheDocument();
|
|
371
|
+
});
|
|
372
|
+
},
|
|
373
|
+
args: {
|
|
374
|
+
description: 'Static info',
|
|
375
|
+
'aria-live': 'off',
|
|
376
|
+
},
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Test that a dismissed prompt also removes the live region.
|
|
381
|
+
*/
|
|
382
|
+
export const LiveRegionDismiss: Story = {
|
|
383
|
+
play: async ({ canvasElement, step }) => {
|
|
384
|
+
const canvas = within(canvasElement);
|
|
385
|
+
|
|
386
|
+
await step('Verify live region exists before dismiss', async () => {
|
|
387
|
+
await waitFor(async () => {
|
|
388
|
+
await expect(canvas.getByRole('status')).toBeInTheDocument();
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
await step('Dismiss the prompt', async () => {
|
|
393
|
+
const dismissButton = canvas.getByRole('button', { name: /close/i });
|
|
394
|
+
await userEvent.click(dismissButton);
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
await step('Verify live region is removed', async () => {
|
|
398
|
+
await waitFor(async () => {
|
|
399
|
+
await expect(canvas.queryByRole('status')).not.toBeInTheDocument();
|
|
400
|
+
});
|
|
401
|
+
});
|
|
402
|
+
},
|
|
403
|
+
render: function Render(args: InfoPromptProps) {
|
|
404
|
+
const [isVisible, setIsVisible] = useState(true);
|
|
405
|
+
|
|
406
|
+
if (!isVisible) {
|
|
407
|
+
return <div data-testid="dismissed">Prompt dismissed</div>;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
return (
|
|
411
|
+
<InfoPrompt
|
|
412
|
+
{...args}
|
|
413
|
+
description="Dismissable live region"
|
|
414
|
+
onDismiss={() => setIsVisible(false)}
|
|
415
|
+
/>
|
|
416
|
+
);
|
|
417
|
+
},
|
|
418
|
+
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { HTMLAttributes, ReactNode, useState } from 'react';
|
|
2
|
-
import { Sentiment, Typography } from '../../common';
|
|
2
|
+
import { LiveRegion, Sentiment, Typography } from '../../common';
|
|
3
|
+
import type { AriaLive } from '../../common';
|
|
3
4
|
import { GiftBox } from '@transferwise/icons';
|
|
4
5
|
import type { Sentiment as SurfaceSentiment } from '../../sentimentSurface';
|
|
5
6
|
import StatusIcon from '../../statusIcon';
|
|
@@ -23,7 +24,7 @@ export type InfoPromptMedia = {
|
|
|
23
24
|
asset: ReactNode;
|
|
24
25
|
};
|
|
25
26
|
|
|
26
|
-
export type InfoPromptProps = Omit<HTMLAttributes<HTMLDivElement>, 'title'> &
|
|
27
|
+
export type InfoPromptProps = Omit<HTMLAttributes<HTMLDivElement>, 'title' | 'aria-live' | 'role'> &
|
|
27
28
|
Pick<PrimitivePromptProps, 'data-testid'> & {
|
|
28
29
|
/**
|
|
29
30
|
* The sentiment determines the colour scheme
|
|
@@ -53,6 +54,14 @@ export type InfoPromptProps = Omit<HTMLAttributes<HTMLDivElement>, 'title'> &
|
|
|
53
54
|
*/
|
|
54
55
|
description: string;
|
|
55
56
|
className?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Sets the ARIA live region politeness level.
|
|
59
|
+
* - `'polite'` — announced after the current speech (default)
|
|
60
|
+
* - `'assertive'` — interrupts the current speech immediately
|
|
61
|
+
* - `'off'` — disables the live region entirely
|
|
62
|
+
* @default 'polite'
|
|
63
|
+
*/
|
|
64
|
+
'aria-live'?: AriaLive;
|
|
56
65
|
};
|
|
57
66
|
|
|
58
67
|
/**
|
|
@@ -72,6 +81,7 @@ export const InfoPrompt = ({
|
|
|
72
81
|
title,
|
|
73
82
|
description,
|
|
74
83
|
className,
|
|
84
|
+
'aria-live': ariaLive = 'polite',
|
|
75
85
|
'data-testid': dataTestId,
|
|
76
86
|
...restProps
|
|
77
87
|
}: InfoPromptProps) => {
|
|
@@ -112,39 +122,42 @@ export const InfoPrompt = ({
|
|
|
112
122
|
return <StatusIcon size={24} sentiment={statusIconSentiment} />;
|
|
113
123
|
};
|
|
114
124
|
|
|
125
|
+
// Render content directly in LiveRegion
|
|
115
126
|
return (
|
|
116
|
-
<
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
{
|
|
127
|
+
<LiveRegion aria-live={ariaLive}>
|
|
128
|
+
<PrimitivePrompt
|
|
129
|
+
sentiment={sentiment}
|
|
130
|
+
media={renderMedia()}
|
|
131
|
+
data-testid={dataTestId}
|
|
132
|
+
className={clsx('wds-info-prompt', className)}
|
|
133
|
+
{...restProps}
|
|
134
|
+
onTouchStart={handleTouchStart}
|
|
135
|
+
onTouchEnd={handleTouchEnd}
|
|
136
|
+
onTouchMove={handleTouchMove}
|
|
137
|
+
onDismiss={onDismiss}
|
|
138
|
+
>
|
|
139
|
+
<div className="wds-info-prompt__content">
|
|
140
|
+
{title && (
|
|
141
|
+
<Body className="wds-info-prompt__title" type={Typography.BODY_LARGE_BOLD} as="span">
|
|
142
|
+
{title}
|
|
143
|
+
</Body>
|
|
144
|
+
)}
|
|
145
|
+
<Body as="span" className="wds-info-prompt__description">
|
|
146
|
+
{description}
|
|
131
147
|
</Body>
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
)}
|
|
147
|
-
</div>
|
|
148
|
-
</PrimitivePrompt>
|
|
148
|
+
{action && (
|
|
149
|
+
<Link
|
|
150
|
+
href={action.href}
|
|
151
|
+
target={action.target}
|
|
152
|
+
type={Typography.LINK_DEFAULT}
|
|
153
|
+
className="wds-info-prompt__action"
|
|
154
|
+
onClick={action.onClick}
|
|
155
|
+
>
|
|
156
|
+
{action.label}
|
|
157
|
+
</Link>
|
|
158
|
+
)}
|
|
159
|
+
</div>
|
|
160
|
+
</PrimitivePrompt>
|
|
161
|
+
</LiveRegion>
|
|
149
162
|
);
|
|
150
163
|
};
|