@sproutsocial/racine 11.7.1 → 11.9.0-useInteractiveColor.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/__flow__/Checkbox/index.stories.js +73 -56
- package/__flow__/Checkbox/styles.js +75 -75
- package/__flow__/Collapsible/index.js +3 -2
- package/__flow__/Icon/index.stories.js +41 -36
- package/__flow__/Image/index.js +10 -2
- package/__flow__/Input/index.js +49 -25
- package/__flow__/Input/index.stories.js +59 -33
- package/__flow__/Input/index.test.js +20 -0
- package/__flow__/Input/styles.js +2 -2
- package/__flow__/Loader/index.stories.js +18 -14
- package/__flow__/Numeral/index.stories.js +109 -50
- package/__flow__/Radio/index.stories.js +41 -26
- package/__flow__/SegmentedControl/index.js +3 -2
- package/__flow__/Switch/index.stories.js +26 -18
- package/__flow__/TableCell/index.js +9 -2
- package/__flow__/ToggleHint/index.js +9 -2
- package/__flow__/Token/styles.js +13 -12
- package/__flow__/index.js +1 -0
- package/__flow__/systemProps/color.js +1 -2
- package/__flow__/themes/dark/theme.js +0 -5
- package/__flow__/themes/extendedThemes/sproutTheme/dark/theme.js +9 -0
- package/__flow__/themes/extendedThemes/sproutTheme/light/theme.js +9 -0
- package/__flow__/themes/light/theme.js +0 -5
- package/__flow__/types/theme.flow.js +2 -2
- package/__flow__/utils/responsiveProps/index.test.js +10 -2
- package/__flow__/utils/useInteractiveColor.js +32 -0
- package/commonjs/Input/index.js +45 -23
- package/commonjs/Input/styles.js +2 -2
- package/commonjs/Token/styles.js +9 -7
- package/commonjs/index.js +6 -1
- package/commonjs/themes/dark/theme.js +0 -5
- package/commonjs/themes/extendedThemes/sproutTheme/dark/theme.js +11 -2
- package/commonjs/themes/extendedThemes/sproutTheme/light/theme.js +11 -2
- package/commonjs/themes/light/theme.js +0 -5
- package/commonjs/utils/useInteractiveColor.js +33 -0
- package/dist/themes/dark/theme.scss +0 -3
- package/dist/themes/extendedThemes/sproutTheme/dark/theme.scss +14 -3
- package/dist/themes/extendedThemes/sproutTheme/light/theme.scss +14 -3
- package/dist/themes/light/theme.scss +0 -3
- package/lib/Input/index.js +44 -23
- package/lib/Input/styles.js +2 -2
- package/lib/Token/styles.js +8 -7
- package/lib/index.js +1 -0
- package/lib/themes/dark/theme.js +0 -4
- package/lib/themes/extendedThemes/sproutTheme/dark/theme.js +9 -1
- package/lib/themes/extendedThemes/sproutTheme/light/theme.js +9 -1
- package/lib/themes/light/theme.js +0 -4
- package/lib/types/theme.flow.js +1 -1
- package/lib/utils/useInteractiveColor.js +27 -0
- package/package.json +1 -1
- package/CHANGELOG.md +0 -3208
- package/__flow__/themes/utils/interact.js +0 -12
- package/commonjs/themes/utils/interact.js +0 -19
- package/lib/themes/utils/interact.js +0 -13
|
@@ -1,114 +1,131 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { boolean } from "@storybook/addon-knobs";
|
|
3
2
|
import { action } from "@storybook/addon-actions";
|
|
4
3
|
|
|
5
4
|
import Checkbox from "./";
|
|
6
5
|
|
|
7
6
|
export default {
|
|
8
7
|
title: "Checkbox",
|
|
8
|
+
component: Checkbox,
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
export const Checked = () => <Checkbox
|
|
12
|
-
export const Unchecked = () => <Checkbox checked={boolean("checked", false)} />;
|
|
11
|
+
export const Checked = (args) => <Checkbox {...args} />;
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
)
|
|
13
|
+
Checked.args = { checked: true };
|
|
14
|
+
|
|
15
|
+
export const Unchecked = (args) => <Checkbox {...args} />;
|
|
16
|
+
|
|
17
|
+
Unchecked.args = { checked: false };
|
|
18
|
+
|
|
19
|
+
export const PillChecked = (args) => <Checkbox {...args} />;
|
|
20
|
+
|
|
21
|
+
PillChecked.args = {
|
|
22
|
+
checked: true,
|
|
23
|
+
appearance: "pill",
|
|
24
|
+
};
|
|
17
25
|
|
|
18
26
|
PillChecked.story = {
|
|
19
27
|
name: "Pill/Checked",
|
|
20
28
|
};
|
|
21
29
|
|
|
22
|
-
export const PillUnchecked = () =>
|
|
23
|
-
|
|
24
|
-
|
|
30
|
+
export const PillUnchecked = (args) => <Checkbox {...args} />;
|
|
31
|
+
|
|
32
|
+
PillUnchecked.args = {
|
|
33
|
+
checked: false,
|
|
34
|
+
appearance: "pill",
|
|
35
|
+
};
|
|
25
36
|
|
|
26
37
|
PillUnchecked.story = {
|
|
27
38
|
name: "Pill/Unchecked",
|
|
28
39
|
};
|
|
29
40
|
|
|
30
|
-
export const DisabledChecked = () =>
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
41
|
+
export const DisabledChecked = (args) => <Checkbox {...args} />;
|
|
42
|
+
|
|
43
|
+
DisabledChecked.args = {
|
|
44
|
+
checked: true,
|
|
45
|
+
disabled: true,
|
|
46
|
+
};
|
|
36
47
|
|
|
37
48
|
DisabledChecked.story = {
|
|
38
49
|
name: "Disabled/Checked",
|
|
39
50
|
};
|
|
40
51
|
|
|
41
|
-
export const DisabledUnchecked = () =>
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
52
|
+
export const DisabledUnchecked = (args) => <Checkbox {...args} />;
|
|
53
|
+
|
|
54
|
+
DisabledUnchecked.args = {
|
|
55
|
+
checked: false,
|
|
56
|
+
disabled: true,
|
|
57
|
+
};
|
|
47
58
|
|
|
48
59
|
DisabledUnchecked.story = {
|
|
49
60
|
name: "Disabled/Unchecked",
|
|
50
61
|
};
|
|
51
62
|
|
|
52
|
-
export const IndeterminateChecked = () =>
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
63
|
+
export const IndeterminateChecked = (args) => <Checkbox {...args} />;
|
|
64
|
+
|
|
65
|
+
IndeterminateChecked.args = {
|
|
66
|
+
checked: true,
|
|
67
|
+
indeterminate: true,
|
|
68
|
+
};
|
|
58
69
|
|
|
59
70
|
IndeterminateChecked.story = {
|
|
60
71
|
name: "Indeterminate/Checked",
|
|
61
72
|
};
|
|
62
73
|
|
|
63
|
-
export const IndeterminateUnchecked = () =>
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
74
|
+
export const IndeterminateUnchecked = (args) => <Checkbox {...args} />;
|
|
75
|
+
|
|
76
|
+
IndeterminateUnchecked.args = {
|
|
77
|
+
checked: false,
|
|
78
|
+
indeterminate: true,
|
|
79
|
+
};
|
|
69
80
|
|
|
70
81
|
IndeterminateUnchecked.story = {
|
|
71
82
|
name: "Indeterminate/Unchecked",
|
|
72
83
|
};
|
|
73
84
|
|
|
74
|
-
export const LabelledChecked = () =>
|
|
75
|
-
|
|
76
|
-
|
|
85
|
+
export const LabelledChecked = (args) => <Checkbox {...args} />;
|
|
86
|
+
|
|
87
|
+
LabelledChecked.args = {
|
|
88
|
+
checked: true,
|
|
89
|
+
label: "Labelled checkbox",
|
|
90
|
+
};
|
|
77
91
|
|
|
78
92
|
LabelledChecked.story = {
|
|
79
93
|
name: "Labelled/Checked",
|
|
80
94
|
};
|
|
81
95
|
|
|
82
|
-
export const LabelledUnchecked = () =>
|
|
83
|
-
|
|
84
|
-
|
|
96
|
+
export const LabelledUnchecked = (args) => <Checkbox {...args} />;
|
|
97
|
+
|
|
98
|
+
LabelledUnchecked.args = {
|
|
99
|
+
checked: false,
|
|
100
|
+
label: "Labelled checkbox",
|
|
101
|
+
};
|
|
85
102
|
|
|
86
103
|
LabelledUnchecked.story = {
|
|
87
104
|
name: "Labelled/Unchecked",
|
|
88
105
|
};
|
|
89
106
|
|
|
90
|
-
export const LabelledDisabled = () =>
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
107
|
+
export const LabelledDisabled = (args) => <Checkbox {...args} />;
|
|
108
|
+
|
|
109
|
+
LabelledDisabled.args = {
|
|
110
|
+
checked: true,
|
|
111
|
+
disabled: true,
|
|
112
|
+
label: "Labelled checkbox",
|
|
113
|
+
};
|
|
97
114
|
|
|
98
115
|
LabelledDisabled.story = {
|
|
99
116
|
name: "Labelled/Disabled",
|
|
100
117
|
};
|
|
101
118
|
|
|
102
|
-
export const InputPropsOnMouseOver = () =>
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
119
|
+
export const InputPropsOnMouseOver = (args) => <Checkbox {...args} />;
|
|
120
|
+
|
|
121
|
+
InputPropsOnMouseOver.args = {
|
|
122
|
+
checked: true,
|
|
123
|
+
label: "Labelled checkbox",
|
|
124
|
+
inputProps: {
|
|
125
|
+
onMouseOver: action("checkbox-mouseOver"),
|
|
126
|
+
onClick: action("checkbox-click"),
|
|
127
|
+
},
|
|
128
|
+
};
|
|
112
129
|
|
|
113
130
|
InputPropsOnMouseOver.story = {
|
|
114
131
|
name: "InputProps/OnMouseOver",
|
|
@@ -182,90 +182,90 @@ const getIcon = (type, color) => {
|
|
|
182
182
|
|
|
183
183
|
// eslint-disable-next-line prettier/prettier
|
|
184
184
|
export const CheckboxContainer: StyledComponent<any, TypeTheme, *> = styled.span(
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
185
|
+
(props) => css`
|
|
186
|
+
display: inline-flex;
|
|
187
|
+
align-items: center;
|
|
188
|
+
box-sizing: border-box;
|
|
189
|
+
position: relative;
|
|
190
|
+
transition: all ${props.theme.duration.fast} ${props.theme.easing.ease_in};
|
|
191
|
+
|
|
192
|
+
@supports (-webkit-appearance: none) {
|
|
193
|
+
&:before {
|
|
194
|
+
/* stylelint-disable */
|
|
195
|
+
content: url("data:image/svg+xml;utf8,${getIcon(
|
|
196
|
+
props.indeterminate ? "indeterminate" : "check",
|
|
197
|
+
|
|
198
|
+
props.checked
|
|
199
|
+
? props.theme.colors.form.background.base
|
|
200
|
+
: props.theme.colors.form.border.base
|
|
201
|
+
)}");
|
|
202
|
+
opacity: ${props.checked ? 1 : 0};
|
|
203
|
+
position: absolute;
|
|
204
|
+
width: ${props.theme.space[400]};
|
|
205
|
+
height: ${props.theme.space[400]};
|
|
206
|
+
text-align: center;
|
|
207
|
+
transform: translateY(1px);
|
|
208
|
+
line-height: 1;
|
|
209
|
+
margin: auto;
|
|
210
|
+
pointer-events: none;
|
|
211
|
+
transition: ${props.theme.duration.fast}
|
|
212
|
+
${props.theme.easing.ease_inout};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
&:hover:before {
|
|
216
|
+
opacity: ${props.disabled && !props.checked ? 0 : 1};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
${props.disabled &&
|
|
220
|
+
css`
|
|
221
|
+
opacity: 0.4;
|
|
222
|
+
`}
|
|
223
|
+
|
|
224
|
+
input[type='checkbox'] {
|
|
225
|
+
box-sizing: border-box;
|
|
226
|
+
appearance: none;
|
|
227
|
+
margin: 0;
|
|
228
|
+
padding: 0;
|
|
229
|
+
width: ${props.theme.space[400]};
|
|
230
|
+
height: ${props.theme.space[400]};
|
|
231
|
+
border: 1px solid ${props.theme.colors.form.border.base};
|
|
232
|
+
border-radius: 4px;
|
|
233
|
+
background-color: ${props.theme.colors.form.background.base};
|
|
234
|
+
transition: all ${props.theme.duration.fast}
|
|
235
|
+
${props.theme.easing.ease_in};
|
|
236
|
+
cursor: ${props.disabled ? "not-allowed" : "pointer"};
|
|
237
|
+
flex-shrink: 0;
|
|
238
|
+
|
|
239
|
+
&:not(:checked) {
|
|
240
|
+
${!props.indeterminate &&
|
|
241
|
+
css`
|
|
242
|
+
border-color: ${props.theme.colors
|
|
243
|
+
.neutral[300]} !important; /* We don't want the focus ring to remove the border */
|
|
244
|
+
background-color: ${props.theme.colors.form.background.base};
|
|
245
|
+
`}
|
|
213
246
|
}
|
|
214
247
|
|
|
215
|
-
&:
|
|
216
|
-
|
|
248
|
+
&:checked {
|
|
249
|
+
border-color: ${props.theme.colors.form.border.selected};
|
|
250
|
+
background-color: ${props.theme.colors.form.background.selected};
|
|
217
251
|
}
|
|
218
252
|
|
|
219
|
-
${props.
|
|
253
|
+
${props.indeterminate &&
|
|
254
|
+
props.checked &&
|
|
220
255
|
css`
|
|
221
|
-
|
|
256
|
+
border-color: ${props.theme.colors.form.border.selected} !important;
|
|
257
|
+
background-color: ${props.theme.colors.form.background
|
|
258
|
+
.selected} !important;
|
|
222
259
|
`}
|
|
223
260
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
appearance: none;
|
|
227
|
-
margin: 0;
|
|
228
|
-
padding: 0;
|
|
229
|
-
width: ${props.theme.space[400]};
|
|
230
|
-
height: ${props.theme.space[400]};
|
|
231
|
-
border: 1px solid ${props.theme.colors.form.border.base};
|
|
232
|
-
border-radius: 4px;
|
|
233
|
-
background-color: ${props.theme.colors.form.background.base};
|
|
234
|
-
transition: all ${props.theme.duration.fast}
|
|
235
|
-
${props.theme.easing.ease_in};
|
|
236
|
-
cursor: ${props.disabled ? "not-allowed" : "pointer"};
|
|
237
|
-
flex-shrink: 0;
|
|
238
|
-
|
|
239
|
-
&:not(:checked) {
|
|
240
|
-
${!props.indeterminate &&
|
|
241
|
-
css`
|
|
242
|
-
border-color: ${props.theme.colors
|
|
243
|
-
.neutral[300]} !important; /* We don't want the focus ring to remove the border */
|
|
244
|
-
background-color: ${props.theme.colors.form.background.base};
|
|
245
|
-
`}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
&:checked {
|
|
249
|
-
border-color: ${props.theme.colors.form.border.selected};
|
|
250
|
-
background-color: ${props.theme.colors.form.background.selected};
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
${props.indeterminate &&
|
|
254
|
-
props.checked &&
|
|
255
|
-
css`
|
|
256
|
-
border-color: ${props.theme.colors.form.border.selected} !important;
|
|
257
|
-
background-color: ${props.theme.colors.form.background
|
|
258
|
-
.selected} !important;
|
|
259
|
-
`}
|
|
260
|
-
|
|
261
|
-
&:focus {
|
|
262
|
-
${focusRing}
|
|
263
|
-
}
|
|
261
|
+
&:focus {
|
|
262
|
+
${focusRing}
|
|
264
263
|
}
|
|
265
264
|
}
|
|
265
|
+
}
|
|
266
266
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
267
|
+
${COMMON}
|
|
268
|
+
`
|
|
269
|
+
);
|
|
270
270
|
|
|
271
271
|
export default Container;
|
|
@@ -73,8 +73,9 @@ const Trigger = ({ children, ...rest }) => {
|
|
|
73
73
|
};
|
|
74
74
|
|
|
75
75
|
const Panel = ({ children, ...rest }) => {
|
|
76
|
-
const { isOpen, id, offset, collapsedHeight, openHeight } =
|
|
77
|
-
|
|
76
|
+
const { isOpen, id, offset, collapsedHeight, openHeight } = useContext(
|
|
77
|
+
CollapsibleContext
|
|
78
|
+
);
|
|
78
79
|
const ref = useRef();
|
|
79
80
|
const measurement = useMeasure(ref);
|
|
80
81
|
const [isHidden, setIsHidden] = useState(undefined);
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
|
-
import { text } from "@storybook/addon-knobs";
|
|
3
|
-
|
|
4
2
|
import Icon from "./";
|
|
5
3
|
import Button from "../Button";
|
|
6
4
|
import Box from "../Box";
|
|
@@ -10,65 +8,72 @@ export default {
|
|
|
10
8
|
title: "Icon",
|
|
11
9
|
};
|
|
12
10
|
|
|
13
|
-
export const mini12X12 = () =>
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
export const mini12X12 = (args) => <Icon {...args} />;
|
|
12
|
+
|
|
13
|
+
mini12X12.args = {
|
|
14
|
+
color: "icon.base",
|
|
15
|
+
name: "dashboard",
|
|
16
|
+
size: "mini",
|
|
17
|
+
};
|
|
20
18
|
|
|
21
19
|
mini12X12.story = {
|
|
22
20
|
name: "Mini (12 x 12)",
|
|
23
21
|
};
|
|
24
22
|
|
|
25
|
-
export const default16X16 = () =>
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
export const default16X16 = (args) => <Icon {...args} />;
|
|
24
|
+
|
|
25
|
+
default16X16.args = {
|
|
26
|
+
color: "icon.base",
|
|
27
|
+
name: "dashboard",
|
|
28
|
+
};
|
|
28
29
|
|
|
29
30
|
default16X16.story = {
|
|
30
31
|
name: "Default (16 x 16)",
|
|
31
32
|
};
|
|
32
33
|
|
|
33
|
-
export const medium24X24 = () =>
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
export const medium24X24 = (args) => <Icon {...args} />;
|
|
35
|
+
|
|
36
|
+
medium24X24.args = {
|
|
37
|
+
color: "icon.base",
|
|
38
|
+
name: "dashboard",
|
|
39
|
+
size: "medium",
|
|
40
|
+
};
|
|
40
41
|
|
|
41
42
|
medium24X24.story = {
|
|
42
43
|
name: "Medium (24 x 24)",
|
|
43
44
|
};
|
|
44
45
|
|
|
45
|
-
export const large32X32 = () =>
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
export const large32X32 = (args) => <Icon {...args} />;
|
|
47
|
+
|
|
48
|
+
large32X32.args = {
|
|
49
|
+
color: "icon.base",
|
|
50
|
+
name: "dashboard",
|
|
51
|
+
size: "large",
|
|
52
|
+
};
|
|
52
53
|
|
|
53
54
|
large32X32.story = {
|
|
54
55
|
name: "Large (32 x 32)",
|
|
55
56
|
};
|
|
56
57
|
|
|
57
|
-
export const jumbo64X64 = () =>
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
export const jumbo64X64 = (args) => <Icon {...args} />;
|
|
59
|
+
|
|
60
|
+
jumbo64X64.args = {
|
|
61
|
+
color: "icon.base",
|
|
62
|
+
name: "dashboard",
|
|
63
|
+
size: "jumbo",
|
|
64
|
+
};
|
|
64
65
|
|
|
65
66
|
jumbo64X64.story = {
|
|
66
67
|
name: "Jumbo (64 x 64)",
|
|
67
68
|
};
|
|
68
69
|
|
|
69
|
-
export const defaultFixedWidth = () =>
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
export const defaultFixedWidth = (args) => <Icon {...args} />;
|
|
71
|
+
|
|
72
|
+
defaultFixedWidth.args = {
|
|
73
|
+
color: "icon.success",
|
|
74
|
+
name: "dashboard",
|
|
75
|
+
fixedWidth: true,
|
|
76
|
+
};
|
|
72
77
|
|
|
73
78
|
defaultFixedWidth.story = {
|
|
74
79
|
name: "Fixed Width (16 x 16)",
|
package/__flow__/Image/index.js
CHANGED
|
@@ -80,8 +80,16 @@ export default class Image extends React.Component<TypeProps, TypeState> {
|
|
|
80
80
|
};
|
|
81
81
|
|
|
82
82
|
render() {
|
|
83
|
-
const {
|
|
84
|
-
|
|
83
|
+
const {
|
|
84
|
+
alt,
|
|
85
|
+
title,
|
|
86
|
+
onClick,
|
|
87
|
+
onError,
|
|
88
|
+
onLoad,
|
|
89
|
+
src,
|
|
90
|
+
qa,
|
|
91
|
+
...rest
|
|
92
|
+
} = this.props;
|
|
85
93
|
|
|
86
94
|
return (
|
|
87
95
|
<ImageContainer
|
package/__flow__/Input/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import styled from "styled-components";
|
|
|
7
7
|
import type { StyledComponent } from "styled-components";
|
|
8
8
|
import type { TypeTheme } from "../types/theme.flow";
|
|
9
9
|
import { mergeRefs } from "../utils";
|
|
10
|
+
import { useInteractiveColor } from "../utils/useInteractiveColor";
|
|
10
11
|
|
|
11
12
|
type TypeProps = {
|
|
12
13
|
/** ID of the form element, should match the "for" value of the associated label */
|
|
@@ -16,7 +17,7 @@ type TypeProps = {
|
|
|
16
17
|
ariaLabel?: string,
|
|
17
18
|
/** Attribute used to associate other elements that describe the Input, like an error */
|
|
18
19
|
ariaDescribedby?: string,
|
|
19
|
-
/** Label for Input.ClearButton. Required when using
|
|
20
|
+
/** Label for Input.ClearButton. Required when using Input.ClearButton. */
|
|
20
21
|
clearButtonLabel?: string,
|
|
21
22
|
/** Placeholder text for when value is undefined or empty */
|
|
22
23
|
placeholder?: string,
|
|
@@ -52,8 +53,6 @@ type TypeProps = {
|
|
|
52
53
|
| ((React.ElementRef<any> | HTMLElement) => void),
|
|
53
54
|
onBlur?: (e: SyntheticFocusEvent<HTMLInputElement>) => void,
|
|
54
55
|
onChange?: (e: SyntheticInputEvent<HTMLInputElement>, value: string) => void,
|
|
55
|
-
/** Input.ClearButton onClick callback. Required when using <Input type="search"/> or <Input.ClearButton/>.
|
|
56
|
-
The component handles returning focus to Input after onClear is called only. You must reset "value" yourself.*/
|
|
57
56
|
onClear?: (e: SyntheticEvent<HTMLButtonElement>) => void,
|
|
58
57
|
onFocus?: (e: SyntheticFocusEvent<HTMLInputElement>) => void,
|
|
59
58
|
onKeyDown?: (
|
|
@@ -74,10 +73,13 @@ type TypeProps = {
|
|
|
74
73
|
appearance?: "primary" | "secondary",
|
|
75
74
|
};
|
|
76
75
|
|
|
76
|
+
type TypeState = {
|
|
77
|
+
hasValue: boolean,
|
|
78
|
+
};
|
|
79
|
+
|
|
77
80
|
// Using Context so that Input's Input.ClearButton-specific props can be passed to Input.ClearButton,
|
|
78
81
|
// regardless of whether it is manually included as elemAfter or automatically included for type="search" Inputs.
|
|
79
82
|
type TypeInputContext = $Shape<{
|
|
80
|
-
onClear?: (e: SyntheticEvent<HTMLButtonElement>) => void,
|
|
81
83
|
handleClear: (e: SyntheticEvent<HTMLButtonElement>) => void,
|
|
82
84
|
clearButtonLabel: string,
|
|
83
85
|
hasValue: boolean,
|
|
@@ -89,14 +91,12 @@ const InputContext = React.createContext<TypeInputContext>({});
|
|
|
89
91
|
const StyledButton: StyledComponent<any, TypeTheme, *> = styled(Button)`
|
|
90
92
|
&:hover,
|
|
91
93
|
&:active {
|
|
92
|
-
color: ${(props) =>
|
|
93
|
-
props.theme.utils.interact(props.theme.colors.icon.base)};
|
|
94
|
+
color: ${(props) => useInteractiveColor(props.theme.colors.icon.base)};
|
|
94
95
|
}
|
|
95
96
|
`;
|
|
96
97
|
|
|
97
98
|
const ClearButton = () => {
|
|
98
99
|
const {
|
|
99
|
-
onClear,
|
|
100
100
|
handleClear,
|
|
101
101
|
clearButtonLabel,
|
|
102
102
|
hasValue,
|
|
@@ -108,15 +108,6 @@ const ClearButton = () => {
|
|
|
108
108
|
return null;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
// Log a warning and hide the button when no onClear callback is provided.
|
|
112
|
-
// If we called handleClear with no onClear prop, all the button would do is focus the Input.
|
|
113
|
-
if (!onClear) {
|
|
114
|
-
console.warn(
|
|
115
|
-
"Warning: No onClear prop provided to Input when using Input.ClearButton. Omitting Input.ClearButton."
|
|
116
|
-
);
|
|
117
|
-
return null;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
111
|
// Warn if clearButtonLabel is not included, so that the unlocalized fallback will not be mistaken for a proper label.
|
|
121
112
|
if (!clearButtonLabel) {
|
|
122
113
|
console.warn(
|
|
@@ -154,7 +145,16 @@ const isClearButton = (elem: any) => {
|
|
|
154
145
|
return false;
|
|
155
146
|
};
|
|
156
147
|
|
|
157
|
-
class Input extends React.Component<TypeProps> {
|
|
148
|
+
class Input extends React.Component<TypeProps, TypeState> {
|
|
149
|
+
constructor(props: TypeProps) {
|
|
150
|
+
super(props);
|
|
151
|
+
this.state = {
|
|
152
|
+
// Tracking hasValue in state allows us to hide ClearButton when there is no value to clear
|
|
153
|
+
// for both controlled and uncontrolled inputs.
|
|
154
|
+
hasValue: !!props.value,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
158
|
static defaultProps = {
|
|
159
159
|
autoFocus: false,
|
|
160
160
|
disabled: false,
|
|
@@ -165,7 +165,7 @@ class Input extends React.Component<TypeProps> {
|
|
|
165
165
|
|
|
166
166
|
static ClearButton = ClearButton;
|
|
167
167
|
|
|
168
|
-
// Define our own ref for
|
|
168
|
+
// Define our own ref for use in handleClear.
|
|
169
169
|
// We use mergeRefs to pass both this.inputRef and this.props.innerRef to input.
|
|
170
170
|
inputRef = React.createRef<HTMLInputElement>();
|
|
171
171
|
|
|
@@ -173,12 +173,28 @@ class Input extends React.Component<TypeProps> {
|
|
|
173
173
|
this.props.onBlur?.(e);
|
|
174
174
|
|
|
175
175
|
handleClear = (e: SyntheticEvent<HTMLButtonElement>) => {
|
|
176
|
-
this.inputRef
|
|
177
|
-
|
|
176
|
+
const input = this.inputRef.current;
|
|
177
|
+
if (input) {
|
|
178
|
+
// Clear the value via the input prototype, then dispatch an input event in order to trigger handleChange
|
|
179
|
+
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
|
|
180
|
+
window.HTMLInputElement.prototype,
|
|
181
|
+
"value"
|
|
182
|
+
)?.set;
|
|
183
|
+
nativeInputValueSetter?.call(input, "");
|
|
184
|
+
const inputEvent = new Event("input", { bubbles: true });
|
|
185
|
+
input.dispatchEvent(inputEvent);
|
|
186
|
+
|
|
187
|
+
// Focus the input, update hasValue, and call any onClear callback
|
|
188
|
+
input.focus();
|
|
189
|
+
this.updateState("");
|
|
190
|
+
this.props.onClear?.(e);
|
|
191
|
+
}
|
|
178
192
|
};
|
|
179
193
|
|
|
180
|
-
handleChange = (e: SyntheticInputEvent<HTMLInputElement>) =>
|
|
194
|
+
handleChange = (e: SyntheticInputEvent<HTMLInputElement>) => {
|
|
181
195
|
this.props.onChange?.(e, e.currentTarget.value);
|
|
196
|
+
this.updateState(e.currentTarget.value);
|
|
197
|
+
};
|
|
182
198
|
|
|
183
199
|
handleFocus = (e: SyntheticFocusEvent<HTMLInputElement>) =>
|
|
184
200
|
this.props.onFocus?.(e);
|
|
@@ -192,6 +208,15 @@ class Input extends React.Component<TypeProps> {
|
|
|
192
208
|
handlePaste = (e: SyntheticInputEvent<HTMLInputElement>) =>
|
|
193
209
|
this.props.onPaste?.(e, e.currentTarget.value);
|
|
194
210
|
|
|
211
|
+
updateState = (inputValue: string) => {
|
|
212
|
+
const hasValue = inputValue !== "";
|
|
213
|
+
const previousHasValue = this.state.hasValue;
|
|
214
|
+
// Only update state if the value of `hasValue` has changed to avoid unnecessary renders.
|
|
215
|
+
if (hasValue !== previousHasValue) {
|
|
216
|
+
this.setState({ hasValue });
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
|
|
195
220
|
render() {
|
|
196
221
|
const {
|
|
197
222
|
autoComplete,
|
|
@@ -240,9 +265,9 @@ class Input extends React.Component<TypeProps> {
|
|
|
240
265
|
) : (
|
|
241
266
|
elemBefore
|
|
242
267
|
);
|
|
243
|
-
// Do not add a ClearButton if
|
|
268
|
+
// Do not add a ClearButton if an elemAfter prop was passed.
|
|
244
269
|
const elementAfter =
|
|
245
|
-
type === "search" &&
|
|
270
|
+
type === "search" && !elemAfter ? <ClearButton /> : elemAfter;
|
|
246
271
|
|
|
247
272
|
return (
|
|
248
273
|
<Container
|
|
@@ -259,9 +284,8 @@ class Input extends React.Component<TypeProps> {
|
|
|
259
284
|
<InputContext.Provider
|
|
260
285
|
value={{
|
|
261
286
|
handleClear: this.handleClear,
|
|
262
|
-
hasValue:
|
|
287
|
+
hasValue: this.state.hasValue,
|
|
263
288
|
clearButtonLabel,
|
|
264
|
-
onClear,
|
|
265
289
|
size,
|
|
266
290
|
}}
|
|
267
291
|
>
|