@hyphen/hyphen-components 2.15.5 → 2.16.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.
@@ -7,7 +7,7 @@ export declare const BREAKPOINTS: Breakpoint[];
7
7
  export declare const BASE_COLOR_OPTIONS: ("black" | "white" | "magenta" | "primary-50" | "primary-100" | "primary-200" | "primary-300" | "primary-400" | "primary-500" | "primary-600" | "primary-700" | "primary-800" | "primary-900" | "green-50" | "green-100" | "green-200" | "green-300" | "green-400" | "green-500" | "green-600" | "green-700" | "green-800" | "green-900" | "blue-50" | "blue-100" | "blue-200" | "blue-300" | "blue-400" | "blue-500" | "blue-600" | "blue-700" | "blue-800" | "blue-900" | "purple-50" | "purple-100" | "purple-200" | "purple-300" | "purple-400" | "purple-500" | "purple-600" | "purple-700" | "purple-800" | "purple-900" | "yellow-50" | "yellow-100" | "yellow-200" | "yellow-300" | "yellow-400" | "yellow-500" | "yellow-600" | "yellow-700" | "yellow-800" | "yellow-900" | "red-50" | "red-100" | "red-200" | "red-300" | "red-400" | "red-500" | "red-600" | "red-700" | "red-800" | "red-900" | "grey-50" | "grey-100" | "grey-200" | "grey-300" | "grey-400" | "grey-500" | "grey-600" | "grey-700" | "grey-800" | "grey-900")[];
8
8
  export declare const BASE_COLOR_NAMES: ("black" | "white" | "magenta" | "primary" | "green" | "blue" | "purple" | "yellow" | "red" | "grey")[];
9
9
  export declare const FONT_COLOR_OPTIONS: ("info" | "base" | "white" | "grey" | "secondary" | "tertiary" | "inverse" | "disabled" | "success" | "success-disabled" | "warn" | "warn-disabled" | "danger" | "danger-input" | "danger-disabled" | "button-primary" | "button-primary-hover" | "button-primary-active" | "button-secondary" | "button-secondary-hover" | "button-secondary-active" | "button-tertiary" | "button-tertiary-hover" | "button-tertiary-active" | "button-danger" | "button-danger-hover" | "button-danger-active" | "toast" | "toast-error" | "placeholder" | "brand-yellow" | "brand-orange" | "brand-magenta" | "brand-dark-grey" | "brand-cyan" | "brand-pink" | "brand-light-purple" | "brand-medium-purple" | "brand-dark-purple")[];
10
- export declare const BACKGROUND_COLOR_OPTIONS: ("info" | "white" | "primary" | "secondary" | "tertiary" | "inverse" | "success" | "danger" | "button-primary" | "button-primary-hover" | "button-primary-active" | "button-secondary" | "button-secondary-hover" | "button-secondary-active" | "button-tertiary" | "button-tertiary-hover" | "button-tertiary-active" | "button-danger" | "button-danger-hover" | "button-danger-active" | "toast" | "toast-error" | "brand-yellow" | "brand-orange" | "brand-magenta" | "brand-dark-grey" | "brand-cyan" | "brand-pink" | "brand-light-purple" | "brand-medium-purple" | "brand-dark-purple" | "error" | "warning" | "transparent" | "brand-gradient" | "brand-gradient-purple" | "brand-gradient-cyan" | "chart-1" | "chart-2" | "chart-3")[];
10
+ export declare const BACKGROUND_COLOR_OPTIONS: ("info" | "white" | "primary" | "secondary" | "tertiary" | "inverse" | "disabled" | "success" | "danger" | "button-primary" | "button-primary-hover" | "button-primary-active" | "button-secondary" | "button-secondary-hover" | "button-secondary-active" | "button-tertiary" | "button-tertiary-hover" | "button-tertiary-active" | "button-danger" | "button-danger-hover" | "button-danger-active" | "toast" | "toast-error" | "brand-yellow" | "brand-orange" | "brand-magenta" | "brand-dark-grey" | "brand-cyan" | "brand-pink" | "brand-light-purple" | "brand-medium-purple" | "brand-dark-purple" | "error" | "warning" | "transparent" | "brand-gradient" | "brand-gradient-purple" | "brand-gradient-cyan" | "chart-1" | "chart-2" | "chart-3")[];
11
11
  export declare const BORDER_COLOR_OPTIONS: ("info" | "inverse" | "danger" | "button-secondary" | "button-secondary-hover" | "button-secondary-active" | "button-tertiary" | "button-tertiary-hover" | "button-tertiary-active" | "toast" | "toast-error" | "brand-yellow" | "brand-orange" | "brand-magenta" | "brand-dark-grey" | "brand-cyan" | "brand-pink" | "brand-light-purple" | "brand-medium-purple" | "brand-dark-purple" | "warning" | "default" | "subtle" | "hover" | "active")[];
