@sproutsocial/racine 11.2.5 → 11.3.0-beta.2

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.
Files changed (178) hide show
  1. package/CHANGELOG.md +0 -6
  2. package/__flow__/Input/index.js +126 -67
  3. package/__flow__/Input/index.stories.js +33 -0
  4. package/__flow__/Input/index.test.js +199 -1
  5. package/__flow__/Input/styles.js +1 -1
  6. package/commonjs/Avatar/index.js +7 -4
  7. package/commonjs/Badge/index.js +3 -1
  8. package/commonjs/Badge/styles.js +1 -1
  9. package/commonjs/Banner/index.js +3 -1
  10. package/commonjs/Banner/styles.js +1 -1
  11. package/commonjs/Box/styles.js +1 -1
  12. package/commonjs/Breadcrumb/index.js +5 -2
  13. package/commonjs/Button/index.js +3 -1
  14. package/commonjs/Card/index.js +3 -1
  15. package/commonjs/Card/styles.js +3 -3
  16. package/commonjs/CharacterCounter/index.js +3 -1
  17. package/commonjs/CharacterCounter/styles.js +1 -1
  18. package/commonjs/ChartLegend/index.js +3 -1
  19. package/commonjs/ChartLegend/styles.js +3 -3
  20. package/commonjs/Checkbox/index.js +3 -1
  21. package/commonjs/Checkbox/styles.js +1 -1
  22. package/commonjs/Collapsible/index.js +5 -2
  23. package/commonjs/DatePicker/DateRangePicker.js +3 -1
  24. package/commonjs/DatePicker/SingleDatePicker.js +3 -1
  25. package/commonjs/DatePicker/StatefulDateRangePicker.js +3 -1
  26. package/commonjs/DatePicker/StatefulSingleDatePicker.js +3 -1
  27. package/commonjs/DatePicker/common.js +1 -1
  28. package/commonjs/DatePicker/styles.js +2 -6
  29. package/commonjs/Drawer/SlideTransition.js +3 -1
  30. package/commonjs/Drawer/index.js +9 -4
  31. package/commonjs/Drawer/styles.js +2 -2
  32. package/commonjs/EmptyState/index.js +3 -1
  33. package/commonjs/Fieldset/index.js +7 -3
  34. package/commonjs/FormField/index.js +3 -1
  35. package/commonjs/Icon/index.js +5 -2
  36. package/commonjs/Icon/styles.js +1 -1
  37. package/commonjs/Image/index.js +3 -1
  38. package/commonjs/Image/styles.js +1 -1
  39. package/commonjs/Indicator/index.js +3 -1
  40. package/commonjs/Input/index.js +80 -29
  41. package/commonjs/Input/styles.js +3 -3
  42. package/commonjs/KeyboardKey/index.js +3 -1
  43. package/commonjs/Label/index.js +4 -2
  44. package/commonjs/Link/index.js +3 -1
  45. package/commonjs/Link/styles.js +1 -1
  46. package/commonjs/Listbox/index.js +7 -4
  47. package/commonjs/Loader/index.js +3 -1
  48. package/commonjs/Loader/styles.js +2 -2
  49. package/commonjs/LoaderButton/index.js +3 -1
  50. package/commonjs/Menu/constants.js +1 -1
  51. package/commonjs/Menu/descendants.js +10 -7
  52. package/commonjs/Menu/hooks.js +1 -1
  53. package/commonjs/Menu/index.js +22 -16
  54. package/commonjs/Menu/styles.js +2 -2
  55. package/commonjs/Message/index.js +3 -1
  56. package/commonjs/Message/styles.js +1 -1
  57. package/commonjs/Modal/index.js +7 -3
  58. package/commonjs/Modal/styles.js +4 -6
  59. package/commonjs/Numeral/constants.js +1 -1
  60. package/commonjs/Numeral/index.js +3 -1
  61. package/commonjs/Numeral/styles.js +3 -3
  62. package/commonjs/OverflowList/styles.js +1 -1
  63. package/commonjs/Popout/index.js +7 -3
  64. package/commonjs/Popout/styles.js +1 -1
  65. package/commonjs/Radio/index.js +3 -1
  66. package/commonjs/Radio/styles.js +4 -4
  67. package/commonjs/SegmentedControl/index.js +5 -2
  68. package/commonjs/Select/index.js +3 -1
  69. package/commonjs/Stack/index.js +3 -1
  70. package/commonjs/Switch/index.js +3 -1
  71. package/commonjs/Switch/styles.js +1 -1
  72. package/commonjs/Table/index.js +10 -5
  73. package/commonjs/TableCell/index.js +3 -1
  74. package/commonjs/TableHeaderCell/index.js +3 -1
  75. package/commonjs/TableRowAccordion/index.js +3 -1
  76. package/commonjs/Tabs/index.js +5 -2
  77. package/commonjs/Tabs/styles.js +4 -4
  78. package/commonjs/Text/index.js +3 -1
  79. package/commonjs/Text/styles.js +1 -1
  80. package/commonjs/Textarea/index.js +3 -1
  81. package/commonjs/Toast/index.js +15 -15
  82. package/commonjs/Toast/styles.js +4 -7
  83. package/commonjs/ToggleHint/index.js +3 -1
  84. package/commonjs/Token/index.js +3 -1
  85. package/commonjs/Token/styles.js +1 -1
  86. package/commonjs/TokenInput/index.js +3 -1
  87. package/commonjs/Tooltip/index.js +5 -2
  88. package/commonjs/Tooltip/styles.js +1 -1
  89. package/commonjs/VisuallyHidden/index.js +1 -1
  90. package/commonjs/index.js +1 -1
  91. package/commonjs/themes/dark/decorative-palettes.js +1 -1
  92. package/commonjs/themes/dark/theme.js +1 -1
  93. package/commonjs/themes/light/decorative-palettes.js +1 -1
  94. package/commonjs/themes/light/theme.js +1 -1
  95. package/commonjs/utils/hooks.js +3 -2
  96. package/commonjs/utils/mixins.js +1 -1
  97. package/commonjs/utils/system-props.js +1 -1
  98. package/dist/themes/dark/dark.scss +5 -5
  99. package/dist/themes/light/light.scss +95 -95
  100. package/lib/Avatar/index.js +7 -4
  101. package/lib/Badge/index.js +3 -1
  102. package/lib/Badge/styles.js +1 -1
  103. package/lib/Banner/index.js +3 -1
  104. package/lib/Banner/styles.js +1 -1
  105. package/lib/Box/styles.js +1 -1
  106. package/lib/Breadcrumb/index.js +5 -2
  107. package/lib/Button/index.js +3 -1
  108. package/lib/Card/index.js +3 -1
  109. package/lib/Card/styles.js +2 -2
  110. package/lib/CharacterCounter/index.js +3 -1
  111. package/lib/CharacterCounter/styles.js +1 -1
  112. package/lib/ChartLegend/index.js +3 -1
  113. package/lib/ChartLegend/styles.js +3 -3
  114. package/lib/Checkbox/index.js +3 -1
  115. package/lib/Collapsible/index.js +5 -2
  116. package/lib/DatePicker/DateRangePicker.js +3 -1
  117. package/lib/DatePicker/SingleDatePicker.js +3 -1
  118. package/lib/DatePicker/StatefulDateRangePicker.js +3 -1
  119. package/lib/DatePicker/StatefulSingleDatePicker.js +3 -1
  120. package/lib/DatePicker/styles.js +2 -6
  121. package/lib/Drawer/SlideTransition.js +3 -1
  122. package/lib/Drawer/index.js +9 -4
  123. package/lib/Drawer/styles.js +2 -2
  124. package/lib/EmptyState/index.js +3 -1
  125. package/lib/Fieldset/index.js +7 -3
  126. package/lib/FormField/index.js +3 -1
  127. package/lib/Icon/index.js +5 -2
  128. package/lib/Icon/styles.js +1 -1
  129. package/lib/Image/index.js +3 -1
  130. package/lib/Image/styles.js +1 -1
  131. package/lib/Indicator/index.js +3 -1
  132. package/lib/Input/index.js +73 -28
  133. package/lib/Input/styles.js +3 -3
  134. package/lib/KeyboardKey/index.js +3 -1
  135. package/lib/Label/index.js +4 -2
  136. package/lib/Link/index.js +3 -1
  137. package/lib/Link/styles.js +1 -1
  138. package/lib/Listbox/index.js +6 -3
  139. package/lib/Loader/index.js +3 -1
  140. package/lib/Loader/styles.js +2 -2
  141. package/lib/LoaderButton/index.js +3 -1
  142. package/lib/Menu/descendants.js +5 -2
  143. package/lib/Menu/index.js +20 -16
  144. package/lib/Menu/styles.js +2 -2
  145. package/lib/Message/index.js +3 -1
  146. package/lib/Modal/index.js +7 -3
  147. package/lib/Modal/styles.js +3 -5
  148. package/lib/Numeral/index.js +3 -1
  149. package/lib/Numeral/styles.js +2 -2
  150. package/lib/OverflowList/styles.js +1 -1
  151. package/lib/Popout/index.js +7 -3
  152. package/lib/Popout/styles.js +1 -1
  153. package/lib/Radio/index.js +3 -1
  154. package/lib/Radio/styles.js +4 -4
  155. package/lib/SegmentedControl/index.js +5 -2
  156. package/lib/Select/index.js +3 -1
  157. package/lib/Stack/index.js +3 -1
  158. package/lib/Switch/index.js +3 -1
  159. package/lib/Switch/styles.js +1 -1
  160. package/lib/Table/index.js +9 -4
  161. package/lib/TableCell/index.js +3 -1
  162. package/lib/TableHeaderCell/index.js +3 -1
  163. package/lib/TableRowAccordion/index.js +3 -1
  164. package/lib/Tabs/index.js +5 -2
  165. package/lib/Tabs/styles.js +3 -3
  166. package/lib/Text/index.js +3 -1
  167. package/lib/Text/styles.js +1 -1
  168. package/lib/Textarea/index.js +3 -1
  169. package/lib/Toast/index.js +14 -14
  170. package/lib/Toast/styles.js +3 -7
  171. package/lib/ToggleHint/index.js +3 -1
  172. package/lib/Token/index.js +3 -1
  173. package/lib/Token/styles.js +1 -1
  174. package/lib/TokenInput/index.js +3 -1
  175. package/lib/Tooltip/index.js +5 -2
  176. package/lib/Tooltip/styles.js +1 -1
  177. package/lib/VisuallyHidden/index.js +1 -1
  178. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,11 +1,5 @@
