@transferwise/components 0.0.0-experimental-67869a3 → 0.0.0-experimental-6039481
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/accordion/AccordionItem/AccordionItem.js +2 -1
- package/build/accordion/AccordionItem/AccordionItem.js.map +1 -1
- package/build/accordion/AccordionItem/AccordionItem.mjs +2 -1
- package/build/accordion/AccordionItem/AccordionItem.mjs.map +1 -1
- package/build/main.css +16 -16
- package/build/styles/accordion/Accordion.css +4 -1
- package/build/styles/main.css +16 -16
- package/build/styles/switch/Switch.css +22 -41
- package/build/styles/switchOption/SwitchOption.css +4 -0
- package/build/switch/Switch.js +7 -18
- package/build/switch/Switch.js.map +1 -1
- package/build/switch/Switch.mjs +8 -19
- package/build/switch/Switch.mjs.map +1 -1
- package/build/switchOption/SwitchOption.js +1 -0
- package/build/switchOption/SwitchOption.js.map +1 -1
- package/build/switchOption/SwitchOption.mjs +1 -0
- package/build/switchOption/SwitchOption.mjs.map +1 -1
- package/build/types/accordion/AccordionItem/AccordionItem.d.ts.map +1 -1
- package/build/types/switch/Switch.d.ts.map +1 -1
- package/build/types/switchOption/SwitchOption.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/accordion/Accordion.css +4 -1
- package/src/accordion/Accordion.less +10 -5
- package/src/accordion/AccordionItem/AccordionItem.tsx +1 -0
- package/src/main.css +16 -16
- package/src/main.less +1 -0
- package/src/switch/Switch.css +22 -41
- package/src/switch/Switch.less +6 -12
- package/src/switch/Switch.spec.tsx +11 -9
- package/src/switch/Switch.story.tsx +158 -33
- package/src/switch/Switch.tsx +6 -15
- package/src/switchOption/SwitchOption.css +4 -0
- package/src/switchOption/SwitchOption.less +8 -0
- package/src/switchOption/SwitchOption.spec.tsx +4 -5
- package/src/switchOption/SwitchOption.story.tsx +42 -38
- package/src/switchOption/SwitchOption.tsx +1 -0
- package/src/switch/__snapshots__/Switch.spec.tsx.snap +0 -44
|
@@ -102,6 +102,7 @@ const AccordionItem: FC<AccordionItemProps> = ({
|
|
|
102
102
|
className={clsx('np-accordion-item', {
|
|
103
103
|
'np-accordion-item--open': open,
|
|
104
104
|
'np-accordion-item--with-icon': Boolean(icon),
|
|
105
|
+
'np-accordion-item--with-media': Boolean(media),
|
|
105
106
|
})}
|
|
106
107
|
>
|
|
107
108
|
<Option
|
package/src/main.css
CHANGED
|
@@ -72,11 +72,15 @@
|
|
|
72
72
|
font-weight: 600;
|
|
73
73
|
font-weight: var(--font-weight-semi-bold);
|
|
74
74
|
}
|
|
75
|
+
.np-theme-personal .np-accordion-item--with-media .media {
|
|
76
|
+
display: flex;
|
|
77
|
+
align-items: center;
|
|
78
|
+
}
|
|
75
79
|
.np-theme-personal .np-accordion-item--with-icon .np-accordion-item__content {
|
|
76
80
|
padding: 0 56px 16px;
|
|
77
81
|
padding: 0 var(--size-56) var(--size-16);
|
|
78
82
|
}
|
|
79
|
-
.np-theme-personal .np-accordion-item .media {
|
|
83
|
+
.np-theme-personal .np-accordion-item--with-icon .media {
|
|
80
84
|
display: flex;
|
|
81
85
|
align-items: flex-start;
|
|
82
86
|
}
|
|
@@ -5145,6 +5149,8 @@ html:not([dir="rtl"]) .np-navigation-option {
|
|
|
5145
5149
|
margin-top: var(--size-24);
|
|
5146
5150
|
}
|
|
5147
5151
|
.np-switch {
|
|
5152
|
+
all: unset;
|
|
5153
|
+
box-sizing: border-box;
|
|
5148
5154
|
display: inline-flex;
|
|
5149
5155
|
overflow: hidden;
|
|
5150
5156
|
width: 50px;
|
|
@@ -5153,6 +5159,7 @@ html:not([dir="rtl"]) .np-navigation-option {
|
|
|
5153
5159
|
-webkit-user-select: none;
|
|
5154
5160
|
-moz-user-select: none;
|
|
5155
5161
|
user-select: none;
|
|
5162
|
+
cursor: pointer;
|
|
5156
5163
|
}
|
|
5157
5164
|
.np-switch:focus {
|
|
5158
5165
|
outline: none;
|
|
@@ -5185,21 +5192,10 @@ html:not([dir="rtl"]) .np-navigation-option {
|
|
|
5185
5192
|
[dir="rtl"] .np-switch--checked .np-switch--thumb {
|
|
5186
5193
|
transform: translateX(-20px) ;
|
|
5187
5194
|
}
|
|
5188
|
-
.np-switch
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
width: 0;
|
|
5193
|
-
height: 0;
|
|
5194
|
-
opacity: 0;
|
|
5195
|
-
}
|
|
5196
|
-
[dir="rtl"] .np-switch input {
|
|
5197
|
-
right: -100%;
|
|
5198
|
-
left: auto;
|
|
5199
|
-
left: initial;
|
|
5200
|
-
}
|
|
5201
|
-
.np-switch:not([aria-disabled]) {
|
|
5202
|
-
cursor: pointer;
|
|
5195
|
+
.np-switch.disabled {
|
|
5196
|
+
filter: grayscale(1);
|
|
5197
|
+
opacity: 0.45;
|
|
5198
|
+
cursor: not-allowed !important;
|
|
5203
5199
|
}
|
|
5204
5200
|
.np-theme-personal .np-switch {
|
|
5205
5201
|
padding: 1px 2px;
|
|
@@ -5215,6 +5211,10 @@ html:not([dir="rtl"]) .np-navigation-option {
|
|
|
5215
5211
|
background-color: #ffffff;
|
|
5216
5212
|
background-color: var(--color-background-screen);
|
|
5217
5213
|
}
|
|
5214
|
+
.np-switch-option.disabled .np-switch {
|
|
5215
|
+
filter: none;
|
|
5216
|
+
opacity: 1;
|
|
5217
|
+
}
|
|
5218
5218
|
.tabs {
|
|
5219
5219
|
position: relative;
|
|
5220
5220
|
}
|
package/src/main.less
CHANGED
|
@@ -65,6 +65,7 @@
|
|
|
65
65
|
@import "./segmentedControl/SegmentedControl.less";
|
|
66
66
|
@import "./summary/Summary.less";
|
|
67
67
|
@import "./switch/Switch.less";
|
|
68
|
+
@import "./switchOption/SwitchOption.less";
|
|
68
69
|
@import "./tabs/Tabs.less";
|
|
69
70
|
@import "./table/Table.less";
|
|
70
71
|
@import "./tile/Tile.less";
|
package/src/switch/Switch.css
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
@media (min-width: 768px) {
|
|
2
|
+
}@media (min-width: 768px) {
|
|
3
|
+
}.np-switch {
|
|
4
|
+
all: unset;
|
|
5
|
+
box-sizing: border-box;
|
|
2
6
|
display: inline-flex;
|
|
3
7
|
overflow: hidden;
|
|
4
8
|
width: 50px;
|
|
@@ -7,61 +11,38 @@
|
|
|
7
11
|
-webkit-user-select: none;
|
|
8
12
|
-moz-user-select: none;
|
|
9
13
|
user-select: none;
|
|
10
|
-
|
|
11
|
-
.np-switch:focus {
|
|
14
|
+
cursor: pointer;
|
|
15
|
+
}.np-switch:focus {
|
|
12
16
|
outline: none;
|
|
13
|
-
}
|
|
14
|
-
.np-switch:focus-visible {
|
|
17
|
+
}.np-switch:focus-visible {
|
|
15
18
|
outline: var(--ring-outline-color) solid var(--ring-outline-width);
|
|
16
19
|
outline-offset: var(--ring-outline-offset);
|
|
17
|
-
}
|
|
18
|
-
.np-switch--thumb {
|
|
20
|
+
}.np-switch--thumb {
|
|
19
21
|
display: flex;
|
|
20
22
|
transition: transform cubic-bezier(0, 0.94, 0.62, 1) 350ms;
|
|
21
|
-
}
|
|
22
|
-
.np-switch--thumb .tw-icon {
|
|
23
|
+
}.np-switch--thumb .tw-icon {
|
|
23
24
|
color: #fff;
|
|
24
|
-
}
|
|
25
|
-
.np-switch--unchecked {
|
|
25
|
+
}.np-switch--unchecked {
|
|
26
26
|
background: #c9cbce;
|
|
27
27
|
background: var(--color-interactive-secondary);
|
|
28
|
-
}
|
|
29
|
-
.np-switch--unchecked .switch--thumb {
|
|
28
|
+
}.np-switch--unchecked .switch--thumb {
|
|
30
29
|
transform: translateX(0);
|
|
31
|
-
}
|
|
32
|
-
.np-switch--checked {
|
|
30
|
+
}.np-switch--checked {
|
|
33
31
|
background: #00a2dd;
|
|
34
32
|
background: var(--color-interactive-accent);
|
|
35
|
-
}
|
|
36
|
-
.np-switch--checked .np-switch--thumb {
|
|
33
|
+
}.np-switch--checked .np-switch--thumb {
|
|
37
34
|
transform: translateX(20px) ;
|
|
38
|
-
}
|
|
39
|
-
[dir="rtl"] .np-switch--checked .np-switch--thumb {
|
|
35
|
+
}[dir="rtl"] .np-switch--checked .np-switch--thumb {
|
|
40
36
|
transform: translateX(-20px) ;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
width: 0;
|
|
47
|
-
height: 0;
|
|
48
|
-
opacity: 0;
|
|
49
|
-
}
|
|
50
|
-
[dir="rtl"] .np-switch input {
|
|
51
|
-
right: -100%;
|
|
52
|
-
left: auto;
|
|
53
|
-
left: initial;
|
|
54
|
-
}
|
|
55
|
-
.np-switch:not([aria-disabled]) {
|
|
56
|
-
cursor: pointer;
|
|
57
|
-
}
|
|
58
|
-
.np-theme-personal .np-switch {
|
|
37
|
+
}.np-switch.disabled {
|
|
38
|
+
filter: grayscale(1);
|
|
39
|
+
opacity: 0.45;
|
|
40
|
+
cursor: not-allowed !important;
|
|
41
|
+
}.np-theme-personal .np-switch {
|
|
59
42
|
padding: 1px 2px;
|
|
60
|
-
}
|
|
61
|
-
.np-theme-personal .np-switch--checked {
|
|
43
|
+
}.np-theme-personal .np-switch--checked {
|
|
62
44
|
background: var(--color-interactive-primary);
|
|
63
|
-
}
|
|
64
|
-
.np-theme-personal .np-switch--thumb {
|
|
45
|
+
}.np-theme-personal .np-switch--thumb {
|
|
65
46
|
width: 20px;
|
|
66
47
|
height: 20px;
|
|
67
48
|
margin: 3px;
|
package/src/switch/Switch.less
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
@import (reference) "../../node_modules/@transferwise/neptune-css/src/variables/neptune-tokens.less";
|
|
2
2
|
@import (reference) "../../node_modules/@transferwise/neptune-css/src/less/mixins/_logical-properties.less";
|
|
3
3
|
@import (reference) "../../node_modules/@transferwise/neptune-css/src/less/ring.less";
|
|
4
|
+
@import (reference) "../../node_modules/@transferwise/neptune-css/src/less/core/_scaffolding.less";
|
|
4
5
|
|
|
5
6
|
.np-switch {
|
|
7
|
+
all: unset;
|
|
8
|
+
box-sizing: border-box;
|
|
6
9
|
display: inline-flex;
|
|
7
10
|
overflow: hidden;
|
|
8
11
|
width: 50px;
|
|
9
12
|
padding: 2px;
|
|
10
13
|
border-radius: 16px;
|
|
11
14
|
user-select: none;
|
|
15
|
+
cursor: pointer;
|
|
12
16
|
|
|
13
17
|
.focus-ring();
|
|
14
18
|
|
|
@@ -37,18 +41,8 @@
|
|
|
37
41
|
}
|
|
38
42
|
}
|
|
39
43
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
.left(-100%);
|
|
43
|
-
|
|
44
|
-
display: none;
|
|
45
|
-
width: 0;
|
|
46
|
-
height: 0;
|
|
47
|
-
opacity: 0;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
&:not([aria-disabled]) {
|
|
51
|
-
cursor: pointer;
|
|
44
|
+
&.disabled{
|
|
45
|
+
.disabled();
|
|
52
46
|
}
|
|
53
47
|
}
|
|
54
48
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Field } from '../field/Field';
|
|
2
|
-
import { render, fireEvent, screen } from '../test-utils';
|
|
2
|
+
import { render, userEvent, fireEvent, screen } from '../test-utils';
|
|
3
3
|
|
|
4
4
|
import Switch from './Switch';
|
|
5
5
|
|
|
@@ -22,7 +22,7 @@ describe('Switch', () => {
|
|
|
22
22
|
onClick={props.onClick}
|
|
23
23
|
/>,
|
|
24
24
|
);
|
|
25
|
-
expect(
|
|
25
|
+
expect(screen.getByRole('switch')).toBeChecked();
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
it('renders component associated with label', () => {
|
|
@@ -54,25 +54,27 @@ describe('Switch', () => {
|
|
|
54
54
|
onClick={props.onClick}
|
|
55
55
|
/>,
|
|
56
56
|
);
|
|
57
|
-
expect(
|
|
57
|
+
expect(screen.getByLabelText(props['aria-label'])).not.toBeChecked();
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
-
it('calls onClick when user press space key', () => {
|
|
60
|
+
it('calls onClick when user press space or enter key', async () => {
|
|
61
61
|
render(
|
|
62
62
|
<Switch
|
|
63
|
-
checked={props.checked}
|
|
64
63
|
className={props.className}
|
|
65
|
-
id={props.id}
|
|
66
64
|
aria-label={props['aria-label']}
|
|
67
65
|
onClick={props.onClick}
|
|
68
66
|
/>,
|
|
69
67
|
);
|
|
70
68
|
|
|
71
|
-
const input = screen.getAllByRole('checkbox')[0];
|
|
72
|
-
fireEvent.keyDown(input, { key: 'Enter' });
|
|
73
69
|
expect(props.onClick).not.toHaveBeenCalled();
|
|
74
|
-
|
|
70
|
+
|
|
71
|
+
await userEvent.tab();
|
|
72
|
+
|
|
73
|
+
await userEvent.keyboard(' ');
|
|
75
74
|
expect(props.onClick).toHaveBeenCalledTimes(1);
|
|
75
|
+
|
|
76
|
+
await userEvent.keyboard('{Enter}');
|
|
77
|
+
expect(props.onClick).toHaveBeenCalledTimes(2);
|
|
76
78
|
});
|
|
77
79
|
|
|
78
80
|
it('should not call onClick if disabled', () => {
|
|
@@ -1,59 +1,184 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
|
+
import { fn } from '@storybook/test';
|
|
2
3
|
|
|
3
|
-
import Switch from './Switch';
|
|
4
|
+
import Switch, { SwitchProps } from './Switch';
|
|
4
5
|
import { Field } from '../field/Field';
|
|
6
|
+
import { Meta, StoryObj } from '@storybook/react';
|
|
7
|
+
import { Label } from '../label';
|
|
5
8
|
|
|
6
|
-
|
|
9
|
+
const meta: Meta<typeof Switch> = {
|
|
7
10
|
component: Switch,
|
|
8
11
|
title: 'Actions/Switch',
|
|
12
|
+
args: {
|
|
13
|
+
checked: false,
|
|
14
|
+
disabled: false,
|
|
15
|
+
id: 'switchId',
|
|
16
|
+
className: 'switchClassName',
|
|
17
|
+
'aria-labelledby': undefined,
|
|
18
|
+
'aria-label': undefined,
|
|
19
|
+
onClick: fn(),
|
|
20
|
+
},
|
|
21
|
+
tags: ['autodocs'],
|
|
22
|
+
} satisfies Meta<typeof Switch>;
|
|
23
|
+
|
|
24
|
+
export default meta;
|
|
25
|
+
type Story = StoryObj<typeof Switch>;
|
|
26
|
+
|
|
27
|
+
export const Basic: Story = {
|
|
28
|
+
tags: ['!autodocs', '!dev'],
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const Playground: Story = {
|
|
32
|
+
tags: ['!autodocs'],
|
|
33
|
+
argTypes: {
|
|
34
|
+
onClick: { table: { disable: true } },
|
|
35
|
+
},
|
|
9
36
|
};
|
|
10
37
|
|
|
11
|
-
export const
|
|
38
|
+
export const Interactivity: Story = {
|
|
39
|
+
render: function Render({ disabled }: SwitchProps) {
|
|
40
|
+
const [checked1, setCheck1] = useState(false);
|
|
41
|
+
const [checked2, setCheck2] = useState(true);
|
|
42
|
+
const [checked3, setCheck3] = useState(false);
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<>
|
|
46
|
+
<Field id="fieldId" label="Using Field component">
|
|
47
|
+
<Switch checked={checked1} disabled={disabled} onClick={() => setCheck1(!checked1)} />
|
|
48
|
+
</Field>
|
|
49
|
+
|
|
50
|
+
<div>
|
|
51
|
+
<Label htmlFor="labelId">Using Label component</Label>
|
|
52
|
+
<Switch
|
|
53
|
+
id="labelId"
|
|
54
|
+
checked={checked2}
|
|
55
|
+
disabled={disabled}
|
|
56
|
+
onClick={() => setCheck2(!checked2)}
|
|
57
|
+
/>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<div>
|
|
61
|
+
<strong id="ariaId" className="d-block">
|
|
62
|
+
Using `aria-labelledby`
|
|
63
|
+
</strong>
|
|
64
|
+
<Switch
|
|
65
|
+
aria-labelledby="ariaId"
|
|
66
|
+
checked={checked3}
|
|
67
|
+
disabled={disabled}
|
|
68
|
+
onClick={() => setCheck3(!checked3)}
|
|
69
|
+
/>
|
|
70
|
+
</div>
|
|
71
|
+
</>
|
|
72
|
+
);
|
|
73
|
+
},
|
|
74
|
+
parameters: {
|
|
75
|
+
docs: {
|
|
76
|
+
source: {
|
|
77
|
+
code: `
|
|
78
|
+
function Render() {
|
|
12
79
|
const [checked1, setCheck1] = useState(false);
|
|
13
80
|
const [checked2, setCheck2] = useState(true);
|
|
14
|
-
|
|
81
|
+
const [checked3, setCheck3] = useState(false);
|
|
82
|
+
|
|
15
83
|
return (
|
|
16
|
-
|
|
17
|
-
<Field id="
|
|
84
|
+
<>
|
|
85
|
+
<Field id="fieldId" label="Using Field component">
|
|
18
86
|
<Switch
|
|
19
87
|
checked={checked1}
|
|
20
|
-
className="a-class-name"
|
|
21
|
-
id="switchId"
|
|
22
88
|
onClick={() => setCheck1(!checked1)}
|
|
23
89
|
/>
|
|
24
90
|
</Field>
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
91
|
+
|
|
92
|
+
<div>
|
|
93
|
+
<Label htmlFor="labelId">Using standalone Label component</Label>
|
|
94
|
+
<Switch
|
|
95
|
+
id="labelId"
|
|
96
|
+
checked={checked2}
|
|
97
|
+
onClick={() => setCheck2(!checked2)}
|
|
98
|
+
/>
|
|
99
|
+
</div>
|
|
100
|
+
|
|
101
|
+
<div>
|
|
102
|
+
<strong id="ariaId" className="d-block">Using \`aria-labelledby\`</strong>
|
|
103
|
+
<Switch
|
|
104
|
+
aria-labelledby="ariaId"
|
|
105
|
+
checked={checked3}
|
|
106
|
+
onClick={() => setCheck3(!setCheck3)}
|
|
107
|
+
/>
|
|
108
|
+
</div>
|
|
109
|
+
</>
|
|
110
|
+
)
|
|
111
|
+
}`,
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
decorators: [
|
|
116
|
+
(Story: any) => (
|
|
117
|
+
<div className="d-flex flex-column" style={{ gap: '1rem' }}>
|
|
118
|
+
<Story />
|
|
119
|
+
</div>
|
|
120
|
+
),
|
|
121
|
+
],
|
|
122
|
+
args: {},
|
|
123
|
+
argTypes: {
|
|
124
|
+
checked: { table: { disable: true } },
|
|
125
|
+
onClick: { table: { disable: true } },
|
|
126
|
+
},
|
|
33
127
|
};
|
|
34
128
|
|
|
35
|
-
export const Disabled =
|
|
36
|
-
|
|
129
|
+
export const Disabled: Story = {
|
|
130
|
+
render: function Render(args) {
|
|
131
|
+
const [checked1, setCheck1] = useState(false);
|
|
132
|
+
const [checked2, setCheck2] = useState(true);
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<>
|
|
136
|
+
<Field label="Switch label">
|
|
137
|
+
<Switch checked={checked1} disabled onClick={() => setCheck1(!checked1)} />
|
|
138
|
+
</Field>
|
|
139
|
+
|
|
140
|
+
<Field label="Switch label">
|
|
141
|
+
<Switch checked={checked2} disabled onClick={() => setCheck2(!checked2)} />
|
|
142
|
+
</Field>
|
|
143
|
+
</>
|
|
144
|
+
);
|
|
145
|
+
},
|
|
146
|
+
parameters: {
|
|
147
|
+
docs: {
|
|
148
|
+
source: {
|
|
149
|
+
code: `
|
|
150
|
+
function Render() {
|
|
151
|
+
const [checked1, setCheck1] = useState(false);
|
|
152
|
+
const [checked2, setCheck2] = useState(true);
|
|
37
153
|
|
|
38
154
|
return (
|
|
39
|
-
|
|
40
|
-
<Field
|
|
155
|
+
<>
|
|
156
|
+
<Field label="Switch label">
|
|
157
|
+
<Switch
|
|
158
|
+
checked={checked1}
|
|
159
|
+
disabled
|
|
160
|
+
onClick={() => setCheck1(!checked1)}
|
|
161
|
+
/>
|
|
162
|
+
</Fie
|
|
163
|
+
<Field label="Switch label">
|
|
41
164
|
<Switch
|
|
42
|
-
checked={
|
|
165
|
+
checked={checked2}
|
|
43
166
|
disabled
|
|
44
|
-
|
|
45
|
-
id="switchId"
|
|
46
|
-
onClick={() => setCheck(!checked)}
|
|
167
|
+
onClick={() => setCheck2(!checked2)}
|
|
47
168
|
/>
|
|
48
169
|
</Field>
|
|
49
|
-
|
|
50
|
-
aria-label="I'm a switch without label"
|
|
51
|
-
checked={!checked}
|
|
52
|
-
disabled
|
|
53
|
-
className="a-class-name"
|
|
54
|
-
id="switchId1"
|
|
55
|
-
onClick={() => setCheck(!checked)}
|
|
56
|
-
/>
|
|
57
|
-
</div>
|
|
170
|
+
</>
|
|
58
171
|
);
|
|
172
|
+
}`,
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
args: {
|
|
177
|
+
disabled: true,
|
|
178
|
+
},
|
|
179
|
+
argTypes: {
|
|
180
|
+
disabled: { table: { readonly: true } },
|
|
181
|
+
onClick: { table: { disable: true } },
|
|
182
|
+
checked: { table: { disable: true } },
|
|
183
|
+
},
|
|
59
184
|
};
|
package/src/switch/Switch.tsx
CHANGED
|
@@ -34,18 +34,11 @@ const Switch = (props: SwitchProps) => {
|
|
|
34
34
|
disabled,
|
|
35
35
|
} = props;
|
|
36
36
|
|
|
37
|
-
const handleKeyDown: KeyboardEventHandler = (event) => {
|
|
38
|
-
if (event.key === ' ') {
|
|
39
|
-
event.preventDefault();
|
|
40
|
-
onClick();
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
|
|
44
37
|
const ariaLabelledby =
|
|
45
38
|
(ariaLabel ? undefined : ariaLabelledbyProp) ?? inputAttributes['aria-labelledby'];
|
|
46
39
|
|
|
47
40
|
return (
|
|
48
|
-
<
|
|
41
|
+
<button
|
|
49
42
|
className={clsx(
|
|
50
43
|
'np-switch',
|
|
51
44
|
{
|
|
@@ -55,20 +48,18 @@ const Switch = (props: SwitchProps) => {
|
|
|
55
48
|
},
|
|
56
49
|
className,
|
|
57
50
|
)}
|
|
58
|
-
|
|
51
|
+
type="button"
|
|
59
52
|
role="switch"
|
|
53
|
+
{...inputAttributes}
|
|
54
|
+
id={id}
|
|
60
55
|
aria-checked={checked}
|
|
61
56
|
aria-label={ariaLabel}
|
|
62
|
-
{...inputAttributes}
|
|
63
57
|
aria-labelledby={ariaLabelledby}
|
|
64
|
-
|
|
65
|
-
aria-disabled={disabled}
|
|
58
|
+
disabled={disabled}
|
|
66
59
|
onClick={!disabled ? onClick : undefined}
|
|
67
|
-
onKeyDown={!disabled ? handleKeyDown : undefined}
|
|
68
60
|
>
|
|
69
61
|
<span className="np-switch--thumb" />
|
|
70
|
-
|
|
71
|
-
</span>
|
|
62
|
+
</button>
|
|
72
63
|
);
|
|
73
64
|
};
|
|
74
65
|
|
|
@@ -22,12 +22,12 @@ describe('SwitchOption', () => {
|
|
|
22
22
|
expect(screen.getByText('title')).toBeInTheDocument();
|
|
23
23
|
expect(screen.getByText('content')).toBeInTheDocument();
|
|
24
24
|
expect(screen.getByTestId('fast-flag')).toBeInTheDocument();
|
|
25
|
-
expect(screen.getAllByRole('
|
|
25
|
+
expect(screen.getAllByRole('switch')[0]).toBeInTheDocument();
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
it('checks the switch when the user interacts with it', () => {
|
|
29
29
|
// Uses first in array to bypass the fact theres a hidden readonly input
|
|
30
|
-
const getSwitch = () => screen.getAllByRole('
|
|
30
|
+
const getSwitch = () => screen.getAllByRole('switch')[0];
|
|
31
31
|
|
|
32
32
|
const mockOnChange = jest.fn();
|
|
33
33
|
|
|
@@ -64,8 +64,7 @@ describe('SwitchOption', () => {
|
|
|
64
64
|
);
|
|
65
65
|
|
|
66
66
|
expect(getSwitch()).toBeChecked();
|
|
67
|
-
|
|
68
|
-
fireEvent.keyDown(getSwitch(), { key: ' ' });
|
|
67
|
+
fireEvent.click(getSwitch());
|
|
69
68
|
|
|
70
69
|
expect(mockOnChange).toHaveBeenLastCalledWith(false);
|
|
71
70
|
expect(mockOnChange).toHaveBeenCalledTimes(2);
|
|
@@ -107,7 +106,7 @@ describe('SwitchOption', () => {
|
|
|
107
106
|
|
|
108
107
|
expect(mockOnChange).toHaveBeenCalledTimes(2);
|
|
109
108
|
|
|
110
|
-
fireEvent.click(screen.getAllByRole('
|
|
109
|
+
fireEvent.click(screen.getAllByRole('switch')[0]);
|
|
111
110
|
|
|
112
111
|
expect(mockOnChange).toHaveBeenCalledTimes(3);
|
|
113
112
|
});
|
|
@@ -1,65 +1,69 @@
|
|
|
1
1
|
import { action } from '@storybook/addon-actions';
|
|
2
|
-
import { text, boolean } from '@storybook/addon-knobs';
|
|
3
2
|
import { FastFlag as FastFlagIcon } from '@transferwise/icons';
|
|
4
3
|
import { useState } from 'react';
|
|
5
4
|
|
|
6
5
|
import { Nudge, Title, Typography } from '..';
|
|
7
6
|
|
|
8
|
-
import SwitchOption from './SwitchOption';
|
|
7
|
+
import SwitchOption, { type SwitchOptionProps } from './SwitchOption';
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
const meta = {
|
|
11
10
|
component: SwitchOption,
|
|
12
11
|
title: 'Option/SwitchOption',
|
|
12
|
+
tags: ['autodocs'],
|
|
13
|
+
argTypes: {
|
|
14
|
+
title: { control: 'text' },
|
|
15
|
+
content: { control: 'text' },
|
|
16
|
+
disabled: { control: 'boolean' },
|
|
17
|
+
showMediaAtAllSizes: { control: 'boolean' },
|
|
18
|
+
isContainerAligned: { control: 'boolean' },
|
|
19
|
+
checked: { control: 'boolean' },
|
|
20
|
+
},
|
|
21
|
+
args: {
|
|
22
|
+
title: 'Switch option',
|
|
23
|
+
content: 'Normally, the button and icon are vertically centered.',
|
|
24
|
+
disabled: false,
|
|
25
|
+
showMediaAtAllSizes: false,
|
|
26
|
+
isContainerAligned: false,
|
|
27
|
+
checked: false,
|
|
28
|
+
id: 'id',
|
|
29
|
+
complex: false,
|
|
30
|
+
'aria-label': 'Switch option',
|
|
31
|
+
},
|
|
13
32
|
};
|
|
33
|
+
export default meta;
|
|
14
34
|
|
|
15
|
-
|
|
16
|
-
const [checked, setChecked] = useState(
|
|
17
|
-
const title = text('title', 'Switch option');
|
|
18
|
-
const content = text('content', 'Normally, the button and icon are vertically centered.');
|
|
19
|
-
const disabled = boolean('disabled', false);
|
|
20
|
-
const showMediaAtAllSizes = boolean('showMediaAtAllSizes', false);
|
|
21
|
-
const isContainerAligned = boolean('isContainerAligned', false);
|
|
35
|
+
function Template(args: SwitchOptionProps) {
|
|
36
|
+
const [checked, setChecked] = useState(args.checked);
|
|
22
37
|
|
|
23
38
|
return (
|
|
24
|
-
<SwitchOption
|
|
25
|
-
media={<FastFlagIcon />}
|
|
26
|
-
title={title}
|
|
27
|
-
content={content}
|
|
28
|
-
id="id"
|
|
29
|
-
checked={checked}
|
|
30
|
-
complex={false}
|
|
31
|
-
disabled={disabled}
|
|
32
|
-
showMediaAtAllSizes={showMediaAtAllSizes}
|
|
33
|
-
isContainerAligned={isContainerAligned}
|
|
34
|
-
aria-label={title}
|
|
35
|
-
onChange={setChecked}
|
|
36
|
-
/>
|
|
39
|
+
<SwitchOption {...args} media={<FastFlagIcon />} checked={checked} onChange={setChecked} />
|
|
37
40
|
);
|
|
38
|
-
}
|
|
41
|
+
}
|
|
39
42
|
|
|
40
|
-
export const
|
|
41
|
-
|
|
43
|
+
export const Playground = {
|
|
44
|
+
render: (args: SwitchOptionProps) => <Template {...args} />,
|
|
45
|
+
tags: ['!autodocs'],
|
|
42
46
|
};
|
|
43
47
|
|
|
44
|
-
export const Multiple =
|
|
45
|
-
|
|
48
|
+
export const Multiple = {
|
|
49
|
+
render: (args: SwitchOptionProps) => (
|
|
46
50
|
<>
|
|
47
|
-
<Template />
|
|
48
|
-
<Template />
|
|
49
|
-
<Template />
|
|
51
|
+
<Template {...args} />
|
|
52
|
+
<Template {...args} />
|
|
53
|
+
<Template {...args} />
|
|
50
54
|
</>
|
|
51
|
-
)
|
|
55
|
+
),
|
|
52
56
|
};
|
|
53
57
|
|
|
54
|
-
export const WithContainerContent =
|
|
55
|
-
|
|
58
|
+
export const WithContainerContent = {
|
|
59
|
+
render: (args: SwitchOptionProps) => (
|
|
56
60
|
<>
|
|
57
61
|
<Title type={Typography.TITLE_SUBSECTION} className="m-b-1">
|
|
58
62
|
Choose how to pay
|
|
59
63
|
</Title>
|
|
60
|
-
<Template />
|
|
61
|
-
<Template />
|
|
62
|
-
<Template />
|
|
64
|
+
<Template {...args} />
|
|
65
|
+
<Template {...args} />
|
|
66
|
+
<Template {...args} />
|
|
63
67
|
<div>
|
|
64
68
|
<Nudge
|
|
65
69
|
className="m-t-2"
|
|
@@ -72,5 +76,5 @@ export const WithContainerContent = () => {
|
|
|
72
76
|
/>
|
|
73
77
|
</div>
|
|
74
78
|
</>
|
|
75
|
-
)
|
|
79
|
+
),
|
|
76
80
|
};
|