12
12
  export declare const FONT_SIZE_OPTIONS: ("xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "base" | "2xs" | "4xl" | "5xl" | "6xl" | "7xl")[];
13
13
  export declare const FONT_FAMILY_OPTIONS: ("monospace" | "body" | "brand")[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyphen/hyphen-components",
3
- "version": "2.15.5",
3
+ "version": "2.16.0",
4
4
  "license": "MIT",
5
5
  "author": {
6
6
  "name": "@hyphen"
@@ -119,7 +119,7 @@
119
119
  "@rollup/rollup-linux-x64-gnu": "^4.21.2"
120
120
  },
121
121
  "dependencies": {
122
- "@hyphen/hyphen-design-tokens": "^4.9.0",
122
+ "@hyphen/hyphen-design-tokens": "^4.10.0",
123
123
  "@popperjs/core": "^2.11.8",
124
124
  "@types/react-modal": "^3.16.3",
125
125
  "classnames": "^2.5.1",
@@ -9,6 +9,6 @@
9
9
  }
10
10
 
11
11
  &.disabled {
12
- color: var(--color-base-grey-300);
12
+ color: var(--color-font-disabled);
13
13
  }
14
14
  }
@@ -69,6 +69,18 @@ Use the `isMulti` and `isCreatable` props to allow the selection of multiple opt
69
69
 
70
70
  <Canvas of={Stories.MultiSelectCreatable} />
71
71
 
72
+ ## Async Select
73
+
74
+ Use the `options` prop to pass a function that returns a promise to load options asynchronously.
75
+
76
+ <Canvas of={Stories.AsyncSelect} />
77
+
78
+ ## Async Creatable Select
79
+
80
+ Use the `isCreatable` prop to allow the creation of new options and the `options` prop to pass a function that returns a promise to load options asynchronously.
81
+
82
+ <Canvas of={Stories.AsyncCreatableSelect} />
83
+
72
84
  ## Autofocus
73
85
 
74
86
  Use the `autoFocus` prop to autofocus a SelectInput.
@@ -278,6 +278,7 @@
278
278
  &:global(.react-select__control--is-disabled) {
279
279
  :global(.react-select__dropdown-indicator) {
280
280
  color: var(--color-font-disabled);
281
+ background-color: var(--color-background-disabled);
281
282
  }
282
283
  }
283
284
  }
@@ -314,8 +315,6 @@
314
315
  }
315
316
  }
316
317
 
317
-
318
-
319
318
  &.size-sm {
320
319
  @extend %size-sm;
321
320
  }
@@ -364,10 +363,10 @@
364
363
 
365
364
  &.disabled {
366
365
  :global(.react-select__control--is-disabled) {
367
- background-color: var(
368
- --form-control-background-color-disabled,
369
- var(--INTERNAL_form-control-background-color-disabled)
370
- );
366
+ background-color: var(--color-background-disabled);
367
+ }
368
+ &:hover {
369
+ cursor: not-allowed;
371
370
  }
372
371
  }
373
372
  }
@@ -143,6 +143,92 @@ export const CreatableSelect = () => {
143
143
  );
144
144
  };
145
145
 
