@loadsmart/loadsmart-ui 5.7.0 → 5.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.
@@ -67,6 +67,8 @@ export type Components = {
67
67
  CreatableOption?: CreatableOptionType
68
68
  }
69
69
 
70
+ export type CreateOptionPosition = 'first' | 'last'
71
+
70
72
  export interface SelectProps extends DropdownProps {
71
73
  name: string
72
74
  placeholder?: string
@@ -79,6 +81,8 @@ export interface SelectProps extends DropdownProps {
79
81
  onChange?: (event: EventLike<Option | Option[] | null>) => void
80
82
  onCreate?: (query: string) => Promise<void | Option> | void | Option
81
83
  onQueryChange?: (e: ChangeEvent<HTMLInputElement>) => void
84
+ isValidNewOption?: ((query: string) => boolean) | boolean
85
+ createOptionPosition?: CreateOptionPosition
82
86
  }
83
87
 
84
88
  export type SelectOptionProps = {
@@ -123,6 +127,7 @@ export type useSelectReturn = {
123
127
  }
124
128
  getCreatebleProps: () => CreatableProps
125
129
  isCreatable: () => boolean
130
+ createOptionPosition?: CreateOptionPosition
126
131
  }
127
132
 
128
133
  export type useSelectExternalReturn = {
@@ -3,6 +3,7 @@ import { isFunction } from '@loadsmart/utils-function'
3
3
  import { isNil } from '@loadsmart/utils-object'
4
4
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
5
5
 
6
+ import isEmpty from 'utils/toolset/isEmpty'
6
7
  import { useDropdown } from 'components/Dropdown'
7
8
  import { useDidMount } from 'hooks/useDidMount'
8
9
  import { useFocusTrap } from 'hooks/useFocusTrap'
@@ -191,8 +192,18 @@ function useOptions<T = any>(props: { datasources: SelectDatasource<T>[]; adapte
191
192
  */
192
193
  function useSelect(props: SelectProps): useSelectReturn {
193
194
  const didMount = useDidMount()
194
- const { multiple, onQueryChange, onChange, onCreate, id, name, disabled = false, onBlur } = props
195
- const dropdown = useDropdown(props)
195
+ const {
196
+ multiple,
197
+ onQueryChange,
198
+ onChange,
199
+ onCreate,
200
+ id,
201
+ name,
202
+ disabled = false,
203
+ onBlur,
204
+ isValidNewOption = (query: string) => Boolean(query),
205
+ createOptionPosition = 'last',
206
+ } = props
196
207
 
197
208
  // eslint-disable-next-line react-hooks/exhaustive-deps
198
209
  const datasources = useMemo<SelectDatasource<any>[]>(() => getDatasources(props), [
@@ -221,11 +232,26 @@ function useSelect(props: SelectProps): useSelectReturn {
221
232
  },
222
233
  })
223
234
 
235
+ const [queryTyped, setQueryTyped] = useState(false)
224
236
  const [query, setQuery] = useState<string>(
225
237
  getDisplayValue(adapters, selectable.selected, multiple)
226
238
  )
227
239
  const options = useOptions({ datasources, adapters })
228
240
 
241
+ const expandDisabled = useMemo(
242
+ () => !query.length && isEmpty(options.get()) && isEmpty(selectable.selected),
243
+ [query, options, selectable.selected]
244
+ )
245
+
246
+ const dropdown = useDropdown({ ...props, expandDisabled })
247
+
248
+ useEffect(() => {
249
+ if (queryTyped) {
250
+ options.fetch(query)
251
+ dropdown.expand()
252
+ }
253
+ }, [query, queryTyped])
254
+
229
255
  const getSelectableOption = useCallback(
230
256
  function getSelectableOption(option: Option) {
231
257
  const adapter = getAdapter(adapters, option._type)
@@ -274,9 +300,19 @@ function useSelect(props: SelectProps): useSelectReturn {
274
300
 
275
301
  onBlur?.(event)
276
302
  },
303
+ expandDisabled,
277
304
  }
278
305
  },
279
- [adapters, dropdown.expanded, dropdown.toggle, multiple, options, selectable.selected, onBlur]
306
+ [
307
+ adapters,
308
+ dropdown.expanded,
309
+ dropdown.toggle,
310
+ multiple,
311
+ options,
312
+ selectable.selected,
313
+ onBlur,
314
+ expandDisabled,
315
+ ]
280
316
  )
281
317
 
282
318
  const getTriggerProps = useCallback(
@@ -292,15 +328,13 @@ function useSelect(props: SelectProps): useSelectReturn {
292
328
  onChange(e: ChangeEvent<HTMLInputElement>) {
293
329
  onQueryChange?.(e)
294
330
 
295
- const query = e.target.value
296
- setQuery(query)
297
- dropdown.expand()
298
- options.fetch(query)
331
+ setQuery(e.target.value)
332
+ setQueryTyped(true)
299
333
  },
300
334
  onFocus: TriggerOnFocusHandler,
301
335
  }
302
336
  },
303
- [id, query, onQueryChange, dropdown, options]
337
+ [id, query, onQueryChange, dropdown, options, selectable.selected]
304
338
  )
305
339
 
306
340
  const getClearProps = useCallback(
@@ -396,14 +430,22 @@ function useSelect(props: SelectProps): useSelectReturn {
396
430
  )
397
431
  }
398
432
 
433
+ function getIsValidNewOption() {
434
+ if (isFunction(isValidNewOption)) {
435
+ return isValidNewOption(query)
436
+ }
437
+
438
+ return isValidNewOption
439
+ }
440
+
399
441
  return (
400
442
  isFunction(onCreate) &&
401
- Boolean(query) &&
443
+ getIsValidNewOption() &&
402
444
  options.status === 'queried' &&
403
445
  !isQueryEqualAnOption()
404
446
  )
405
447
  },
406
- [getSelectableOption, onCreate, options, query, selectable.selected]
448
+ [getSelectableOption, isValidNewOption, onCreate, options, query, selectable.selected]
407
449
  )
408
450
 
409
451
  useEffect(
@@ -461,6 +503,7 @@ function useSelect(props: SelectProps): useSelectReturn {
461
503
  getDropdownProps,
462
504
  getCreatebleProps,
463
505
  isCreatable,
506
+ createOptionPosition,
464
507
  }
465
508
  }
466
509
 
@@ -2,20 +2,26 @@ import { Meta } from '@storybook/addon-docs/blocks'
2
2
 
3
3
  <Meta title="Getting started page" />
4
4
 
5
-
6
5
  Miranda UI is a [React](https://reactjs.org/) components library. It works with [Styled Components](https://www.styled-components.com).
7
6
 
8
7
  ## Steps to install
9
8
 
10
- 1. Install node 11
11
- 2. Install dependencies: `yarn`
9
+ 1. You need to have node 16 or the latest LTS version installed;
10
+
11
+ One option is to use [NVM](https://github.com/nvm-sh/nvm). You can run `nvm use` or set it to run automatically in a directory with a .nvmrc file.
12
+
13
+ 1. Install dependencies: `yarn`
14
+
15
+ If you don't have yarn, follow yarn [installation docs](https://classic.yarnpkg.com/lang/en/docs/install/#windows-stable)
12
16
 
13
17
  ## Usage
14
18
 
15
19
  ```sh
16
20
  yarn add @loadsmart/loadsmart-ui
17
21
  ```
22
+
18
23
  or
24
+
19
25
  ```sh
20
26
  npm install @loadsmart/loadsmart-ui
21
27
  ```
@@ -43,7 +49,9 @@ Install all dependencies:
43
49
  ```bash
44
50
  yarn
45
51
  ```
46
- or
52
+
53
+ or
54
+
47
55
  ```bash
48
56
  npm i
49
57
  ```
@@ -53,7 +61,9 @@ Run the application - you'll be able to see all components documentation in Stor
53
61
  ```bash
54
62
  yarn dev
55
63
  ```
56
- or
64
+
65
+ or
66
+
57
67
  ```bash
58
68
  npm run dev
59
69
  ```
@@ -65,7 +75,9 @@ To run tests:
65
75
  ```bash
66
76
  yarn test
67
77
  ```
68
- or
78
+
79
+ or
80
+
69
81
  ```bash
70
82
  npm run test
71
83
  ```
@@ -77,7 +89,7 @@ When creating a new one, you must add its new folder following this pattern.
77
89
 
78
90
  It's also essential to include the `.stories` and `.test` files to cover both documentation and quality standards.
79
91
 
80
-
81
- <br /><br />
92
+ <br />
93
+ <br />
82
94
 
83
95
  We use [`semantic-release`](https://github.com/semantic-release/) to evaluate our commits and trigger automatic release to NPM. For that, please follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/), so your changes will properly be evaluated and published if that's the case.
@@ -42,6 +42,10 @@ async function expand(input: HTMLElement): Promise<void> {
42
42
  return
43
43
  }
44
44
 
45
+ await waitFor(() => {
46
+ expect(within(selectContainer).getByTestId('select-trigger-handle')).toBeEnabled()
47
+ })
48
+
45
49
  await act(async () => {
46
50
  userEvent.click(within(selectContainer).getByTestId('select-trigger-handle'))
47
51