@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.
- package/README.md +13 -7
- package/dist/components/Dropdown/Dropdown.types.d.ts +6 -0
- package/dist/components/Dropdown/useDropdown.d.ts +1 -1
- package/dist/components/Select/Select.stories.d.ts +1 -0
- package/dist/components/Select/Select.types.d.ts +4 -0
- package/dist/index.js +172 -172
- package/dist/index.js.map +1 -1
- package/dist/testing/index.js +1 -1
- package/dist/testing/index.js.map +1 -1
- package/package.json +3 -5
- package/src/components/Dropdown/Dropdown.context.ts +1 -0
- package/src/components/Dropdown/Dropdown.tsx +12 -4
- package/src/components/Dropdown/Dropdown.types.ts +6 -0
- package/src/components/Dropdown/DropdownTrigger.tsx +5 -4
- package/src/components/Dropdown/useDropdown.ts +9 -8
- package/src/components/Select/Select.fixtures.ts +1 -1
- package/src/components/Select/Select.stories.tsx +98 -15
- package/src/components/Select/Select.test.tsx +78 -9
- package/src/components/Select/Select.tsx +14 -8
- package/src/components/Select/Select.types.ts +5 -0
- package/src/components/Select/useSelect.ts +53 -10
- package/src/stories/startPage.stories.mdx +20 -8
- package/src/testing/SelectEvent/SelectEvent.ts +4 -0
|
@@ -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 {
|
|
195
|
-
|
|
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
|
-
[
|
|
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
|
-
|
|
296
|
-
|
|
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
|
-
|
|
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.
|
|
11
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|