146
+ export const AsyncSelect = () => {
147
+ type Option = {
148
+ value: string;
149
+ label: string;
150
+ };
151
+
152
+ const [value, setValue] = useState(null);
153
+ const options = [
154
+ { value: 'chocolate', label: 'Chocolate' },
155
+ { value: 'strawberry', label: 'Strawberry' },
156
+ ];
157
+
158
+ const filterOptions = (inputValue: string) => {
159
+ return options.filter((i) =>
160
+ i.label.toLowerCase().includes(inputValue.toLowerCase())
161
+ );
162
+ };
163
+ const loadOptions = (inputValue: string) => {
164
+ return new Promise<Option[]>((resolve) => {
165
+ setTimeout(() => {
166
+ resolve(filterOptions(inputValue));
167
+ }, 1000);
168
+ });
169
+ };
170
+
171
+ return (
172
+ <div style={{ height: '200px' }}>
173
+ <SelectInput
174
+ id="asyncSelect"
175
+ label="Label"
176
+ value={value}
177
+ // @ts-ignore - TS is not recognizing the value as a valid option
178
+ onChange={(event) => setValue(event.target.value)}
179
+ options={loadOptions}
180
+ defaultOptions
181
+ cacheOptions
182
+ isAsync
183
+ />
184
+ </div>
185
+ );
186
+ };
187
+
188
+ export const AsyncCreatableSelect = () => {
189
+ type Option = {
190
+ value: string;
191
+ label: string;
192
+ };
193
+
194
+ const options = [
195
+ { value: 'chocolate', label: 'Chocolate' },
196
+ { value: 'strawberry', label: 'Strawberry' },
197
+ ];
198
+
199
+ const [value, setValue] = useState(null);
200
+
201
+ const filterOptions = (inputValue: string) => {
202
+ return options.filter((i) =>
203
+ i.label.toLowerCase().includes(inputValue.toLowerCase())
204
+ );
205
+ };
206
+ const loadOptions = (inputValue: string) => {
207
+ return new Promise<Option[]>((resolve) => {
208
+ setTimeout(() => {
209
+ resolve(filterOptions(inputValue));
210
+ }, 1000);
211
+ });
212
+ };
213
+
214
+ return (
215
+ <div style={{ height: '200px' }}>
216
+ <SelectInput
217
+ id="asyncCreateSelect"
218
+ label="Label"
219
+ value={value}
220
+ // @ts-ignore - TS is not recognizing the value as a valid option
221
+ onChange={(event) => setValue(event.target.value)}
222
+ options={loadOptions}
223
+ isCreatable
224
+ defaultOptions
225
+ cacheOptions
226
+ isAsync
227
+ />
228
+ </div>
229
+ );
230
+ };
231
+
146
232
  export const MultiSelect = () => {
147
233
  const [value, setValue] = useState(null);
148
234
  const options = [
@@ -1,5 +1,11 @@
1
1
  import React from 'react';
2
- import { render, fireEvent, screen, Matcher } from '@testing-library/react';
2
+ import {
3
+ render,
4
+ fireEvent,
5
+ screen,
6
+ Matcher,
7
+ waitFor,
8
+ } from '@testing-library/react';
3
9
  import selectEvent from 'react-select-event';
4
10
  import { SelectInput, TextInputSize } from './SelectInput';
5
11
 
@@ -200,6 +206,31 @@ describe('SelectInput', () => {
200
206
  });
201
207
  });
202
208
 
