@coopdigital/react 1.29.2 → 1.30.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/dist/components/Combobox/Combobox.context.js +1 -1
- package/dist/components/Combobox/Combobox.d.ts +79 -20
- package/dist/components/Combobox/Combobox.js +195 -102
- package/dist/components/Combobox/combobox-utils.js +9 -4
- package/dist/components/Combobox/index.d.ts +3 -2
- package/dist/components/Combobox/index.js +2 -2
- package/dist/components/ValidationSummary/validationsummary-utils.js +10 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -2
- package/package.json +3 -3
- package/src/components/Combobox/Combobox.context.ts +11 -12
- package/src/components/Combobox/Combobox.tsx +324 -121
- package/src/components/Combobox/combobox-utils.ts +24 -3
- package/src/components/ValidationSummary/validationsummary-utils.ts +21 -2
|
@@ -20,20 +20,41 @@ export interface ComboboxItem extends HTMLAttributes<HTMLLIElement> {
|
|
|
20
20
|
|
|
21
21
|
export type ComboboxOption = ComboboxItem | string
|
|
22
22
|
|
|
23
|
-
export const defaultFilter = (items: ComboboxItem[], inputValue: string): ComboboxItem[] =>
|
|
24
|
-
|
|
23
|
+
export const defaultFilter = (items: ComboboxItem[], inputValue: string): ComboboxItem[] =>
|
|
24
|
+
inputValue !== ""
|
|
25
25
|
? matchSorter(items, inputValue, {
|
|
26
26
|
keys: [(item) => [item.label ?? item.value]],
|
|
27
27
|
})
|
|
28
28
|
: items
|
|
29
|
-
}
|
|
30
29
|
|
|
31
30
|
export const defaultIsItemDisabled = (item: ComboboxItem): boolean => item.disabled ?? false
|
|
32
31
|
|
|
33
32
|
export const defaultItemToString = (item: ComboboxItem | null): string =>
|
|
34
33
|
item?.label ?? item?.value ?? ""
|
|
35
34
|
|
|
35
|
+
// Convert options given as strings or objects into a list of items
|
|
36
36
|
export const normalizeOptions = (options: ComboboxOption[]): ComboboxItem[] =>
|
|
37
37
|
options
|
|
38
38
|
.filter((option) => option && typeof option !== "number")
|
|
39
39
|
.map((option) => (typeof option === "string" ? { value: option } : option))
|
|
40
|
+
|
|
41
|
+
// Created once, as localeCompare with options creates a new collator on
|
|
42
|
+
// every call, which is slow when checking a long list of options
|
|
43
|
+
const collator = new Intl.Collator(undefined, { sensitivity: "base" })
|
|
44
|
+
|
|
45
|
+
// Check whether typed text matches an option, ignoring case, accents and
|
|
46
|
+
// surrounding whitespace
|
|
47
|
+
export const isMatchingText = (optionText: string, text: string): boolean =>
|
|
48
|
+
collator.compare(optionText.trim(), text.trim()) === 0
|
|
49
|
+
|
|
50
|
+
// Find the item for an option given as a value or an object. An option that
|
|
51
|
+
// is not in the list, such as a custom value, is kept as it is.
|
|
52
|
+
export const resolveOption = (
|
|
53
|
+
option: ComboboxOption | null | undefined,
|
|
54
|
+
items: ComboboxItem[]
|
|
55
|
+
): ComboboxItem | null => {
|
|
56
|
+
if (option === null || option === undefined) return null
|
|
57
|
+
|
|
58
|
+
const item = typeof option === "string" ? { value: option } : option
|
|
59
|
+
return items.find(({ value }) => value === item.value) ?? item
|
|
60
|
+
}
|
|
@@ -26,9 +26,28 @@ const getFieldKeys = (field: string, id?: string): string[] => [
|
|
|
26
26
|
field,
|
|
27
27
|
]
|
|
28
28
|
|
|
29
|
+
const VISIBLE_CONTROL = 'input:not([type="hidden"]), select, textarea'
|
|
30
|
+
|
|
31
|
+
const isHiddenInput = (el: Element): el is HTMLInputElement =>
|
|
32
|
+
el instanceof HTMLInputElement && el.type === "hidden"
|
|
33
|
+
|
|
34
|
+
// Components such as Combobox submit their value from a hidden input, which
|
|
35
|
+
// cannot be focused. Find the visible control in the closest wrapper that
|
|
36
|
+
// has one. The search stops before the form, so a hidden input placed
|
|
37
|
+
// directly in the form is not matched to an unrelated field.
|
|
38
|
+
const getVisibleControl = (form: HTMLFormElement, el: Element): Element => {
|
|
39
|
+
for (let node = el.parentElement; node && node !== form; node = node.parentElement) {
|
|
40
|
+
const control = node.querySelector(VISIBLE_CONTROL)
|
|
41
|
+
if (control) return control
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return el
|
|
45
|
+
}
|
|
46
|
+
|
|
29
47
|
// Find the field for a validation error. Try each key from getFieldKeys
|
|
30
48
|
// and return immediately if there is a match. For a RadioNodeList,
|
|
31
|
-
// return the first element only.
|
|
49
|
+
// return the first element only. A hidden input is swapped for its visible
|
|
50
|
+
// control.
|
|
32
51
|
export const getFieldElement = (
|
|
33
52
|
form: HTMLFormElement,
|
|
34
53
|
field: string,
|
|
@@ -37,7 +56,7 @@ export const getFieldElement = (
|
|
|
37
56
|
for (const key of getFieldKeys(field, id)) {
|
|
38
57
|
const item = form.elements.namedItem(key)
|
|
39
58
|
const el = item instanceof RadioNodeList ? item[0] : item
|
|
40
|
-
if (el instanceof Element) return el
|
|
59
|
+
if (el instanceof Element) return isHiddenInput(el) ? getVisibleControl(form, el) : el
|
|
41
60
|
}
|
|
42
61
|
|
|
43
62
|
return null
|