@sproutsocial/racine 11.6.1 → 11.7.0-input-beta.4

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 (35) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/__flow__/Checkbox/styles.js +75 -75
  3. package/__flow__/Collapsible/index.js +3 -2
  4. package/__flow__/Image/index.js +10 -2
  5. package/__flow__/Input/index.js +47 -23
  6. package/__flow__/Input/index.stories.js +14 -0
  7. package/__flow__/Input/index.test.js +20 -0
  8. package/__flow__/Input/styles.js +2 -2
  9. package/__flow__/SegmentedControl/index.js +3 -2
  10. package/__flow__/TableCell/index.js +9 -2
  11. package/__flow__/ToggleHint/index.js +9 -2
  12. package/__flow__/systemProps/color.js +1 -2
  13. package/__flow__/themes/extendedThemes/sproutTheme/dark/theme.js +12 -0
  14. package/__flow__/themes/extendedThemes/sproutTheme/light/theme.js +12 -0
  15. package/__flow__/themes/{dark → utils}/_themed.scss +4 -3
  16. package/__flow__/types/theme.flow.js +5 -1
  17. package/__flow__/utils/responsiveProps/index.test.js +10 -2
  18. package/commonjs/Input/index.js +40 -22
  19. package/commonjs/Input/styles.js +2 -2
  20. package/commonjs/themes/extendedThemes/sproutTheme/dark/theme.js +14 -2
  21. package/commonjs/themes/extendedThemes/sproutTheme/light/theme.js +14 -2
  22. package/dist/themes/dark/_themed.scss +4 -3
  23. package/dist/themes/dark/{dark.scss → theme.scss} +1 -1
  24. package/{__flow__/themes/light → dist/themes/extendedThemes/sproutTheme/dark}/_themed.scss +4 -3
  25. package/dist/themes/extendedThemes/sproutTheme/dark/theme.scss +692 -0
  26. package/dist/themes/extendedThemes/sproutTheme/light/_themed.scss +119 -0
  27. package/dist/themes/extendedThemes/sproutTheme/light/theme.scss +692 -0
  28. package/dist/themes/light/_themed.scss +4 -3
  29. package/dist/themes/light/{light.scss → theme.scss} +1 -1
  30. package/lib/Input/index.js +40 -22
  31. package/lib/Input/styles.js +2 -2
  32. package/lib/themes/extendedThemes/sproutTheme/dark/theme.js +12 -1
  33. package/lib/themes/extendedThemes/sproutTheme/light/theme.js +12 -1
  34. package/lib/types/theme.flow.js +1 -1
  35. package/package.json +3 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Change Log
2
2
 
3
+ ## 11.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 90438e2: Add \_themed.scss for Sprout theme
8
+ - This allows usage of Sprout theme-specific values in SCSS
9
+
10
+ ## 11.6.2
11
+
12
+ ### Patch Changes
13
+
14
+ - 560c463: Added a new extended theme for the compare to period for the date picker.
15
+
3
16
  ## 11.6.1
4
17
 
5
18
  ### Patch Changes
@@ -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
- (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};
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
- &:hover:before {
216
- opacity: ${props.disabled && !props.checked ? 0 : 1};
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.disabled &&
253
+ ${props.indeterminate &&
254
+ props.checked &&
220
255
  css`
221
- opacity: 0.4;
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
- 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
- `}
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
- ${COMMON}
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
- useContext(CollapsibleContext);
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);
@@ -80,8 +80,16 @@ export default class Image extends React.Component<TypeProps, TypeState> {
80
80
  };
81
81
 
82
82
  render() {
83
- const { alt, title, onClick, onError, onLoad, src, qa, ...rest } =
84
- this.props;
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
@@ -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 <Input type="search"/> or <Input.ClearButton/>. */
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 focus management.
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?.current?.focus();
177
- this.props.onClear?.(e);
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 no onClear callback is provided or if an elemAfter prop was passed.
268
+ // Do not add a ClearButton if an elemAfter prop was passed.
244
269
  const elementAfter =
245
- type === "search" && onClear && !elemAfter ? <ClearButton /> : elemAfter;
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: !!value,
287
+ hasValue: this.state.hasValue,
263
288
  clearButtonLabel,
264
- onClear,
265
289
  size,
266
290
  }}
267
291
  >
@@ -188,6 +188,20 @@ largeSearchInput.story = {
188
188
  name: "Large Search Input",
189
189
  };
190
190
 
191
+ export const uncontrolledSearchInput = () => (
192
+ <Input
193
+ type="search"
194
+ placeholder={text("placeholder", "Please enter a value...")}
195
+ onClear={() => window.alert("Cleared!")}
196
+ clearButtonLabel={text("clearButtonLabel", "Clear search")}
197
+ ariaLabel={text("ariaLabel", "Descriptive label goes here")}
198
+ />
199
+ );
200
+
201
+ uncontrolledSearchInput.story = {
202
+ name: "Uncontrolled Search Input",
203
+ };
204
+
191
205
  export const nonSearchClearButtonInput = () => {
192
206
  return (
193
207
  <Input
@@ -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
@@ -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[350]};
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[350]};
143
+ right: ${props.isClearButton ? 0 : props.theme.space[300]};
144
144
  `};
145
145
  `;
146
146
 
@@ -17,8 +17,9 @@ type TypeSegmentedControlContext = {
17
17
  onChange: (e: SyntheticInputEvent<HTMLInputElement>) => void,
18
18
  };
19
19
 
20
- const SegmentedControlContext =
21
- React.createContext<?TypeSegmentedControlContext>(null);
20
+ const SegmentedControlContext = React.createContext<?TypeSegmentedControlContext>(
21
+ null
22
+ );
22
23
 