209
+ describe('Async select', () => {
210
+ it('it renders with loading state', async () => {
211
+ const mockedHandleChange = jest.fn();
212
+ const loadOptions = jest.fn(() => Promise.resolve([]));
213
+
214
+ const { getByLabelText } = render(
215
+ <SelectInput
216
+ id="testId"
217
+ onChange={mockedHandleChange}
218
+ label="Select Label"
219
+ options={loadOptions}
220
+ value={''}
221
+ isAsync
222
+ />
223
+ );
224
+
225
+ const inputElement = getByLabelText('Select Label');
226
+ fireEvent.change(inputElement, { target: { value: 'test' } });
227
+
228
+ await waitFor(() => {
229
+ expect(loadOptions).toHaveBeenCalledTimes(1);
230
+ });
231
+ });
232
+ });
233
+
203
234
  describe('Multi select, no selection', () => {
204
235
  it('it renders input with a label, and with a default placeholder', () => {
205
236
  const mockedHandleChange = jest.fn();
@@ -1,4 +1,4 @@
1
- import React, { FC, FocusEvent, ReactNode, FocusEventHandler } from 'react';
1
+ import React, { FocusEvent, ReactNode, FocusEventHandler } from 'react';
2
2
  import classNames from 'classnames';
3
3
  import Select, {
4
4
  components,
@@ -6,6 +6,8 @@ import Select, {
6
6
  OptionsOrGroups,
7
7
  OnChangeValue,
8
8
  } from 'react-select';
9
+ import AsyncCreatableSelect from 'react-select/async-creatable';
10
+ import AsyncSelect from 'react-select/async';
9
11
  import CreatableSelect from 'react-select/creatable';
10
12
  import { ResponsiveProp } from '../../types';
11
13
  import { generateResponsiveClasses, Z_INDEX_VALUES } from '../../lib';
@@ -49,10 +51,6 @@ export interface SelectInputProps {
49
51
  * Callback function to call on change event.
50
52
  */
51
53
  onChange: (event: SimulatedEventPayloadType) => void;
52
- /**
53
- * Options for dropdown list.
54
- */
55
- options: SelectInputOptions;
56
54
  /**
57
55
  * The value(s) of select.
58
56
  */
@@ -135,31 +133,67 @@ export interface SelectInputProps {
135
133
  [x: string]: any; // eslint-disable-line
136
134
  }
137
135
 
138
- export const SelectInput: FC<SelectInputProps> = ({
139
- id,
140
- label,
141
- onChange,
142
- options,
143
- value,
144
- autoFocus = false,
145
- className = '',
146
- error = false,
147
- helpText,
148
- hideLabel = false,
149
- isClearable = false,
150
- isCreatable = false,
151
- isDisabled = false,
152
- isMulti = false,
153
- isRequired = false,
154
- menuPortalTarget = null,
155
- name = '',
156
- onFocus = null,
157
- onBlur = null,
158
- placeholder = undefined,
159
- requiredIndicator = ' *',
160
- size = 'md',
161
- ...restProps
162
- }) => {
136
+ type AsyncOptions = (inputValue: string) => Promise<SelectInputOptions>;
137
+ type AsyncSelectInputProps = SelectInputProps & {
138
+ /**
139
+ * Load the input asynchronously.
140
+ */
141
+ isAsync: true;
142
+ /**
143
+ * Load options asynchronously.
144
+ */
145
+ options: AsyncOptions;
146
+ /**
147
+ * If cacheOptions is passed, then the loaded data will be cached.
148
+ */
149
+ cacheOptions?: boolean;
150
+ /**
151
+ * The default set of options to show before the user starts searching.
152
+ */
153
+ defaultOptions?: boolean;
154
+ };
155
+
156
+ type SyncSelectInputProps = SelectInputProps & {
157
+ /**
158
+ * Load the input synchronously.
159
+ */
160
+ isAsync?: false;
161
+ /**
162
+ * Options for dropdown list.
163
+ */
164
+ options: SelectInputOptions;
165
+ };
166
+
167
+ export function SelectInput(props: AsyncSelectInputProps): JSX.Element;
168
+ export function SelectInput(props: SyncSelectInputProps): JSX.Element;
169
+ export function SelectInput(props: SelectInputProps): JSX.Element {
170
+ const {
171
+ id,
172
+ label,
173
+ onChange,
174
+ options,
175
+ value,
176
+ autoFocus = false,
177
+ className = '',
178
+ error = false,
179
+ helpText,
180
+ hideLabel = false,
181
+ isClearable = false,
182
+ isAsync = false,
183
+ isCreatable = false,
184
+ isDisabled = false,
185
+ isMulti = false,
186
+ isRequired = false,
187
+ menuPortalTarget = null,
188
+ name = '',
189
+ onFocus = null,
190
+ onBlur = null,
191
+ placeholder = undefined,
192
+ requiredIndicator = ' *',
193
+ size = 'md',
194
+ ...restProps
195
+ } = props;
196
+
163
197
  const handleChange = (values: OnChangeValue<SelectInputOptions, boolean>) => {
164
198
  const simulatedEventPayloadType: SimulatedEventPayloadType = {
165
199
  target: {
@@ -212,13 +246,21 @@ export const SelectInput: FC<SelectInputProps> = ({
212
246
  </components.ClearIndicator>
213
247
  );
214
248
 
215
- const Component = isCreatable ? CreatableSelect : Select;
249
+ const Component =
250
+ isCreatable && isAsync
251
+ ? AsyncCreatableSelect
252
+ : isCreatable
253
+ ? CreatableSelect
254
+ : isAsync
255
+ ? AsyncSelect
256
+ : Select;
216
257
 
217
258
  return (
218
259
  <Box width="100%" className={wrapperClasses}>
219
260
  {label && !hideLabel && <FormLabel {...labelProps}>{label}</FormLabel>}
220
261
  <Component
221
262
  {...restProps}
263
+ {...(isAsync ? { loadOptions: options } : { options })}
222
264
  inputId={id}
223
265
  aria-label={label}
224
266
  components={{ ClearIndicator }}
@@ -232,7 +274,6 @@ export const SelectInput: FC<SelectInputProps> = ({
232
274
  menuPortalTarget={menuPortalTarget}
233
275
  name={name}
234
276
  autoFocus={autoFocus}
235
- options={options}
236
277
  onChange={handleChange}
237
278
  onFocus={handleFocus}
238
279
  onBlur={handleBlur}
@@ -249,4 +290,4 @@ export const SelectInput: FC<SelectInputProps> = ({
249
290
  )}
250
291
  </Box>
251
292
  );
252
- };
293
+ }
@@ -165,18 +165,9 @@
165
165
 
166
166
  // https://stackoverflow.com/questions/262158/disabled-input-text-color-on-ios
167
167
  %disabled-base {
168
- background-color: var(
169
- --form-control-background-color-disabled,
170
- var(--INTERNAL_form-control-background-color-disabled)
171
- );
172
- color: var(
173
- --form-control-font-color-disabled,
174
- var(--INTERNAL_form-control-font-color-disabled)
175
- );
176
- -webkit-text-fill-color: var(
177
- --form-control-font-color-disabled,
178
- var(--INTERNAL_form-control-font-color-disabled)
179
- );
168
+ background-color: var(--color-background-disabled);
169
+ color: var(--color-font-disabled);
170
+ -webkit-text-fill-color: var(--color-font-disabled);
180
171
  opacity: 1;
181
172
 
182
173
  &:hover {
@@ -1,14 +1,9 @@
1
1
  @import '@hyphen/hyphen-design-tokens/build/scss/variables';
2
2
 
3
3
  %disabled-base {
4
- background-color: var(
5
- --form-control-background-color-disabled,
6
- var(--INTERNAL_form-control-background-color-disabled)
7
- );
8
- color: var(
9
- --form-control-font-color-disabled,
10
- var(--INTERNAL_form-control-font-color-disabled)
11
- );
4
+ background-color: var(--color-background-disabled);
5
+ color: var(--color-font-disabled);
6
+ -webkit-text-fill-color: var(--color-font-disabled);
12
7
  opacity: 1;
13
8
 
14
9
  &:hover {
@@ -200,18 +200,9 @@
200
200
 
201
201
  // https://stackoverflow.com/questions/262158/disabled-input-text-color-on-ios
202
202
  %disabled-base {
203
- background-color: var(
204
- --form-control-background-color-disabled,
205
- var(--INTERNAL_form-control-background-color-disabled)
206
- );
207
- color: var(
208
- --form-control-font-color-disabled,
209
- var(--INTERNAL_form-control-font-color-disabled)
210
- );
211
- -webkit-text-fill-color: var(
212
- --form-control-font-color-disabled,
213
- var(--INTERNAL_form-control-font-color-disabled)
214
- );
203
+ background-color: var(--color-background-disabled);
204
+ color: var(--color-font-disabled);
205
+ -webkit-text-fill-color: var(--color-font-disabled);
215
206
  opacity: 1;
216
207
 
217
208
  &:hover {
@@ -193,18 +193,9 @@
193
193
 
194
194
  // https://stackoverflow.com/questions/262158/disabled-input-text-color-on-ios
195
195
  %disabled-base {
196
- background-color: var(
197
- --form-control-background-color-disabled,
198
- var(--INTERNAL_form-control-background-color-disabled)
199
- );
200
- color: var(
201
- --form-control-font-color-disabled,
202
- var(--INTERNAL_form-control-font-color-disabled)
203
- );
204
- -webkit-text-fill-color: var(
205
- --form-control-font-color-disabled,
206
- var(--INTERNAL_form-control-font-color-disabled)
207
- );
196
+ background-color: var(--color-background-disabled);
197
+ color: var(--color-font-disabled);
198
+ -webkit-text-fill-color: var(--color-font-disabled);
208
199
  opacity: 1;
209
200
 
210
201
  &:hover {
@@ -91,14 +91,10 @@
91
91
  }
92
92
 
93
93
  %disabled-base {
94
- background-color: var(
95
- --form-control-background-color-disabled,
96
- var(--INTERNAL_form-control-background-color-disabled)
97
- );
98
- color: var(
99
- --form-control-font-color-disabled,
100
- var(--INTERNAL_form-control-font-color-disabled)
101
- );
94
+ background-color: var(--color-background-disabled);
95
+ color: var(--color-font-disabled);
96
+ -webkit-text-fill-color: var(--color-font-disabled);
97
+ opacity: 1;
102
98
 
103
99
  &:hover {
104
100
  cursor: not-allowed;
@@ -142,18 +142,9 @@
142
142
 
143
143
  // https://stackoverflow.com/questions/262158/disabled-input-text-color-on-ios
144
144
  %disabled-base {
145
- background-color: var(
146
- --form-control-background-color-disabled,
147
- var(--INTERNAL_form-control-background-color-disabled)
148
- );
149
- color: var(
150
- --form-control-font-color-disabled,
151
- var(--INTERNAL_form-control-font-color-disabled)
152
- );
153
- -webkit-text-fill-color: var(
154
- --form-control-font-color-disabled,
155
- var(--INTERNAL_form-control-font-color-disabled)
156
- );
145
+ background-color: var(--color-background-disabled);
146
+ color: var(--color-font-disabled);
147
+ -webkit-text-fill-color: var(--color-font-disabled);
157
148
  opacity: 1;
158
149
 
159
150
  &:hover {