@transferwise/components 0.0.0-experimental-109c7ae → 0.0.0-experimental-2118b34
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/field/Field.js +6 -3
- package/build/field/Field.js.map +1 -1
- package/build/field/Field.mjs +6 -3
- 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/inputs/SelectInput.js +1 -5
- package/build/inputs/SelectInput.js.map +1 -1
- package/build/inputs/SelectInput.mjs +1 -5
- package/build/inputs/SelectInput.mjs.map +1 -1
- package/build/main.css +9 -1
- package/build/styles/field/Field.css +5 -1
- package/build/styles/main.css +9 -1
- package/build/styles/typeahead/Typeahead.css +4 -0
- package/build/typeahead/Typeahead.js +18 -6
- package/build/typeahead/Typeahead.js.map +1 -1
- package/build/typeahead/Typeahead.mjs +18 -6
- package/build/typeahead/Typeahead.mjs.map +1 -1
- package/build/types/alert/Alert.d.ts +1 -1
- package/build/types/alert/Alert.d.ts.map +1 -1
- package/build/types/field/Field.d.ts +8 -4
- package/build/types/field/Field.d.ts.map +1 -1
- package/build/types/inlineAlert/InlineAlert.d.ts +1 -7
- package/build/types/inlineAlert/InlineAlert.d.ts.map +1 -1
- package/build/types/inputs/SelectInput.d.ts.map +1 -1
- package/build/types/typeahead/Typeahead.d.ts +4 -4
- package/build/types/typeahead/Typeahead.d.ts.map +1 -1
- package/build/types/upload/Upload.d.ts +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 +3 -3
- package/build/upload/steps/uploadImageStep/uploadImageStep.js.map +1 -1
- package/build/upload/steps/uploadImageStep/uploadImageStep.mjs +3 -3
- 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/field/Field.css +5 -1
- package/src/field/Field.less +7 -2
- package/src/field/Field.spec.tsx +19 -3
- package/src/field/Field.story.tsx +18 -0
- package/src/field/Field.tsx +16 -5
- package/src/inlineAlert/InlineAlert.story.tsx +4 -0
- package/src/inlineAlert/InlineAlert.tsx +1 -7
- package/src/inputs/SelectInput.spec.tsx +0 -40
- package/src/inputs/SelectInput.story.tsx +1 -7
- package/src/inputs/SelectInput.tsx +1 -10
- package/src/main.css +9 -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 +80 -3
- package/src/typeahead/Typeahead.tsx +28 -8
- 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 +3 -3
|
@@ -38,16 +38,7 @@ import { ButtonProps } from '../button/Button.types';
|
|
|
38
38
|
const MAX_ITEMS_WITHOUT_VIRTUALIZATION = 50;
|
|
39
39
|
|
|
40
40
|
function searchableString(value: string) {
|
|
41
|
-
return (
|
|
42
|
-
value
|
|
43
|
-
.trim()
|
|
44
|
-
.replace(/\s+/gu, ' ')
|
|
45
|
-
// NFD converts an Å to A + ̊ (and other special characters)
|
|
46
|
-
.normalize('NFD')
|
|
47
|
-
// and then this replaces the ̊ with nothing (and other special characters)
|
|
48
|
-
.replace(/[\u0300-\u036f]/g, '')
|
|
49
|
-
.toLowerCase()
|
|
50
|
-
);
|
|
41
|
+
return value.trim().replace(/\s+/gu, ' ').normalize('NFKC').toLowerCase();
|
|
51
42
|
}
|
|
52
43
|
|
|
53
44
|
function inferSearchableStrings(value: unknown) {
|
package/src/main.css
CHANGED
|
@@ -3822,10 +3822,14 @@ html:not([dir="rtl"]) .np-flow-navigation--sm .np-flow-navigation__stepper {
|
|
|
3822
3822
|
stroke-dasharray: calc(12px * 0.5) calc(12px * 0.5);
|
|
3823
3823
|
stroke-dasharray: var(--wds-list-item-spotlight-strokeDashSize) var(--wds-list-item-spotlight-strokeDashSize);
|
|
3824
3824
|
}
|
|
3825
|
-
.np-field-control
|
|
3825
|
+
.np-field-control,
|
|
3826
|
+
.np-field__prompt {
|
|
3826
3827
|
margin-top: 4px;
|
|
3827
3828
|
margin-top: var(--size-4);
|
|
3828
3829
|
}
|
|
3830
|
+
.np-field:has(.np-checkbox) label {
|
|
3831
|
+
margin-bottom: 0;
|
|
3832
|
+
}
|
|
3829
3833
|
.np-input-group {
|
|
3830
3834
|
display: inline-grid;
|
|
3831
3835
|
width: 100%;
|
|
@@ -6920,6 +6924,10 @@ html:not([dir="rtl"]) .np-navigation-option {
|
|
|
6920
6924
|
padding: 0;
|
|
6921
6925
|
margin: 0;
|
|
6922
6926
|
}
|
|
6927
|
+
.typeahead--prompt {
|
|
6928
|
+
margin-top: 4px;
|
|
6929
|
+
margin-top: var(--size-4);
|
|
6930
|
+
}
|
|
6923
6931
|
.typeahead-sm.typeahead--multiple .typeahead__input-container {
|
|
6924
6932
|
min-height: 32px;
|
|
6925
6933
|
}
|
|
@@ -122,6 +122,10 @@
|
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
&--prompt {
|
|
126
|
+
margin-top: var(--size-4);
|
|
127
|
+
}
|
|
128
|
+
|
|
125
129
|
// SIZES
|
|
126
130
|
&-sm {
|
|
127
131
|
&.typeahead--multiple {
|
|
@@ -207,7 +211,7 @@
|
|
|
207
211
|
}
|
|
208
212
|
|
|
209
213
|
.np-theme-personal & {
|
|
210
|
-
.input-group:not(.disabled
|
|
214
|
+
.input-group:not(.disabled, :disabled, .input-group--has-error):focus-within .tw-icon-search {
|
|
211
215
|
color: var(--color-interactive-primary);
|
|
212
216
|
}
|
|
213
217
|
.tw-icon-search {
|
|
@@ -102,7 +102,7 @@ describe('Typeahead', () => {
|
|
|
102
102
|
expect(onChange).toHaveBeenCalledWith([]);
|
|
103
103
|
});
|
|
104
104
|
|
|
105
|
-
it('shows
|
|
105
|
+
it('shows InlinePrompt when alert prop is provided', () => {
|
|
106
106
|
render(
|
|
107
107
|
<Typeahead
|
|
108
108
|
id="test"
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
2
2
|
import { Search as SearchIcon } from '@transferwise/icons';
|
|
3
3
|
import { userEvent, within, fn } from 'storybook/test';
|
|
4
|
-
|
|
5
|
-
import Typeahead, { type TypeaheadOption } from './Typeahead';
|
|
6
|
-
import { Size } from '../common';
|
|
7
4
|
import { useState } from 'react';
|
|
5
|
+
|
|
6
|
+
import { Sentiment, Size } from '../common';
|
|
8
7
|
import { Input } from '../inputs/Input';
|
|
9
8
|
import { Field } from '../field/Field';
|
|
10
9
|
import Button from '../button';
|
|
11
10
|
import Modal from '../modal';
|
|
11
|
+
import { InlinePromptProps } from '../prompt';
|
|
12
|
+
import Typeahead, { type TypeaheadOption } from './Typeahead';
|
|
12
13
|
|
|
14
|
+
// needed for SB to display correct name in the docs
|
|
15
|
+
(Typeahead as React.FC).displayName = 'Typeahead';
|
|
13
16
|
type Story = StoryObj<typeof Typeahead>;
|
|
14
17
|
|
|
15
18
|
/**
|
|
@@ -37,6 +40,7 @@ const meta: Meta<typeof Typeahead> = {
|
|
|
37
40
|
searchDelay: 200,
|
|
38
41
|
showSuggestions: true,
|
|
39
42
|
showNewEntry: true,
|
|
43
|
+
alert: undefined,
|
|
40
44
|
size: Size.MEDIUM,
|
|
41
45
|
initialValue: [],
|
|
42
46
|
id: 'myTypeahead',
|
|
@@ -54,6 +58,17 @@ const meta: Meta<typeof Typeahead> = {
|
|
|
54
58
|
control: 'inline-radio',
|
|
55
59
|
options: [Size.MEDIUM, Size.LARGE],
|
|
56
60
|
},
|
|
61
|
+
alert: {
|
|
62
|
+
description:
|
|
63
|
+
'Displays an [InlinePrompt](?path=/docs/prompts-inlineprompt--docs) below the input when provided. <blockquote class="m-0">**⚠️ `error`, `info` and `success` are deprecated as alert types and will be soon removed.**</blockquote>',
|
|
64
|
+
control: 'object',
|
|
65
|
+
table: {
|
|
66
|
+
type: {
|
|
67
|
+
summary:
|
|
68
|
+
'{ message: ReactNode; type?: "negative" | "neutral" | "positive" | "warning" | "proposition" }',
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
},
|
|
57
72
|
},
|
|
58
73
|
};
|
|
59
74
|
export default meta;
|
|
@@ -237,6 +252,14 @@ export const Search: Story = {
|
|
|
237
252
|
};
|
|
238
253
|
|
|
239
254
|
export const AutoFocusInModal: Story = {
|
|
255
|
+
parameters: {
|
|
256
|
+
docs: {
|
|
257
|
+
story: {
|
|
258
|
+
inline: false,
|
|
259
|
+
iframeHeight: 500,
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
},
|
|
240
263
|
render: function Render() {
|
|
241
264
|
const [open, setOpen] = useState<boolean>(true);
|
|
242
265
|
return (
|
|
@@ -279,3 +302,57 @@ export const AutoFocusInModal: Story = {
|
|
|
279
302
|
);
|
|
280
303
|
},
|
|
281
304
|
};
|
|
305
|
+
|
|
306
|
+
export const WithPrompt: Story = {
|
|
307
|
+
args: {
|
|
308
|
+
autoFillOnBlur: undefined,
|
|
309
|
+
chipSeparators: undefined,
|
|
310
|
+
clearable: undefined,
|
|
311
|
+
initialValue: undefined,
|
|
312
|
+
inputAutoComplete: undefined,
|
|
313
|
+
minQueryLength: undefined,
|
|
314
|
+
onBlur: undefined,
|
|
315
|
+
onFocus: undefined,
|
|
316
|
+
onInputChange: undefined,
|
|
317
|
+
onSearch: undefined,
|
|
318
|
+
placeholder: undefined,
|
|
319
|
+
searchDelay: undefined,
|
|
320
|
+
showSuggestions: undefined,
|
|
321
|
+
showNewEntry: undefined,
|
|
322
|
+
size: undefined,
|
|
323
|
+
},
|
|
324
|
+
render: function Render(args) {
|
|
325
|
+
const options = [{ label: 'Option 1' }];
|
|
326
|
+
|
|
327
|
+
const alertTypes: InlinePromptProps['sentiment'][] = [
|
|
328
|
+
Sentiment.NEGATIVE,
|
|
329
|
+
Sentiment.WARNING,
|
|
330
|
+
Sentiment.NEUTRAL,
|
|
331
|
+
Sentiment.POSITIVE,
|
|
332
|
+
'proposition',
|
|
333
|
+
];
|
|
334
|
+
|
|
335
|
+
return (
|
|
336
|
+
<>
|
|
337
|
+
{alertTypes.map((sentiment) => (
|
|
338
|
+
<Typeahead
|
|
339
|
+
key={sentiment}
|
|
340
|
+
{...args}
|
|
341
|
+
id={`typeahead-${sentiment}`}
|
|
342
|
+
name={`typeahead-${sentiment}`}
|
|
343
|
+
options={options}
|
|
344
|
+
alert={{
|
|
345
|
+
message: `This is a ${sentiment} message`,
|
|
346
|
+
type: sentiment,
|
|
347
|
+
}}
|
|
348
|
+
/>
|
|
349
|
+
))}
|
|
350
|
+
</>
|
|
351
|
+
);
|
|
352
|
+
},
|
|
353
|
+
decorators: (Story) => (
|
|
354
|
+
<div className="d-flex flex-column">
|
|
355
|
+
<Story />
|
|
356
|
+
</div>
|
|
357
|
+
),
|
|
358
|
+
};
|
|
@@ -16,8 +16,7 @@ import {
|
|
|
16
16
|
removeClickClassFromDocumentOnIos,
|
|
17
17
|
stopPropagation,
|
|
18
18
|
} from '../common';
|
|
19
|
-
import
|
|
20
|
-
import { InlineAlertProps } from '../inlineAlert/InlineAlert';
|
|
19
|
+
import { InlinePrompt, InlinePromptProps } from '../prompt';
|
|
21
20
|
import { withInputAttributes, WithInputAttributesProps } from '../inputs/contexts';
|
|
22
21
|
|
|
23
22
|
import TypeaheadInput from './typeaheadInput/TypeaheadInput';
|
|
@@ -41,8 +40,12 @@ export interface TypeaheadProps<T> extends Partial<WrappedComponentProps> {
|
|
|
41
40
|
name: string;
|
|
42
41
|
addon?: ReactNode;
|
|
43
42
|
alert?: {
|
|
44
|
-
message:
|
|
45
|
-
type?:
|
|
43
|
+
message: InlinePromptProps['children'];
|
|
44
|
+
type?:
|
|
45
|
+
| InlinePromptProps['sentiment']
|
|
46
|
+
| `${Sentiment.ERROR}`
|
|
47
|
+
| `${Sentiment.INFO}`
|
|
48
|
+
| `${Sentiment.SUCCESS}`;
|
|
46
49
|
};
|
|
47
50
|
allowNew?: boolean;
|
|
48
51
|
autoFillOnBlur?: boolean;
|
|
@@ -474,9 +477,20 @@ class Typeahead<T> extends Component<TypeaheadPropsWithInputAttributes<T>, Typea
|
|
|
474
477
|
dropdownOpen,
|
|
475
478
|
});
|
|
476
479
|
|
|
477
|
-
const alertType =
|
|
478
|
-
|
|
479
|
-
|
|
480
|
+
const alertType = (() => {
|
|
481
|
+
if (!alert?.type || alert.type === Sentiment.INFO) {
|
|
482
|
+
return Sentiment.NEUTRAL;
|
|
483
|
+
}
|
|
484
|
+
if (alert.type === Sentiment.ERROR) {
|
|
485
|
+
return Sentiment.NEGATIVE;
|
|
486
|
+
}
|
|
487
|
+
if (alert.type === Sentiment.SUCCESS) {
|
|
488
|
+
return Sentiment.POSITIVE;
|
|
489
|
+
}
|
|
490
|
+
return alert.type;
|
|
491
|
+
})();
|
|
492
|
+
const hasError = errorState || (alert && alertType === Sentiment.NEGATIVE);
|
|
493
|
+
const displayAlert = (!errorState && alert) || (alert && alertType === Sentiment.NEGATIVE);
|
|
480
494
|
const hasWarning = displayAlert && alertType === Sentiment.WARNING;
|
|
481
495
|
const hasInfo = displayAlert && alertType === Sentiment.NEUTRAL;
|
|
482
496
|
return (
|
|
@@ -546,7 +560,13 @@ class Typeahead<T> extends Component<TypeaheadPropsWithInputAttributes<T>, Typea
|
|
|
546
560
|
</div>
|
|
547
561
|
)}
|
|
548
562
|
</div>
|
|
549
|
-
{displayAlert ?
|
|
563
|
+
{displayAlert ? (
|
|
564
|
+
<InlinePrompt sentiment={alertType} className="typeahead--prompt">
|
|
565
|
+
{alert.message}
|
|
566
|
+
</InlinePrompt>
|
|
567
|
+
) : (
|
|
568
|
+
menu
|
|
569
|
+
)}
|
|
550
570
|
</div>
|
|
551
571
|
</div>
|
|
552
572
|
);
|
|
@@ -71,7 +71,7 @@ const meta = {
|
|
|
71
71
|
},
|
|
72
72
|
errorIconLabel: {
|
|
73
73
|
description:
|
|
74
|
-
"Override for the
|
|
74
|
+
"Override for the InlinePrompt icon's default, accessible name announced by the screen readers",
|
|
75
75
|
},
|
|
76
76
|
usHelpImage: {
|
|
77
77
|
control: false,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { Meta } from '@storybook/react-webpack5';
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
2
|
+
import { userEvent, within, waitFor } from 'storybook/test';
|
|
2
3
|
|
|
3
4
|
import Upload from '.';
|
|
4
5
|
import { Field } from '../field/Field';
|
|
@@ -73,6 +74,40 @@ export const MaxSizes = () => {
|
|
|
73
74
|
);
|
|
74
75
|
};
|
|
75
76
|
|
|
77
|
+
export const WithError: StoryObj<typeof meta> = {
|
|
78
|
+
render: () => (
|
|
79
|
+
<Field label="Upload a file">
|
|
80
|
+
<Upload
|
|
81
|
+
usAccept="*"
|
|
82
|
+
usLabel="Upload a file"
|
|
83
|
+
usPlaceholder="Maximum filesize is 1B"
|
|
84
|
+
usDisabled={false}
|
|
85
|
+
maxSize={1}
|
|
86
|
+
errorIconLabel="File too large error"
|
|
87
|
+
httpOptions={{
|
|
88
|
+
url: 'https://httpbin.org/post',
|
|
89
|
+
method: 'POST',
|
|
90
|
+
}}
|
|
91
|
+
/>
|
|
92
|
+
</Field>
|
|
93
|
+
),
|
|
94
|
+
play: async ({ canvasElement }) => {
|
|
95
|
+
const canvas = within(canvasElement);
|
|
96
|
+
const fileInput = canvasElement.querySelector<HTMLInputElement>('input[type="file"]');
|
|
97
|
+
|
|
98
|
+
if (!fileInput) {
|
|
99
|
+
throw new Error('File input not found');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const file = new File(['This file content is definitely more than 1 byte'], 'test-file.txt', {
|
|
103
|
+
type: 'text/plain',
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
await userEvent.upload(fileInput, file);
|
|
107
|
+
await waitFor(async () => canvas.findByText(/Maximum filesize is/i), { timeout: 3000 });
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
|
|
76
111
|
export const AllVariants = () => {
|
|
77
112
|
return (
|
|
78
113
|
<div style={{ display: 'flex', gap: '1rem' }}>
|
package/src/upload/Upload.tsx
CHANGED
|
@@ -55,7 +55,7 @@ export interface UploadProps extends WrappedComponentProps {
|
|
|
55
55
|
usLabel?: string;
|
|
56
56
|
usPlaceholder?: string;
|
|
57
57
|
/**
|
|
58
|
-
* Override for the [
|
|
58
|
+
* Override for the [InlinePrompt icon's default, accessible name](/?path=/docs/other-statusicon-accessibility--docs)
|
|
59
59
|
* announced by the screen readers
|
|
60
60
|
* */
|
|
61
61
|
errorIconLabel?: string;
|
|
@@ -2,7 +2,7 @@ import { Upload as UploadIcon } from '@transferwise/icons';
|
|
|
2
2
|
import { createRef, PureComponent } from 'react';
|
|
3
3
|
import StatusIcon from '../../../statusIcon';
|
|
4
4
|
import { Sentiment, Size } from '../../../common';
|
|
5
|
-
import
|
|
5
|
+
import { InlinePrompt } from '../../../prompt/InlinePrompt/InlinePrompt';
|
|
6
6
|
|
|
7
7
|
export interface UploadImageStepProps {
|
|
8
8
|
fileDropped: (file: File) => void;
|
|
@@ -105,9 +105,9 @@ export default class UploadImageStep extends PureComponent<UploadImageStepProps>
|
|
|
105
105
|
{errorMessage && (
|
|
106
106
|
<div className="upload-error-message">
|
|
107
107
|
<div className="m-t-3 has-error">
|
|
108
|
-
<
|
|
108
|
+
<InlinePrompt sentiment={Sentiment.NEGATIVE} iconLabel={errorIconLabel}>
|
|
109
109
|
{errorMessage}
|
|
110
|
-
</
|
|
110
|
+
</InlinePrompt>
|
|
111
111
|
</div>
|
|
112
112
|
</div>
|
|
113
113
|
)}
|