1
1
  # Change Log
2
2
 
3
- ## 11.2.5
4
-
5
- ### Patch Changes
6
-
7
- - a73cdfb: remove weird characters from props descriptions
8
-
9
3
  ## 11.2.4
10
4
 
11
5
  ### Patch Changes
@@ -1,6 +1,8 @@
1
1
  // @flow
2
2
  import * as React from "react";
3
3
  import Container, { Accessory } from "./styles";
4
+ import Button from "../Button";
5
+ import Icon from "../Icon";
4
6
 
5
7
  type TypeProps = {
6
8
  /** ID of the form element, should match the "for" value of the associated label */
@@ -10,6 +12,8 @@ type TypeProps = {
10
12
  ariaLabel?: string,
11
13
  /** Attribute used to associate other elements that describe the Input, like an error */
12
14
  ariaDescribedby?: string,
15
+ /** Label for Input.ClearButton. Required when using <Input type="search"/> or <Input.ClearButton/>. */
16
+ clearButtonLabel?: string,
13
17
  /** Placeholder text for when value is undefined or empty */
14
18
  placeholder?: string,
15
19
  /** Current value of the input */
@@ -42,6 +46,8 @@ type TypeProps = {
42
46
  innerRef?: React.Ref<"input">,
43
47
  onBlur?: (e: SyntheticFocusEvent<HTMLInputElement>) => void,
44
48
  onChange?: (e: SyntheticInputEvent<HTMLInputElement>, value: string) => void,
49
+ /** Called on Input.ClearButton trigger. */
50
+ onClear?: (e: SyntheticEvent<HTMLButtonElement>) => void,
45
51
  onFocus?: (e: SyntheticFocusEvent<HTMLInputElement>) => void,
46
52
  onKeyDown?: (
47
53
  e: SyntheticKeyboardEvent<HTMLInputElement>,
@@ -54,14 +60,50 @@ type TypeProps = {
54
60
  onPaste?: (e: SyntheticInputEvent<HTMLInputElement>, value: string) => void,
55
61
  size?: "large" | "small" | "default",
56
62
  qa?: Object,
57
- /**
58
- Controls the styles of the input. Primary is our standard input styles and secondary is a borderless input.
63
+ /**
64
+ Controls the styles of the input. Primary is our standard input styles and secondary is a borderless input.
59
65
  Note that this prop should only be used to alter styles and not functionality.
60
66
  */
61
67
  appearance?: "primary" | "secondary",
62
68
  };
63
69
 
64
- export default class Input extends React.Component<TypeProps> {
70
+ // Using Context so that Input's Input.ClearButton-specific props can be passed to Input.ClearButton,
71
+ // regardless of whether it is manually included as elemAfter or automatically included for type="search" Inputs.
72
+ type TypeInputContext = $Shape<{
73
+ handleClear: (e: SyntheticEvent<HTMLButtonElement>) => void,
74
+ clearButtonLabel: string,
75
+ hasValue: boolean,
76
+ }>;
77
+
78
+ const InputContext = React.createContext<TypeInputContext>({});
79
+
80
+ const ClearButton = () => {
81
+ const { handleClear, clearButtonLabel, hasValue } =
82
+ React.useContext(InputContext);
83
+
84
+ // Hide the button when there is no text to clear.
85
+ if (!hasValue) {
86
+ return null;
87
+ }
88
+ return (
89
+ <Button onClick={handleClear}>
90
+ {/*Unlocalized fallback should not be used. Always include a localized
91
+ clearButtonLabel when using <Input.ClearButton/> or <Input type="search"/>.*/}
92
+ <Icon name="circlex" title={clearButtonLabel || "Clear"} />
93
+ </Button>
94
+ );
95
+ };
96
+
97
+ // Used for positioning elementAfter. This logic will detect if the element is a ClearButton,
98
+ // regardless of whether it was manually passed as elemAfter or automatically added to a search Input.
99
+ const isClearButton = (elem: any) => {
100
+ if (elem?.type) {
101
+ return elem.type.displayName === "Input.ClearButton";
102
+ }
103
+ return false;
104
+ };
105
+
106
+ class Input extends React.Component<TypeProps> {
65
107
  static defaultProps = {
66
108
  autoFocus: false,
67
109
  disabled: false,
@@ -70,41 +112,28 @@ export default class Input extends React.Component<TypeProps> {
70
112
  appearance: "primary",
71
113
  };
72
114
 
73
- handleBlur = (e: SyntheticFocusEvent<HTMLInputElement>) => {
74
- if (this.props.onBlur) {
75
- this.props.onBlur(e);
76
- }
77
- };
115
+ static ClearButton = ClearButton;
78
116
 
79
- handleChange = (e: SyntheticInputEvent<HTMLInputElement>) => {
80
- if (this.props.onChange) {
81
- this.props.onChange(e, e.currentTarget.value);
82
- }
83
- };
117
+ handleBlur = (e: SyntheticFocusEvent<HTMLInputElement>) =>
118
+ this.props.onBlur?.(e);
84
119
 
85
- handleFocus = (e: SyntheticFocusEvent<HTMLInputElement>) => {
86
- if (this.props.onFocus) {
87
- this.props.onFocus(e);
88
- }
89
- };
120
+ handleClear = (e: SyntheticEvent<HTMLButtonElement>) =>
121
+ this.props.onClear?.(e);
90
122
 
91
- handleKeyDown = (e: SyntheticKeyboardEvent<HTMLInputElement>) => {
92
- if (this.props.onKeyDown) {
93
- this.props.onKeyDown(e, e.currentTarget.value);
94
- }
95
- };
123
+ handleChange = (e: SyntheticInputEvent<HTMLInputElement>) =>
124
+ this.props.onChange?.(e, e.currentTarget.value);
96
125
 
97
- handleKeyUp = (e: SyntheticKeyboardEvent<HTMLInputElement>) => {
98
- if (this.props.onKeyUp) {
99
- this.props.onKeyUp(e, e.currentTarget.value);
100
- }
101
- };
126
+ handleFocus = (e: SyntheticFocusEvent<HTMLInputElement>) =>
127
+ this.props.onFocus?.(e);
102
128
 
103
- handlePaste = (e: SyntheticInputEvent<HTMLInputElement>) => {
104
- if (this.props.onPaste) {
105
- this.props.onPaste(e, e.currentTarget.value);
106
- }
107
- };
129
+ handleKeyDown = (e: SyntheticKeyboardEvent<HTMLInputElement>) =>
130
+ this.props.onKeyDown?.(e, e.currentTarget.value);
131
+
132
+ handleKeyUp = (e: SyntheticKeyboardEvent<HTMLInputElement>) =>
133
+ this.props.onKeyUp?.(e, e.currentTarget.value);
134
+
135
+ handlePaste = (e: SyntheticInputEvent<HTMLInputElement>) =>
136
+ this.props.onPaste?.(e, e.currentTarget.value);
108
137
 
109
138
  render() {
110
139
  const {
@@ -125,9 +154,11 @@ export default class Input extends React.Component<TypeProps> {
125
154
  maxLength,
126
155
  ariaLabel,
127
156
  ariaDescribedby,
157
+ clearButtonLabel,
128
158
  innerRef,
129
159
  onBlur,
130
160
  onChange,
161
+ onClear,
131
162
  onFocus,
132
163
  onKeyDown,
133
164
  onKeyUp,
@@ -138,15 +169,27 @@ export default class Input extends React.Component<TypeProps> {
138
169
  ...rest
139
170
  } = this.props;
140
171
 
172
+ // Convert autoComplete from a boolean prop to a string value.
141
173
  let autoCompleteValue = undefined;
142
174
  if (autoComplete !== undefined) {
143
175
  autoCompleteValue = autoComplete ? "on" : "off";
144
176
  }
145
177
 
178
+ // Add default elemBefore and elemAfter elements if type is search.
179
+ const elementBefore =
180
+ type === "search" && !elemBefore ? (
181
+ <Icon name="search" ariaHidden />
182
+ ) : (
183
+ elemBefore
184
+ );
185
+ // Do not add a ClearButton if no onClear callback is provided or if an elemAfter prop was passed.
186
+ const elementAfter =
187
+ type === "search" && onClear && !elemAfter ? <ClearButton /> : elemAfter;
188
+
146
189
  return (
147
190
  <Container
148
- hasBeforeElement={!!elemBefore}
149
- hasAfterElement={!!elemAfter}
191
+ hasBeforeElement={!!elementBefore}
192
+ hasAfterElement={!!elementAfter}
150
193
  disabled={disabled}
151
194
  invalid={!!isInvalid}
152
195
  warning={hasWarning}
@@ -154,39 +197,55 @@ export default class Input extends React.Component<TypeProps> {
154
197
  // $FlowIssue - upgrade v0.112.0
155
198
  {...rest}
156
199
  >
157
- {elemBefore && <Accessory before>{elemBefore}</Accessory>}
158
-
159
- <input
160
- aria-invalid={!!isInvalid}
161
- aria-label={ariaLabel}
162
- aria-describedby={ariaDescribedby}
163
- autoComplete={autoCompleteValue}
164
- autoFocus={autoFocus}
165
- disabled={disabled}
166
- readOnly={readOnly}
167
- id={id}
168
- name={name}
169
- placeholder={placeholder}
170
- type={type}
171
- required={required}
172
- value={value}
173
- maxLength={maxLength}
174
- onBlur={this.handleBlur}
175
- onChange={this.handleChange}
176
- onFocus={this.handleFocus}
177
- onKeyDown={this.handleKeyDown}
178
- onKeyUp={this.handleKeyUp}
179
- onPaste={this.handlePaste}
180
- ref={innerRef}
181
- data-qa-input={name || ""}
182
- data-qa-input-isdisabled={disabled === true}
183
- data-qa-input-isrequired={required === true}
184
- {...qa}
185
- {...inputProps}
186
- />
187
-
188
- {elemAfter && <Accessory after>{elemAfter}</Accessory>}
200
+ <InputContext.Provider
201
+ value={{
202
+ handleClear: this.handleClear,
203
+ clearButtonLabel,
204
+ hasValue: !!value,
205
+ }}
206
+ >
207
+ {elementBefore && <Accessory before>{elementBefore}</Accessory>}
208
+
209
+ <input
210
+ aria-invalid={!!isInvalid}
211
+ aria-label={ariaLabel}
212
+ aria-describedby={ariaDescribedby}
213
+ autoComplete={autoCompleteValue}
214
+ autoFocus={autoFocus}
215
+ disabled={disabled}
216
+ readOnly={readOnly}
217
+ id={id}
218
+ name={name}
219
+ placeholder={placeholder}
220
+ type={type}
221
+ required={required}
222
+ value={value}
223
+ maxLength={maxLength}
224
+ onBlur={this.handleBlur}
225
+ onChange={this.handleChange}
226
+ onFocus={this.handleFocus}
227
+ onKeyDown={this.handleKeyDown}
228
+ onKeyUp={this.handleKeyUp}
229
+ onPaste={this.handlePaste}
230
+ ref={innerRef}
231
+ data-qa-input={name || ""}
232
+ data-qa-input-isdisabled={disabled === true}
233
+ data-qa-input-isrequired={required === true}
234
+ {...qa}
235
+ {...inputProps}
236
+ />
237
+
238
+ {elementAfter && (
239
+ <Accessory after isClearButton={isClearButton(elementAfter)}>
240
+ {elementAfter}
241
+ </Accessory>
242
+ )}
243
+ </InputContext.Provider>
189
244
  </Container>
190
245
  );
191
246
  }
192
247
  }
248
+
249
+ Input.ClearButton.displayName = "Input.ClearButton";
250
+
251
+ export default Input;
@@ -141,6 +141,39 @@ leftAndRightIcons.story = {
141
141
  name: "Left and right icons",
142
142
  };
143
143
 
144
+ export const searchInput = () => (
145
+ <Input
146
+ type="search"
147
+ placeholder={text("placeholder", "Please enter a value...")}
148
+ value={text("value", "val")}
149
+ onClear={() => window.alert("Cleared!")}
150
+ clearButtonLabel={text("clearButtonLabel", "Clear search")}
151
+ ariaLabel={text("ariaLabel", "Descriptive label goes here")}
152
+ />
153
+ );
154
+
155
+ searchInput.story = {
156
+ name: "Search Input",
157
+ };
158
+
159
+ export const nonSearchClearButtonInput = () => {
160
+ return (
161
+ <Input
162
+ type="text"
163
+ placeholder={text("placeholder", "Please enter a value...")}
164
+ value={text("value", "val")}
165
+ onClear={() => window.alert("Cleared!")}
166
+ ariaLabel={text("ariaLabel", "Descriptive label goes here")}
167
+ clearButtonLabel={text("clearButtonLabel", "Clear text")}
168
+ elemAfter={<Input.ClearButton />}
169
+ />
170
+ );
171
+ };
172
+
173
+ nonSearchClearButtonInput.story = {
174
+ name: "Input.ClearButton usage",
175
+ };
176
+
144
177
  export const autofocus = () => (
145
178
  <Input
146
179
  autoFocus
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
2
  import "jest-styled-components";
3
- import { render, fireEvent } from "../utils/react-testing-library";
3
+ import { render, fireEvent, userEvent } from "../utils/react-testing-library";
4
4
  import Input from "./";
5
5
  import Text from "../Text";
6
6
 
@@ -69,6 +69,204 @@ describe("Input", () => {
69
69
  expect(getByText("After")).toBeTruthy();
70
70
  });
71
71
 
72
+ describe("Input.ClearButton", () => {
73
+ describe("Input type=search", () => {
74
+ it("should render a clear button for search Inputs", () => {
75
+ const { getByRole } = render(
76
+ <Input
77
+ id="name"
78
+ name="name"
79
+ value="mic"
80
+ type="search"
81
+ onClear={jest.fn()}
82
+ clearButtonLabel="Clear search"
83
+ />
84
+ );
85
+ expect(getByRole("button")).toBeTruthy();
86
+ });
87
+
88
+ it("should not override an elemAfter prop if passed", () => {
89
+ const { getByText, queryByTitle } = render(
90
+ <Input
91
+ id="name"
92
+ name="name"
93
+ value="mic"
94
+ type="search"
95
+ onClear={jest.fn()}
96
+ elemAfter={<Text>After</Text>}
97
+ />
98
+ );
99
+ expect(getByText("After")).toBeTruthy();
100
+ expect(queryByTitle("Clear")).not.toBeInTheDocument();
101
+ });
102
+
103
+ it("should use the fallback title if clearButtonLabel is not provided", () => {
104
+ const { getByTitle } = render(
105
+ <Input
106
+ id="name"
107
+ name="name"
108
+ value="mic"
109
+ type="search"
110
+ onClear={jest.fn()}
111
+ />
112
+ );
113
+ expect(getByTitle("Clear")).toBeTruthy();
114
+ });
115
+
116
+ it("should call onClear when clicked", () => {
117
+ const mockOnClear = jest.fn();
118
+ const { getByRole } = render(
119
+ <Input
120
+ id="name"
121
+ name="name"
122
+ value="mic"
123
+ type="search"
124
+ onClear={mockOnClear}
125
+ clearButtonLabel="Clear search"
126
+ />
127
+ );
128
+ fireEvent.click(getByRole("button"));
129
+ expect(mockOnClear).toHaveBeenCalled();
130
+ });
131
+
132
+ it("should allow keyboard access to and Space key triggering of the clear button", () => {
133
+ const mockOnClear = jest.fn();
134
+ const { getByRole } = render(
135
+ <Input
136
+ id="name"
137
+ name="name"
138
+ value="mic"
139
+ type="search"
140
+ onClear={mockOnClear}
141
+ clearButtonLabel="Clear search"
142
+ />
143
+ );
144
+ const input = getByRole("searchbox");
145
+ const button = getByRole("button");
146
+ userEvent.tab();
147
+ expect(input).toHaveFocus();
148
+ userEvent.tab();
149
+ expect(button).toHaveFocus();
150
+ userEvent.keyboard("{space}");
151
+ expect(mockOnClear).toHaveBeenCalled();
152
+ });
153
+
154
+ it("should allow keyboard access to and Enter key triggering of the clear button", () => {
155
+ const mockOnClear = jest.fn();
156
+ const { getByRole } = render(
157
+ <Input
158
+ id="name"
159
+ name="name"
160
+ value="mic"
161
+ type="search"
162
+ onClear={mockOnClear}
163
+ clearButtonLabel="Clear search"
164
+ />
165
+ );
166
+ const input = getByRole("searchbox");
167
+ const button = getByRole("button");
168
+ userEvent.tab();
169
+ expect(input).toHaveFocus();
170
+ userEvent.tab();
171
+ expect(button).toHaveFocus();
172
+ userEvent.keyboard("{enter}");
173
+ expect(mockOnClear).toHaveBeenCalled();
174
+ });
175
+ });
176
+
177
+ describe("Manual Input.ClearButton usage", () => {
178
+ it("should render a clear button when passed with elemAfter", () => {
179
+ const { getByRole } = render(
180
+ <Input
181
+ id="name"
182
+ name="name"
183
+ value="mic"
184
+ type="text"
185
+ elemAfter={<Input.ClearButton />}
186
+ clearButtonLabel="Clear search"
187
+ />
188
+ );
189
+ expect(getByRole("button")).toBeTruthy();
190
+ });
191
+
192
+ it("should use the fallback title if clearButtonLabel is not provided", () => {
193
+ const { getByTitle } = render(
194
+ <Input
195
+ id="name"
196
+ name="name"
197
+ value="mic"
198
+ type="text"
199
+ elemAfter={<Input.ClearButton />}
200
+ />
201
+ );
202
+ expect(getByTitle("Clear")).toBeTruthy();
203
+ });
204
+
205
+ it("should call onClear when Input.ClearButton is clicked", () => {
206
+ const mockOnClear = jest.fn();
207
+ const { getByRole } = render(
208
+ <Input
209
+ id="name"
210
+ name="name"
211
+ value="mic"
212
+ type="text"
213
+ elemAfter={<Input.ClearButton />}
214
+ onClear={mockOnClear}
215
+ clearButtonLabel="Clear search"
216
+ />
217
+ );
218
+ fireEvent.click(getByRole("button"));
219
+ expect(mockOnClear).toHaveBeenCalled();
220
+ });
221
+
222
+ it("should allow keyboard access to and Space key triggering of the clear button", () => {
223
+ const mockOnClear = jest.fn();
224
+ const { getByRole } = render(
225
+ <Input
226
+ id="name"
227
+ name="name"
228
+ value="mic"
229
+ type="text"
230
+ elemAfter={<Input.ClearButton />}
231
+ onClear={mockOnClear}
232
+ clearButtonLabel="Clear search"
233
+ />
234
+ );
235
+ const input = getByRole("textbox");
236
+ const button = getByRole("button");
237
+ userEvent.tab();
238
+ expect(input).toHaveFocus();
239
+ userEvent.tab();
240
+ expect(button).toHaveFocus();
241
+ userEvent.keyboard("{space}");
242
+ expect(mockOnClear).toHaveBeenCalled();
243
+ });
244
+
245
+ it("should allow keyboard access to and Enter key triggering of the clear button", () => {
246
+ const mockOnClear = jest.fn();
247
+ const { getByRole } = render(
248
+ <Input
249
+ id="name"
250
+ name="name"
251
+ value="mic"
252
+ type="text"
253
+ elemAfter={<Input.ClearButton />}
254
+ onClear={mockOnClear}
255
+ clearButtonLabel="Clear search"
256
+ />
257
+ );
258
+ const input = getByRole("textbox");
259
+ const button = getByRole("button");
260
+ userEvent.tab();
261
+ expect(input).toHaveFocus();
262
+ userEvent.tab();
263
+ expect(button).toHaveFocus();
264
+ userEvent.keyboard("{enter}");
265
+ expect(mockOnClear).toHaveBeenCalled();
266
+ });
267
+ });
268
+ });
269
+
72
270
  describe("autoComplete prop", () => {
73
271
  it("should not have autoComplete attribute when passed prop is not passed", () => {
74
272
  const { getByDataQaLabel } = render(<Input id="name" name="name" />);
@@ -140,7 +140,7 @@ export const Accessory: StyledComponent<any, TypeTheme, *> = styled.div`
140
140
  ${(props) =>
141
141
  props.after &&
142
142
  css`
143
- right: ${props.theme.space[350]};
143
+ right: ${props.isClearButton ? 0 : props.theme.space[350]};
144
144
  `};
145
145
  `;
146
146
 
@@ -15,6 +15,9 @@ var _Image = _interopRequireDefault(require("../Image"));
15
15
 
16
16
  var _Text = _interopRequireDefault(require("../Text"));
17
17
 
18
+ var _excluded = ["fontSize"],
19
+ _excluded2 = ["appearance", "name", "src", "variant", "type", "size", "bg", "color"];
20
+
18
21
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
19
22
 
20
23
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
@@ -27,12 +30,12 @@ function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) r
27
30
 
28
31
  var AvatarText = (0, _styledComponents.default)(function (_ref) {
29
32
  var fontSize = _ref.fontSize,
30
- rest = _objectWithoutPropertiesLoose(_ref, ["fontSize"]);
33
+ rest = _objectWithoutPropertiesLoose(_ref, _excluded);
31
34
 
32
35
  return /*#__PURE__*/React.createElement(_Text.default, rest);
33
36
  }).withConfig({
34
37
  displayName: "Avatar__AvatarText",
35
- componentId: "yx873f-0"
38
+ componentId: "sc-yx873f-0"
36
39
  })(["font-size:", "px;color:", "px;"], function (props) {
37
40
  return props.fontSize;
38
41
  }, function (props) {
@@ -40,7 +43,7 @@ var AvatarText = (0, _styledComponents.default)(function (_ref) {
40
43
  });
41
44
  var Container = (0, _styledComponents.default)(_Box.default).withConfig({
42
45
  displayName: "Avatar__Container",
43
- componentId: "yx873f-1"
46
+ componentId: "sc-yx873f-1"
44
47
  })(["", ""], function (_ref2) {
45
48
  var theme = _ref2.theme,
46
49
  type = _ref2.type,
@@ -75,7 +78,7 @@ var Avatar = function Avatar(_ref3) {
75
78
  size = _ref3$size === void 0 ? "40px" : _ref3$size,
76
79
  bg = _ref3.bg,
77
80
  color = _ref3.color,
78
- rest = _objectWithoutPropertiesLoose(_ref3, ["appearance", "name", "src", "variant", "type", "size", "bg", "color"]);
81
+ rest = _objectWithoutPropertiesLoose(_ref3, _excluded2);
79
82
 
80
83
  var _useState = (0, React.useState)(false),
81
84
  imageFailedLoading = _useState[0],
@@ -11,6 +11,8 @@ var _styles = _interopRequireDefault(require("./styles"));
11
11
 
12
12
  var _Box = _interopRequireDefault(require("../Box"));
13
13
 
14
+ var _excluded = ["children", "text", "iconName", "type", "tip", "size", "badgeColor"];
15
+
14
16
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15
17
 
16
18
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
@@ -31,7 +33,7 @@ var Badge = function Badge(_ref) {
31
33
  size = _ref$size === void 0 ? "small" : _ref$size,
32
34
  _ref$badgeColor = _ref.badgeColor,
33
35
  badgeColor = _ref$badgeColor === void 0 ? "blue" : _ref$badgeColor,
34
- rest = _objectWithoutPropertiesLoose(_ref, ["children", "text", "iconName", "type", "tip", "size", "badgeColor"]);
36
+ rest = _objectWithoutPropertiesLoose(_ref, _excluded);
35
37
 
36
38
  if (children && text) {
37
39
  throw new Error("can't use both `children` and `text` props. Text is deprecated, consider using children.");
@@ -16,7 +16,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
16
16
  // eslint-disable-next-line prettier/prettier
17
17
  var Container = _styledComponents.default.span.withConfig({
18
18
  displayName: "styles__Container",
19
- componentId: "g1g76b-0"
19
+ componentId: "sc-g1g76b-0"
20
20
  })(["font-family:", ";", ";border-radius:9999px;line-height:16px;display:inline-block;color:", ";background:", ";padding:", ";", ";"], function (p) {
21
21
  return p.theme.fontFamily;
22
22
  }, function (p) {
@@ -11,6 +11,8 @@ var _styles = _interopRequireDefault(require("./styles"));
11
11
 
12
12
  var _Box = _interopRequireDefault(require("../Box"));
13
13
 
14
+ var _excluded = ["type", "text"];
15
+
14
16
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15
17
 
16
18
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
@@ -81,7 +83,7 @@ var Banner = /*#__PURE__*/function (_React$Component) {
81
83
  var _this$props = this.props,
82
84
  type = _this$props.type,
83
85
  text = _this$props.text,
84
- rest = _objectWithoutPropertiesLoose(_this$props, ["type", "text"]);
86
+ rest = _objectWithoutPropertiesLoose(_this$props, _excluded);
85
87
 
86
88
  return /*#__PURE__*/React.createElement(_styles.default // danger needs to be properly deprecated and removed DS-1094
87
89
  , _extends({
@@ -13,7 +13,7 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj &&
13
13
 
14
14
  var Container = _styledComponents.default.div.withConfig({
15
15
  displayName: "styles__Container",
16
- componentId: "q43dr4-0"
16
+ componentId: "sc-q43dr4-0"
17
17
  })(function (props) {
18
18
  return (0, _styledComponents.css)(["display:flex;overflow:hidden;align-items:center;justify-content:space-between;color:", ";border-radius:", ";font-family:", ";", " padding:", ";border:1px solid ", ";background-color:", ";.Icon{align-self:flex-start;margin-top:3px;margin-right:", ";min-width:16px;color:", ";}a,button{font-weight:", ";color:", ";font-size:inherit;&:hover{color:", ";text-decoration:underline;}}> span:not(.Icon){display:block;}", " ", ""], props.theme.colors.text.body, props.theme.radii.outer, props.theme.fontFamily, props.theme.typography[200], props.theme.space[300], props.theme.colors.container.border[props.type], props.theme.colors.container.background[props.type], props.theme.space[400], props.theme.colors.icon[props.type], props.theme.fontWeights.semibold, props.theme.colors.text.body, props.theme.colors.text.body, _systemProps.COMMON, _systemProps.LAYOUT);
19
19
  });
@@ -11,7 +11,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
11
11
 
12
12
  var Container = _styledComponents.default.div.withConfig({
13
13
  displayName: "styles__Container",
14
- componentId: "ma8yo6-0"
14
+ componentId: "sc-ma8yo6-0"
15
15
  })(["box-sizing:border-box;font-family:", ";", " ", " ", " ", " ", " ", ""], function (_ref) {
16
16
  var theme = _ref.theme;
17
17
  return theme.fontFamily;