@transferwise/components 0.0.0-experimental-a2f9b4e → 0.0.0-experimental-21d2352
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/field/Field.js +60 -32
- package/build/field/Field.js.map +1 -1
- package/build/field/Field.messages.js +14 -0
- package/build/field/Field.messages.js.map +1 -0
- package/build/field/Field.messages.mjs +10 -0
- package/build/field/Field.messages.mjs.map +1 -0
- package/build/field/Field.mjs +62 -34
- package/build/field/Field.mjs.map +1 -1
- package/build/flowNavigation/FlowNavigation.js +4 -2
- package/build/flowNavigation/FlowNavigation.js.map +1 -1
- package/build/flowNavigation/FlowNavigation.mjs +4 -2
- package/build/flowNavigation/FlowNavigation.mjs.map +1 -1
- package/build/i18n/en.json +1 -0
- package/build/i18n/en.json.js +1 -0
- package/build/i18n/en.json.js.map +1 -1
- package/build/i18n/en.json.mjs +1 -0
- package/build/i18n/en.json.mjs.map +1 -1
- package/build/inputs/TextArea.js +5 -0
- package/build/inputs/TextArea.js.map +1 -1
- package/build/inputs/TextArea.mjs +6 -1
- package/build/inputs/TextArea.mjs.map +1 -1
- package/build/inputs/contexts.js +16 -0
- package/build/inputs/contexts.js.map +1 -1
- package/build/inputs/contexts.mjs +16 -2
- package/build/inputs/contexts.mjs.map +1 -1
- package/build/main.css +19 -2
- package/build/styles/field/Field.css +17 -2
- package/build/styles/main.css +19 -2
- package/build/types/field/Field.d.ts.map +1 -1
- package/build/types/field/Field.messages.d.ts +8 -0
- package/build/types/field/Field.messages.d.ts.map +1 -0
- package/build/types/flowNavigation/FlowNavigation.d.ts.map +1 -1
- package/build/types/inputs/TextArea.d.ts.map +1 -1
- package/build/types/inputs/contexts.d.ts +6 -0
- package/build/types/inputs/contexts.d.ts.map +1 -1
- package/build/types/test-utils/index.d.ts +2 -0
- package/build/types/test-utils/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/field/Field.css +17 -2
- package/src/field/Field.less +15 -2
- package/src/field/Field.messages.ts +8 -0
- package/src/field/Field.story.tsx +5 -1
- package/src/field/Field.tsx +80 -37
- package/src/flowNavigation/FlowNavigation.test.js +2 -2
- package/src/flowNavigation/FlowNavigation.tsx +5 -2
- package/src/i18n/en.json +1 -0
- package/src/inputs/TextArea.story.tsx +56 -0
- package/src/inputs/TextArea.tsx +7 -2
- package/src/inputs/contexts.tsx +18 -1
- package/src/main.css +19 -2
- package/src/textareaWithDisplayFormat/TextareaWithDisplayFormat.story.tsx +1 -0
- package/src/flowNavigation/FlowNavigation.test.story.tsx +0 -88
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
3
|
+
|
|
4
|
+
import { Field } from '../field/Field';
|
|
5
|
+
import { Sentiment } from '../common';
|
|
6
|
+
import { TextArea } from './TextArea';
|
|
7
|
+
|
|
8
|
+
const meta: Meta<typeof TextArea> = {
|
|
9
|
+
title: 'Forms/TextArea',
|
|
10
|
+
component: TextArea,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default meta;
|
|
14
|
+
type Story = StoryObj<typeof TextArea>;
|
|
15
|
+
|
|
16
|
+
export const Basic: Story = {
|
|
17
|
+
render: () => {
|
|
18
|
+
const [value, setValue] = useState('');
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<Field label="Message" required={false}>
|
|
22
|
+
<TextArea maxLength={200} value={value} onChange={({ target }) => setValue(target.value)} />
|
|
23
|
+
</Field>
|
|
24
|
+
);
|
|
25
|
+
},
|
|
26
|
+
parameters: {
|
|
27
|
+
docs: {
|
|
28
|
+
source: {
|
|
29
|
+
code: `<Field label="Message" required={false}>
|
|
30
|
+
<TextArea
|
|
31
|
+
maxLength={200}
|
|
32
|
+
value={value}
|
|
33
|
+
onChange={({ target }) => setValue(target.value)}
|
|
34
|
+
/>
|
|
35
|
+
</Field>`,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const WithError: Story = {
|
|
42
|
+
render: () => {
|
|
43
|
+
const [value, setValue] = useState('');
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<Field
|
|
47
|
+
label="Message"
|
|
48
|
+
required={false}
|
|
49
|
+
sentiment={Sentiment.NEGATIVE}
|
|
50
|
+
message="You have exceeded the character limit"
|
|
51
|
+
>
|
|
52
|
+
<TextArea maxLength={200} value={value} onChange={({ target }) => setValue(target.value)} />
|
|
53
|
+
</Field>
|
|
54
|
+
);
|
|
55
|
+
},
|
|
56
|
+
};
|
package/src/inputs/TextArea.tsx
CHANGED
|
@@ -3,7 +3,7 @@ import { forwardRef } from 'react';
|
|
|
3
3
|
|
|
4
4
|
import { Merge } from '../utils';
|
|
5
5
|
import { inputClassNameBase } from './_common';
|
|
6
|
-
import { useInputAttributes } from './contexts';
|
|
6
|
+
import { useTextareaCharacterCount, useInputAttributes } from './contexts';
|
|
7
7
|
|
|
8
8
|
export interface TextAreaProps extends Merge<
|
|
9
9
|
React.ComponentPropsWithRef<'textarea'>,
|
|
@@ -13,15 +13,20 @@ export interface TextAreaProps extends Merge<
|
|
|
13
13
|
> {}
|
|
14
14
|
|
|
15
15
|
export const TextArea = forwardRef(function TextArea(
|
|
16
|
-
{ className, ...restProps }: TextAreaProps,
|
|
16
|
+
{ className, maxLength, ...restProps }: TextAreaProps,
|
|
17
17
|
reference: React.ForwardedRef<HTMLTextAreaElement | null>,
|
|
18
18
|
) {
|
|
19
19
|
const inputAttributes = useInputAttributes();
|
|
20
|
+
const value = restProps.value ?? restProps.defaultValue ?? '';
|
|
21
|
+
const currentLength = typeof value === 'string' ? value.length : String(value).length;
|
|
22
|
+
|
|
23
|
+
useTextareaCharacterCount(currentLength, maxLength);
|
|
20
24
|
|
|
21
25
|
return (
|
|
22
26
|
<textarea
|
|
23
27
|
ref={reference}
|
|
24
28
|
className={clsx(className, inputClassNameBase(), 'np-text-area')}
|
|
29
|
+
maxLength={maxLength}
|
|
25
30
|
{...inputAttributes}
|
|
26
31
|
{...restProps}
|
|
27
32
|
/>
|
package/src/inputs/contexts.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createContext, useContext } from 'react';
|
|
1
|
+
import { createContext, useContext, useEffect } from 'react';
|
|
2
2
|
|
|
3
3
|
type FieldLabelContextType = {
|
|
4
4
|
id?: string;
|
|
@@ -36,6 +36,23 @@ export function useFieldLabelRef() {
|
|
|
36
36
|
return useContext(FieldLabelContext)?.ref;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
export type TextareaCharacterCountState = { current: number; max: number } | null;
|
|
40
|
+
|
|
41
|
+
const TextareaCharacterCountContext = createContext<
|
|
42
|
+
((state: TextareaCharacterCountState) => void) | undefined
|
|
43
|
+
>(undefined);
|
|
44
|
+
export const TextareaCharacterCountProvider = TextareaCharacterCountContext.Provider;
|
|
45
|
+
|
|
46
|
+
export function useTextareaCharacterCount(current: number, max: number | undefined) {
|
|
47
|
+
const setCharacterCount = useContext(TextareaCharacterCountContext);
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
if (setCharacterCount && max != null) {
|
|
50
|
+
setCharacterCount({ current, max });
|
|
51
|
+
return () => setCharacterCount(null);
|
|
52
|
+
}
|
|
53
|
+
}, [setCharacterCount, current, max]);
|
|
54
|
+
}
|
|
55
|
+
|
|
39
56
|
export interface WithInputAttributesProps {
|
|
40
57
|
inputAttributes: ReturnType<typeof useInputAttributes>;
|
|
41
58
|
}
|
package/src/main.css
CHANGED
|
@@ -30193,12 +30193,29 @@ html:not([dir="rtl"]) .np-flow-navigation--sm .np-flow-navigation__stepper {
|
|
|
30193
30193
|
stroke-dasharray: var(--wds-list-item-spotlight-strokeDashSize) var(--wds-list-item-spotlight-strokeDashSize);
|
|
30194
30194
|
}
|
|
30195
30195
|
|
|
30196
|
-
.np-field-control
|
|
30197
|
-
.np-field__prompt {
|
|
30196
|
+
.np-field-control {
|
|
30198
30197
|
margin-top: 4px;
|
|
30199
30198
|
margin-top: var(--size-4);
|
|
30200
30199
|
}
|
|
30201
30200
|
|
|
30201
|
+
.np-field-validation {
|
|
30202
|
+
display: flex;
|
|
30203
|
+
align-items: flex-start;
|
|
30204
|
+
margin-top: 4px;
|
|
30205
|
+
margin-top: var(--size-4);
|
|
30206
|
+
gap: 8px;
|
|
30207
|
+
gap: var(--size-8);
|
|
30208
|
+
}
|
|
30209
|
+
|
|
30210
|
+
.np-field-counter {
|
|
30211
|
+
margin-left: auto;
|
|
30212
|
+
white-space: nowrap;
|
|
30213
|
+
padding: 4px 0;
|
|
30214
|
+
padding: var(--size-4) 0;
|
|
30215
|
+
color: #768e9c;
|
|
30216
|
+
color: var(--color-content-tertiary);
|
|
30217
|
+
}
|
|
30218
|
+
|
|
30202
30219
|
.np-field .form-group--typeahead[class],
|
|
30203
30220
|
.np-field .np-checkbox-label[class] {
|
|
30204
30221
|
margin-bottom: 0;
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
2
|
-
import AvatarView from '../avatarView';
|
|
3
|
-
import { ProfileType } from '../common';
|
|
4
|
-
import Logo from '../logo';
|
|
5
|
-
import { allModes } from '../../.storybook/modes';
|
|
6
|
-
|
|
7
|
-
import FlowNavigation, { FlowNavigationProps } from './FlowNavigation';
|
|
8
|
-
|
|
9
|
-
const meta: Meta<typeof FlowNavigation> = {
|
|
10
|
-
component: FlowNavigation,
|
|
11
|
-
title: 'Navigation/FlowNavigation/Tests',
|
|
12
|
-
tags: ['!autodocs', '!manifest'],
|
|
13
|
-
parameters: {
|
|
14
|
-
padding: '0',
|
|
15
|
-
},
|
|
16
|
-
};
|
|
17
|
-
export default meta;
|
|
18
|
-
|
|
19
|
-
type Story = StoryObj<typeof FlowNavigation>;
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Verifies no extra spacing when steps is empty at tablet viewport.
|
|
23
|
-
*/
|
|
24
|
-
export const EmptySteps: Story = {
|
|
25
|
-
parameters: {
|
|
26
|
-
chromatic: {
|
|
27
|
-
modes: {
|
|
28
|
-
tablet: allModes.tablet,
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
globals: {
|
|
33
|
-
viewport: 'tablet',
|
|
34
|
-
},
|
|
35
|
-
render: () => {
|
|
36
|
-
const commonProps: FlowNavigationProps = {
|
|
37
|
-
steps: [],
|
|
38
|
-
activeStep: 0,
|
|
39
|
-
avatar: <AvatarView profileType={ProfileType.PERSONAL} />,
|
|
40
|
-
logo: <Logo />,
|
|
41
|
-
onClose: () => {},
|
|
42
|
-
onGoBack: () => {},
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
return <FlowNavigation {...commonProps} />;
|
|
46
|
-
},
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Verifies FlowNavigation handles long step labels (e.g. Hungarian) at a narrow viewport.
|
|
51
|
-
*/
|
|
52
|
-
export const LongLabel: Story = {
|
|
53
|
-
parameters: {
|
|
54
|
-
chromatic: {
|
|
55
|
-
viewports: [360],
|
|
56
|
-
},
|
|
57
|
-
viewport: {
|
|
58
|
-
options: {
|
|
59
|
-
narrow: {
|
|
60
|
-
name: '360px',
|
|
61
|
-
styles: { width: '360px', height: '600px' },
|
|
62
|
-
type: 'mobile',
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
},
|
|
67
|
-
globals: {
|
|
68
|
-
viewport: 'narrow',
|
|
69
|
-
},
|
|
70
|
-
render: () => {
|
|
71
|
-
const commonProps: FlowNavigationProps = {
|
|
72
|
-
steps: [
|
|
73
|
-
{ label: 'Amount', hoverLabel: '100 GBP', onClick: () => {} },
|
|
74
|
-
{ label: 'You', hoverLabel: 'Elena Durante', onClick: () => {} },
|
|
75
|
-
{ label: 'Kedvezményezett', hoverLabel: 'Daniele Tomboro', onClick: () => {} },
|
|
76
|
-
{ label: 'Review', hoverLabel: 'Antonio Dozortevo', onClick: () => {} },
|
|
77
|
-
{ label: 'Pay', hoverLabel: 'Enrico Gusso', onClick: () => {} },
|
|
78
|
-
],
|
|
79
|
-
activeStep: 2,
|
|
80
|
-
avatar: <AvatarView profileType={ProfileType.PERSONAL} />,
|
|
81
|
-
logo: <Logo />,
|
|
82
|
-
onClose: () => {},
|
|
83
|
-
onGoBack: () => {},
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
return <FlowNavigation {...commonProps} />;
|
|
87
|
-
},
|
|
88
|
-
};
|