23
24
  type TypeSegmentedControlItemProps = {
24
25
  /** The value of this item. Should be unique among sibling items. */
@@ -22,8 +22,15 @@ export type TypeTableCell = {
22
22
  */
23
23
  export default class TableCell extends React.Component<TypeTableCell> {
24
24
  render() {
25
- const { id, content, colSpan, width, align, children, ...rest } =
26
- this.props;
25
+ const {
26
+ id,
27
+ content,
28
+ colSpan,
29
+ width,
30
+ align,
31
+ children,
32
+ ...rest
33
+ } = this.props;
27
34
 
28
35
  return (
29
36
  <Container
@@ -32,8 +32,15 @@ export default class ToggleHint extends React.Component<TypeProps> {
32
32
  };
33
33
 
34
34
  render() {
35
- const { icon, isOpen, openString, closeString, qa, className, ...rest } =
36
- this.props;
35
+ const {
36
+ icon,
37
+ isOpen,
38
+ openString,
39
+ closeString,
40
+ qa,
41
+ className,
42
+ ...rest
43
+ } = this.props;
37
44
 
38
45
  return (
39
46
  <Container
@@ -14,8 +14,7 @@ import type {
14
14
 
15
15
  // https://styled-system.com/table#color
16
16
 
17
- type TypeBackgroundColorSystemProp =
18
- TypeResponsiveBaseSystemProp<BackgroundColorProperty>;
17
+ type TypeBackgroundColorSystemProp = TypeResponsiveBaseSystemProp<BackgroundColorProperty>;
19
18
  export type TypeColorSystemProps = $ReadOnly<{|
20
19
  backgroundColor?: TypeBackgroundColorSystemProp,
21
20
  bg?: TypeBackgroundColorSystemProp,
@@ -31,11 +31,23 @@ export const navigation = {
31
31
  },
32
32
  };
33
33
 
34
+ export const datePicker = {
35
+ comparison: {
36
+ background: {
37
+ base: baseDarkTheme.colors.neutral[400],
38
+ },
39
+ text: {
40
+ base: baseDarkTheme.colors.neutral[800],
41
+ },
42
+ },
43
+ };
44
+
34
45
  const darkTheme: TypeSproutTheme = {
35
46
  ...baseDarkTheme,
36
47
  colors: {
37
48
  ...baseDarkTheme.colors,
38
49
  navigation,
50
+ datePicker,
39
51
  },
40
52
  };
41
53
 
@@ -31,11 +31,23 @@ export const navigation = {
31
31
  },
32
32
  };
33
33
 
34
+ export const datePicker = {
35
+ comparison: {
36
+ background: {
37
+ base: baseLightTheme.colors.neutral[400],
38
+ },
39
+ text: {
40
+ base: baseLightTheme.colors.neutral[800],
41
+ },
42
+ },
43
+ };
44
+
34
45
  const lightTheme: TypeSproutTheme = {
35
46
  ...baseLightTheme,
36
47
  colors: {
37
48
  ...baseLightTheme.colors,
38
49
  navigation,
50
+ datePicker,
39
51
  },
40
52
  };
41
53
 
@@ -2,12 +2,13 @@
2
2
  // This file is excluded from stylelint, because stylelint is only set up to lint styled-components at the moment.
3
3
 
4
4
  // SET-UP
5
- // This file is auto-generated based on the JS theme file, ensuring our SCSS theme variables stay in sync.
6
- @import "../../../dist/themes/dark/dark.scss";
5
+ // theme.scss is auto-generated based on the JS theme file, ensuring our SCSS theme variables stay in sync.
6
+ // _themed.scss will be copied to each theme folder in /dist, where the theme.scss file for that theme will be.
7
+ @import "./theme.scss";
7
8
 
8
9
  // In the JS theme file, the theme object is exported as "default" (i.e., using "export default"),
9
10
  // so we need to map-get "default" to access it.
10
- $theme: map-get($dark, "default");
11
+ $theme: map-get($theme, "default");
11
12
 
12
13
  // FUNCTIONS
13
14
  // This function will allow you to get any value from the theme.
@@ -13,7 +13,10 @@ import {
13
13
  } from "../themes/light/theme";
14
14
  import type { TypeColors } from "./theme.colors.flow.js";
15
15
  import type { TypeFontFamilyString } from "../themes/light/theme";
16
- import { navigation } from "../themes/extendedThemes/sproutTheme/light/theme";
16
+ import {
17
+ datePicker,
18
+ navigation,
19
+ } from "../themes/extendedThemes/sproutTheme/light/theme";
17
20
 
18
21
  export type TypeThemeUtils = {| interact: (color: string) => string |};
19
22
  export type TypeThemeMode = "light" | "dark";
@@ -53,5 +56,6 @@ export type TypeSproutTheme = {
53
56
  colors: {|
54
57
  ...$Exact<TypeColor>,
55
58
  navigation: typeof navigation,
59
+ datePicker: typeof datePicker,
56
60
  |},
57
61
  };
@@ -19,13 +19,21 @@ describe("normalizeResponsiveProp", () => {
19
19
 
20
20
  it("should handle arrays with 4 values", () => {
21
21
  expect(normalizeResponsiveProp([0, 1, 2, 3])).toMatchObject([
22
- 0, 1, 2, 3, 3,
22
+ 0,
23
+ 1,
24
+ 2,
25
+ 3,
26
+ 3,
23
27
  ]);
24
28
  });
25
29
 
26
30
  it("should handle arrays with 5 values", () => {
27
31
  expect(normalizeResponsiveProp([0, 1, 2, 3, 4])).toMatchObject([
28
- 0, 1, 2, 3, 4,
32
+ 0,
33
+ 1,
34
+ 2,
35
+ 3,
36
+ 4,
29
37
  ]);
30
38
  });
31
39
  });