@transferwise/components 46.136.1 → 46.137.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/common/hooks/useContainerSize.js +30 -0
- package/build/common/hooks/useContainerSize.js.map +1 -0
- package/build/common/hooks/useContainerSize.mjs +28 -0
- package/build/common/hooks/useContainerSize.mjs.map +1 -0
- package/build/common/hooks/useResizeObserver.js +3 -3
- package/build/common/hooks/useResizeObserver.js.map +1 -1
- package/build/common/hooks/useResizeObserver.mjs +3 -3
- package/build/common/hooks/useResizeObserver.mjs.map +1 -1
- package/build/criticalBanner/CriticalCommsBanner.js +3 -0
- package/build/criticalBanner/CriticalCommsBanner.js.map +1 -1
- package/build/criticalBanner/CriticalCommsBanner.mjs +3 -0
- package/build/criticalBanner/CriticalCommsBanner.mjs.map +1 -1
- package/build/field/Field.js +3 -2
- package/build/field/Field.js.map +1 -1
- package/build/field/Field.mjs +3 -2
- package/build/field/Field.mjs.map +1 -1
- package/build/i18n/cs.json +2 -0
- package/build/i18n/cs.json.js +2 -0
- package/build/i18n/cs.json.js.map +1 -1
- package/build/i18n/cs.json.mjs +2 -0
- package/build/i18n/cs.json.mjs.map +1 -1
- package/build/i18n/de.json +2 -0
- package/build/i18n/de.json.js +2 -0
- package/build/i18n/de.json.js.map +1 -1
- package/build/i18n/de.json.mjs +2 -0
- package/build/i18n/de.json.mjs.map +1 -1
- package/build/i18n/en.json +2 -0
- package/build/i18n/en.json.js +2 -0
- package/build/i18n/en.json.js.map +1 -1
- package/build/i18n/en.json.mjs +2 -0
- package/build/i18n/en.json.mjs.map +1 -1
- package/build/i18n/es.json +2 -0
- package/build/i18n/es.json.js +2 -0
- package/build/i18n/es.json.js.map +1 -1
- package/build/i18n/es.json.mjs +2 -0
- package/build/i18n/es.json.mjs.map +1 -1
- package/build/i18n/fr.json +2 -0
- package/build/i18n/fr.json.js +2 -0
- package/build/i18n/fr.json.js.map +1 -1
- package/build/i18n/fr.json.mjs +2 -0
- package/build/i18n/fr.json.mjs.map +1 -1
- package/build/i18n/hu.json +2 -0
- package/build/i18n/hu.json.js +2 -0
- package/build/i18n/hu.json.js.map +1 -1
- package/build/i18n/hu.json.mjs +2 -0
- package/build/i18n/hu.json.mjs.map +1 -1
- package/build/i18n/id.json +2 -0
- package/build/i18n/id.json.js +2 -0
- package/build/i18n/id.json.js.map +1 -1
- package/build/i18n/id.json.mjs +2 -0
- package/build/i18n/id.json.mjs.map +1 -1
- package/build/i18n/it.json +2 -0
- package/build/i18n/it.json.js +2 -0
- package/build/i18n/it.json.js.map +1 -1
- package/build/i18n/it.json.mjs +2 -0
- package/build/i18n/it.json.mjs.map +1 -1
- package/build/i18n/ja.json +2 -0
- package/build/i18n/ja.json.js +2 -0
- package/build/i18n/ja.json.js.map +1 -1
- package/build/i18n/ja.json.mjs +2 -0
- package/build/i18n/ja.json.mjs.map +1 -1
- package/build/i18n/nl.json +2 -0
- package/build/i18n/nl.json.js +2 -0
- package/build/i18n/nl.json.js.map +1 -1
- package/build/i18n/nl.json.mjs +2 -0
- package/build/i18n/nl.json.mjs.map +1 -1
- package/build/i18n/pl.json +2 -0
- package/build/i18n/pl.json.js +2 -0
- package/build/i18n/pl.json.js.map +1 -1
- package/build/i18n/pl.json.mjs +2 -0
- package/build/i18n/pl.json.mjs.map +1 -1
- package/build/i18n/pt.json +2 -0
- package/build/i18n/pt.json.js +2 -0
- package/build/i18n/pt.json.js.map +1 -1
- package/build/i18n/pt.json.mjs +2 -0
- package/build/i18n/pt.json.mjs.map +1 -1
- package/build/i18n/ro.json +2 -0
- package/build/i18n/ro.json.js +2 -0
- package/build/i18n/ro.json.js.map +1 -1
- package/build/i18n/ro.json.mjs +2 -0
- package/build/i18n/ro.json.mjs.map +1 -1
- package/build/i18n/ru.json +2 -0
- package/build/i18n/ru.json.js +2 -0
- package/build/i18n/ru.json.js.map +1 -1
- package/build/i18n/ru.json.mjs +2 -0
- package/build/i18n/ru.json.mjs.map +1 -1
- package/build/i18n/th.json +2 -0
- package/build/i18n/th.json.js +2 -0
- package/build/i18n/th.json.js.map +1 -1
- package/build/i18n/th.json.mjs +2 -0
- package/build/i18n/th.json.mjs.map +1 -1
- package/build/i18n/tr.json +2 -0
- package/build/i18n/tr.json.js +2 -0
- package/build/i18n/tr.json.js.map +1 -1
- package/build/i18n/tr.json.mjs +2 -0
- package/build/i18n/tr.json.mjs.map +1 -1
- package/build/i18n/zh-CN.json +2 -0
- package/build/i18n/zh-CN.json.js +2 -0
- package/build/i18n/zh-CN.json.js.map +1 -1
- package/build/i18n/zh-CN.json.mjs +2 -0
- package/build/i18n/zh-CN.json.mjs.map +1 -1
- package/build/i18n/zh-HK.json +2 -0
- package/build/i18n/zh-HK.json.js +2 -0
- package/build/i18n/zh-HK.json.js.map +1 -1
- package/build/i18n/zh-HK.json.mjs +2 -0
- package/build/i18n/zh-HK.json.mjs.map +1 -1
- package/build/index.js +2 -0
- package/build/index.js.map +1 -1
- package/build/index.mjs +1 -0
- package/build/index.mjs.map +1 -1
- package/build/listItem/Prompt/ListItemPrompt.js +3 -2
- package/build/listItem/Prompt/ListItemPrompt.js.map +1 -1
- package/build/listItem/Prompt/ListItemPrompt.mjs +3 -2
- package/build/listItem/Prompt/ListItemPrompt.mjs.map +1 -1
- package/build/logo/Logo.js +77 -25
- package/build/logo/Logo.js.map +1 -1
- package/build/logo/Logo.mjs +79 -27
- package/build/logo/Logo.mjs.map +1 -1
- package/build/logo/logo-assets.js +68 -97
- package/build/logo/logo-assets.js.map +1 -1
- package/build/logo/logo-assets.mjs +62 -90
- package/build/logo/logo-assets.mjs.map +1 -1
- package/build/main.css +225 -59
- package/build/prompt/ActionPrompt/ActionPrompt.js +8 -40
- package/build/prompt/ActionPrompt/ActionPrompt.js.map +1 -1
- package/build/prompt/ActionPrompt/ActionPrompt.mjs +8 -40
- package/build/prompt/ActionPrompt/ActionPrompt.mjs.map +1 -1
- package/build/prompt/CriticalBanner/CriticalBanner.js +143 -0
- package/build/prompt/CriticalBanner/CriticalBanner.js.map +1 -0
- package/build/prompt/CriticalBanner/CriticalBanner.mjs +141 -0
- package/build/prompt/CriticalBanner/CriticalBanner.mjs.map +1 -0
- package/build/prompt/CriticalBanner/helpers.js +29 -0
- package/build/prompt/CriticalBanner/helpers.js.map +1 -0
- package/build/prompt/CriticalBanner/helpers.mjs +26 -0
- package/build/prompt/CriticalBanner/helpers.mjs.map +1 -0
- package/build/prompt/InfoPrompt/InfoPrompt.js +3 -2
- package/build/prompt/InfoPrompt/InfoPrompt.js.map +1 -1
- package/build/prompt/InfoPrompt/InfoPrompt.mjs +3 -2
- package/build/prompt/InfoPrompt/InfoPrompt.mjs.map +1 -1
- package/build/prompt/PrimitivePrompt/PrimitivePrompt.js +11 -4
- package/build/prompt/PrimitivePrompt/PrimitivePrompt.js.map +1 -1
- package/build/prompt/PrimitivePrompt/PrimitivePrompt.mjs +11 -4
- package/build/prompt/PrimitivePrompt/PrimitivePrompt.mjs.map +1 -1
- package/build/prompt/common/Expander/Expander.js +35 -0
- package/build/prompt/common/Expander/Expander.js.map +1 -0
- package/build/prompt/common/Expander/Expander.messages.js +17 -0
- package/build/prompt/common/Expander/Expander.messages.js.map +1 -0
- package/build/prompt/common/Expander/Expander.messages.mjs +13 -0
- package/build/prompt/common/Expander/Expander.messages.mjs.map +1 -0
- package/build/prompt/common/Expander/Expander.mjs +33 -0
- package/build/prompt/common/Expander/Expander.mjs.map +1 -0
- package/build/prompt/helpers/promptMedia.js +52 -0
- package/build/prompt/helpers/promptMedia.js.map +1 -0
- package/build/prompt/helpers/promptMedia.mjs +50 -0
- package/build/prompt/helpers/promptMedia.mjs.map +1 -0
- package/build/styles/css/neptune.css +0 -1
- package/build/styles/logo/Logo.css +3 -23
- package/build/styles/main.css +225 -59
- package/build/styles/prompt/CriticalBanner/CriticalBanner.css +134 -0
- package/build/styles/prompt/CriticalBanner/CriticalBanner.vars.css +0 -0
- package/build/styles/prompt/InfoPrompt/InfoPrompt.css +24 -0
- package/build/styles/prompt/common/Expander/Expander.css +8 -0
- package/build/styles/styles/less/neptune.css +0 -1
- package/build/typeahead/Typeahead.js +3 -2
- package/build/typeahead/Typeahead.js.map +1 -1
- package/build/typeahead/Typeahead.mjs +3 -2
- package/build/typeahead/Typeahead.mjs.map +1 -1
- package/build/types/common/hooks/useContainerSize.d.ts +14 -0
- package/build/types/common/hooks/useContainerSize.d.ts.map +1 -0
- package/build/types/common/hooks/useResizeObserver.d.ts +1 -1
- package/build/types/common/hooks/useResizeObserver.d.ts.map +1 -1
- package/build/types/criticalBanner/CriticalCommsBanner.d.ts +3 -0
- package/build/types/criticalBanner/CriticalCommsBanner.d.ts.map +1 -1
- package/build/types/index.d.ts +2 -2
- package/build/types/index.d.ts.map +1 -1
- package/build/types/logo/Logo.d.ts +33 -1
- package/build/types/logo/Logo.d.ts.map +1 -1
- package/build/types/logo/logo-assets.d.ts +33 -9
- package/build/types/logo/logo-assets.d.ts.map +1 -1
- package/build/types/prompt/ActionPrompt/ActionPrompt.d.ts +2 -11
- package/build/types/prompt/ActionPrompt/ActionPrompt.d.ts.map +1 -1
- package/build/types/prompt/CriticalBanner/CriticalBanner.d.ts +39 -0
- package/build/types/prompt/CriticalBanner/CriticalBanner.d.ts.map +1 -0
- package/build/types/prompt/CriticalBanner/helpers.d.ts +18 -0
- package/build/types/prompt/CriticalBanner/helpers.d.ts.map +1 -0
- package/build/types/prompt/CriticalBanner/index.d.ts +3 -0
- package/build/types/prompt/CriticalBanner/index.d.ts.map +1 -0
- package/build/types/prompt/InfoPrompt/InfoPrompt.d.ts.map +1 -1
- package/build/types/prompt/PrimitivePrompt/PrimitivePrompt.d.ts +35 -3
- package/build/types/prompt/PrimitivePrompt/PrimitivePrompt.d.ts.map +1 -1
- package/build/types/prompt/common/Expander/Expander.d.ts +20 -0
- package/build/types/prompt/common/Expander/Expander.d.ts.map +1 -0
- package/build/types/prompt/common/Expander/Expander.messages.d.ts +14 -0
- package/build/types/prompt/common/Expander/Expander.messages.d.ts.map +1 -0
- package/build/types/prompt/helpers/promptMedia.d.ts +22 -0
- package/build/types/prompt/helpers/promptMedia.d.ts.map +1 -0
- package/build/types/prompt/index.d.ts +2 -0
- package/build/types/prompt/index.d.ts.map +1 -1
- package/build/types/test-utils/index.d.ts +4 -0
- package/build/types/test-utils/index.d.ts.map +1 -1
- package/package.json +22 -18
- package/src/alert/Alert.story.tsx +30 -1
- package/src/avatarWrapper/AvatarWrapper.story.tsx +1 -1
- package/src/button/_stories/Button.story.tsx +11 -0
- package/src/common/hooks/useContainerSize.test.tsx +125 -0
- package/src/common/hooks/useContainerSize.ts +32 -0
- package/src/common/hooks/useResizeObserver.ts +3 -2
- package/src/criticalBanner/CriticalCommsBanner.story.tsx +4 -0
- package/src/criticalBanner/CriticalCommsBanner.test.story.tsx +1 -1
- package/src/criticalBanner/CriticalCommsBanner.tsx +3 -0
- package/src/i18n/cs.json +2 -0
- package/src/i18n/de.json +2 -0
- package/src/i18n/en.json +2 -0
- package/src/i18n/es.json +2 -0
- package/src/i18n/fr.json +2 -0
- package/src/i18n/hu.json +2 -0
- package/src/i18n/id.json +2 -0
- package/src/i18n/it.json +2 -0
- package/src/i18n/ja.json +2 -0
- package/src/i18n/nl.json +2 -0
- package/src/i18n/pl.json +2 -0
- package/src/i18n/pt.json +2 -0
- package/src/i18n/ro.json +2 -0
- package/src/i18n/ru.json +2 -0
- package/src/i18n/th.json +2 -0
- package/src/i18n/tr.json +2 -0
- package/src/i18n/zh-CN.json +2 -0
- package/src/i18n/zh-HK.json +2 -0
- package/src/index.ts +2 -2
- package/src/logo/Logo.css +3 -23
- package/src/logo/Logo.less +3 -29
- package/src/logo/Logo.story.tsx +117 -89
- package/src/logo/Logo.test.story.tsx +15 -24
- package/src/logo/Logo.tsx +90 -28
- package/src/logo/logo-assets.tsx +36 -92
- package/src/main.css +225 -59
- package/src/main.less +3 -1
- package/src/prompt/ActionPrompt/ActionPrompt.tsx +9 -62
- package/src/prompt/CriticalBanner/CriticalBanner.accessibility.docs.mdx +113 -0
- package/src/prompt/CriticalBanner/CriticalBanner.css +134 -0
- package/src/prompt/CriticalBanner/CriticalBanner.less +155 -0
- package/src/prompt/CriticalBanner/CriticalBanner.story.tsx +635 -0
- package/src/prompt/CriticalBanner/CriticalBanner.test.story.tsx +422 -0
- package/src/prompt/CriticalBanner/CriticalBanner.tsx +179 -0
- package/src/prompt/CriticalBanner/CriticalBanner.vars.css +0 -0
- package/src/prompt/CriticalBanner/CriticalBanner.vars.less +6 -0
- package/src/prompt/CriticalBanner/helpers.ts +39 -0
- package/src/prompt/CriticalBanner/index.ts +2 -0
- package/src/prompt/InfoPrompt/InfoPrompt.css +24 -0
- package/src/prompt/InfoPrompt/InfoPrompt.less +23 -0
- package/src/prompt/InfoPrompt/InfoPrompt.tsx +5 -1
- package/src/prompt/PrimitivePrompt/PrimitivePrompt.tsx +56 -40
- package/src/prompt/common/Expander/Expander.css +8 -0
- package/src/prompt/common/Expander/Expander.less +9 -0
- package/src/prompt/common/Expander/Expander.messages.ts +14 -0
- package/src/prompt/common/Expander/Expander.test.tsx +167 -0
- package/src/prompt/common/Expander/Expander.tsx +83 -0
- package/src/prompt/helpers/promptMedia.tsx +79 -0
- package/src/prompt/index.ts +4 -0
- package/src/radio/Radio.story.tsx +1 -1
- package/src/section/Section.story.tsx +2 -8
- package/src/sentimentSurface/SentimentSurface.story.tsx +43 -17
- package/src/statusIcon/StatusIcon.test.tsx +0 -2
- package/src/styles/less/neptune.css +0 -1
|
@@ -33,5 +33,28 @@
|
|
|
33
33
|
|
|
34
34
|
.wds-prompt__media-wrapper {
|
|
35
35
|
padding: 0;
|
|
36
|
+
|
|
37
|
+
@media (--screen-400-zoom) {
|
|
38
|
+
padding-top: var(--size-4);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
&:has(.wds-info-prompt__media > .tw-icon) {
|
|
42
|
+
@media (--screen-400-zoom) {
|
|
43
|
+
padding-top: var(--size-8);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// More padding for when there is a title
|
|
48
|
+
&:has(+ .wds-info-prompt__content .wds-info-prompt__title:first-child) {
|
|
49
|
+
@media (--screen-400-zoom) {
|
|
50
|
+
padding-top: var(--size-8);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
&:has(.wds-info-prompt__media > .tw-icon) {
|
|
54
|
+
@media (--screen-400-zoom) {
|
|
55
|
+
padding-top: var(--size-12);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
36
59
|
}
|
|
37
60
|
}
|
|
@@ -117,7 +117,11 @@ export const InfoPrompt = ({
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
if (sentiment === 'proposition') {
|
|
120
|
-
return
|
|
120
|
+
return (
|
|
121
|
+
<span className="wds-info-prompt__media">
|
|
122
|
+
<GiftBox />
|
|
123
|
+
</span>
|
|
124
|
+
);
|
|
121
125
|
}
|
|
122
126
|
|
|
123
127
|
return <StatusIcon size={24} sentiment={statusIconSentiment} />;
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import { Cross } from '@transferwise/icons';
|
|
2
2
|
import { clsx } from 'clsx';
|
|
3
|
-
import SentimentSurface, { Sentiment } from '../../sentimentSurface';
|
|
3
|
+
import SentimentSurface, { Emphasis, Sentiment } from '../../sentimentSurface';
|
|
4
4
|
import IconButton from '../../iconButton';
|
|
5
5
|
import { useIntl } from 'react-intl';
|
|
6
6
|
import closeBtnMessages from '../../common/closeButton/CloseButton.messages';
|
|
7
|
-
import { HTMLAttributes, ReactNode } from 'react';
|
|
7
|
+
import { forwardRef, HTMLAttributes, ReactNode } from 'react';
|
|
8
8
|
|
|
9
9
|
export type PrimitivePromptProps = HTMLAttributes<HTMLDivElement> & {
|
|
10
10
|
/**
|
|
11
|
-
* The sentiment determines the colour scheme
|
|
11
|
+
* The sentiment determines the colour scheme.
|
|
12
12
|
* @default success
|
|
13
13
|
*/
|
|
14
14
|
sentiment?: Sentiment;
|
|
15
|
+
/**
|
|
16
|
+
* The emphasis level affecting background and text contrast.
|
|
17
|
+
* @default 'base'
|
|
18
|
+
*/
|
|
19
|
+
emphasis?: Emphasis;
|
|
15
20
|
/**
|
|
16
21
|
* Media to be displayed on the prompt (icon/image/etc).
|
|
17
22
|
*/
|
|
@@ -33,42 +38,53 @@ export type PrimitivePromptProps = HTMLAttributes<HTMLDivElement> & {
|
|
|
33
38
|
/**
|
|
34
39
|
* PrimitivePrompt is a low-level component that provides the structure, sentiment support and styling for various prompts.
|
|
35
40
|
* Uses several css variables to handle styling from within the consuming component, e.g. --Prompt-padding. */
|
|
36
|
-
export const PrimitivePrompt = (
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
export const PrimitivePrompt = forwardRef<HTMLDivElement, PrimitivePromptProps>(
|
|
42
|
+
(
|
|
43
|
+
{
|
|
44
|
+
sentiment = 'success',
|
|
45
|
+
emphasis = 'base',
|
|
46
|
+
media,
|
|
47
|
+
actions,
|
|
48
|
+
onDismiss,
|
|
49
|
+
className,
|
|
50
|
+
children,
|
|
51
|
+
...restProps
|
|
52
|
+
},
|
|
53
|
+
ref,
|
|
54
|
+
) => {
|
|
55
|
+
const intl = useIntl();
|
|
46
56
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
'wds-prompt__content-wrapper--with-dismiss': !!onDismiss,
|
|
56
|
-
})}
|
|
57
|
+
return (
|
|
58
|
+
<SentimentSurface
|
|
59
|
+
// @ts-expect-error - SentimentSurface forwardRef types don't expose ref in props
|
|
60
|
+
ref={ref}
|
|
61
|
+
sentiment={sentiment}
|
|
62
|
+
emphasis={emphasis}
|
|
63
|
+
className={clsx('wds-prompt', `wds-prompt--${sentiment}`, className)}
|
|
64
|
+
{...restProps}
|
|
57
65
|
>
|
|
58
|
-
<div
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
66
|
+
<div
|
|
67
|
+
className={clsx('wds-prompt__content-wrapper', {
|
|
68
|
+
'wds-prompt__content-wrapper--with-dismiss': !!onDismiss,
|
|
69
|
+
})}
|
|
70
|
+
>
|
|
71
|
+
<div className={clsx('wds-prompt__media-wrapper')}>{media}</div>
|
|
72
|
+
{children}
|
|
73
|
+
{onDismiss && (
|
|
74
|
+
<IconButton
|
|
75
|
+
size={24}
|
|
76
|
+
priority="secondary"
|
|
77
|
+
aria-label={intl.formatMessage(closeBtnMessages.ariaLabel)}
|
|
78
|
+
onClick={onDismiss}
|
|
79
|
+
>
|
|
80
|
+
<Cross />
|
|
81
|
+
</IconButton>
|
|
82
|
+
)}
|
|
83
|
+
{actions && <div className="wds-prompt__actions-wrapper">{actions}</div>}
|
|
84
|
+
</div>
|
|
85
|
+
</SentimentSurface>
|
|
86
|
+
);
|
|
87
|
+
},
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
PrimitivePrompt.displayName = 'PrimitivePrompt';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { defineMessages } from 'react-intl';
|
|
2
|
+
|
|
3
|
+
export default defineMessages({
|
|
4
|
+
expandAriaLabel: {
|
|
5
|
+
id: 'neptune.Expander.expandAriaLabel',
|
|
6
|
+
defaultMessage: 'Expand',
|
|
7
|
+
description: 'Expand button for expander',
|
|
8
|
+
},
|
|
9
|
+
collapseAriaLabel: {
|
|
10
|
+
id: 'neptune.Expander.collapseAriaLabel',
|
|
11
|
+
defaultMessage: 'Collapse',
|
|
12
|
+
description: 'Collapse button for expander',
|
|
13
|
+
},
|
|
14
|
+
});
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { renderHook, act, screen } from '@testing-library/react';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
|
|
4
|
+
import { useExpanderState, shouldShowWhenExpanded, ExpanderToggle } from './Expander';
|
|
5
|
+
import { mockMatchMedia, render } from '../../../test-utils';
|
|
6
|
+
|
|
7
|
+
mockMatchMedia();
|
|
8
|
+
|
|
9
|
+
describe('useExpanderState', () => {
|
|
10
|
+
it('initializes with default expanded state (true)', () => {
|
|
11
|
+
const { result } = renderHook(() => useExpanderState());
|
|
12
|
+
|
|
13
|
+
expect(result.current.expanded).toBe(true);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('initializes with custom expanded state', () => {
|
|
17
|
+
const { result } = renderHook(() => useExpanderState(false));
|
|
18
|
+
|
|
19
|
+
expect(result.current.expanded).toBe(false);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('toggles expanded state', () => {
|
|
23
|
+
const { result } = renderHook(() => useExpanderState(true));
|
|
24
|
+
|
|
25
|
+
expect(result.current.expanded).toBe(true);
|
|
26
|
+
|
|
27
|
+
act(() => {
|
|
28
|
+
result.current.toggle();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
expect(result.current.expanded).toBe(false);
|
|
32
|
+
|
|
33
|
+
act(() => {
|
|
34
|
+
result.current.toggle();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
expect(result.current.expanded).toBe(true);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('sets expanded state programmatically', () => {
|
|
41
|
+
const { result } = renderHook(() => useExpanderState(true));
|
|
42
|
+
|
|
43
|
+
act(() => {
|
|
44
|
+
result.current.setExpanded(false);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
expect(result.current.expanded).toBe(false);
|
|
48
|
+
|
|
49
|
+
act(() => {
|
|
50
|
+
result.current.setExpanded(true);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
expect(result.current.expanded).toBe(true);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('memoizes toggle callback', () => {
|
|
57
|
+
const { result, rerender } = renderHook(() => useExpanderState(true));
|
|
58
|
+
|
|
59
|
+
const firstToggle = result.current.toggle;
|
|
60
|
+
|
|
61
|
+
rerender();
|
|
62
|
+
|
|
63
|
+
expect(result.current.toggle).toBe(firstToggle);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe('shouldShowWhenExpanded', () => {
|
|
68
|
+
it('returns false when hasContent is false', () => {
|
|
69
|
+
expect(shouldShowWhenExpanded({ expanded: true, hasContent: false })).toBe(false);
|
|
70
|
+
expect(shouldShowWhenExpanded({ expanded: false, hasContent: false })).toBe(false);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('returns true when expanded and hasContent are true', () => {
|
|
74
|
+
expect(shouldShowWhenExpanded({ expanded: true, hasContent: true })).toBe(true);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('returns false when collapsed but hasContent is true', () => {
|
|
78
|
+
expect(shouldShowWhenExpanded({ expanded: false, hasContent: true })).toBe(false);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('returns true when alwaysShow is true and hasContent is true, regardless of expanded', () => {
|
|
82
|
+
expect(shouldShowWhenExpanded({ expanded: true, hasContent: true, alwaysShow: true })).toBe(
|
|
83
|
+
true,
|
|
84
|
+
);
|
|
85
|
+
expect(shouldShowWhenExpanded({ expanded: false, hasContent: true, alwaysShow: true })).toBe(
|
|
86
|
+
true,
|
|
87
|
+
);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('returns false when alwaysShow is true but hasContent is false', () => {
|
|
91
|
+
expect(shouldShowWhenExpanded({ expanded: true, hasContent: false, alwaysShow: true })).toBe(
|
|
92
|
+
false,
|
|
93
|
+
);
|
|
94
|
+
expect(shouldShowWhenExpanded({ expanded: false, hasContent: false, alwaysShow: true })).toBe(
|
|
95
|
+
false,
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe('ExpanderToggle', () => {
|
|
101
|
+
it('renders with correct aria-label when expanded', () => {
|
|
102
|
+
render(<ExpanderToggle expanded onToggle={jest.fn()} />);
|
|
103
|
+
|
|
104
|
+
expect(screen.getByRole('button')).toHaveAttribute('aria-label', 'Collapse');
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('renders with correct aria-label when collapsed', () => {
|
|
108
|
+
render(<ExpanderToggle expanded={false} onToggle={jest.fn()} />);
|
|
109
|
+
|
|
110
|
+
expect(screen.getByRole('button')).toHaveAttribute('aria-label', 'Expand');
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('renders with custom labels', () => {
|
|
114
|
+
const { rerender } = render(<ExpanderToggle expanded onToggle={jest.fn()} />);
|
|
115
|
+
|
|
116
|
+
expect(screen.getByRole('button')).toHaveAttribute('aria-label', 'Collapse');
|
|
117
|
+
|
|
118
|
+
rerender(<ExpanderToggle expanded={false} onToggle={jest.fn()} />);
|
|
119
|
+
|
|
120
|
+
expect(screen.getByRole('button')).toHaveAttribute('aria-label', 'Expand');
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('sets aria-expanded attribute correctly', () => {
|
|
124
|
+
const { rerender } = render(<ExpanderToggle expanded onToggle={jest.fn()} />);
|
|
125
|
+
|
|
126
|
+
expect(screen.getByRole('button')).toHaveAttribute('aria-expanded', 'true');
|
|
127
|
+
|
|
128
|
+
rerender(<ExpanderToggle expanded={false} onToggle={jest.fn()} />);
|
|
129
|
+
|
|
130
|
+
expect(screen.getByRole('button')).toHaveAttribute('aria-expanded', 'false');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('calls onToggle when clicked', async () => {
|
|
134
|
+
const user = userEvent.setup();
|
|
135
|
+
const onToggle = jest.fn();
|
|
136
|
+
|
|
137
|
+
render(<ExpanderToggle expanded onToggle={onToggle} />);
|
|
138
|
+
|
|
139
|
+
await user.click(screen.getByRole('button'));
|
|
140
|
+
|
|
141
|
+
expect(onToggle).toHaveBeenCalledTimes(1);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('applies custom className', () => {
|
|
145
|
+
render(<ExpanderToggle expanded className="custom-class" onToggle={jest.fn()} />);
|
|
146
|
+
|
|
147
|
+
expect(screen.getByRole('button')).toHaveClass('custom-class');
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('applies base class and collapsed class correctly', () => {
|
|
151
|
+
const { rerender } = render(<ExpanderToggle expanded onToggle={jest.fn()} />);
|
|
152
|
+
|
|
153
|
+
expect(screen.getByRole('button')).toHaveClass('wds-expander-toggle');
|
|
154
|
+
expect(screen.getByRole('button')).not.toHaveClass('wds-expander-toggle--collapsed');
|
|
155
|
+
|
|
156
|
+
rerender(<ExpanderToggle expanded={false} onToggle={jest.fn()} />);
|
|
157
|
+
|
|
158
|
+
expect(screen.getByRole('button')).toHaveClass('wds-expander-toggle');
|
|
159
|
+
expect(screen.getByRole('button')).toHaveClass('wds-expander-toggle--collapsed');
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('applies data-testid attribute', () => {
|
|
163
|
+
render(<ExpanderToggle expanded data-testid="expander-toggle" onToggle={jest.fn()} />);
|
|
164
|
+
|
|
165
|
+
expect(screen.getByTestId('expander-toggle')).toBeInTheDocument();
|
|
166
|
+
});
|
|
167
|
+
});
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { useState, useCallback } from 'react';
|
|
2
|
+
import { clsx } from 'clsx';
|
|
3
|
+
import { ChevronUp } from '@transferwise/icons';
|
|
4
|
+
import IconButton from '../../../iconButton';
|
|
5
|
+
import { useIntl } from 'react-intl';
|
|
6
|
+
import messages from './Expander.messages';
|
|
7
|
+
|
|
8
|
+
export type ExpanderState = {
|
|
9
|
+
expanded: boolean;
|
|
10
|
+
toggle: () => void;
|
|
11
|
+
setExpanded: (expanded: boolean) => void;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// Hook for managing expander state
|
|
15
|
+
export function useExpanderState(initialExpanded = true): ExpanderState {
|
|
16
|
+
const [expanded, setExpanded] = useState(initialExpanded);
|
|
17
|
+
|
|
18
|
+
const toggle = useCallback(() => {
|
|
19
|
+
setExpanded((prev) => !prev);
|
|
20
|
+
}, []);
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
expanded,
|
|
24
|
+
toggle,
|
|
25
|
+
setExpanded,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Helper for conditional rendering based on expanded state
|
|
30
|
+
export function shouldShowWhenExpanded({
|
|
31
|
+
expanded,
|
|
32
|
+
hasContent,
|
|
33
|
+
alwaysShow = false,
|
|
34
|
+
}: {
|
|
35
|
+
expanded: boolean;
|
|
36
|
+
hasContent: boolean;
|
|
37
|
+
alwaysShow?: boolean;
|
|
38
|
+
}): boolean {
|
|
39
|
+
if (alwaysShow) {
|
|
40
|
+
return hasContent;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return expanded && hasContent;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type ExpanderToggleProps = {
|
|
47
|
+
expanded: boolean;
|
|
48
|
+
onToggle?: () => void;
|
|
49
|
+
size?: 16 | 24 | 32 | 40 | 48 | 56 | 72;
|
|
50
|
+
className?: string;
|
|
51
|
+
'data-testid'?: string;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// Reusable toggle button component
|
|
55
|
+
export const ExpanderToggle = ({
|
|
56
|
+
expanded,
|
|
57
|
+
onToggle,
|
|
58
|
+
size = 24,
|
|
59
|
+
className,
|
|
60
|
+
'data-testid': testId,
|
|
61
|
+
}: ExpanderToggleProps) => {
|
|
62
|
+
const intl = useIntl();
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<IconButton
|
|
66
|
+
size={size}
|
|
67
|
+
priority="secondary"
|
|
68
|
+
aria-label={intl.formatMessage(
|
|
69
|
+
expanded ? messages.collapseAriaLabel : messages.expandAriaLabel,
|
|
70
|
+
)}
|
|
71
|
+
aria-expanded={expanded}
|
|
72
|
+
className={clsx(
|
|
73
|
+
'wds-expander-toggle',
|
|
74
|
+
{ 'wds-expander-toggle--collapsed': !expanded },
|
|
75
|
+
className,
|
|
76
|
+
)}
|
|
77
|
+
data-testid={testId}
|
|
78
|
+
onClick={onToggle}
|
|
79
|
+
>
|
|
80
|
+
<ChevronUp />
|
|
81
|
+
</IconButton>
|
|
82
|
+
);
|
|
83
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { GiftBox } from '@transferwise/icons';
|
|
3
|
+
|
|
4
|
+
import AvatarView, { AvatarViewProps } from '../../avatarView';
|
|
5
|
+
import Image from '../../image';
|
|
6
|
+
import StatusIcon from '../../statusIcon';
|
|
7
|
+
import { BadgeAssetsProps } from '../../badge';
|
|
8
|
+
import { PrimitivePromptProps } from '../PrimitivePrompt';
|
|
9
|
+
|
|
10
|
+
export type PromptMedia = {
|
|
11
|
+
imgSrc?: string;
|
|
12
|
+
avatar?: Pick<AvatarViewProps, 'imgSrc' | 'profileName' | 'profileType'> & {
|
|
13
|
+
asset?: AvatarViewProps['children'];
|
|
14
|
+
badge?: Pick<BadgeAssetsProps, 'flagCode'>;
|
|
15
|
+
};
|
|
16
|
+
'aria-label'?: string;
|
|
17
|
+
'aria-hidden'?: boolean;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
type RenderPromptMediaOptions = {
|
|
21
|
+
media: PromptMedia;
|
|
22
|
+
sentiment: PrimitivePromptProps['sentiment'];
|
|
23
|
+
mediaId: string;
|
|
24
|
+
imgClassName: string;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export function renderPromptMedia({
|
|
28
|
+
media,
|
|
29
|
+
sentiment,
|
|
30
|
+
mediaId,
|
|
31
|
+
imgClassName,
|
|
32
|
+
}: RenderPromptMediaOptions): ReactNode {
|
|
33
|
+
if (media?.imgSrc) {
|
|
34
|
+
return (
|
|
35
|
+
<Image
|
|
36
|
+
id={mediaId}
|
|
37
|
+
src={media.imgSrc}
|
|
38
|
+
className={imgClassName}
|
|
39
|
+
alt={media['aria-label'] ?? ''}
|
|
40
|
+
/>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
if (media?.avatar) {
|
|
44
|
+
const badge = media.avatar.badge
|
|
45
|
+
? media.avatar.badge
|
|
46
|
+
: sentiment === 'proposition'
|
|
47
|
+
? {}
|
|
48
|
+
: { status: sentiment };
|
|
49
|
+
return (
|
|
50
|
+
<AvatarView
|
|
51
|
+
{...media.avatar}
|
|
52
|
+
badge={badge}
|
|
53
|
+
aria-label={media['aria-label']}
|
|
54
|
+
aria-hidden={media['aria-hidden']}
|
|
55
|
+
id={mediaId}
|
|
56
|
+
size={48}
|
|
57
|
+
>
|
|
58
|
+
{media.avatar.asset}
|
|
59
|
+
</AvatarView>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
return sentiment === 'proposition' ? (
|
|
63
|
+
<AvatarView
|
|
64
|
+
id={mediaId}
|
|
65
|
+
size={48}
|
|
66
|
+
aria-label={media['aria-label']}
|
|
67
|
+
aria-hidden={media['aria-hidden']}
|
|
68
|
+
>
|
|
69
|
+
<GiftBox />
|
|
70
|
+
</AvatarView>
|
|
71
|
+
) : (
|
|
72
|
+
<StatusIcon
|
|
73
|
+
id={mediaId}
|
|
74
|
+
size={48}
|
|
75
|
+
sentiment={sentiment}
|
|
76
|
+
iconLabel={media['aria-hidden'] ? null : media['aria-label']}
|
|
77
|
+
/>
|
|
78
|
+
);
|
|
79
|
+
}
|
package/src/prompt/index.ts
CHANGED
|
@@ -12,3 +12,7 @@ export { ActionPrompt } from './ActionPrompt';
|
|
|
12
12
|
// InfoPrompt
|
|
13
13
|
export type { InfoPromptProps, InfoPromptAction, InfoPromptMedia } from './InfoPrompt';
|
|
14
14
|
export { InfoPrompt } from './InfoPrompt';
|
|
15
|
+
|
|
16
|
+
// CriticalBanner
|
|
17
|
+
export type { CriticalBannerProps } from './CriticalBanner';
|
|
18
|
+
export { CriticalBanner } from './CriticalBanner';
|
|
@@ -108,14 +108,8 @@ export const WithListItems = () => {
|
|
|
108
108
|
return (
|
|
109
109
|
<Section>
|
|
110
110
|
<Header title="Section with list items" />
|
|
111
|
-
<ListItem
|
|
112
|
-
|
|
113
|
-
control={<ListItem.Navigation onClick={() => action('Item 1')} />}
|
|
114
|
-
/>
|
|
115
|
-
<ListItem
|
|
116
|
-
title="Item 2"
|
|
117
|
-
control={<ListItem.Navigation onClick={() => action('Item 2')} />}
|
|
118
|
-
/>
|
|
111
|
+
<ListItem title="Item 1" control={<ListItem.Navigation onClick={() => action('Item 1')} />} />
|
|
112
|
+
<ListItem title="Item 2" control={<ListItem.Navigation onClick={() => action('Item 2')} />} />
|
|
119
113
|
</Section>
|
|
120
114
|
);
|
|
121
115
|
};
|
|
@@ -202,18 +202,26 @@ const TokenSwatch = ({ token }: { token: string }) => (
|
|
|
202
202
|
export const EmphasisLevels: Story = {
|
|
203
203
|
render: (args) => (
|
|
204
204
|
<>
|
|
205
|
-
|
|
206
|
-
<div
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
205
|
+
{sentiments.map((sentiment) => (
|
|
206
|
+
<div key={sentiment}>
|
|
207
|
+
<SentimentSurface
|
|
208
|
+
key={`${sentiment}-base`}
|
|
209
|
+
{...args}
|
|
210
|
+
sentiment={sentiment}
|
|
211
|
+
emphasis="base"
|
|
212
|
+
>
|
|
213
|
+
<div className="p-a-2 text-capitalize">{sentiment} - Base emphasis</div>
|
|
214
|
+
</SentimentSurface>
|
|
215
|
+
<SentimentSurface
|
|
216
|
+
key={`${sentiment}-elevated`}
|
|
217
|
+
{...args}
|
|
218
|
+
sentiment={sentiment}
|
|
219
|
+
emphasis="elevated"
|
|
220
|
+
>
|
|
221
|
+
<div className="p-a-2 text-capitalize">{sentiment} - Elevated emphasis</div>
|
|
222
|
+
</SentimentSurface>
|
|
223
|
+
</div>
|
|
224
|
+
))}
|
|
217
225
|
</>
|
|
218
226
|
),
|
|
219
227
|
parameters: {
|
|
@@ -369,8 +377,8 @@ export const SentimentAwareComponents: Story = {
|
|
|
369
377
|
{STATUS_NEGATIVE}
|
|
370
378
|
</div>
|
|
371
379
|
|
|
372
|
-
<Header level="group" title="Success sentiment surface" />
|
|
373
|
-
<SentimentSurface sentiment="success">
|
|
380
|
+
<Header level="group" title="Success sentiment surface - Base emphasis" />
|
|
381
|
+
<SentimentSurface sentiment="success" emphasis="base">
|
|
374
382
|
<div className="p-a-2 m-b-2 sentimentAwareComponent">
|
|
375
383
|
{BODY}
|
|
376
384
|
{AVATAR_SUCCESS}
|
|
@@ -378,8 +386,26 @@ export const SentimentAwareComponents: Story = {
|
|
|
378
386
|
</div>
|
|
379
387
|
</SentimentSurface>
|
|
380
388
|
|
|
381
|
-
<Header level="group" title="
|
|
382
|
-
<SentimentSurface sentiment="
|
|
389
|
+
<Header level="group" title="Success sentiment surface - Elevated emphasis" />
|
|
390
|
+
<SentimentSurface sentiment="success" emphasis="elevated">
|
|
391
|
+
<div className="p-a-2 m-b-2 sentimentAwareComponent">
|
|
392
|
+
{BODY}
|
|
393
|
+
{AVATAR_SUCCESS}
|
|
394
|
+
{STATUS_SUCCESS}
|
|
395
|
+
</div>
|
|
396
|
+
</SentimentSurface>
|
|
397
|
+
|
|
398
|
+
<Header level="group" title="Negative sentiment surface - Base emphasis" />
|
|
399
|
+
<SentimentSurface sentiment="negative" emphasis="base">
|
|
400
|
+
<div className="p-a-2 m-b-2 sentimentAwareComponent">
|
|
401
|
+
{BODY}
|
|
402
|
+
{AVATAR_NEGATIVE}
|
|
403
|
+
{STATUS_NEGATIVE}
|
|
404
|
+
</div>
|
|
405
|
+
</SentimentSurface>
|
|
406
|
+
|
|
407
|
+
<Header level="group" title="Negative sentiment surface - Elevated emphasis" />
|
|
408
|
+
<SentimentSurface sentiment="negative" emphasis="elevated">
|
|
383
409
|
<div className="p-a-2 m-b-2 sentimentAwareComponent">
|
|
384
410
|
{BODY}
|
|
385
411
|
{AVATAR_NEGATIVE}
|
|
@@ -427,7 +453,7 @@ function CustomCard({ children, className }: { className?: string; children: Rea
|
|
|
427
453
|
|
|
428
454
|
export const CustomComponents: Story = {
|
|
429
455
|
tags: ['!autodocs', '!dev'],
|
|
430
|
-
render: (
|
|
456
|
+
render: () => (
|
|
431
457
|
<>
|
|
432
458
|
<style>{`
|
|
433
459
|
.custom-card {
|
|
@@ -42,7 +42,6 @@ describe('StatusIcon', () => {
|
|
|
42
42
|
},
|
|
43
43
|
);
|
|
44
44
|
|
|
45
|
-
|
|
46
45
|
it.each([
|
|
47
46
|
[Sentiment.WARNING, 'alert-icon'],
|
|
48
47
|
[Sentiment.PENDING, 'clock-borderless-icon'],
|
|
@@ -61,7 +60,6 @@ describe('StatusIcon', () => {
|
|
|
61
60
|
},
|
|
62
61
|
);
|
|
63
62
|
|
|
64
|
-
|
|
65
63
|
describe('accessible name', () => {
|
|
66
64
|
it.each([
|
|
67
65
|
['Error', Sentiment.NEGATIVE],
|