@sproutsocial/racine 11.7.1 → 11.8.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/CHANGELOG.md +18 -0
- package/__flow__/Input/index.js +47 -23
- package/__flow__/Input/index.stories.js +59 -33
- package/__flow__/Input/index.test.js +20 -0
- package/__flow__/Input/styles.js +2 -2
- package/commonjs/Input/index.js +42 -22
- package/commonjs/Input/styles.js +2 -2
- package/lib/Input/index.js +42 -22
- package/lib/Input/styles.js +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 11.8.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 1b66f38: Input.ClearButton no longer requires onClear prop
|
|
8
|
+
- Input has been updated to be able to clear itself for both controlled and uncontrolled Inputs
|
|
9
|
+
- The onChange function will be called upon clearing the input.
|
|
10
|
+
- Due to this change, Inputs with type "search" will now automatically include a ClearButton, whereas previously a ClearButton would not be rendered unless an onClear prop was provided.
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- 1b66f38: Adjust Input elemBefore and elemAfter positioning
|
|
15
|
+
- These elements were previously positioned 12px from the side of the Input
|
|
16
|
+
- They are now positioned 8px from the side of the Input, for better alignment with Input.ClearButton.
|
|
17
|
+
- 1b66f38: Expose Input.ClearButton for uncontrolled Inputs
|
|
18
|
+
- Previously, Input.ClearButton would not be usable for uncontrolled Inputs due to checking whether a value was in the input to clear only via the "value" prop.
|
|
19
|
+
- Now, Input.ClearButton is usable for uncontrolled Inputs because it is checking for updates to the value in its handleChange function.
|
|
20
|
+
|
|
3
21
|
## 11.7.1
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/__flow__/Input/index.js
CHANGED
|
@@ -16,7 +16,7 @@ type TypeProps = {
|
|
|
16
16
|
ariaLabel?: string,
|
|
17
17
|
/** Attribute used to associate other elements that describe the Input, like an error */
|
|
18
18
|
ariaDescribedby?: string,
|
|
19
|
-
/** Label for Input.ClearButton. Required when using
|
|
19
|
+
/** Label for Input.ClearButton. Required when using Input.ClearButton. */
|
|
20
20
|
clearButtonLabel?: string,
|
|
21
21
|
/** Placeholder text for when value is undefined or empty */
|
|
22
22
|
placeholder?: string,
|
|
@@ -52,8 +52,6 @@ type TypeProps = {
|
|
|
52
52
|
| ((React.ElementRef<any> | HTMLElement) => void),
|
|
53
53
|
onBlur?: (e: SyntheticFocusEvent<HTMLInputElement>) => void,
|
|
54
54
|
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
55
|
onClear?: (e: SyntheticEvent<HTMLButtonElement>) => void,
|
|
58
56
|
onFocus?: (e: SyntheticFocusEvent<HTMLInputElement>) => void,
|
|
59
57
|
onKeyDown?: (
|
|
@@ -74,10 +72,13 @@ type TypeProps = {
|
|
|
74
72
|
appearance?: "primary" | "secondary",
|
|
75
73
|
};
|
|
76
74
|
|
|
75
|
+
type TypeState = {
|
|
76
|
+
hasValue: boolean,
|
|
77
|
+
};
|
|
78
|
+
|
|
77
79
|
// Using Context so that Input's Input.ClearButton-specific props can be passed to Input.ClearButton,
|
|
78
80
|
// regardless of whether it is manually included as elemAfter or automatically included for type="search" Inputs.
|
|
79
81
|
type TypeInputContext = $Shape<{
|
|
80
|
-
onClear?: (e: SyntheticEvent<HTMLButtonElement>) => void,
|
|
81
82
|
handleClear: (e: SyntheticEvent<HTMLButtonElement>) => void,
|
|
82
83
|
clearButtonLabel: string,
|
|
83
84
|
hasValue: boolean,
|
|
@@ -96,7 +97,6 @@ const StyledButton: StyledComponent<any, TypeTheme, *> = styled(Button)`
|
|
|
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
|
>
|
|
@@ -141,60 +141,86 @@ leftAndRightIcons.story = {
|
|
|
141
141
|
name: "Left and right icons",
|
|
142
142
|
};
|
|
143
143
|
|
|
144
|
-
export const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
)
|
|
144
|
+
export const SearchInput = () => {
|
|
145
|
+
const [value, setValue] = React.useState("");
|
|
146
|
+
return (
|
|
147
|
+
<Input
|
|
148
|
+
type="search"
|
|
149
|
+
placeholder={text("placeholder", "Please enter a value...")}
|
|
150
|
+
value={value}
|
|
151
|
+
onChange={(e, value) => setValue(value)}
|
|
152
|
+
clearButtonLabel={text("clearButtonLabel", "Clear search")}
|
|
153
|
+
ariaLabel={text("ariaLabel", "Descriptive label goes here")}
|
|
154
|
+
/>
|
|
155
|
+
);
|
|
156
|
+
};
|
|
154
157
|
|
|
155
|
-
|
|
158
|
+
SearchInput.story = {
|
|
156
159
|
name: "Search Input",
|
|
157
160
|
};
|
|
158
161
|
|
|
159
|
-
export const
|
|
160
|
-
|
|
161
|
-
type="search"
|
|
162
|
-
size="small"
|
|
163
|
-
placeholder={text("placeholder", "Please enter a value...")}
|
|
164
|
-
value={text("value", "val")}
|
|
165
|
-
onClear={() => window.alert("Cleared!")}
|
|
166
|
-
clearButtonLabel={text("clearButtonLabel", "Clear search")}
|
|
167
|
-
ariaLabel={text("ariaLabel", "Descriptive label goes here")}
|
|
168
|
-
/>
|
|
169
|
-
);
|
|
162
|
+
export const SmallSearchInput = () => {
|
|
163
|
+
const [value, setValue] = React.useState("");
|
|
170
164
|
|
|
171
|
-
|
|
165
|
+
return (
|
|
166
|
+
<Input
|
|
167
|
+
type="search"
|
|
168
|
+
size="small"
|
|
169
|
+
placeholder={text("placeholder", "Please enter a value...")}
|
|
170
|
+
value={value}
|
|
171
|
+
onChange={(e, value) => setValue(value)}
|
|
172
|
+
clearButtonLabel={text("clearButtonLabel", "Clear search")}
|
|
173
|
+
ariaLabel={text("ariaLabel", "Descriptive label goes here")}
|
|
174
|
+
/>
|
|
175
|
+
);
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
SmallSearchInput.story = {
|
|
172
179
|
name: "Small Search Input",
|
|
173
180
|
};
|
|
174
181
|
|
|
175
|
-
export const
|
|
182
|
+
export const LargeSearchInput = () => {
|
|
183
|
+
const [value, setValue] = React.useState("");
|
|
184
|
+
|
|
185
|
+
return (
|
|
186
|
+
<Input
|
|
187
|
+
type="search"
|
|
188
|
+
size="large"
|
|
189
|
+
placeholder={text("placeholder", "Please enter a value...")}
|
|
190
|
+
value={value}
|
|
191
|
+
onChange={(e, value) => setValue(value)}
|
|
192
|
+
clearButtonLabel={text("clearButtonLabel", "Clear search")}
|
|
193
|
+
ariaLabel={text("ariaLabel", "Descriptive label goes here")}
|
|
194
|
+
/>
|
|
195
|
+
);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
LargeSearchInput.story = {
|
|
199
|
+
name: "Large Search Input",
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
export const uncontrolledSearchInput = () => (
|
|
176
203
|
<Input
|
|
177
204
|
type="search"
|
|
178
|
-
size="large"
|
|
179
205
|
placeholder={text("placeholder", "Please enter a value...")}
|
|
180
|
-
value={text("value", "val")}
|
|
181
|
-
onClear={() => window.alert("Cleared!")}
|
|
182
206
|
clearButtonLabel={text("clearButtonLabel", "Clear search")}
|
|
183
207
|
ariaLabel={text("ariaLabel", "Descriptive label goes here")}
|
|
184
208
|
/>
|
|
185
209
|
);
|
|
186
210
|
|
|
187
|
-
|
|
188
|
-
name: "
|
|
211
|
+
uncontrolledSearchInput.story = {
|
|
212
|
+
name: "Uncontrolled Search Input",
|
|
189
213
|
};
|
|
190
214
|
|
|
191
|
-
export const
|
|
215
|
+
export const NonSearchClearButtonInput = () => {
|
|
216
|
+
const [value, setValue] = React.useState("");
|
|
217
|
+
|
|
192
218
|
return (
|
|
193
219
|
<Input
|
|
194
220
|
type="text"
|
|
195
221
|
placeholder={text("placeholder", "Please enter a value...")}
|
|
196
|
-
value={
|
|
197
|
-
|
|
222
|
+
value={value}
|
|
223
|
+
onChange={(e, value) => setValue(value)}
|
|
198
224
|
ariaLabel={text("ariaLabel", "Descriptive label goes here")}
|
|
199
225
|
clearButtonLabel={text("clearButtonLabel", "Clear text")}
|
|
200
226
|
elemAfter={<Input.ClearButton />}
|
|
@@ -202,7 +228,7 @@ export const nonSearchClearButtonInput = () => {
|
|
|
202
228
|
);
|
|
203
229
|
};
|
|
204
230
|
|
|
205
|
-
|
|
231
|
+
NonSearchClearButtonInput.story = {
|
|
206
232
|
name: "Manual Input.ClearButton usage",
|
|
207
233
|
};
|
|
208
234
|
|
|
@@ -85,6 +85,26 @@ describe("Input", () => {
|
|
|
85
85
|
expect(getByRole("button")).toBeTruthy();
|
|
86
86
|
});
|
|
87
87
|
|
|
88
|
+
it("should render a clear button for an uncontrolled search Input only when it has text", () => {
|
|
89
|
+
const { getByRole, queryByRole } = render(
|
|
90
|
+
<Input
|
|
91
|
+
id="name"
|
|
92
|
+
name="name"
|
|
93
|
+
type="search"
|
|
94
|
+
clearButtonLabel="Clear search"
|
|
95
|
+
/>
|
|
96
|
+
);
|
|
97
|
+
expect(queryByRole("button")).toBeFalsy();
|
|
98
|
+
const input = getByRole("searchbox");
|
|
99
|
+
userEvent.tab();
|
|
100
|
+
expect(input).toHaveFocus();
|
|
101
|
+
userEvent.keyboard("some text");
|
|
102
|
+
expect(getByRole("button")).toBeTruthy();
|
|
103
|
+
userEvent.tab();
|
|
104
|
+
userEvent.keyboard("{enter}");
|
|
105
|
+
expect(queryByRole("button")).toBeFalsy();
|
|
106
|
+
});
|
|
107
|
+
|
|
88
108
|
it("should not render a clear button for search Inputs if there is no text", () => {
|
|
89
109
|
const { queryByRole } = render(
|
|
90
110
|
<Input
|
package/__flow__/Input/styles.js
CHANGED
|
@@ -134,13 +134,13 @@ export const Accessory: StyledComponent<any, TypeTheme, *> = styled.div`
|
|
|
134
134
|
${(props) =>
|
|
135
135
|
props.before &&
|
|
136
136
|
css`
|
|
137
|
-
left: ${props.theme.space[
|
|
137
|
+
left: ${props.theme.space[300]};
|
|
138
138
|
`};
|
|
139
139
|
|
|
140
140
|
${(props) =>
|
|
141
141
|
props.after &&
|
|
142
142
|
css`
|
|
143
|
-
right: ${props.isClearButton ? 0 : props.theme.space[
|
|
143
|
+
right: ${props.isClearButton ? 0 : props.theme.space[300]};
|
|
144
144
|
`};
|
|
145
145
|
`;
|
|
146
146
|
|
package/commonjs/Input/index.js
CHANGED
|
@@ -41,7 +41,6 @@ var StyledButton = (0, _styledComponents.default)(_Button.default).withConfig({
|
|
|
41
41
|
|
|
42
42
|
var ClearButton = function ClearButton() {
|
|
43
43
|
var _React$useContext = React.useContext(InputContext),
|
|
44
|
-
onClear = _React$useContext.onClear,
|
|
45
44
|
handleClear = _React$useContext.handleClear,
|
|
46
45
|
clearButtonLabel = _React$useContext.clearButtonLabel,
|
|
47
46
|
hasValue = _React$useContext.hasValue,
|
|
@@ -50,13 +49,6 @@ var ClearButton = function ClearButton() {
|
|
|
50
49
|
|
|
51
50
|
if (!hasValue) {
|
|
52
51
|
return null;
|
|
53
|
-
} // Log a warning and hide the button when no onClear callback is provided.
|
|
54
|
-
// If we called handleClear with no onClear prop, all the button would do is focus the Input.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (!onClear) {
|
|
58
|
-
console.warn("Warning: No onClear prop provided to Input when using Input.ClearButton. Omitting Input.ClearButton.");
|
|
59
|
-
return null;
|
|
60
52
|
} // Warn if clearButtonLabel is not included, so that the unlocalized fallback will not be mistaken for a proper label.
|
|
61
53
|
|
|
62
54
|
|
|
@@ -95,14 +87,10 @@ var isClearButton = function isClearButton(elem) {
|
|
|
95
87
|
var Input = /*#__PURE__*/function (_React$Component) {
|
|
96
88
|
_inheritsLoose(Input, _React$Component);
|
|
97
89
|
|
|
98
|
-
function Input() {
|
|
90
|
+
function Input(props) {
|
|
99
91
|
var _this;
|
|
100
92
|
|
|
101
|
-
|
|
102
|
-
args[_key] = arguments[_key];
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
_this = _React$Component.call.apply(_React$Component, [this].concat(args)) || this;
|
|
93
|
+
_this = _React$Component.call(this, props) || this;
|
|
106
94
|
_this.inputRef = /*#__PURE__*/React.createRef();
|
|
107
95
|
|
|
108
96
|
_this.handleBlur = function (e) {
|
|
@@ -110,14 +98,31 @@ var Input = /*#__PURE__*/function (_React$Component) {
|
|
|
110
98
|
};
|
|
111
99
|
|
|
112
100
|
_this.handleClear = function (e) {
|
|
113
|
-
var
|
|
101
|
+
var input = _this.inputRef.current;
|
|
102
|
+
|
|
103
|
+
if (input) {
|
|
104
|
+
var _Object$getOwnPropert;
|
|
105
|
+
|
|
106
|
+
// Clear the value via the input prototype, then dispatch an input event in order to trigger handleChange
|
|
107
|
+
var nativeInputValueSetter = (_Object$getOwnPropert = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")) == null ? void 0 : _Object$getOwnPropert.set;
|
|
108
|
+
nativeInputValueSetter == null ? void 0 : nativeInputValueSetter.call(input, "");
|
|
109
|
+
var inputEvent = new Event("input", {
|
|
110
|
+
bubbles: true
|
|
111
|
+
});
|
|
112
|
+
input.dispatchEvent(inputEvent); // Focus the input, update hasValue, and call any onClear callback
|
|
113
|
+
|
|
114
|
+
input.focus();
|
|
115
|
+
|
|
116
|
+
_this.updateState("");
|
|
114
117
|
|
|
115
|
-
|
|
116
|
-
|
|
118
|
+
_this.props.onClear == null ? void 0 : _this.props.onClear(e);
|
|
119
|
+
}
|
|
117
120
|
};
|
|
118
121
|
|
|
119
122
|
_this.handleChange = function (e) {
|
|
120
|
-
|
|
123
|
+
_this.props.onChange == null ? void 0 : _this.props.onChange(e, e.currentTarget.value);
|
|
124
|
+
|
|
125
|
+
_this.updateState(e.currentTarget.value);
|
|
121
126
|
};
|
|
122
127
|
|
|
123
128
|
_this.handleFocus = function (e) {
|
|
@@ -136,6 +141,22 @@ var Input = /*#__PURE__*/function (_React$Component) {
|
|
|
136
141
|
return _this.props.onPaste == null ? void 0 : _this.props.onPaste(e, e.currentTarget.value);
|
|
137
142
|
};
|
|
138
143
|
|
|
144
|
+
_this.updateState = function (inputValue) {
|
|
145
|
+
var hasValue = inputValue !== "";
|
|
146
|
+
var previousHasValue = _this.state.hasValue; // Only update state if the value of `hasValue` has changed to avoid unnecessary renders.
|
|
147
|
+
|
|
148
|
+
if (hasValue !== previousHasValue) {
|
|
149
|
+
_this.setState({
|
|
150
|
+
hasValue: hasValue
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
_this.state = {
|
|
156
|
+
// Tracking hasValue in state allows us to hide ClearButton when there is no value to clear
|
|
157
|
+
// for both controlled and uncontrolled inputs.
|
|
158
|
+
hasValue: !!props.value
|
|
159
|
+
};
|
|
139
160
|
return _this;
|
|
140
161
|
}
|
|
141
162
|
|
|
@@ -189,9 +210,9 @@ var Input = /*#__PURE__*/function (_React$Component) {
|
|
|
189
210
|
name: "search",
|
|
190
211
|
ariaHidden: true,
|
|
191
212
|
color: "icon.base"
|
|
192
|
-
}) : elemBefore; // Do not add a ClearButton if
|
|
213
|
+
}) : elemBefore; // Do not add a ClearButton if an elemAfter prop was passed.
|
|
193
214
|
|
|
194
|
-
var elementAfter = type === "search" &&
|
|
215
|
+
var elementAfter = type === "search" && !elemAfter ? /*#__PURE__*/React.createElement(ClearButton, null) : elemAfter;
|
|
195
216
|
return /*#__PURE__*/React.createElement(_styles.default, _extends({
|
|
196
217
|
hasBeforeElement: !!elementBefore,
|
|
197
218
|
hasAfterElement: !!elementAfter,
|
|
@@ -204,9 +225,8 @@ var Input = /*#__PURE__*/function (_React$Component) {
|
|
|
204
225
|
}, rest), /*#__PURE__*/React.createElement(InputContext.Provider, {
|
|
205
226
|
value: {
|
|
206
227
|
handleClear: this.handleClear,
|
|
207
|
-
hasValue:
|
|
228
|
+
hasValue: this.state.hasValue,
|
|
208
229
|
clearButtonLabel: clearButtonLabel,
|
|
209
|
-
onClear: onClear,
|
|
210
230
|
size: size
|
|
211
231
|
}
|
|
212
232
|
}, elementBefore && /*#__PURE__*/React.createElement(_styles.Accessory, {
|
package/commonjs/Input/styles.js
CHANGED
|
@@ -84,9 +84,9 @@ var Accessory = _styledComponents.default.div.withConfig({
|
|
|
84
84
|
})(["position:absolute;top:50%;transform:translateY(-50%);color:", ";display:flex;align-items:center;", ";", ";"], function (props) {
|
|
85
85
|
return props.theme.colors.icon.base;
|
|
86
86
|
}, function (props) {
|
|
87
|
-
return props.before && (0, _styledComponents.css)(["left:", ";"], props.theme.space[
|
|
87
|
+
return props.before && (0, _styledComponents.css)(["left:", ";"], props.theme.space[300]);
|
|
88
88
|
}, function (props) {
|
|
89
|
-
return props.after && (0, _styledComponents.css)(["right:", ";"], props.isClearButton ? 0 : props.theme.space[
|
|
89
|
+
return props.after && (0, _styledComponents.css)(["right:", ";"], props.isClearButton ? 0 : props.theme.space[300]);
|
|
90
90
|
});
|
|
91
91
|
|
|
92
92
|
exports.Accessory = Accessory;
|
package/lib/Input/index.js
CHANGED
|
@@ -24,7 +24,6 @@ var StyledButton = styled(Button).withConfig({
|
|
|
24
24
|
|
|
25
25
|
var ClearButton = function ClearButton() {
|
|
26
26
|
var _React$useContext = React.useContext(InputContext),
|
|
27
|
-
onClear = _React$useContext.onClear,
|
|
28
27
|
handleClear = _React$useContext.handleClear,
|
|
29
28
|
clearButtonLabel = _React$useContext.clearButtonLabel,
|
|
30
29
|
hasValue = _React$useContext.hasValue,
|
|
@@ -33,13 +32,6 @@ var ClearButton = function ClearButton() {
|
|
|
33
32
|
|
|
34
33
|
if (!hasValue) {
|
|
35
34
|
return null;
|
|
36
|
-
} // Log a warning and hide the button when no onClear callback is provided.
|
|
37
|
-
// If we called handleClear with no onClear prop, all the button would do is focus the Input.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (!onClear) {
|
|
41
|
-
console.warn("Warning: No onClear prop provided to Input when using Input.ClearButton. Omitting Input.ClearButton.");
|
|
42
|
-
return null;
|
|
43
35
|
} // Warn if clearButtonLabel is not included, so that the unlocalized fallback will not be mistaken for a proper label.
|
|
44
36
|
|
|
45
37
|
|
|
@@ -78,14 +70,10 @@ var isClearButton = function isClearButton(elem) {
|
|
|
78
70
|
var Input = /*#__PURE__*/function (_React$Component) {
|
|
79
71
|
_inheritsLoose(Input, _React$Component);
|
|
80
72
|
|
|
81
|
-
function Input() {
|
|
73
|
+
function Input(props) {
|
|
82
74
|
var _this;
|
|
83
75
|
|
|
84
|
-
|
|
85
|
-
args[_key] = arguments[_key];
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
_this = _React$Component.call.apply(_React$Component, [this].concat(args)) || this;
|
|
76
|
+
_this = _React$Component.call(this, props) || this;
|
|
89
77
|
_this.inputRef = /*#__PURE__*/React.createRef();
|
|
90
78
|
|
|
91
79
|
_this.handleBlur = function (e) {
|
|
@@ -93,14 +81,31 @@ var Input = /*#__PURE__*/function (_React$Component) {
|
|
|
93
81
|
};
|
|
94
82
|
|
|
95
83
|
_this.handleClear = function (e) {
|
|
96
|
-
var
|
|
84
|
+
var input = _this.inputRef.current;
|
|
85
|
+
|
|
86
|
+
if (input) {
|
|
87
|
+
var _Object$getOwnPropert;
|
|
88
|
+
|
|
89
|
+
// Clear the value via the input prototype, then dispatch an input event in order to trigger handleChange
|
|
90
|
+
var nativeInputValueSetter = (_Object$getOwnPropert = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")) == null ? void 0 : _Object$getOwnPropert.set;
|
|
91
|
+
nativeInputValueSetter == null ? void 0 : nativeInputValueSetter.call(input, "");
|
|
92
|
+
var inputEvent = new Event("input", {
|
|
93
|
+
bubbles: true
|
|
94
|
+
});
|
|
95
|
+
input.dispatchEvent(inputEvent); // Focus the input, update hasValue, and call any onClear callback
|
|
96
|
+
|
|
97
|
+
input.focus();
|
|
98
|
+
|
|
99
|
+
_this.updateState("");
|
|
97
100
|
|
|
98
|
-
|
|
99
|
-
|
|
101
|
+
_this.props.onClear == null ? void 0 : _this.props.onClear(e);
|
|
102
|
+
}
|
|
100
103
|
};
|
|
101
104
|
|
|
102
105
|
_this.handleChange = function (e) {
|
|
103
|
-
|
|
106
|
+
_this.props.onChange == null ? void 0 : _this.props.onChange(e, e.currentTarget.value);
|
|
107
|
+
|
|
108
|
+
_this.updateState(e.currentTarget.value);
|
|
104
109
|
};
|
|
105
110
|
|
|
106
111
|
_this.handleFocus = function (e) {
|
|
@@ -119,6 +124,22 @@ var Input = /*#__PURE__*/function (_React$Component) {
|
|
|
119
124
|
return _this.props.onPaste == null ? void 0 : _this.props.onPaste(e, e.currentTarget.value);
|
|
120
125
|
};
|
|
121
126
|
|
|
127
|
+
_this.updateState = function (inputValue) {
|
|
128
|
+
var hasValue = inputValue !== "";
|
|
129
|
+
var previousHasValue = _this.state.hasValue; // Only update state if the value of `hasValue` has changed to avoid unnecessary renders.
|
|
130
|
+
|
|
131
|
+
if (hasValue !== previousHasValue) {
|
|
132
|
+
_this.setState({
|
|
133
|
+
hasValue: hasValue
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
_this.state = {
|
|
139
|
+
// Tracking hasValue in state allows us to hide ClearButton when there is no value to clear
|
|
140
|
+
// for both controlled and uncontrolled inputs.
|
|
141
|
+
hasValue: !!props.value
|
|
142
|
+
};
|
|
122
143
|
return _this;
|
|
123
144
|
}
|
|
124
145
|
|
|
@@ -172,9 +193,9 @@ var Input = /*#__PURE__*/function (_React$Component) {
|
|
|
172
193
|
name: "search",
|
|
173
194
|
ariaHidden: true,
|
|
174
195
|
color: "icon.base"
|
|
175
|
-
}) : elemBefore; // Do not add a ClearButton if
|
|
196
|
+
}) : elemBefore; // Do not add a ClearButton if an elemAfter prop was passed.
|
|
176
197
|
|
|
177
|
-
var elementAfter = type === "search" &&
|
|
198
|
+
var elementAfter = type === "search" && !elemAfter ? /*#__PURE__*/React.createElement(ClearButton, null) : elemAfter;
|
|
178
199
|
return /*#__PURE__*/React.createElement(Container, _extends({
|
|
179
200
|
hasBeforeElement: !!elementBefore,
|
|
180
201
|
hasAfterElement: !!elementAfter,
|
|
@@ -187,9 +208,8 @@ var Input = /*#__PURE__*/function (_React$Component) {
|
|
|
187
208
|
}, rest), /*#__PURE__*/React.createElement(InputContext.Provider, {
|
|
188
209
|
value: {
|
|
189
210
|
handleClear: this.handleClear,
|
|
190
|
-
hasValue:
|
|
211
|
+
hasValue: this.state.hasValue,
|
|
191
212
|
clearButtonLabel: clearButtonLabel,
|
|
192
|
-
onClear: onClear,
|
|
193
213
|
size: size
|
|
194
214
|
}
|
|
195
215
|
}, elementBefore && /*#__PURE__*/React.createElement(Accessory, {
|
package/lib/Input/styles.js
CHANGED
|
@@ -71,9 +71,9 @@ export var Accessory = styled.div.withConfig({
|
|
|
71
71
|
})(["position:absolute;top:50%;transform:translateY(-50%);color:", ";display:flex;align-items:center;", ";", ";"], function (props) {
|
|
72
72
|
return props.theme.colors.icon.base;
|
|
73
73
|
}, function (props) {
|
|
74
|
-
return props.before && css(["left:", ";"], props.theme.space[
|
|
74
|
+
return props.before && css(["left:", ";"], props.theme.space[300]);
|
|
75
75
|
}, function (props) {
|
|
76
|
-
return props.after && css(["right:", ";"], props.isClearButton ? 0 : props.theme.space[
|
|
76
|
+
return props.after && css(["right:", ";"], props.isClearButton ? 0 : props.theme.space[300]);
|
|
77
77
|
});
|
|
78
78
|
Container.displayName = "InputContainer";
|
|
79
79
|
Accessory.displayName = "InputAccessory";
|