@transferwise/components 0.0.0-experimental-6a46f89 → 0.0.0-experimental-94246de
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/dateLookup/dateTrigger/DateTrigger.js +4 -8
- package/build/dateLookup/dateTrigger/DateTrigger.js.map +1 -1
- package/build/dateLookup/dateTrigger/DateTrigger.mjs +4 -8
- package/build/dateLookup/dateTrigger/DateTrigger.mjs.map +1 -1
- package/build/field/Field.js +8 -36
- package/build/field/Field.js.map +1 -1
- package/build/field/Field.mjs +9 -37
- package/build/field/Field.mjs.map +1 -1
- package/build/i18n/en.json +1 -1
- package/build/i18n/en.json.js +1 -1
- package/build/i18n/en.json.mjs +1 -1
- package/build/inlineAlert/InlineAlert.js +6 -13
- package/build/inlineAlert/InlineAlert.js.map +1 -1
- package/build/inlineAlert/InlineAlert.mjs +6 -13
- package/build/inlineAlert/InlineAlert.mjs.map +1 -1
- package/build/label/Label.js +4 -35
- package/build/label/Label.js.map +1 -1
- package/build/label/Label.mjs +5 -36
- package/build/label/Label.mjs.map +1 -1
- package/build/main.css +14 -4
- package/build/styles/dateLookup/dateTrigger/DateTrigger.css +8 -0
- package/build/styles/main.css +14 -4
- package/build/styles/upload/Upload.css +6 -0
- package/build/types/dateLookup/dateTrigger/DateTrigger.d.ts.map +1 -1
- package/build/types/field/Field.d.ts +2 -4
- package/build/types/field/Field.d.ts.map +1 -1
- package/build/types/index.d.ts +1 -1
- package/build/types/index.d.ts.map +1 -1
- package/build/types/inlineAlert/InlineAlert.d.ts +0 -9
- package/build/types/inlineAlert/InlineAlert.d.ts.map +1 -1
- package/build/types/label/Label.d.ts +2 -22
- package/build/types/label/Label.d.ts.map +1 -1
- package/build/types/upload/Upload.d.ts +1 -0
- package/build/types/upload/Upload.d.ts.map +1 -1
- package/build/types/upload/Upload.messages.d.ts +4 -0
- package/build/types/upload/Upload.messages.d.ts.map +1 -1
- package/build/types/upload/steps/completeStep/completeStep.d.ts +1 -3
- package/build/types/upload/steps/completeStep/completeStep.d.ts.map +1 -1
- package/build/types/upload/steps/uploadImageStep/uploadImageStep.d.ts +1 -0
- package/build/types/upload/steps/uploadImageStep/uploadImageStep.d.ts.map +1 -1
- package/build/upload/Upload.js +26 -12
- package/build/upload/Upload.js.map +1 -1
- package/build/upload/Upload.messages.js +3 -0
- package/build/upload/Upload.messages.js.map +1 -1
- package/build/upload/Upload.messages.mjs +3 -0
- package/build/upload/Upload.messages.mjs.map +1 -1
- package/build/upload/Upload.mjs +26 -12
- package/build/upload/Upload.mjs.map +1 -1
- package/build/upload/steps/completeStep/completeStep.js +15 -30
- package/build/upload/steps/completeStep/completeStep.js.map +1 -1
- package/build/upload/steps/completeStep/completeStep.mjs +16 -31
- package/build/upload/steps/completeStep/completeStep.mjs.map +1 -1
- package/build/upload/steps/uploadImageStep/uploadImageStep.js +56 -32
- package/build/upload/steps/uploadImageStep/uploadImageStep.js.map +1 -1
- package/build/upload/steps/uploadImageStep/uploadImageStep.mjs +56 -32
- package/build/upload/steps/uploadImageStep/uploadImageStep.mjs.map +1 -1
- package/package.json +3 -3
- package/src/dateInput/DateInput.tests.story.tsx +41 -6
- package/src/dateLookup/DateLookup.rtl.spec.tsx +1 -1
- package/src/dateLookup/dateTrigger/DateTrigger.css +8 -0
- package/src/dateLookup/dateTrigger/DateTrigger.less +8 -0
- package/src/dateLookup/dateTrigger/DateTrigger.spec.js +1 -1
- package/src/dateLookup/dateTrigger/DateTrigger.tsx +4 -9
- package/src/field/Field.spec.tsx +5 -41
- package/src/field/Field.story.tsx +7 -105
- package/src/field/Field.tests.story.tsx +33 -0
- package/src/field/Field.tsx +10 -34
- package/src/i18n/en.json +1 -1
- package/src/index.ts +1 -1
- package/src/inlineAlert/InlineAlert.story.tsx +72 -7
- package/src/inlineAlert/InlineAlert.tsx +3 -14
- package/src/inputWithDisplayFormat/InputWithDisplayFormat.story.js +10 -5
- package/src/inputs/InputGroup.spec.tsx +1 -1
- package/src/inputs/SearchInput.spec.tsx +1 -1
- package/src/inputs/SelectInput.spec.tsx +1 -1
- package/src/label/Label.spec.tsx +5 -53
- package/src/label/Label.story.tsx +25 -33
- package/src/label/Label.tsx +3 -48
- package/src/main.css +14 -4
- package/src/main.less +0 -1
- package/src/moneyInput/MoneyInput.story.tsx +11 -11
- package/src/radioGroup/RadioGroup.rtl.spec.tsx +1 -1
- package/src/select/Select.rtl.spec.tsx +1 -1
- package/src/switch/Switch.spec.tsx +1 -1
- package/src/switch/Switch.story.tsx +21 -19
- package/src/upload/Upload.css +6 -0
- package/src/upload/Upload.less +5 -0
- package/src/upload/Upload.messages.ts +4 -0
- package/src/upload/Upload.spec.js +8 -7
- package/src/upload/Upload.story.tsx +1 -0
- package/src/upload/Upload.tsx +39 -20
- package/src/upload/steps/completeStep/completeStep.spec.js +0 -9
- package/src/upload/steps/completeStep/completeStep.tsx +14 -29
- package/src/upload/steps/uploadImageStep/uploadImageStep.spec.js +12 -0
- package/src/upload/steps/uploadImageStep/uploadImageStep.tsx +43 -24
- package/build/label/Label.messages.js +0 -12
- package/build/label/Label.messages.js.map +0 -1
- package/build/label/Label.messages.mjs +0 -10
- package/build/label/Label.messages.mjs.map +0 -1
- package/build/styles/field/Field.css +0 -4
- package/build/types/label/Label.messages.d.ts +0 -8
- package/build/types/label/Label.messages.d.ts.map +0 -1
- package/build/types/label/index.d.ts +0 -3
- package/build/types/label/index.d.ts.map +0 -1
- package/src/field/Field.css +0 -4
- package/src/field/Field.less +0 -5
- package/src/label/Label.messages.tsx +0 -8
- package/src/label/index.ts +0 -2
package/src/field/Field.tsx
CHANGED
|
@@ -9,18 +9,16 @@ import {
|
|
|
9
9
|
InputIdContextProvider,
|
|
10
10
|
InputInvalidProvider,
|
|
11
11
|
} from '../inputs/contexts';
|
|
12
|
-
import { Label } from '../label';
|
|
12
|
+
import { Label } from '../label/Label';
|
|
13
13
|
|
|
14
14
|
export type FieldProps = {
|
|
15
15
|
/** `null` disables auto-generating the `id` attribute, falling back to nesting-based label association over setting `htmlFor` explicitly. */
|
|
16
16
|
id?: string | null;
|
|
17
17
|
/** Should be specified unless the wrapped control has its own labeling mechanism, e.g. `Checkbox`. */
|
|
18
18
|
label?: React.ReactNode;
|
|
19
|
-
|
|
20
|
-
/** @deprecated use `description` prop instead */
|
|
19
|
+
/** @deprecated use `message` and `type={Sentiment.NEUTRAL}` prop instead */
|
|
21
20
|
hint?: React.ReactNode;
|
|
22
21
|
message?: React.ReactNode;
|
|
23
|
-
description?: React.ReactNode;
|
|
24
22
|
/** @deprecated use `message` and `type={Sentiment.NEGATIVE}` prop instead */
|
|
25
23
|
error?: React.ReactNode;
|
|
26
24
|
sentiment?: `${Sentiment.NEGATIVE | Sentiment.NEUTRAL | Sentiment.POSITIVE | Sentiment.WARNING}`;
|
|
@@ -31,17 +29,14 @@ export type FieldProps = {
|
|
|
31
29
|
export const Field = ({
|
|
32
30
|
id,
|
|
33
31
|
label,
|
|
34
|
-
required = false,
|
|
35
32
|
message: propMessage,
|
|
36
|
-
hint,
|
|
37
|
-
description = hint,
|
|
38
33
|
sentiment: propType = Sentiment.NEUTRAL,
|
|
39
34
|
className,
|
|
40
35
|
children,
|
|
41
36
|
...props
|
|
42
37
|
}: FieldProps) => {
|
|
43
38
|
const sentiment = props.error ? Sentiment.NEGATIVE : propType;
|
|
44
|
-
const message =
|
|
39
|
+
const message = props.error || props.hint || propMessage;
|
|
45
40
|
const hasError = sentiment === Sentiment.NEGATIVE;
|
|
46
41
|
|
|
47
42
|
const labelId = useId();
|
|
@@ -49,32 +44,16 @@ export const Field = ({
|
|
|
49
44
|
const fallbackInputId = useId();
|
|
50
45
|
const inputId = id !== null ? (id ?? fallbackInputId) : undefined;
|
|
51
46
|
|
|
52
|
-
const messageId = useId();
|
|
53
47
|
const descriptionId = useId();
|
|
54
48
|
|
|
55
|
-
/**
|
|
56
|
-
* form control can have multiple messages to describe it,
|
|
57
|
-
* e.g the description underneath the label and inline alert
|
|
58
|
-
*/
|
|
59
|
-
function ariaDescribedbyByIds() {
|
|
60
|
-
const messageIds = [];
|
|
61
|
-
if (description) {
|
|
62
|
-
messageIds.push(descriptionId);
|
|
63
|
-
}
|
|
64
|
-
if (message) {
|
|
65
|
-
messageIds.push(messageId);
|
|
66
|
-
}
|
|
67
|
-
return messageIds.length > 0 ? messageIds.join(' ') : undefined;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
49
|
return (
|
|
71
50
|
<FieldLabelIdContextProvider value={labelId}>
|
|
72
51
|
<InputIdContextProvider value={inputId}>
|
|
73
|
-
<InputDescribedByProvider value={
|
|
52
|
+
<InputDescribedByProvider value={message ? descriptionId : undefined}>
|
|
74
53
|
<InputInvalidProvider value={hasError}>
|
|
75
54
|
<div
|
|
76
55
|
className={clsx(
|
|
77
|
-
'
|
|
56
|
+
'form-group d-block',
|
|
78
57
|
{
|
|
79
58
|
'has-success': sentiment === Sentiment.POSITIVE,
|
|
80
59
|
'has-warning': sentiment === Sentiment.WARNING,
|
|
@@ -85,19 +64,16 @@ export const Field = ({
|
|
|
85
64
|
)}
|
|
86
65
|
>
|
|
87
66
|
{label != null ? (
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
<Label.Description id={descriptionId}>{description}</Label.Description>
|
|
93
|
-
<div className="np-field-control">{children}</div>
|
|
94
|
-
</>
|
|
67
|
+
<Label id={labelId} htmlFor={inputId}>
|
|
68
|
+
{label}
|
|
69
|
+
{children}
|
|
70
|
+
</Label>
|
|
95
71
|
) : (
|
|
96
72
|
children
|
|
97
73
|
)}
|
|
98
74
|
|
|
99
75
|
{message && (
|
|
100
|
-
<InlineAlert type={sentiment} id={
|
|
76
|
+
<InlineAlert type={sentiment} id={descriptionId}>
|
|
101
77
|
{message}
|
|
102
78
|
</InlineAlert>
|
|
103
79
|
)}
|
package/src/i18n/en.json
CHANGED
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
"neptune.DateLookup.year": "year",
|
|
19
19
|
"neptune.FlowNavigation.back": "back to previous step",
|
|
20
20
|
"neptune.Info.ariaLabel": "More information",
|
|
21
|
-
"neptune.Label.optional": "(Optional)",
|
|
22
21
|
"neptune.Link.opensInNewTab": "(opens in new tab)",
|
|
23
22
|
"neptune.MoneyInput.Select.placeholder": "Select an option...",
|
|
24
23
|
"neptune.PhoneNumberInput.SelectInput.placeholder": "Select an option...",
|
|
@@ -36,6 +35,7 @@
|
|
|
36
35
|
"neptune.Upload.csWrongTypeMessage": "File type not supported. Please try again with a different file",
|
|
37
36
|
"neptune.Upload.psButtonText": "Cancel",
|
|
38
37
|
"neptune.Upload.psProcessingText": "Uploading...",
|
|
38
|
+
"neptune.Upload.retry": "Retry",
|
|
39
39
|
"neptune.Upload.usButtonText": "Or select a file",
|
|
40
40
|
"neptune.Upload.usDropMessage": "Drop file to start upload",
|
|
41
41
|
"neptune.Upload.usPlaceholder": "Drag and drop a file less than {maxSize}MB",
|
package/src/index.ts
CHANGED
|
@@ -44,7 +44,7 @@ export type {
|
|
|
44
44
|
} from './inputs/SelectInput';
|
|
45
45
|
export type { TextAreaProps } from './inputs/TextArea';
|
|
46
46
|
export type { InstructionsListProps } from './instructionsList';
|
|
47
|
-
export type { LabelProps
|
|
47
|
+
export type { LabelProps } from './label/Label';
|
|
48
48
|
export type { LoaderProps } from './loader';
|
|
49
49
|
export type { MarkdownProps } from './markdown';
|
|
50
50
|
export type { ModalProps } from './modal';
|
|
@@ -2,9 +2,9 @@ import { select, text } from '@storybook/addon-knobs';
|
|
|
2
2
|
import { Meta } from '@storybook/react';
|
|
3
3
|
|
|
4
4
|
import { Sentiment } from '../common';
|
|
5
|
+
import { Input } from '../inputs/Input';
|
|
5
6
|
|
|
6
7
|
import InlineAlert, { InlineAlertProps } from './InlineAlert';
|
|
7
|
-
import { lorem40 } from '../test-utils';
|
|
8
8
|
|
|
9
9
|
export default {
|
|
10
10
|
component: InlineAlert,
|
|
@@ -29,16 +29,81 @@ export const Basic = () => {
|
|
|
29
29
|
|
|
30
30
|
const message = text('message', 'Please enter a password over 5 characters');
|
|
31
31
|
|
|
32
|
+
let typeClass = '';
|
|
33
|
+
switch (type) {
|
|
34
|
+
case Sentiment.ERROR:
|
|
35
|
+
case Sentiment.NEGATIVE:
|
|
36
|
+
typeClass = 'has-error';
|
|
37
|
+
break;
|
|
38
|
+
case Sentiment.SUCCESS:
|
|
39
|
+
case Sentiment.POSITIVE:
|
|
40
|
+
typeClass = 'has-success';
|
|
41
|
+
break;
|
|
42
|
+
case Sentiment.INFO:
|
|
43
|
+
case Sentiment.NEUTRAL:
|
|
44
|
+
typeClass = 'has-info';
|
|
45
|
+
break;
|
|
46
|
+
case Sentiment.WARNING:
|
|
47
|
+
typeClass = 'has-warning';
|
|
48
|
+
break;
|
|
49
|
+
case Sentiment.PENDING:
|
|
50
|
+
}
|
|
51
|
+
|
|
32
52
|
return (
|
|
33
53
|
<>
|
|
54
|
+
{/* eslint-disable-next-line react/no-adjacent-inline-elements */}
|
|
55
|
+
<p>
|
|
56
|
+
The styling for the input (the coloured border) and the visibility of the inline alert is
|
|
57
|
+
controlled through the use of <code>has-***</code> classes which are applied to the{' '}
|
|
58
|
+
<code>form-group</code> element. For example, to display an inline alert of type error, you
|
|
59
|
+
must also apply the class <code>has-error</code> to the parent <code>form-group</code>{' '}
|
|
60
|
+
element. The available classes are <code>has-error</code>, <code>has-info</code>,{' '}
|
|
61
|
+
<code>has-warning</code> and <code>has-success</code>.
|
|
62
|
+
</p>
|
|
34
63
|
<p>
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
64
|
+
Where possible consumers should use{' '}
|
|
65
|
+
<a href="https://storybook.wise.design/?path=/story/field--basic">Field</a> instead of doing
|
|
66
|
+
this manually.
|
|
38
67
|
</p>
|
|
39
|
-
<
|
|
40
|
-
|
|
41
|
-
|
|
68
|
+
<div className={`form-group ${typeClass}`}>
|
|
69
|
+
<label className="control-label" htmlFor="id0">
|
|
70
|
+
Toggleable
|
|
71
|
+
</label>
|
|
72
|
+
<Input id="id0" value="Neptune is cool" />
|
|
73
|
+
<InlineAlert type={type}>{message}</InlineAlert>
|
|
74
|
+
</div>
|
|
75
|
+
<div className="form-group has-error">
|
|
76
|
+
<label className="control-label" htmlFor="id1">
|
|
77
|
+
Negative
|
|
78
|
+
</label>
|
|
79
|
+
<Input id="id1" value="Neptune is cool" />
|
|
80
|
+
<InlineAlert type="negative">{message}</InlineAlert>
|
|
81
|
+
</div>
|
|
82
|
+
<div className="form-group has-success">
|
|
83
|
+
<label className="control-label" htmlFor="id2">
|
|
84
|
+
Positive
|
|
85
|
+
</label>
|
|
86
|
+
<Input id="id2" value="Neptune is cool" />
|
|
87
|
+
<InlineAlert type="positive">
|
|
88
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
|
|
89
|
+
ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
|
|
90
|
+
ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
|
91
|
+
</InlineAlert>
|
|
92
|
+
</div>
|
|
93
|
+
<div className="form-group has-neutral">
|
|
94
|
+
<label className="control-label" htmlFor="id3">
|
|
95
|
+
Neutral
|
|
96
|
+
</label>
|
|
97
|
+
<Input id="id3" value="Neptune is cool" />
|
|
98
|
+
<InlineAlert type="neutral">{message}</InlineAlert>
|
|
99
|
+
</div>
|
|
100
|
+
<div className="form-group">
|
|
101
|
+
<label className="control-label" htmlFor="id4">
|
|
102
|
+
No has-* class
|
|
103
|
+
</label>
|
|
104
|
+
<Input id="id4" value="Neptune is cool" />
|
|
105
|
+
<InlineAlert type="negative">{message}</InlineAlert>
|
|
106
|
+
</div>
|
|
42
107
|
</>
|
|
43
108
|
);
|
|
44
109
|
};
|
|
@@ -3,7 +3,6 @@ import { ReactNode } from 'react';
|
|
|
3
3
|
|
|
4
4
|
import { Sentiment, Size } from '../common';
|
|
5
5
|
import StatusIcon from '../statusIcon';
|
|
6
|
-
import Body from '../body';
|
|
7
6
|
|
|
8
7
|
export interface InlineAlertProps {
|
|
9
8
|
id?: string;
|
|
@@ -20,15 +19,6 @@ const iconTypes = new Set<NonNullable<InlineAlertProps['type']>>([
|
|
|
20
19
|
Sentiment.WARNING,
|
|
21
20
|
]);
|
|
22
21
|
|
|
23
|
-
/**
|
|
24
|
-
* Avoid using `<InlineAlert>` component directly
|
|
25
|
-
* it's for edge cases when `<Field />` isn't suitable for some reasons.
|
|
26
|
-
*
|
|
27
|
-
* Example:
|
|
28
|
-
* ```
|
|
29
|
-
* <Field sentiment={..} message={..}>..</Field>
|
|
30
|
-
* ```
|
|
31
|
-
*/
|
|
32
22
|
export default function InlineAlert({
|
|
33
23
|
id,
|
|
34
24
|
type = 'neutral',
|
|
@@ -36,18 +26,17 @@ export default function InlineAlert({
|
|
|
36
26
|
children,
|
|
37
27
|
}: InlineAlertProps) {
|
|
38
28
|
return (
|
|
39
|
-
<
|
|
29
|
+
<div
|
|
40
30
|
role="alert"
|
|
41
31
|
id={id}
|
|
42
32
|
className={clsx(
|
|
43
33
|
'alert alert-detach',
|
|
44
34
|
`alert-${type === Sentiment.NEGATIVE || type === Sentiment.ERROR ? 'danger' : type}`,
|
|
45
|
-
'd-flex',
|
|
46
35
|
className,
|
|
47
36
|
)}
|
|
48
37
|
>
|
|
49
38
|
{iconTypes.has(type) && <StatusIcon sentiment={type} size={Size.SMALL} />}
|
|
50
|
-
{children}
|
|
51
|
-
</
|
|
39
|
+
<div className="np-text-body-default">{children}</div>
|
|
40
|
+
</div>
|
|
52
41
|
);
|
|
53
42
|
}
|
|
@@ -2,7 +2,6 @@ import { text } from '@storybook/addon-knobs';
|
|
|
2
2
|
import { userEvent, within } from '@storybook/test';
|
|
3
3
|
|
|
4
4
|
import InputWithDisplayFormat from '.';
|
|
5
|
-
import { Field } from '../field/Field';
|
|
6
5
|
|
|
7
6
|
export default {
|
|
8
7
|
component: InputWithDisplayFormat,
|
|
@@ -14,7 +13,10 @@ export const Basic = () => {
|
|
|
14
13
|
const displayPattern = text('DisplayPattern', '**-**-**');
|
|
15
14
|
|
|
16
15
|
return (
|
|
17
|
-
|
|
16
|
+
<>
|
|
17
|
+
<label id="template" htmlFor="Basic">
|
|
18
|
+
Display pattern is controlled via knobs
|
|
19
|
+
</label>
|
|
18
20
|
<InputWithDisplayFormat
|
|
19
21
|
id="Basic"
|
|
20
22
|
placeholder={placeholder}
|
|
@@ -24,7 +26,7 @@ export const Basic = () => {
|
|
|
24
26
|
onBlur={(v) => console.log(v)}
|
|
25
27
|
onFocus={(v) => console.log(v)}
|
|
26
28
|
/>
|
|
27
|
-
|
|
29
|
+
</>
|
|
28
30
|
);
|
|
29
31
|
};
|
|
30
32
|
|
|
@@ -35,7 +37,10 @@ const Template = (args) => {
|
|
|
35
37
|
const id = label.replaceAll(' ', '-').toLowerCase();
|
|
36
38
|
|
|
37
39
|
return (
|
|
38
|
-
|
|
40
|
+
<>
|
|
41
|
+
<label id="template" htmlFor={id}>
|
|
42
|
+
{label}
|
|
43
|
+
</label>
|
|
39
44
|
<InputWithDisplayFormat
|
|
40
45
|
id={id}
|
|
41
46
|
placeholder={placeholder}
|
|
@@ -45,7 +50,7 @@ const Template = (args) => {
|
|
|
45
50
|
onBlur={(v) => console.log(v)}
|
|
46
51
|
onFocus={(v) => console.log(v)}
|
|
47
52
|
/>
|
|
48
|
-
|
|
53
|
+
</>
|
|
49
54
|
);
|
|
50
55
|
};
|
|
51
56
|
|
|
@@ -214,6 +214,6 @@ describe('SelectInput', () => {
|
|
|
214
214
|
<SelectInput items={[{ type: 'option', value: 'USD' }]} value="USD" />
|
|
215
215
|
</Field>,
|
|
216
216
|
);
|
|
217
|
-
expect(screen.getByLabelText(
|
|
217
|
+
expect(screen.getByLabelText('Currency')).toHaveAttribute('aria-haspopup');
|
|
218
218
|
});
|
|
219
219
|
});
|
package/src/label/Label.spec.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Input } from '../inputs/Input';
|
|
2
|
-
import {
|
|
2
|
+
import { render, screen } from '../test-utils';
|
|
3
3
|
import { Label } from './Label';
|
|
4
4
|
|
|
5
5
|
describe('Label', () => {
|
|
@@ -7,68 +7,20 @@ describe('Label', () => {
|
|
|
7
7
|
render(
|
|
8
8
|
<Label>
|
|
9
9
|
Phone number
|
|
10
|
-
<Input
|
|
10
|
+
<Input readOnly />
|
|
11
11
|
</Label>,
|
|
12
12
|
);
|
|
13
13
|
|
|
14
14
|
expect(screen.getByLabelText('Phone number')).toBeInTheDocument();
|
|
15
15
|
});
|
|
16
|
-
|
|
17
|
-
it('renders string labels with (Optional) suffix', () => {
|
|
16
|
+
it('renders node type labels', () => {
|
|
18
17
|
render(
|
|
19
18
|
<Label>
|
|
20
|
-
<
|
|
21
|
-
<Input
|
|
19
|
+
<span>Phone number</span>
|
|
20
|
+
<Input readOnly />
|
|
22
21
|
</Label>,
|
|
23
22
|
);
|
|
24
23
|
|
|
25
|
-
expect(screen.getByLabelText(/Phone number/)).toBeInTheDocument();
|
|
26
|
-
expect(screen.getByLabelText(/(Optional)/)).toBeInTheDocument();
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it('renders description', () => {
|
|
30
|
-
const descriptionId = 'desc-test';
|
|
31
|
-
render(
|
|
32
|
-
<>
|
|
33
|
-
<Label htmlFor="test">Phone number</Label>
|
|
34
|
-
<Label.Description id={descriptionId}>{lorem10}</Label.Description>
|
|
35
|
-
<Input id="test" readOnly aria-describedby={descriptionId} />
|
|
36
|
-
</>,
|
|
37
|
-
);
|
|
38
|
-
|
|
39
24
|
expect(screen.getByLabelText('Phone number')).toBeInTheDocument();
|
|
40
|
-
expect(screen.getByText(lorem10)).toBeInTheDocument();
|
|
41
|
-
expect(screen.getByText(lorem10)).toHaveAttribute('id', descriptionId);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('renders description with optional', () => {
|
|
45
|
-
const descriptionId = 'desc-test';
|
|
46
|
-
render(
|
|
47
|
-
<>
|
|
48
|
-
<Label htmlFor="test">
|
|
49
|
-
<Label.Optional>Phone number</Label.Optional>
|
|
50
|
-
</Label>
|
|
51
|
-
<Label.Description id={descriptionId}>{lorem10}</Label.Description>
|
|
52
|
-
<Input id="test" readOnly aria-describedby={descriptionId} />
|
|
53
|
-
</>,
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
expect(screen.getByLabelText(/Phone number/)).toBeInTheDocument();
|
|
57
|
-
expect(screen.getByText(lorem10)).toBeInTheDocument();
|
|
58
|
-
expect(screen.getByText(lorem10)).toHaveAttribute('id', descriptionId);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('connects label with form control via `id` + `aria-labelledby`', () => {
|
|
62
|
-
const labelId = 'label-id';
|
|
63
|
-
render(
|
|
64
|
-
<>
|
|
65
|
-
<Label id={labelId}>Phone number</Label>
|
|
66
|
-
<div role="group" aria-labelledby={labelId}>
|
|
67
|
-
Custom complex component
|
|
68
|
-
</div>
|
|
69
|
-
</>,
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
expect(screen.getByLabelText('Phone number')).toHaveAttribute('aria-labelledby', labelId);
|
|
73
25
|
});
|
|
74
26
|
});
|
|
@@ -1,45 +1,37 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import Info from '../info/Info';
|
|
4
|
+
import { Input } from '../inputs/Input';
|
|
2
5
|
import { Label } from './Label';
|
|
3
6
|
|
|
4
7
|
export default {
|
|
5
8
|
component: Label,
|
|
6
9
|
title: 'Label',
|
|
7
|
-
tags: ['autodocs'],
|
|
8
10
|
};
|
|
9
11
|
|
|
10
12
|
export const Basic = () => {
|
|
13
|
+
const [value, setValue] = useState<string | undefined>('This is some text');
|
|
11
14
|
return (
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
</p>
|
|
19
|
-
<Label className="m-b-2">Text Input</Label>
|
|
20
|
-
|
|
21
|
-
<Label className="m-b-2">
|
|
22
|
-
<Label.Optional>Text Input</Label.Optional>
|
|
23
|
-
</Label>
|
|
24
|
-
|
|
25
|
-
<Label className="m-b-2">
|
|
26
|
-
<Label.Optional>Text Input with Description</Label.Optional>
|
|
27
|
-
</Label>
|
|
28
|
-
<Label.Description>This a field Description</Label.Description>
|
|
29
|
-
|
|
30
|
-
<Label className="m-b-2">
|
|
31
|
-
Text Input with Description
|
|
32
|
-
<Info content="This is some help in popover" aria-label="The aria label" />{' '}
|
|
33
|
-
</Label>
|
|
34
|
-
<Label.Description>This a field Description</Label.Description>
|
|
15
|
+
<Label>
|
|
16
|
+
Phone number
|
|
17
|
+
<Input value={value} id="input" onChange={({ target }) => setValue(target.value)} />
|
|
18
|
+
</Label>
|
|
19
|
+
);
|
|
20
|
+
};
|
|
35
21
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
22
|
+
export const WithInfo = () => {
|
|
23
|
+
const [value, setValue] = useState<string | undefined>('This is some text');
|
|
24
|
+
return (
|
|
25
|
+
<Label>
|
|
26
|
+
<span className="d-flex">
|
|
27
|
+
Phone number{' '}
|
|
28
|
+
<Info
|
|
29
|
+
content="This is some help in popover"
|
|
30
|
+
aria-label="The aria label"
|
|
31
|
+
className="m-l-1"
|
|
32
|
+
/>
|
|
33
|
+
</span>
|
|
34
|
+
<Input value={value} id="input" onChange={({ target }) => setValue(target.value)} />
|
|
35
|
+
</Label>
|
|
44
36
|
);
|
|
45
37
|
};
|
package/src/label/Label.tsx
CHANGED
|
@@ -1,65 +1,20 @@
|
|
|
1
1
|
import { clsx } from 'clsx';
|
|
2
|
-
import messages from './Label.messages';
|
|
3
|
-
import { useIntl } from 'react-intl';
|
|
4
|
-
import Body from '../body';
|
|
5
|
-
import { CommonProps } from '../common';
|
|
6
|
-
import { PropsWithChildren } from 'react';
|
|
7
2
|
|
|
8
3
|
export type LabelProps = {
|
|
9
4
|
id?: string;
|
|
10
|
-
htmlFor?: string
|
|
5
|
+
htmlFor?: string;
|
|
11
6
|
className?: string;
|
|
12
7
|
children?: React.ReactNode;
|
|
13
8
|
};
|
|
14
9
|
|
|
15
|
-
|
|
16
|
-
* Avoid using `<Label>` component directly
|
|
17
|
-
* it's for edge cases when `<Field />` isn't suitable for some reasons.
|
|
18
|
-
*
|
|
19
|
-
* Example:
|
|
20
|
-
* ```
|
|
21
|
-
* <Field label={..} description={..} required={..}>..</Field>
|
|
22
|
-
* ```
|
|
23
|
-
*/
|
|
24
|
-
const Label = ({ className, children, htmlFor, id }: LabelProps) => {
|
|
10
|
+
export const Label = ({ id, htmlFor, className, children }: LabelProps) => {
|
|
25
11
|
return (
|
|
26
12
|
<label
|
|
27
13
|
id={id}
|
|
28
14
|
htmlFor={htmlFor}
|
|
29
|
-
className={clsx(
|
|
30
|
-
'np-label d-flex flex-column np-text-body-default-bold text-primary m-b-0',
|
|
31
|
-
className,
|
|
32
|
-
)}
|
|
15
|
+
className={clsx('control-label d-flex flex-column gap-y-1 m-b-0', className)}
|
|
33
16
|
>
|
|
34
17
|
{children}
|
|
35
18
|
</label>
|
|
36
19
|
);
|
|
37
20
|
};
|
|
38
|
-
|
|
39
|
-
export type LabelOptionalProps = PropsWithChildren<CommonProps>;
|
|
40
|
-
|
|
41
|
-
// eslint-disable-next-line functional/immutable-data
|
|
42
|
-
Label.Optional = function Optional({ children, className }: LabelOptionalProps) {
|
|
43
|
-
const { formatMessage } = useIntl();
|
|
44
|
-
return (
|
|
45
|
-
<div>
|
|
46
|
-
{children}
|
|
47
|
-
<Body as="span" className={clsx('text-secondary', 'm-l-1', className)}>
|
|
48
|
-
{formatMessage(messages.optionalLabel)}
|
|
49
|
-
</Body>
|
|
50
|
-
</div>
|
|
51
|
-
);
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
export type LabelDescriptionProps = PropsWithChildren<CommonProps> & { id?: string };
|
|
55
|
-
|
|
56
|
-
// eslint-disable-next-line functional/immutable-data
|
|
57
|
-
Label.Description = function Description({ id, children, className }: LabelDescriptionProps) {
|
|
58
|
-
return children ? (
|
|
59
|
-
<Body id={id} className={clsx('text-secondary', className)}>
|
|
60
|
-
{children}
|
|
61
|
-
</Body>
|
|
62
|
-
) : null;
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
export { Label };
|
package/src/main.css
CHANGED
|
@@ -1719,10 +1719,18 @@ button.np-option {
|
|
|
1719
1719
|
white-space: nowrap;
|
|
1720
1720
|
width: 100%;
|
|
1721
1721
|
}
|
|
1722
|
+
.np-date-trigger .control-label {
|
|
1723
|
+
font-weight: 400;
|
|
1724
|
+
font-weight: var(--font-weight-regular);
|
|
1725
|
+
}
|
|
1722
1726
|
.np-theme-personal .np-date-trigger {
|
|
1723
1727
|
padding-left: 16px;
|
|
1724
1728
|
padding-left: var(--size-16);
|
|
1725
1729
|
}
|
|
1730
|
+
.np-theme-personal .np-date-trigger .control-label + span {
|
|
1731
|
+
font-weight: 400;
|
|
1732
|
+
font-weight: var(--font-weight-regular);
|
|
1733
|
+
}
|
|
1726
1734
|
.clear-btn {
|
|
1727
1735
|
transition: color 0.15s ease-in-out;
|
|
1728
1736
|
color: #c9cbce;
|
|
@@ -2377,10 +2385,6 @@ html:not([dir="rtl"]) .np-flow-navigation--sm .np-flow-navigation__stepper {
|
|
|
2377
2385
|
border-radius: 9999px !important;
|
|
2378
2386
|
border-radius: var(--radius-full) !important;
|
|
2379
2387
|
}
|
|
2380
|
-
.np-field-control {
|
|
2381
|
-
margin-top: 4px;
|
|
2382
|
-
margin-top: var(--size-4);
|
|
2383
|
-
}
|
|
2384
2388
|
.np-input-group {
|
|
2385
2389
|
display: inline-grid;
|
|
2386
2390
|
width: 100%;
|
|
@@ -5440,6 +5444,12 @@ html:not([dir="rtl"]) .np-navigation-option {
|
|
|
5440
5444
|
.tw-droppable-sm {
|
|
5441
5445
|
min-height: 245px;
|
|
5442
5446
|
}
|
|
5447
|
+
.upload-error-message {
|
|
5448
|
+
margin-top: 24px;
|
|
5449
|
+
margin-top: var(--padding-medium);
|
|
5450
|
+
border-top: 1px solid rgba(0,0,0,0.10196);
|
|
5451
|
+
border-top: 1px solid var(--color-border-neutral);
|
|
5452
|
+
}
|
|
5443
5453
|
.np-upload-button-container {
|
|
5444
5454
|
border-style: solid;
|
|
5445
5455
|
}
|
package/src/main.less
CHANGED
|
@@ -4,7 +4,8 @@ import { Lock } from '@transferwise/icons';
|
|
|
4
4
|
import { useState } from 'react';
|
|
5
5
|
|
|
6
6
|
import MoneyInput, { CurrencyOptionItem } from '.';
|
|
7
|
-
import
|
|
7
|
+
import Provider from '../provider/Provider';
|
|
8
|
+
import translations from '../i18n';
|
|
8
9
|
|
|
9
10
|
export default {
|
|
10
11
|
component: MoneyInput,
|
|
@@ -16,13 +17,14 @@ export default {
|
|
|
16
17
|
const handleOnCurrencyChange = (value: CurrencyOptionItem) => setSelectedCurrency(value);
|
|
17
18
|
|
|
18
19
|
return (
|
|
19
|
-
|
|
20
|
+
<>
|
|
21
|
+
<label htmlFor={args.id}>Editable money input label</label>
|
|
20
22
|
<MoneyInput
|
|
21
23
|
{...args}
|
|
22
24
|
selectedCurrency={selectedCurrency}
|
|
23
25
|
onCurrencyChange={handleOnCurrencyChange}
|
|
24
26
|
/>
|
|
25
|
-
|
|
27
|
+
</>
|
|
26
28
|
);
|
|
27
29
|
},
|
|
28
30
|
args: {
|
|
@@ -159,19 +161,17 @@ export const SmallInput: Story = {
|
|
|
159
161
|
render: (args) => {
|
|
160
162
|
return (
|
|
161
163
|
<>
|
|
162
|
-
<
|
|
163
|
-
|
|
164
|
-
</Field>
|
|
164
|
+
<label htmlFor={args.id}>Money inputs</label>
|
|
165
|
+
<MoneyInput {...args} {...SingleCurrency.args} />
|
|
165
166
|
<br />
|
|
166
167
|
<MoneyInput {...args} {...MultipleCurrencies.args} />
|
|
167
168
|
<hr />
|
|
168
|
-
<
|
|
169
|
+
<div className="has-error">
|
|
170
|
+
<label htmlFor={args.id}>Error states</label>
|
|
169
171
|
<MoneyInput {...args} {...SingleCurrency.args} />
|
|
170
|
-
|
|
171
|
-
<br />
|
|
172
|
-
<Field sentiment="negative">
|
|
172
|
+
<br />
|
|
173
173
|
<MoneyInput {...args} {...MultipleCurrencies.args} />
|
|
174
|
-
</
|
|
174
|
+
</div>
|
|
175
175
|
</>
|
|
176
176
|
);
|
|
177
177
|
},
|