@transferwise/components 0.0.0-experimental-1d00fb5 → 0.0.0-experimental-d44dcb8
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.js.map +1 -1
- package/build/alert/Alert.mjs +1 -1
- package/build/alert/Alert.mjs.map +1 -1
- package/build/checkbox/Checkbox.js +1 -1
- package/build/checkbox/Checkbox.js.map +1 -1
- package/build/checkbox/Checkbox.mjs +1 -1
- package/build/checkbox/Checkbox.mjs.map +1 -1
- package/build/field/Field.js +8 -4
- package/build/field/Field.js.map +1 -1
- package/build/field/Field.mjs +8 -4
- package/build/field/Field.mjs.map +1 -1
- package/build/inlineAlert/InlineAlert.js +1 -7
- package/build/inlineAlert/InlineAlert.js.map +1 -1
- package/build/inlineAlert/InlineAlert.mjs +1 -7
- package/build/inlineAlert/InlineAlert.mjs.map +1 -1
- package/build/main.css +27 -12
- package/build/nudge/Nudge.js +8 -12
- package/build/nudge/Nudge.js.map +1 -1
- package/build/nudge/Nudge.mjs +8 -12
- package/build/nudge/Nudge.mjs.map +1 -1
- package/build/prompt/InlinePrompt/InlinePrompt.js +2 -0
- package/build/prompt/InlinePrompt/InlinePrompt.js.map +1 -1
- package/build/prompt/InlinePrompt/InlinePrompt.mjs +2 -0
- package/build/prompt/InlinePrompt/InlinePrompt.mjs.map +1 -1
- package/build/radioGroup/RadioGroup.js +1 -0
- package/build/radioGroup/RadioGroup.js.map +1 -1
- package/build/radioGroup/RadioGroup.mjs +1 -0
- package/build/radioGroup/RadioGroup.mjs.map +1 -1
- package/build/styles/field/Field.css +10 -1
- package/build/styles/main.css +27 -12
- package/build/styles/nudge/Nudge.css +7 -11
- package/build/styles/prompt/InlinePrompt/InlinePrompt.css +3 -0
- package/build/styles/radioGroup/RadioGroup.css +3 -0
- package/build/styles/typeahead/Typeahead.css +4 -0
- package/build/typeahead/Typeahead.js +20 -7
- package/build/typeahead/Typeahead.js.map +1 -1
- package/build/typeahead/Typeahead.mjs +20 -7
- package/build/typeahead/Typeahead.mjs.map +1 -1
- package/build/types/components/src/alert/Alert.d.ts +1 -1
- package/build/types/components/src/alert/Alert.d.ts.map +1 -1
- package/build/types/components/src/field/Field.d.ts +8 -4
- package/build/types/components/src/field/Field.d.ts.map +1 -1
- package/build/types/components/src/inlineAlert/InlineAlert.d.ts +1 -7
- package/build/types/components/src/inlineAlert/InlineAlert.d.ts.map +1 -1
- package/build/types/components/src/nudge/Nudge.d.ts +2 -2
- package/build/types/components/src/nudge/Nudge.d.ts.map +1 -1
- package/build/types/components/src/prompt/InlinePrompt/InlinePrompt.d.ts +6 -1
- package/build/types/components/src/prompt/InlinePrompt/InlinePrompt.d.ts.map +1 -1
- package/build/types/components/src/radioGroup/RadioGroup.d.ts.map +1 -1
- package/build/types/components/src/typeahead/Typeahead.d.ts +8 -4
- package/build/types/components/src/typeahead/Typeahead.d.ts.map +1 -1
- package/build/types/components/src/upload/Upload.d.ts +1 -1
- package/build/types/components/src/upload/steps/uploadImageStep/uploadImageStep.d.ts.map +1 -1
- package/build/upload/Upload.js.map +1 -1
- package/build/upload/Upload.mjs.map +1 -1
- package/build/upload/steps/uploadImageStep/uploadImageStep.js +5 -4
- package/build/upload/steps/uploadImageStep/uploadImageStep.js.map +1 -1
- package/build/upload/steps/uploadImageStep/uploadImageStep.mjs +5 -4
- package/build/upload/steps/uploadImageStep/uploadImageStep.mjs.map +1 -1
- package/package.json +3 -3
- package/src/alert/Alert.spec.tsx +1 -1
- package/src/alert/Alert.tsx +2 -2
- package/src/checkbox/Checkbox.story.tsx +11 -3
- package/src/checkbox/Checkbox.tsx +1 -1
- package/src/checkbox/__snapshots__/Checkbox.spec.tsx.snap +1 -1
- package/src/field/Field.css +10 -1
- package/src/field/Field.less +13 -2
- package/src/field/Field.spec.tsx +19 -3
- package/src/field/Field.story.tsx +18 -0
- package/src/field/Field.tsx +17 -5
- package/src/inlineAlert/InlineAlert.story.tsx +4 -0
- package/src/inlineAlert/InlineAlert.tsx +1 -7
- package/src/main.css +27 -12
- package/src/main.less +1 -0
- package/src/nudge/Nudge.css +7 -11
- package/src/nudge/Nudge.less +13 -10
- package/src/nudge/Nudge.spec.tsx +6 -5
- package/src/nudge/Nudge.story.tsx +0 -9
- package/src/nudge/Nudge.tsx +3 -14
- package/src/prompt/InlinePrompt/InlinePrompt.css +3 -0
- package/src/prompt/InlinePrompt/InlinePrompt.less +5 -1
- package/src/prompt/InlinePrompt/InlinePrompt.spec.tsx +17 -0
- package/src/prompt/InlinePrompt/InlinePrompt.story.tsx +35 -0
- package/src/prompt/InlinePrompt/InlinePrompt.tsx +7 -0
- package/src/radioGroup/RadioGroup.css +3 -0
- package/src/radioGroup/RadioGroup.less +3 -0
- package/src/radioGroup/RadioGroup.story.tsx +2 -0
- package/src/radioGroup/RadioGroup.test.story.tsx +62 -0
- package/src/radioGroup/RadioGroup.tsx +6 -1
- package/src/typeahead/Typeahead.css +4 -0
- package/src/typeahead/Typeahead.less +5 -1
- package/src/typeahead/Typeahead.spec.tsx +1 -1
- package/src/typeahead/Typeahead.story.tsx +151 -3
- package/src/typeahead/Typeahead.tsx +33 -9
- package/src/upload/Upload.story.tsx +1 -1
- package/src/upload/Upload.tests.story.tsx +36 -1
- package/src/upload/Upload.tsx +1 -1
- package/src/upload/steps/uploadImageStep/uploadImageStep.tsx +7 -3
|
@@ -46,6 +46,7 @@ export const Basic = (args: FieldProps) => {
|
|
|
46
46
|
sentiment={Sentiment.NEGATIVE}
|
|
47
47
|
message="Validation error, please take a look"
|
|
48
48
|
messageIconLabel={args.messageIconLabel}
|
|
49
|
+
messageLoading={args.messageLoading}
|
|
49
50
|
>
|
|
50
51
|
<Input value={value} onChange={({ target }) => setValue(target.value)} />
|
|
51
52
|
</Field>
|
|
@@ -81,6 +82,7 @@ export const Basic = (args: FieldProps) => {
|
|
|
81
82
|
message={lorem10}
|
|
82
83
|
sentiment="negative"
|
|
83
84
|
messageIconLabel={args.messageIconLabel}
|
|
85
|
+
messageLoading={args.messageLoading}
|
|
84
86
|
>
|
|
85
87
|
<TextArea />
|
|
86
88
|
</Field>
|
|
@@ -109,6 +111,17 @@ export const WithStatusMessages = (args: FieldProps) => {
|
|
|
109
111
|
sentiment={Sentiment.POSITIVE}
|
|
110
112
|
message="Positive message"
|
|
111
113
|
messageIconLabel={args.messageIconLabel}
|
|
114
|
+
messageLoading={args.messageLoading}
|
|
115
|
+
>
|
|
116
|
+
<Input value={value} onChange={({ target }) => setValue(target.value)} />
|
|
117
|
+
</Field>
|
|
118
|
+
|
|
119
|
+
<Field
|
|
120
|
+
label="Text Input with Proposition Message"
|
|
121
|
+
sentiment="proposition"
|
|
122
|
+
message="Proposition message"
|
|
123
|
+
messageIconLabel={args.messageIconLabel}
|
|
124
|
+
messageLoading={args.messageLoading}
|
|
112
125
|
>
|
|
113
126
|
<Input value={value} onChange={({ target }) => setValue(target.value)} />
|
|
114
127
|
</Field>
|
|
@@ -118,6 +131,7 @@ export const WithStatusMessages = (args: FieldProps) => {
|
|
|
118
131
|
sentiment={Sentiment.WARNING}
|
|
119
132
|
message="Warning message"
|
|
120
133
|
messageIconLabel={args.messageIconLabel}
|
|
134
|
+
messageLoading={args.messageLoading}
|
|
121
135
|
>
|
|
122
136
|
<Input value={value} onChange={({ target }) => setValue(target.value)} />
|
|
123
137
|
</Field>
|
|
@@ -127,6 +141,7 @@ export const WithStatusMessages = (args: FieldProps) => {
|
|
|
127
141
|
sentiment={Sentiment.NEGATIVE}
|
|
128
142
|
message="This is a required field"
|
|
129
143
|
messageIconLabel={args.messageIconLabel}
|
|
144
|
+
messageLoading={args.messageLoading}
|
|
130
145
|
>
|
|
131
146
|
<Input value={value} onChange={({ target }) => setValue(target.value)} />
|
|
132
147
|
</Field>
|
|
@@ -138,6 +153,7 @@ export const WithStatusMessages = (args: FieldProps) => {
|
|
|
138
153
|
sentiment={Sentiment.NEGATIVE}
|
|
139
154
|
message="Validation error, please take a look"
|
|
140
155
|
messageIconLabel={args.messageIconLabel}
|
|
156
|
+
messageLoading={args.messageLoading}
|
|
141
157
|
>
|
|
142
158
|
<Input value={value} onChange={({ target }) => setValue(target.value)} />
|
|
143
159
|
</Field>
|
|
@@ -148,6 +164,7 @@ export const WithStatusMessages = (args: FieldProps) => {
|
|
|
148
164
|
hint="This is a helpful message"
|
|
149
165
|
error="Validation error, please take a look"
|
|
150
166
|
messageIconLabel={args.messageIconLabel}
|
|
167
|
+
messageLoading={args.messageLoading}
|
|
151
168
|
>
|
|
152
169
|
<Input value={value} onChange={({ target }) => setValue(target.value)} />
|
|
153
170
|
</Field>
|
|
@@ -157,6 +174,7 @@ export const WithStatusMessages = (args: FieldProps) => {
|
|
|
157
174
|
label="Text Input with Info under the field"
|
|
158
175
|
message="This is a helpful message"
|
|
159
176
|
messageIconLabel={args.messageIconLabel}
|
|
177
|
+
messageLoading={args.messageLoading}
|
|
160
178
|
>
|
|
161
179
|
<Input value={value} onChange={({ target }) => setValue(target.value)} />
|
|
162
180
|
</Field>
|
package/src/field/Field.tsx
CHANGED
|
@@ -2,7 +2,7 @@ import { clsx } from 'clsx';
|
|
|
2
2
|
import { useId, useRef } from 'react';
|
|
3
3
|
|
|
4
4
|
import { Sentiment } from '../common';
|
|
5
|
-
import
|
|
5
|
+
import { InlinePrompt, type InlinePromptProps } from '../prompt';
|
|
6
6
|
import {
|
|
7
7
|
FieldLabelContextProvider,
|
|
8
8
|
InputDescribedByProvider,
|
|
@@ -21,14 +21,18 @@ export type FieldProps = {
|
|
|
21
21
|
hint?: React.ReactNode;
|
|
22
22
|
message?: React.ReactNode;
|
|
23
23
|
/**
|
|
24
|
-
* Override for the [
|
|
24
|
+
* Override for the [InlinePrompt icon's default, accessible name](/?path=/docs/other-statusicon-accessibility--docs)
|
|
25
25
|
* announced by the screen readers
|
|
26
26
|
* */
|
|
27
27
|
messageIconLabel?: string;
|
|
28
|
+
/**
|
|
29
|
+
* If true, shows a loading spinner in place of the message icon of the InlinePrompt
|
|
30
|
+
*/
|
|
31
|
+
messageLoading?: boolean;
|
|
28
32
|
description?: React.ReactNode;
|
|
29
33
|
/** @deprecated use `message` and `type={Sentiment.NEGATIVE}` prop instead */
|
|
30
34
|
error?: React.ReactNode;
|
|
31
|
-
sentiment?:
|
|
35
|
+
sentiment?: InlinePromptProps['sentiment'];
|
|
32
36
|
className?: string;
|
|
33
37
|
children?: React.ReactNode;
|
|
34
38
|
};
|
|
@@ -39,6 +43,7 @@ export const Field = ({
|
|
|
39
43
|
required = true,
|
|
40
44
|
message: propMessage,
|
|
41
45
|
messageIconLabel,
|
|
46
|
+
messageLoading,
|
|
42
47
|
hint,
|
|
43
48
|
description = hint,
|
|
44
49
|
sentiment: propType = Sentiment.NEUTRAL,
|
|
@@ -104,9 +109,16 @@ export const Field = ({
|
|
|
104
109
|
)}
|
|
105
110
|
|
|
106
111
|
{message && (
|
|
107
|
-
<
|
|
112
|
+
<InlinePrompt
|
|
113
|
+
sentiment={sentiment}
|
|
114
|
+
id={messageId}
|
|
115
|
+
mediaLabel={messageIconLabel}
|
|
116
|
+
className="np-field__prompt"
|
|
117
|
+
loading={messageLoading}
|
|
118
|
+
width="full"
|
|
119
|
+
>
|
|
108
120
|
{message}
|
|
109
|
-
</
|
|
121
|
+
</InlinePrompt>
|
|
110
122
|
)}
|
|
111
123
|
</div>
|
|
112
124
|
</InputInvalidProvider>
|
|
@@ -4,9 +4,13 @@ import InlineAlert, { InlineAlertProps } from './InlineAlert';
|
|
|
4
4
|
import { lorem10, lorem40 } from '../test-utils';
|
|
5
5
|
import Link from '../link';
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* > **DEPRECATED** Use [InlinePrompt](?path=/docs/prompts-inlineprompt--docs) or [Field](?path=/docs/forms-field--docs) instead.
|
|
9
|
+
*/
|
|
7
10
|
export default {
|
|
8
11
|
component: InlineAlert,
|
|
9
12
|
title: 'Prompts/InlineAlert',
|
|
13
|
+
tags: ['deprecated'],
|
|
10
14
|
} as Meta<InlineAlertProps>;
|
|
11
15
|
|
|
12
16
|
export const Basic = () => {
|
|
@@ -22,13 +22,7 @@ const iconTypes = new Set<NonNullable<InlineAlertProps['type']>>([
|
|
|
22
22
|
]);
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
26
|
-
* it's for edge cases when `<Field />` isn't suitable for some reasons.
|
|
27
|
-
*
|
|
28
|
-
* Example:
|
|
29
|
-
* ```
|
|
30
|
-
* <Field sentiment={..} message={..}>..</Field>
|
|
31
|
-
* ```
|
|
25
|
+
* @deprecated Use [<InlinePrompt />](https://storybook.wise.design/?path=/docs/prompts-inlineprompt--docs) of [<Field />](https://storybook.wise.design/?path=/docs/forms-field--docs) instead.
|
|
32
26
|
*/
|
|
33
27
|
export default function InlineAlert({
|
|
34
28
|
id,
|
package/src/main.css
CHANGED
|
@@ -949,6 +949,9 @@
|
|
|
949
949
|
.wds-inline-prompt:has(button):active {
|
|
950
950
|
background-color: var(--color-sentiment-background-surface-active);
|
|
951
951
|
}
|
|
952
|
+
.wds-inline-prompt--full-width {
|
|
953
|
+
width: 100%;
|
|
954
|
+
}
|
|
952
955
|
.wds-inline-prompt--muted {
|
|
953
956
|
opacity: 0.93;
|
|
954
957
|
filter: grayscale(1);
|
|
@@ -3822,10 +3825,19 @@ html:not([dir="rtl"]) .np-flow-navigation--sm .np-flow-navigation__stepper {
|
|
|
3822
3825
|
stroke-dasharray: calc(12px * 0.5) calc(12px * 0.5);
|
|
3823
3826
|
stroke-dasharray: var(--wds-list-item-spotlight-strokeDashSize) var(--wds-list-item-spotlight-strokeDashSize);
|
|
3824
3827
|
}
|
|
3825
|
-
.np-field-control
|
|
3828
|
+
.np-field-control,
|
|
3829
|
+
.np-field__prompt {
|
|
3826
3830
|
margin-top: 4px;
|
|
3827
3831
|
margin-top: var(--size-4);
|
|
3828
3832
|
}
|
|
3833
|
+
.np-field .form-group--typeahead[class],
|
|
3834
|
+
.np-field .np-checkbox-label[class] {
|
|
3835
|
+
margin-bottom: 0;
|
|
3836
|
+
}
|
|
3837
|
+
.np-field:has(.wds-radio-group) .np-field__prompt {
|
|
3838
|
+
margin-top: 12px;
|
|
3839
|
+
margin-top: var(--size-12);
|
|
3840
|
+
}
|
|
3829
3841
|
.np-input-group {
|
|
3830
3842
|
display: inline-grid;
|
|
3831
3843
|
width: 100%;
|
|
@@ -5052,17 +5064,6 @@ html:not([dir="rtl"]) .np-navigation-option {
|
|
|
5052
5064
|
margin-left: 0;
|
|
5053
5065
|
margin-right: -16px;
|
|
5054
5066
|
}
|
|
5055
|
-
.wds-nudge-media-gift-box {
|
|
5056
|
-
margin-left: -22px;
|
|
5057
|
-
margin-top: 6px;
|
|
5058
|
-
position: absolute;
|
|
5059
|
-
width: 129px;
|
|
5060
|
-
}
|
|
5061
|
-
[dir="rtl"] .wds-nudge-media-gift-box {
|
|
5062
|
-
transform: scaleX(-1);
|
|
5063
|
-
margin-left: 0;
|
|
5064
|
-
margin-right: -22px;
|
|
5065
|
-
}
|
|
5066
5067
|
.wds-nudge-container {
|
|
5067
5068
|
align-items: stretch;
|
|
5068
5069
|
display: flex;
|
|
@@ -5098,6 +5099,13 @@ html:not([dir="rtl"]) .np-navigation-option {
|
|
|
5098
5099
|
-webkit-text-decoration: var(--nudge-link-text-decoration);
|
|
5099
5100
|
text-decoration: var(--nudge-link-text-decoration);
|
|
5100
5101
|
}
|
|
5102
|
+
.wds-nudge-container .wds-nudge-control {
|
|
5103
|
+
max-width: var(--nudge-control-width);
|
|
5104
|
+
flex-basis: var(--nudge-control-width);
|
|
5105
|
+
flex: 0 0 var(--nudge-control-width);
|
|
5106
|
+
height: var(--nudge-control-width);
|
|
5107
|
+
background-color: var(--nudge-control-background-color);
|
|
5108
|
+
}
|
|
5101
5109
|
.np-overlay-header .np-overlay-header__content {
|
|
5102
5110
|
min-height: 97px;
|
|
5103
5111
|
width: 100%;
|
|
@@ -5398,6 +5406,9 @@ html:not([dir="rtl"]) .np-navigation-option {
|
|
|
5398
5406
|
.np-CardGroup .np-Card.np-Card--promoCard {
|
|
5399
5407
|
max-width: 100%;
|
|
5400
5408
|
}
|
|
5409
|
+
.wds-radio-group .np-radio:last-child label {
|
|
5410
|
+
margin-bottom: 0;
|
|
5411
|
+
}
|
|
5401
5412
|
.np-section {
|
|
5402
5413
|
margin-top: 32px;
|
|
5403
5414
|
margin-top: var(--size-32);
|
|
@@ -6924,6 +6935,10 @@ html:not([dir="rtl"]) .np-navigation-option {
|
|
|
6924
6935
|
padding: 0;
|
|
6925
6936
|
margin: 0;
|
|
6926
6937
|
}
|
|
6938
|
+
.typeahead--prompt {
|
|
6939
|
+
margin-top: 4px;
|
|
6940
|
+
margin-top: var(--size-4);
|
|
6941
|
+
}
|
|
6927
6942
|
.typeahead-sm.typeahead--multiple .typeahead__input-container {
|
|
6928
6943
|
min-height: 32px;
|
|
6929
6944
|
}
|
package/src/main.less
CHANGED
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
@import "./phoneNumberInput/PhoneNumberInput.less";
|
|
62
62
|
@import "./popover/Popover.less";
|
|
63
63
|
@import "./promoCard/PromoCard.less";
|
|
64
|
+
@import "./radioGroup/RadioGroup.less";
|
|
64
65
|
@import "./section/Section.less";
|
|
65
66
|
@import "./slidingPanel/SlidingPanel.less";
|
|
66
67
|
@import "./snackbar/Snackbar.less";
|
package/src/nudge/Nudge.css
CHANGED
|
@@ -158,17 +158,6 @@
|
|
|
158
158
|
margin-left: 0;
|
|
159
159
|
margin-right: -16px;
|
|
160
160
|
}
|
|
161
|
-
.wds-nudge-media-gift-box {
|
|
162
|
-
margin-left: -22px;
|
|
163
|
-
margin-top: 6px;
|
|
164
|
-
position: absolute;
|
|
165
|
-
width: 129px;
|
|
166
|
-
}
|
|
167
|
-
[dir="rtl"] .wds-nudge-media-gift-box {
|
|
168
|
-
transform: scaleX(-1);
|
|
169
|
-
margin-left: 0;
|
|
170
|
-
margin-right: -22px;
|
|
171
|
-
}
|
|
172
161
|
.wds-nudge-container {
|
|
173
162
|
align-items: stretch;
|
|
174
163
|
display: flex;
|
|
@@ -204,3 +193,10 @@
|
|
|
204
193
|
-webkit-text-decoration: var(--nudge-link-text-decoration);
|
|
205
194
|
text-decoration: var(--nudge-link-text-decoration);
|
|
206
195
|
}
|
|
196
|
+
.wds-nudge-container .wds-nudge-control {
|
|
197
|
+
max-width: var(--nudge-control-width);
|
|
198
|
+
flex-basis: var(--nudge-control-width);
|
|
199
|
+
flex: 0 0 var(--nudge-control-width);
|
|
200
|
+
height: var(--nudge-control-width);
|
|
201
|
+
background-color: var(--nudge-control-background-color);
|
|
202
|
+
}
|
package/src/nudge/Nudge.less
CHANGED
|
@@ -53,7 +53,8 @@
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
&-personal-card,
|
|
56
|
-
&-business-card
|
|
56
|
+
&-business-card
|
|
57
|
+
{
|
|
57
58
|
.media-position(104px, -15px, 2px);
|
|
58
59
|
}
|
|
59
60
|
|
|
@@ -68,7 +69,7 @@
|
|
|
68
69
|
&-shopping-bag {
|
|
69
70
|
.media-position(116px, -9px, 14px);
|
|
70
71
|
}
|
|
71
|
-
|
|
72
|
+
|
|
72
73
|
&-flower {
|
|
73
74
|
.media-position(156px, -24px, 11px);
|
|
74
75
|
}
|
|
@@ -76,10 +77,6 @@
|
|
|
76
77
|
&-backpack {
|
|
77
78
|
.media-position(123px, -16px, 6px);
|
|
78
79
|
}
|
|
79
|
-
|
|
80
|
-
&-gift-box {
|
|
81
|
-
.media-position(129px, -22px, 6px);
|
|
82
|
-
}
|
|
83
80
|
}
|
|
84
81
|
|
|
85
82
|
&-container {
|
|
@@ -88,13 +85,11 @@
|
|
|
88
85
|
flex: 1;
|
|
89
86
|
gap: var(--nudge-flex-gap);
|
|
90
87
|
justify-content: space-between;
|
|
91
|
-
padding: var(--nudge-container-padding) var(--nudge-container-padding)
|
|
92
|
-
var(--nudge-container-padding) 0;
|
|
88
|
+
padding: var(--nudge-container-padding) var(--nudge-container-padding) var(--nudge-container-padding) 0;
|
|
93
89
|
width: 100%;
|
|
94
90
|
|
|
95
91
|
[dir="rtl"] & {
|
|
96
|
-
padding: var(--nudge-container-padding) 0 var(--nudge-container-padding)
|
|
97
|
-
var(--nudge-container-padding);
|
|
92
|
+
padding: var(--nudge-container-padding) 0 var(--nudge-container-padding) var(--nudge-container-padding);
|
|
98
93
|
}
|
|
99
94
|
|
|
100
95
|
.np-theme-personal & {
|
|
@@ -122,6 +117,14 @@
|
|
|
122
117
|
text-decoration: var(--nudge-link-text-decoration);
|
|
123
118
|
}
|
|
124
119
|
}
|
|
120
|
+
|
|
121
|
+
.wds-nudge-control {
|
|
122
|
+
max-width: var(--nudge-control-width);
|
|
123
|
+
flex-basis: var(--nudge-control-width);
|
|
124
|
+
flex: 0 0 var(--nudge-control-width);
|
|
125
|
+
height: var(--nudge-control-width);
|
|
126
|
+
background-color: var(--nudge-control-background-color);
|
|
127
|
+
}
|
|
125
128
|
}
|
|
126
129
|
}
|
|
127
130
|
|
package/src/nudge/Nudge.spec.tsx
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import { render, fireEvent, cleanup, screen
|
|
1
|
+
import { render, fireEvent, cleanup, screen } from '../test-utils';
|
|
2
2
|
|
|
3
3
|
import Nudge from '.';
|
|
4
4
|
|
|
5
|
-
mockMatchMedia();
|
|
6
|
-
|
|
7
5
|
describe('Nudge', () => {
|
|
8
6
|
const defaultProps = {
|
|
7
|
+
media: (
|
|
8
|
+
<span role="img" aria-label="Party popper emoji">
|
|
9
|
+
🎉
|
|
10
|
+
</span>
|
|
11
|
+
),
|
|
9
12
|
title: 'A nudge title',
|
|
10
13
|
link: 'CTA',
|
|
11
14
|
href: '#',
|
|
@@ -72,7 +75,6 @@ describe('Nudge', () => {
|
|
|
72
75
|
});
|
|
73
76
|
|
|
74
77
|
it('shows a nudge if localStorage has been set with a different id for a different nudge and calls is previously dismissed with FALSE', () => {
|
|
75
|
-
mockMatchMedia();
|
|
76
78
|
jest.spyOn(Storage.prototype, 'getItem').mockReturnValue('["BANANA"]');
|
|
77
79
|
const isPreviouslyDismissed = jest.fn();
|
|
78
80
|
|
|
@@ -90,7 +92,6 @@ describe('Nudge', () => {
|
|
|
90
92
|
});
|
|
91
93
|
|
|
92
94
|
it('calls local storage with updated dismissed nudges value', () => {
|
|
93
|
-
mockMatchMedia();
|
|
94
95
|
jest.spyOn(Storage.prototype, 'getItem').mockReturnValue('["BANANA"]');
|
|
95
96
|
const setItem = jest.spyOn(Storage.prototype, 'setItem');
|
|
96
97
|
|
|
@@ -119,15 +119,6 @@ export const Default = () => {
|
|
|
119
119
|
onClick={action('action clicked')}
|
|
120
120
|
onDismiss={action('dismissed')}
|
|
121
121
|
/>
|
|
122
|
-
<Nudge
|
|
123
|
-
mediaName={Assets.GIFT_BOX}
|
|
124
|
-
className="m-b-2"
|
|
125
|
-
title="Text that is no longer than two lines."
|
|
126
|
-
link="Link"
|
|
127
|
-
href="#"
|
|
128
|
-
onClick={action('action clicked')}
|
|
129
|
-
onDismiss={action('dismissed')}
|
|
130
|
-
/>
|
|
131
122
|
</div>
|
|
132
123
|
);
|
|
133
124
|
};
|
package/src/nudge/Nudge.tsx
CHANGED
|
@@ -4,12 +4,9 @@ import { ReactNode, useEffect, useState, MouseEvent } from 'react';
|
|
|
4
4
|
|
|
5
5
|
import Body from '../body';
|
|
6
6
|
import { Typography } from '../common';
|
|
7
|
+
import { CloseButton } from '../common/closeButton';
|
|
7
8
|
import Link from '../link';
|
|
8
9
|
import type { AlertAction } from '../alert';
|
|
9
|
-
import IconButton from '../iconButton';
|
|
10
|
-
import { Cross } from '@transferwise/icons';
|
|
11
|
-
import { useIntl } from 'react-intl';
|
|
12
|
-
import closeBtnMessages from '../common/closeButton/CloseButton.messages';
|
|
13
10
|
|
|
14
11
|
// WARNING: Changing this will cause nudges to reappear wherever persist nudge is used and privacy team will need to be updated too
|
|
15
12
|
export const STORAGE_NAME = 'dismissedNudges';
|
|
@@ -42,7 +39,6 @@ type MediaNameType =
|
|
|
42
39
|
| `${Assets.MULTI_CURRENCY}`
|
|
43
40
|
| `${Assets.SHOPPING_BAG}`
|
|
44
41
|
| `${Assets.FLOWER}`
|
|
45
|
-
| `${Assets.GIFT_BOX}`
|
|
46
42
|
| `${Assets.BACKPACK}`;
|
|
47
43
|
|
|
48
44
|
type BaseProps = {
|
|
@@ -83,6 +79,7 @@ export interface RequiredPersistProps extends BaseProps {
|
|
|
83
79
|
export type Props = OptionalId | RequiredPersistProps;
|
|
84
80
|
|
|
85
81
|
const Nudge = ({
|
|
82
|
+
media,
|
|
86
83
|
mediaName,
|
|
87
84
|
title,
|
|
88
85
|
link,
|
|
@@ -95,7 +92,6 @@ const Nudge = ({
|
|
|
95
92
|
className,
|
|
96
93
|
action,
|
|
97
94
|
}: Props) => {
|
|
98
|
-
const intl = useIntl();
|
|
99
95
|
const [isDismissed, setIsDismissed] = useState(false);
|
|
100
96
|
const [isMounted, setIsMounted] = useState(false);
|
|
101
97
|
|
|
@@ -181,14 +177,7 @@ const Nudge = ({
|
|
|
181
177
|
)}
|
|
182
178
|
</div>
|
|
183
179
|
{onDismiss || persistDismissal ? (
|
|
184
|
-
<
|
|
185
|
-
size={24}
|
|
186
|
-
priority="tertiary"
|
|
187
|
-
aria-label={intl.formatMessage(closeBtnMessages.ariaLabel)}
|
|
188
|
-
onClick={handleOnDismiss}
|
|
189
|
-
>
|
|
190
|
-
<Cross />
|
|
191
|
-
</IconButton>
|
|
180
|
+
<CloseButton className="wds-nudge-control" size="sm" onClick={handleOnDismiss} />
|
|
192
181
|
) : null}
|
|
193
182
|
</div>
|
|
194
183
|
</div>
|
|
@@ -23,6 +23,10 @@
|
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
&--full-width {
|
|
27
|
+
width: 100%;
|
|
28
|
+
}
|
|
29
|
+
|
|
26
30
|
&--muted {
|
|
27
31
|
opacity: 0.93;
|
|
28
32
|
filter: grayscale(1);
|
|
@@ -69,6 +73,6 @@
|
|
|
69
73
|
// styles in the CSS package, so keeping it colocated for now.
|
|
70
74
|
.process-circle {
|
|
71
75
|
stroke: currentColor;
|
|
72
|
-
}
|
|
76
|
+
}
|
|
73
77
|
}
|
|
74
78
|
}
|
|
@@ -155,4 +155,21 @@ describe('InlinePrompt', () => {
|
|
|
155
155
|
expect(el).toHaveClass('wds-sentiment-surface');
|
|
156
156
|
});
|
|
157
157
|
});
|
|
158
|
+
|
|
159
|
+
describe('width', () => {
|
|
160
|
+
it('defaults to auto width (no full-width class)', () => {
|
|
161
|
+
render(<InlinePrompt {...defaultProps} data-testid="prompt" />);
|
|
162
|
+
expect(screen.getByTestId('prompt')).not.toHaveClass('wds-inline-prompt--full-width');
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('applies auto width (no full-width class) when explicitly set', () => {
|
|
166
|
+
render(<InlinePrompt {...defaultProps} width="auto" data-testid="prompt" />);
|
|
167
|
+
expect(screen.getByTestId('prompt')).not.toHaveClass('wds-inline-prompt--full-width');
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('applies full width class when set to full', () => {
|
|
171
|
+
render(<InlinePrompt {...defaultProps} width="full" data-testid="prompt" />);
|
|
172
|
+
expect(screen.getByTestId('prompt')).toHaveClass('wds-inline-prompt--full-width');
|
|
173
|
+
});
|
|
174
|
+
});
|
|
158
175
|
});
|
|
@@ -14,6 +14,9 @@ const withComponentGrid =
|
|
|
14
14
|
width: '100%',
|
|
15
15
|
display: 'flex',
|
|
16
16
|
flexDirection: 'column',
|
|
17
|
+
justifyContent: 'flex-start',
|
|
18
|
+
alignItems: 'flex-start',
|
|
19
|
+
alignContent: 'flex-start',
|
|
17
20
|
gap,
|
|
18
21
|
maxWidth,
|
|
19
22
|
}}
|
|
@@ -26,6 +29,7 @@ export default {
|
|
|
26
29
|
title: 'Prompts/InlinePrompt',
|
|
27
30
|
component: InlinePrompt,
|
|
28
31
|
tags: ['new'],
|
|
32
|
+
decorators: [withComponentGrid()],
|
|
29
33
|
args: {
|
|
30
34
|
loading: false,
|
|
31
35
|
muted: false,
|
|
@@ -243,6 +247,37 @@ export const IconOverrides: StoryObj<PreviewStoryArgs> = {
|
|
|
243
247
|
decorators: [withComponentGrid()],
|
|
244
248
|
};
|
|
245
249
|
|
|
250
|
+
/**
|
|
251
|
+
* `InlinePrompt` can either hug its content or take the full width of its container,
|
|
252
|
+
* depending on the `width` prop.
|
|
253
|
+
* Components like `ListItem`, `ExpressiveMoneyInput` or similar will often set it to
|
|
254
|
+
* `auto`, while plain inputs will prefer `full` to visually match their boundaries.
|
|
255
|
+
*
|
|
256
|
+
* **NB**: `InlinePrompt` in its default – `auto` – width will expand to the full width of its container when the content spans across multiple lines.
|
|
257
|
+
*/
|
|
258
|
+
export const SizingStrategies: StoryObj<PreviewStoryArgs> = {
|
|
259
|
+
render: (args: PreviewStoryArgs) => {
|
|
260
|
+
return (
|
|
261
|
+
<>
|
|
262
|
+
<InlinePrompt {...args} media={<Travel />} sentiment="positive" width="full">
|
|
263
|
+
This prompt will take the full width of its container.
|
|
264
|
+
</InlinePrompt>
|
|
265
|
+
<InlinePrompt {...args} media={<Travel />} sentiment="positive">
|
|
266
|
+
This prompt will hug its content.
|
|
267
|
+
</InlinePrompt>
|
|
268
|
+
<InlinePrompt {...args} media={<Travel />} sentiment="positive">
|
|
269
|
+
This prompt is configured to hug its content, but since the content is long enough to span
|
|
270
|
+
across multiple lines, it will expand to the full width of its container. And no, you
|
|
271
|
+
should not put pages of text here in the first place, but we recognise that some
|
|
272
|
+
translations are longer than others, and people also use accessibility features that
|
|
273
|
+
increase font size.
|
|
274
|
+
</InlinePrompt>
|
|
275
|
+
</>
|
|
276
|
+
);
|
|
277
|
+
},
|
|
278
|
+
decorators: [withComponentGrid()],
|
|
279
|
+
};
|
|
280
|
+
|
|
246
281
|
/**
|
|
247
282
|
* When configured with any of the supported sentiments, the colour scheme of the component will propagate to all of its supported descendants, such as instances of a `Link`, `Icon`, and `StatusIcon`.
|
|
248
283
|
*/
|
|
@@ -33,6 +33,11 @@ export type InlinePromptProps = {
|
|
|
33
33
|
* To be used primarily for `proposition` sentiment.
|
|
34
34
|
*/
|
|
35
35
|
mediaLabel?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Defines the sizing strategy of the prompt component - either hugging the content or taking full width of the container.
|
|
38
|
+
* @default auto
|
|
39
|
+
*/
|
|
40
|
+
width?: 'auto' | 'full';
|
|
36
41
|
id?: string;
|
|
37
42
|
className?: string;
|
|
38
43
|
'data-testid'?: string;
|
|
@@ -53,6 +58,7 @@ export const InlinePrompt = ({
|
|
|
53
58
|
children,
|
|
54
59
|
media = null,
|
|
55
60
|
mediaLabel,
|
|
61
|
+
width = 'auto',
|
|
56
62
|
'data-testid': dataTestId,
|
|
57
63
|
...rest
|
|
58
64
|
}: InlinePromptProps) => {
|
|
@@ -91,6 +97,7 @@ export const InlinePrompt = ({
|
|
|
91
97
|
'wds-inline-prompt',
|
|
92
98
|
`wds-inline-prompt--${sentiment}`,
|
|
93
99
|
{
|
|
100
|
+
'wds-inline-prompt--full-width': width === 'full',
|
|
94
101
|
'wds-inline-prompt--muted': muted,
|
|
95
102
|
'wds-inline-prompt--loading': loading,
|
|
96
103
|
},
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import RadioGroup, { RadioGroupProps } from './RadioGroup';
|
|
2
|
+
import { Field } from '../field/Field';
|
|
3
|
+
import { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
4
|
+
import { fn } from 'storybook/test';
|
|
5
|
+
|
|
6
|
+
const meta = {
|
|
7
|
+
component: RadioGroup,
|
|
8
|
+
title: 'Forms/RadioGroup/tests',
|
|
9
|
+
tags: ['!autodocs'],
|
|
10
|
+
args: {
|
|
11
|
+
selectedValue: 'radio-2',
|
|
12
|
+
name: 'radio-group',
|
|
13
|
+
radios: [
|
|
14
|
+
{
|
|
15
|
+
value: 'radio-1',
|
|
16
|
+
label: 'Radio1',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
value: 'radio-2',
|
|
20
|
+
label: 'Radio2',
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
onChange: fn(),
|
|
24
|
+
},
|
|
25
|
+
} satisfies Meta<typeof RadioGroup>;
|
|
26
|
+
|
|
27
|
+
export default meta;
|
|
28
|
+
type Story<T extends string | number = string> = StoryObj<RadioGroupProps<T>>;
|
|
29
|
+
|
|
30
|
+
export const FieldsWithPrompt = {
|
|
31
|
+
render: function Render(args) {
|
|
32
|
+
return (
|
|
33
|
+
<>
|
|
34
|
+
{['i1', 'i2'].map((id) => (
|
|
35
|
+
<Field
|
|
36
|
+
key={id}
|
|
37
|
+
label="Field label"
|
|
38
|
+
description="Field description"
|
|
39
|
+
message="Something went great"
|
|
40
|
+
sentiment="positive"
|
|
41
|
+
>
|
|
42
|
+
<RadioGroup {...args} selectedValue="radio-2" />
|
|
43
|
+
</Field>
|
|
44
|
+
))}
|
|
45
|
+
</>
|
|
46
|
+
);
|
|
47
|
+
},
|
|
48
|
+
} satisfies Story;
|
|
49
|
+
|
|
50
|
+
export const FieldsWithoutPrompt = {
|
|
51
|
+
render: function Render(args) {
|
|
52
|
+
return (
|
|
53
|
+
<>
|
|
54
|
+
{['i1', 'i2'].map((id) => (
|
|
55
|
+
<Field key={id} label="Field label" sentiment="positive">
|
|
56
|
+
<RadioGroup {...args} selectedValue="radio-2" />
|
|
57
|
+
</Field>
|
|
58
|
+
))}
|
|
59
|
+
</>
|
|
60
|
+
);
|
|
61
|
+
},
|
|
62
|
+
} satisfies Story;
|
|
@@ -29,7 +29,12 @@ export default function RadioGroup<T extends string | number = never>({
|
|
|
29
29
|
const [uncontrolledValue, setUncontrolledValue] = useState(controlledValue);
|
|
30
30
|
|
|
31
31
|
return radios.length > 0 ? (
|
|
32
|
-
<div
|
|
32
|
+
<div
|
|
33
|
+
role="radiogroup"
|
|
34
|
+
{...inputAttributes}
|
|
35
|
+
{...UNSAFE_inputAttributes}
|
|
36
|
+
className="wds-radio-group"
|
|
37
|
+
>
|
|
33
38
|
{radios.map(({ value = '' as T, ...restProps }, index) => (
|
|
34
39
|
<Radio
|
|
35
40
|
// eslint-disable-next-line react/no-array-index-key
|