@transferwise/components 46.52.2 → 46.53.0
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/card/Card.js +20 -13
- package/build/card/Card.js.map +1 -1
- package/build/card/Card.mjs +21 -14
- package/build/card/Card.mjs.map +1 -1
- package/build/common/Option/Option.js.map +1 -1
- package/build/common/Option/Option.mjs.map +1 -1
- package/build/dateLookup/dateTrigger/DateTrigger.js +8 -4
- package/build/dateLookup/dateTrigger/DateTrigger.js.map +1 -1
- package/build/dateLookup/dateTrigger/DateTrigger.mjs +8 -4
- package/build/dateLookup/dateTrigger/DateTrigger.mjs.map +1 -1
- package/build/field/Field.js +36 -8
- package/build/field/Field.js.map +1 -1
- package/build/field/Field.mjs +37 -9
- package/build/field/Field.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/index.js +2 -2
- package/build/index.mjs +1 -1
- package/build/inlineAlert/InlineAlert.js +13 -6
- package/build/inlineAlert/InlineAlert.js.map +1 -1
- package/build/inlineAlert/InlineAlert.mjs +13 -6
- package/build/inlineAlert/InlineAlert.mjs.map +1 -1
- package/build/label/Label.js +35 -4
- package/build/label/Label.js.map +1 -1
- package/build/label/Label.messages.js +12 -0
- package/build/label/Label.messages.js.map +1 -0
- package/build/label/Label.messages.mjs +10 -0
- package/build/label/Label.messages.mjs.map +1 -0
- package/build/label/Label.mjs +36 -5
- package/build/label/Label.mjs.map +1 -1
- package/build/main.css +4 -8
- package/build/styles/dateLookup/dateTrigger/DateTrigger.css +0 -8
- package/build/styles/field/Field.css +4 -0
- package/build/styles/main.css +4 -8
- package/build/tabs/Tab.js +13 -38
- package/build/tabs/Tab.js.map +1 -1
- package/build/tabs/Tab.mjs +13 -34
- package/build/tabs/Tab.mjs.map +1 -1
- package/build/tabs/TabList.js +3 -11
- package/build/tabs/TabList.js.map +1 -1
- package/build/tabs/TabList.mjs +3 -7
- package/build/tabs/TabList.mjs.map +1 -1
- package/build/tabs/TabPanel.js +3 -16
- package/build/tabs/TabPanel.js.map +1 -1
- package/build/tabs/TabPanel.mjs +3 -12
- package/build/tabs/TabPanel.mjs.map +1 -1
- package/build/tabs/Tabs.js +24 -48
- package/build/tabs/Tabs.js.map +1 -1
- package/build/tabs/Tabs.mjs +24 -47
- package/build/tabs/Tabs.mjs.map +1 -1
- package/build/tabs/utils.js +0 -1
- package/build/tabs/utils.js.map +1 -1
- package/build/tabs/utils.mjs +0 -1
- package/build/tabs/utils.mjs.map +1 -1
- package/build/types/card/Card.d.ts.map +1 -1
- package/build/types/common/Option/Option.d.ts +2 -0
- package/build/types/common/Option/Option.d.ts.map +1 -1
- package/build/types/dateLookup/dateTrigger/DateTrigger.d.ts.map +1 -1
- package/build/types/field/Field.d.ts +4 -2
- package/build/types/field/Field.d.ts.map +1 -1
- package/build/types/index.d.ts +2 -1
- package/build/types/index.d.ts.map +1 -1
- package/build/types/inlineAlert/InlineAlert.d.ts +9 -0
- package/build/types/inlineAlert/InlineAlert.d.ts.map +1 -1
- package/build/types/label/Label.d.ts +21 -1
- package/build/types/label/Label.d.ts.map +1 -1
- package/build/types/label/Label.messages.d.ts +8 -0
- package/build/types/label/Label.messages.d.ts.map +1 -0
- package/build/types/label/index.d.ts +3 -0
- package/build/types/label/index.d.ts.map +1 -0
- package/build/types/tabs/Tab.d.ts +12 -1
- package/build/types/tabs/Tab.d.ts.map +1 -1
- package/build/types/tabs/TabList.d.ts +3 -8
- package/build/types/tabs/TabList.d.ts.map +1 -1
- package/build/types/tabs/TabPanel.d.ts +6 -14
- package/build/types/tabs/TabPanel.d.ts.map +1 -1
- package/build/types/tabs/Tabs.d.ts +83 -30
- package/build/types/tabs/Tabs.d.ts.map +1 -1
- package/build/types/tabs/index.d.ts +2 -1
- package/build/types/tabs/index.d.ts.map +1 -1
- package/build/types/tabs/utils.d.ts +12 -7
- package/build/types/tabs/utils.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/card/Card.spec.tsx +35 -3
- package/src/card/Card.story.tsx +56 -40
- package/src/card/Card.tsx +32 -14
- package/src/common/Option/Option.tsx +2 -0
- package/src/dateInput/DateInput.tests.story.tsx +6 -42
- package/src/dateLookup/DateLookup.rtl.spec.tsx +1 -1
- package/src/dateLookup/dateTrigger/DateTrigger.css +0 -8
- package/src/dateLookup/dateTrigger/DateTrigger.less +0 -8
- package/src/dateLookup/dateTrigger/DateTrigger.spec.js +1 -1
- package/src/dateLookup/dateTrigger/DateTrigger.tsx +9 -4
- package/src/field/Field.css +4 -0
- package/src/field/Field.less +5 -0
- package/src/field/Field.spec.tsx +41 -5
- package/src/field/Field.story.tsx +105 -7
- package/src/field/Field.tsx +34 -10
- package/src/i18n/en.json +1 -0
- package/src/index.ts +2 -1
- package/src/inlineAlert/InlineAlert.story.tsx +7 -72
- package/src/inlineAlert/InlineAlert.tsx +14 -3
- package/src/inputWithDisplayFormat/InputWithDisplayFormat.story.js +5 -10
- 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.messages.tsx +8 -0
- package/src/label/Label.spec.tsx +53 -4
- package/src/label/Label.story.tsx +32 -26
- package/src/label/Label.tsx +47 -2
- package/src/label/index.ts +2 -0
- package/src/main.css +4 -8
- package/src/main.less +1 -0
- 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 +19 -21
- package/src/tabs/Tab.tsx +72 -0
- package/src/tabs/TabList.tsx +11 -0
- package/src/tabs/TabPanel.tsx +14 -0
- package/src/tabs/{Tabs.story.js → Tabs.story.tsx} +1 -1
- package/src/tabs/{Tabs.js → Tabs.tsx} +111 -74
- package/src/tabs/index.ts +2 -0
- package/src/tabs/{utils.spec.js → utils.spec.ts} +24 -21
- package/src/tabs/{utils.js → utils.ts} +15 -9
- package/src/field/Field.tests.story.tsx +0 -33
- package/src/tabs/Tab.js +0 -71
- package/src/tabs/TabList.js +0 -15
- package/src/tabs/TabPanel.js +0 -20
- package/src/tabs/index.js +0 -1
package/src/card/Card.story.tsx
CHANGED
|
@@ -1,54 +1,70 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react';
|
|
2
2
|
import { FastFlag as FastFlagIcon } from '@transferwise/icons';
|
|
3
3
|
import { useState } from 'react';
|
|
4
|
+
import { fn } from '@storybook/test';
|
|
4
5
|
|
|
5
6
|
import Card from '.';
|
|
6
|
-
|
|
7
|
+
|
|
8
|
+
type Story = StoryObj<typeof Card>;
|
|
7
9
|
|
|
8
10
|
export default {
|
|
9
11
|
component: Card,
|
|
10
12
|
title: 'Layouts/Card',
|
|
11
|
-
|
|
13
|
+
args: {
|
|
14
|
+
title: 'A card',
|
|
15
|
+
details: 'Some details about this card',
|
|
16
|
+
children: 'Lorem ipsum dolor sit amet.',
|
|
17
|
+
onClick: fn(),
|
|
18
|
+
as: 'div',
|
|
19
|
+
},
|
|
20
|
+
tags: ['autodocs'],
|
|
21
|
+
} satisfies Meta<typeof Card>;
|
|
12
22
|
|
|
13
|
-
const Template = (props: Pick<CardProps, 'isExpanded' | 'icon' | 'onClick'>) => {
|
|
14
|
-
const elementType = select('elementType', ['div', 'li'], 'div');
|
|
15
|
-
const title = text('title', 'A card');
|
|
16
|
-
const details = text('details', 'Some details about this card');
|
|
17
|
-
const content = text('content', 'Lorem ipsum dolor sit amet.');
|
|
18
|
-
|
|
19
|
-
return (
|
|
20
|
-
<Card as={elementType} title={title} details={details} {...props}>
|
|
21
|
-
{content}
|
|
22
|
-
</Card>
|
|
23
|
-
);
|
|
24
|
-
};
|
|
25
23
|
|
|
26
|
-
export const Basic =
|
|
27
|
-
|
|
24
|
+
export const Basic: Story = {
|
|
25
|
+
render: function Render(args) {
|
|
26
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
27
|
+
|
|
28
|
+
const handleClick = (isCurrentExpanded: boolean) => {
|
|
29
|
+
setIsExpanded(isCurrentExpanded)
|
|
30
|
+
args?.onClick?.(!isExpanded);
|
|
31
|
+
}
|
|
28
32
|
|
|
29
|
-
|
|
33
|
+
return <Card {...args} isExpanded={isExpanded} onClick={handleClick} />;
|
|
34
|
+
}
|
|
30
35
|
};
|
|
31
36
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
37
|
+
|
|
38
|
+
export const Multiple: Story = {
|
|
39
|
+
render: function Render(args) {
|
|
40
|
+
const [expandedCardIndex, setExpandedCardIndex] = useState<number | null>(0);
|
|
41
|
+
|
|
42
|
+
const handleOnCardClick = (index: number) => {
|
|
43
|
+
args?.onClick?.(index !== expandedCardIndex);
|
|
44
|
+
setExpandedCardIndex(index !== expandedCardIndex ? index : null);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<>
|
|
49
|
+
<Card
|
|
50
|
+
{...args}
|
|
51
|
+
icon={<FastFlagIcon />}
|
|
52
|
+
isExpanded={expandedCardIndex === 0}
|
|
53
|
+
onClick={() => handleOnCardClick(0)}
|
|
54
|
+
/>
|
|
55
|
+
<Card
|
|
56
|
+
{...args}
|
|
57
|
+
icon={<FastFlagIcon />}
|
|
58
|
+
isExpanded={expandedCardIndex === 1}
|
|
59
|
+
onClick={() => handleOnCardClick(1)}
|
|
60
|
+
/>
|
|
61
|
+
<Card
|
|
62
|
+
{...args}
|
|
63
|
+
icon={<FastFlagIcon />}
|
|
64
|
+
isExpanded={expandedCardIndex === 2}
|
|
65
|
+
onClick={() => handleOnCardClick(2)}
|
|
66
|
+
/>
|
|
67
|
+
</>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
54
70
|
};
|
package/src/card/Card.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { clsx } from 'clsx';
|
|
2
|
-
import { forwardRef } from 'react';
|
|
2
|
+
import { forwardRef, useId } from 'react';
|
|
3
3
|
|
|
4
4
|
import Body from '../body';
|
|
5
5
|
import Chevron from '../chevron';
|
|
@@ -40,13 +40,16 @@ const Card = forwardRef(function Card(
|
|
|
40
40
|
}: CardProps,
|
|
41
41
|
reference,
|
|
42
42
|
) {
|
|
43
|
-
const
|
|
43
|
+
const labelId = useId();
|
|
44
|
+
const contentId = useId();
|
|
45
|
+
const isExpandable = Boolean(children);
|
|
46
|
+
|
|
44
47
|
|
|
45
48
|
return (
|
|
46
49
|
<Element
|
|
47
50
|
ref={reference}
|
|
48
51
|
className={clsx('np-card', className, {
|
|
49
|
-
'np-card--expanded':
|
|
52
|
+
'np-card--expanded': isExpandable && isExpanded,
|
|
50
53
|
'np-card--inactive': !children,
|
|
51
54
|
'np-card--has-icon': !!icon,
|
|
52
55
|
})}
|
|
@@ -54,23 +57,38 @@ const Card = forwardRef(function Card(
|
|
|
54
57
|
data-testid={dataTestId}
|
|
55
58
|
>
|
|
56
59
|
<Option
|
|
60
|
+
aria-expanded={isExpandable ? isExpanded : undefined}
|
|
61
|
+
aria-controls={isExpanded ? contentId : undefined}
|
|
62
|
+
id={labelId}
|
|
57
63
|
aria-label={ariaLabel}
|
|
58
|
-
as={
|
|
59
|
-
className=
|
|
64
|
+
as={isExpandable ? 'button' : 'div'}
|
|
65
|
+
className="np-card__button"
|
|
60
66
|
media={icon}
|
|
61
67
|
title={title}
|
|
62
68
|
content={details}
|
|
63
69
|
showMediaAtAllSizes
|
|
64
|
-
button={
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
className={clsx('np-card__divider', {
|
|
69
|
-
'np-card__divider--expanded': isOpen,
|
|
70
|
-
})}
|
|
70
|
+
button={
|
|
71
|
+
isExpandable ? <Chevron orientation={isExpanded ? Position.TOP : Position.BOTTOM} /> : null
|
|
72
|
+
}
|
|
73
|
+
onClick={isExpandable ? () => onClick?.(!isExpanded) : undefined}
|
|
71
74
|
/>
|
|
72
|
-
|
|
73
|
-
|
|
75
|
+
|
|
76
|
+
{isExpandable && (
|
|
77
|
+
<div
|
|
78
|
+
className={clsx('np-card__divider', {
|
|
79
|
+
'np-card__divider--expanded': isExpanded,
|
|
80
|
+
})}
|
|
81
|
+
/>
|
|
82
|
+
)}
|
|
83
|
+
|
|
84
|
+
{isExpandable && isExpanded && (
|
|
85
|
+
<Body
|
|
86
|
+
as="div"
|
|
87
|
+
type={Typography.BODY_LARGE}
|
|
88
|
+
className={clsx('np-card__content', isExpanded ? 'd-block' : 'hide')}
|
|
89
|
+
id={contentId}
|
|
90
|
+
aria-labelledby={labelId}
|
|
91
|
+
>
|
|
74
92
|
{children}
|
|
75
93
|
</Body>
|
|
76
94
|
)}
|
|
@@ -6,6 +6,8 @@ import { LinkProps } from '../commonProps';
|
|
|
6
6
|
|
|
7
7
|
export interface BaseOptionProps extends Omit<HTMLAttributes<HTMLElement>, 'title' | 'content'> {
|
|
8
8
|
'aria-label'?: string;
|
|
9
|
+
'aria-expanded'?: boolean;
|
|
10
|
+
'aria-controls'?: string;
|
|
9
11
|
media?: ReactNode;
|
|
10
12
|
name?: string;
|
|
11
13
|
htmlFor?: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Meta, StoryObj } from '@storybook/react';
|
|
2
2
|
import { userEvent, within, fn } from '@storybook/test';
|
|
3
3
|
|
|
4
|
-
import { DateInput,
|
|
4
|
+
import { DateInput, Field } from '..';
|
|
5
5
|
import { lorem10, storyConfig } from '../test-utils';
|
|
6
6
|
|
|
7
7
|
import Provider from '../provider/Provider';
|
|
@@ -38,45 +38,10 @@ export const WithLabel = {
|
|
|
38
38
|
onChange: fn(),
|
|
39
39
|
},
|
|
40
40
|
render: (args) => {
|
|
41
|
-
const id1 = 'date-input-group-label-1';
|
|
42
|
-
const label = 'Date of Birth';
|
|
43
|
-
|
|
44
41
|
return (
|
|
45
|
-
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
<code>aria-labelledby</code> prop
|
|
49
|
-
</Title>
|
|
50
|
-
<div className="control-label" id={id1}>
|
|
51
|
-
Date of Delivery
|
|
52
|
-
</div>
|
|
53
|
-
<DateInput {...args} aria-labelledby={id1} />
|
|
54
|
-
|
|
55
|
-
<br />
|
|
56
|
-
|
|
57
|
-
<Title type={Typography.TITLE_SUBSECTION}>
|
|
58
|
-
label (as <code>div</code> element) is detached but <code>DateInput</code> has same label
|
|
59
|
-
via <code>aria-label</code> attribute
|
|
60
|
-
</Title>
|
|
61
|
-
<div className="control-label">
|
|
62
|
-
{label}{' '}
|
|
63
|
-
<Info aria-label="Fast transfer hint" title="Fast transfer hint" content={lorem10} />
|
|
64
|
-
</div>
|
|
65
|
-
<DateInput {...args} aria-label={label} />
|
|
66
|
-
|
|
67
|
-
<br />
|
|
68
|
-
|
|
69
|
-
<Title type={Typography.TITLE_SUBSECTION}>
|
|
70
|
-
<code>DateInput</code> wrapped in <code>fieldset</code> + using <code>legend</code> as
|
|
71
|
-
label (rare use case)
|
|
72
|
-
</Title>
|
|
73
|
-
<fieldset>
|
|
74
|
-
<legend className="control-label">
|
|
75
|
-
Expiry Date for Credit Card (example of MONTH_YEAR mode)
|
|
76
|
-
</legend>
|
|
77
|
-
<DateInput {...args} mode={DateMode.MONTH_YEAR} />
|
|
78
|
-
</fieldset>
|
|
79
|
-
</>
|
|
42
|
+
<Field label="Date of Delivery">
|
|
43
|
+
<DateInput {...args} />
|
|
44
|
+
</Field>
|
|
80
45
|
);
|
|
81
46
|
},
|
|
82
47
|
} satisfies Story;
|
|
@@ -84,10 +49,9 @@ export const WithLabel = {
|
|
|
84
49
|
export const InputError = {
|
|
85
50
|
...Basic,
|
|
86
51
|
render: (args) => (
|
|
87
|
-
<
|
|
52
|
+
<Field sentiment="negative" message={lorem10}>
|
|
88
53
|
<DateInput {...args} value={new Date('2010-04-05')} />
|
|
89
|
-
|
|
90
|
-
</div>
|
|
54
|
+
</Field>
|
|
91
55
|
),
|
|
92
56
|
} satisfies Story;
|
|
93
57
|
|
|
@@ -28,7 +28,7 @@ describe('DateLookup', () => {
|
|
|
28
28
|
</Field>,
|
|
29
29
|
);
|
|
30
30
|
// TODO: Replace with `.toHaveAttribute('aria-haspopup')`
|
|
31
|
-
expect(screen.getByLabelText(
|
|
31
|
+
expect(screen.getByLabelText(/Date of birth/)).toHaveTextContent(
|
|
32
32
|
initialValue.getFullYear().toString(),
|
|
33
33
|
);
|
|
34
34
|
});
|
|
@@ -4,18 +4,10 @@
|
|
|
4
4
|
white-space: nowrap;
|
|
5
5
|
width: 100%;
|
|
6
6
|
}
|
|
7
|
-
.np-date-trigger .control-label {
|
|
8
|
-
font-weight: 400;
|
|
9
|
-
font-weight: var(--font-weight-regular);
|
|
10
|
-
}
|
|
11
7
|
.np-theme-personal .np-date-trigger {
|
|
12
8
|
padding-left: 16px;
|
|
13
9
|
padding-left: var(--size-16);
|
|
14
10
|
}
|
|
15
|
-
.np-theme-personal .np-date-trigger .control-label + span {
|
|
16
|
-
font-weight: 400;
|
|
17
|
-
font-weight: var(--font-weight-regular);
|
|
18
|
-
}
|
|
19
11
|
.clear-btn {
|
|
20
12
|
transition: color 0.15s ease-in-out;
|
|
21
13
|
color: #c9cbce;
|
|
@@ -7,16 +7,8 @@
|
|
|
7
7
|
white-space: nowrap;
|
|
8
8
|
width: 100%;
|
|
9
9
|
|
|
10
|
-
.control-label {
|
|
11
|
-
font-weight: var(--font-weight-regular);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
10
|
.np-theme-personal & {
|
|
15
11
|
padding-left: var(--size-16);
|
|
16
|
-
|
|
17
|
-
.control-label + span {
|
|
18
|
-
font-weight: var(--font-weight-regular);
|
|
19
|
-
}
|
|
20
12
|
}
|
|
21
13
|
}
|
|
22
14
|
|
|
@@ -119,5 +119,5 @@ describe('DateTrigger', () => {
|
|
|
119
119
|
const button = () => component.find('.np-date-trigger');
|
|
120
120
|
const clearButton = () => component.find('.clear-btn');
|
|
121
121
|
const chevron = () => component.find(Chevron);
|
|
122
|
-
const label = () => component.find('.
|
|
122
|
+
const label = () => component.find('.np-date-trigger-label');
|
|
123
123
|
});
|
|
@@ -2,12 +2,13 @@ import { formatDate } from '@transferwise/formatting';
|
|
|
2
2
|
import { useIntl } from 'react-intl';
|
|
3
3
|
|
|
4
4
|
import Chevron from '../../chevron';
|
|
5
|
-
import { Size, Position, SizeSmall, SizeMedium, SizeLarge } from '../../common';
|
|
5
|
+
import { Size, Position, SizeSmall, SizeMedium, SizeLarge, Typography } from '../../common';
|
|
6
6
|
import { CloseButton } from '../../common/closeButton/CloseButton';
|
|
7
7
|
|
|
8
8
|
import messages from './DateTrigger.messages';
|
|
9
9
|
import { useContext } from 'react';
|
|
10
10
|
import { OverlayIdContext } from '../../provider/overlay/OverlayIdProvider';
|
|
11
|
+
import Body from '../../body';
|
|
11
12
|
|
|
12
13
|
interface DateTriggerProps {
|
|
13
14
|
selectedDate: Date | null;
|
|
@@ -45,15 +46,19 @@ const DateTrigger: React.FC<DateTriggerProps> = ({
|
|
|
45
46
|
type="button"
|
|
46
47
|
onClick={onClick}
|
|
47
48
|
>
|
|
48
|
-
{label &&
|
|
49
|
+
{label && (
|
|
50
|
+
<Body as="span" className="np-date-trigger-label m-r-1">
|
|
51
|
+
{label}
|
|
52
|
+
</Body>
|
|
53
|
+
)}
|
|
49
54
|
{selectedDate ? (
|
|
50
|
-
<
|
|
55
|
+
<Body as="span" type={Typography.BODY_LARGE}>
|
|
51
56
|
{formatDate(selectedDate, locale, {
|
|
52
57
|
day: 'numeric',
|
|
53
58
|
month: monthFormat,
|
|
54
59
|
year: 'numeric',
|
|
55
60
|
})}
|
|
56
|
-
</
|
|
61
|
+
</Body>
|
|
57
62
|
) : (
|
|
58
63
|
<span
|
|
59
64
|
className="form-control-placeholder visible-xs-inline visible-sm-inline
|
package/src/field/Field.spec.tsx
CHANGED
|
@@ -14,7 +14,7 @@ describe('Field', () => {
|
|
|
14
14
|
</Field>,
|
|
15
15
|
);
|
|
16
16
|
|
|
17
|
-
expect(screen.getByLabelText(
|
|
17
|
+
expect(screen.getByLabelText(/Phone number/)).toBeInTheDocument();
|
|
18
18
|
expect(screen.getByRole('textbox')).not.toHaveAttribute('aria-describedby');
|
|
19
19
|
});
|
|
20
20
|
|
|
@@ -25,7 +25,7 @@ describe('Field', () => {
|
|
|
25
25
|
</Field>,
|
|
26
26
|
);
|
|
27
27
|
|
|
28
|
-
const textbox = screen.getByRole('textbox', { description:
|
|
28
|
+
const textbox = screen.getByRole('textbox', { description: /This is help text/ });
|
|
29
29
|
expect(textbox).toBeInTheDocument();
|
|
30
30
|
expect(textbox).not.toBeInvalid();
|
|
31
31
|
});
|
|
@@ -42,15 +42,51 @@ describe('Field', () => {
|
|
|
42
42
|
expect(textbox).toBeInvalid();
|
|
43
43
|
});
|
|
44
44
|
|
|
45
|
-
it('should
|
|
45
|
+
it('should shows error text and help text when both are provided', () => {
|
|
46
46
|
render(
|
|
47
47
|
<Field label="Phone number" error="This is error text" hint="This is help text">
|
|
48
48
|
<Input />
|
|
49
49
|
</Field>,
|
|
50
50
|
);
|
|
51
51
|
|
|
52
|
-
expect(screen.getByRole('textbox', { description:
|
|
53
|
-
expect(screen.
|
|
52
|
+
expect(screen.getByRole('textbox', { description: /This is error text/ })).toBeInTheDocument();
|
|
53
|
+
expect(screen.getByText('This is help text')).toBeInTheDocument();
|
|
54
|
+
expect(screen.getByLabelText(/Phone number/)).toHaveAttribute('aria-describedby');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should shows message and description when both are provided', () => {
|
|
58
|
+
render(
|
|
59
|
+
<Field
|
|
60
|
+
label="Phone number"
|
|
61
|
+
description="This is help text"
|
|
62
|
+
sentiment="negative"
|
|
63
|
+
message="This is error text"
|
|
64
|
+
>
|
|
65
|
+
<Input />
|
|
66
|
+
</Field>,
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
expect(screen.getByRole('textbox', { description: /This is error text/ })).toBeInTheDocument();
|
|
70
|
+
expect(screen.getByText('This is help text')).toBeInTheDocument();
|
|
71
|
+
expect(screen.getByLabelText(/Phone number/)).toHaveAttribute('aria-describedby');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should show or hide (Optional) suffix depending on required prop', () => {
|
|
75
|
+
render(
|
|
76
|
+
<Field label="Phone number" description="This is help text">
|
|
77
|
+
<Input />
|
|
78
|
+
</Field>,
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
expect(screen.getByText('(Optional)')).toBeInTheDocument();
|
|
82
|
+
|
|
83
|
+
render(
|
|
84
|
+
<Field label="Phone number" description="This is help text" required>
|
|
85
|
+
<Input />
|
|
86
|
+
</Field>,
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
expect(screen.getByLabelText('Phone number')).toBeInTheDocument();
|
|
54
90
|
});
|
|
55
91
|
|
|
56
92
|
it('avoids triggering button within label inadvertently', async () => {
|
|
@@ -3,6 +3,12 @@ import { useState } from 'react';
|
|
|
3
3
|
import { Input } from '../inputs/Input';
|
|
4
4
|
import { Field } from './Field';
|
|
5
5
|
import { Sentiment } from '../common';
|
|
6
|
+
import DateInput from '../dateInput';
|
|
7
|
+
import { fn } from '@storybook/test';
|
|
8
|
+
import { lorem10, lorem40 } from '../test-utils';
|
|
9
|
+
import { TextArea } from '../inputs/TextArea';
|
|
10
|
+
import { SearchInput } from '../inputs/SearchInput';
|
|
11
|
+
import Info from '../info';
|
|
6
12
|
|
|
7
13
|
export default {
|
|
8
14
|
component: Field,
|
|
@@ -13,9 +19,66 @@ export default {
|
|
|
13
19
|
export const Basic = () => {
|
|
14
20
|
const [value, setValue] = useState<string | undefined>('This is some text');
|
|
15
21
|
return (
|
|
16
|
-
<
|
|
17
|
-
<
|
|
18
|
-
|
|
22
|
+
<div className="row">
|
|
23
|
+
<div className="col-md-8 col-md-offset-2">
|
|
24
|
+
<Field label="Required Text Input" required>
|
|
25
|
+
<Input value={value} onChange={({ target }) => setValue(target.value)} />
|
|
26
|
+
</Field>
|
|
27
|
+
<Field label="Text Input">
|
|
28
|
+
<Input value={value} onChange={({ target }) => setValue(target.value)} />
|
|
29
|
+
</Field>
|
|
30
|
+
<Field label="Text Input with Description" description="This a field Description">
|
|
31
|
+
<Input value={value} onChange={({ target }) => setValue(target.value)} />
|
|
32
|
+
</Field>
|
|
33
|
+
<Field
|
|
34
|
+
label="Text Input with Validation Error"
|
|
35
|
+
description="This a field Description"
|
|
36
|
+
sentiment={Sentiment.NEGATIVE}
|
|
37
|
+
message="Validation error, please take a look"
|
|
38
|
+
>
|
|
39
|
+
<Input value={value} onChange={({ target }) => setValue(target.value)} />
|
|
40
|
+
</Field>
|
|
41
|
+
|
|
42
|
+
<Field label="Date Of Birth">
|
|
43
|
+
<DateInput onChange={fn} />
|
|
44
|
+
</Field>
|
|
45
|
+
|
|
46
|
+
<Field label="Date Of Birth with Description" description={lorem10}>
|
|
47
|
+
<DateInput onChange={fn} />
|
|
48
|
+
</Field>
|
|
49
|
+
|
|
50
|
+
<Field label="Search business">
|
|
51
|
+
<SearchInput />
|
|
52
|
+
</Field>
|
|
53
|
+
|
|
54
|
+
<Field label="Textarea">
|
|
55
|
+
<TextArea />
|
|
56
|
+
</Field>
|
|
57
|
+
|
|
58
|
+
<Field label="Textarea with Description" description="This a TextArea Description">
|
|
59
|
+
<TextArea />
|
|
60
|
+
</Field>
|
|
61
|
+
|
|
62
|
+
<Field
|
|
63
|
+
label="Textarea with Validation Error"
|
|
64
|
+
description="This a TextArea Description"
|
|
65
|
+
message={lorem10}
|
|
66
|
+
sentiment="negative"
|
|
67
|
+
>
|
|
68
|
+
<TextArea />
|
|
69
|
+
</Field>
|
|
70
|
+
|
|
71
|
+
<Field
|
|
72
|
+
label={
|
|
73
|
+
<>
|
|
74
|
+
Label with Suffix <Info content={lorem40} />
|
|
75
|
+
</>
|
|
76
|
+
}
|
|
77
|
+
>
|
|
78
|
+
<TextArea />
|
|
79
|
+
</Field>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
19
82
|
);
|
|
20
83
|
};
|
|
21
84
|
|
|
@@ -23,16 +86,51 @@ export const WithStatusMessages = () => {
|
|
|
23
86
|
const [value, setValue] = useState<string | undefined>('This is some text');
|
|
24
87
|
return (
|
|
25
88
|
<div>
|
|
26
|
-
<Field
|
|
89
|
+
<Field
|
|
90
|
+
label="Text Input with Positive Message"
|
|
91
|
+
sentiment={Sentiment.POSITIVE}
|
|
92
|
+
message="Positive message"
|
|
93
|
+
>
|
|
27
94
|
<Input value={value} onChange={({ target }) => setValue(target.value)} />
|
|
28
95
|
</Field>
|
|
29
|
-
|
|
96
|
+
|
|
97
|
+
<Field
|
|
98
|
+
label="Text Input with Warning Message"
|
|
99
|
+
sentiment={Sentiment.WARNING}
|
|
100
|
+
message="Warning message"
|
|
101
|
+
>
|
|
30
102
|
<Input value={value} onChange={({ target }) => setValue(target.value)} />
|
|
31
103
|
</Field>
|
|
32
|
-
|
|
104
|
+
|
|
105
|
+
<Field
|
|
106
|
+
label="Text Input with Validation Error"
|
|
107
|
+
sentiment={Sentiment.NEGATIVE}
|
|
108
|
+
message="This is a required field"
|
|
109
|
+
>
|
|
110
|
+
<Input value={value} onChange={({ target }) => setValue(target.value)} />
|
|
111
|
+
</Field>
|
|
112
|
+
|
|
113
|
+
{/* instance with deprecated `hint` prop */}
|
|
114
|
+
<Field
|
|
115
|
+
label="Text Input with deprecated `hint` prop"
|
|
116
|
+
hint="This is a helpful message"
|
|
117
|
+
sentiment={Sentiment.NEGATIVE}
|
|
118
|
+
message="Validation error, please take a look"
|
|
119
|
+
>
|
|
33
120
|
<Input value={value} onChange={({ target }) => setValue(target.value)} />
|
|
34
121
|
</Field>
|
|
35
|
-
|
|
122
|
+
|
|
123
|
+
{/* instance with deprecated `error` & `hint` props */}
|
|
124
|
+
<Field
|
|
125
|
+
label="Text Input with deprecated `error` & `hint` props"
|
|
126
|
+
hint="This is a helpful message"
|
|
127
|
+
error="Validation error, please take a look"
|
|
128
|
+
>
|
|
129
|
+
<Input value={value} onChange={({ target }) => setValue(target.value)} />
|
|
130
|
+
</Field>
|
|
131
|
+
|
|
132
|
+
{/* instance of info via `message` property, this is rare case (e.g MoneyInput) */}
|
|
133
|
+
<Field label="Text Input with Info under the field" message="This is a helpful message">
|
|
36
134
|
<Input value={value} onChange={({ target }) => setValue(target.value)} />
|
|
37
135
|
</Field>
|
|
38
136
|
</div>
|