@butternutbox/pawprint-native 1.6.2 → 2.0.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/.turbo/turbo-build.log +9 -9
- package/CHANGELOG.md +60 -0
- package/COMPONENT_GUIDELINES.md +57 -1
- package/dist/index.cjs +1459 -1380
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +81 -117
- package/dist/index.d.ts +81 -117
- package/dist/index.js +1206 -1125
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/__mocks__/react-native.tsx +33 -5
- package/src/__mocks__/rn-primitives/portal.tsx +71 -0
- package/src/components/atoms/Input/InputField.tsx +12 -2
- package/src/components/molecules/Drawer/Drawer.test.tsx +54 -2
- package/src/components/molecules/Drawer/DrawerBody.tsx +2 -17
- package/src/components/molecules/Drawer/DrawerContent.tsx +49 -2
- package/src/components/molecules/Drawer/DrawerOverlay.tsx +28 -16
- package/src/components/molecules/SelectField/AsyncSearch.test.tsx +373 -0
- package/src/components/molecules/SelectField/SelectDrawerField.stories.tsx +192 -0
- package/src/components/molecules/SelectField/SelectDrawerField.test.tsx +210 -0
- package/src/components/molecules/SelectField/SelectDrawerField.tsx +245 -0
- package/src/components/molecules/SelectField/SelectField.stories.tsx +221 -421
- package/src/components/molecules/SelectField/SelectField.test.tsx +214 -662
- package/src/components/molecules/SelectField/SelectField.tsx +269 -539
- package/src/components/molecules/SelectField/SelectFieldDropdown.tsx +90 -0
- package/src/components/molecules/SelectField/SelectFieldList.tsx +88 -0
- package/src/components/molecules/SelectField/SelectFieldOption.tsx +90 -0
- package/src/components/molecules/SelectField/core/index.ts +8 -0
- package/src/components/molecules/SelectField/core/listHeight.test.ts +22 -0
- package/src/components/molecules/SelectField/core/listHeight.ts +8 -0
- package/src/components/molecules/SelectField/core/options.ts +35 -0
- package/src/components/molecules/SelectField/core/useSelectOptions.ts +116 -0
- package/src/components/molecules/SelectField/index.ts +12 -9
- package/src/index.ts +0 -1
- package/src/test-utils.tsx +5 -1
- package/vitest.config.ts +7 -0
- package/src/components/molecules/SelectField/SelectFieldContent.tsx +0 -81
- package/src/components/molecules/SelectField/SelectFieldItem.tsx +0 -133
- package/src/components/molecules/SelectField/SelectFieldTrigger.tsx +0 -188
- package/src/components/molecules/SelectField/SelectFieldValue.tsx +0 -33
- package/src/components/molecules/SelectField/hooks/index.ts +0 -2
- package/src/components/molecules/SelectField/hooks/useSelectField.ts +0 -84
- package/src/hooks/index.ts +0 -1
- package/src/hooks/useDrawerKeyboardAvoidance.ts +0 -29
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { screen, fireEvent } from "@testing-library/react"
|
|
3
|
+
import { describe, it, expect, vi } from "vitest"
|
|
4
|
+
import { renderWithTheme } from "../../../test-utils"
|
|
5
|
+
import { SelectDrawerField } from "./SelectDrawerField"
|
|
6
|
+
import type { SelectFieldOptionData } from "./core"
|
|
7
|
+
|
|
8
|
+
const COUNTRIES: SelectFieldOptionData[] = [
|
|
9
|
+
{ value: "uk", label: "United Kingdom" },
|
|
10
|
+
{ value: "us", label: "United States" },
|
|
11
|
+
{ value: "ca", label: "Canada" }
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
type Props = Partial<React.ComponentProps<typeof SelectDrawerField>>
|
|
15
|
+
|
|
16
|
+
const renderField = (props: Props = {}) => {
|
|
17
|
+
renderWithTheme(
|
|
18
|
+
<SelectDrawerField
|
|
19
|
+
label="Country"
|
|
20
|
+
placeholder="Select a country"
|
|
21
|
+
options={COUNTRIES}
|
|
22
|
+
{...props}
|
|
23
|
+
/>
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
return screen.getByRole("combobox") as HTMLInputElement
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const openDrawer = () => {
|
|
30
|
+
const trigger = screen
|
|
31
|
+
.getAllByRole("button")
|
|
32
|
+
.find((candidate) => candidate.querySelector("input"))
|
|
33
|
+
|
|
34
|
+
if (!trigger) throw new Error("field trigger not found")
|
|
35
|
+
fireEvent.click(trigger)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
describe("SelectDrawerField", () => {
|
|
39
|
+
it("renders the label and description", () => {
|
|
40
|
+
renderField({ description: "Pick one" })
|
|
41
|
+
|
|
42
|
+
expect(screen.getByText("Country")).toBeInTheDocument()
|
|
43
|
+
expect(screen.getByText("Pick one")).toBeInTheDocument()
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it("keeps the drawer closed until the field is pressed", () => {
|
|
47
|
+
renderField()
|
|
48
|
+
|
|
49
|
+
expect(screen.queryByText("United Kingdom")).not.toBeInTheDocument()
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it("opens the drawer when the field is pressed", () => {
|
|
53
|
+
renderField()
|
|
54
|
+
openDrawer()
|
|
55
|
+
|
|
56
|
+
COUNTRIES.forEach((option) => {
|
|
57
|
+
expect(screen.getByText(option.label)).toBeInTheDocument()
|
|
58
|
+
})
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it("never makes the field editable", () => {
|
|
62
|
+
const input = renderField()
|
|
63
|
+
|
|
64
|
+
expect(input).toBeDisabled()
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it("filters the list from the search input", () => {
|
|
68
|
+
renderField({ searchPlaceholder: "Search countries" })
|
|
69
|
+
openDrawer()
|
|
70
|
+
|
|
71
|
+
const search = screen.getByPlaceholderText("Search countries")
|
|
72
|
+
fireEvent.change(search, { target: { value: "united" } })
|
|
73
|
+
|
|
74
|
+
expect(screen.getByText("United Kingdom")).toBeInTheDocument()
|
|
75
|
+
expect(screen.queryByText("Canada")).not.toBeInTheDocument()
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it("shows the no-results row when nothing matches", () => {
|
|
79
|
+
renderField({
|
|
80
|
+
searchPlaceholder: "Search countries",
|
|
81
|
+
noResultsText: "No countries found"
|
|
82
|
+
})
|
|
83
|
+
openDrawer()
|
|
84
|
+
|
|
85
|
+
fireEvent.change(screen.getByPlaceholderText("Search countries"), {
|
|
86
|
+
target: { value: "zzzz" }
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
expect(screen.getByText("No countries found")).toBeInTheDocument()
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it("omits the search input when searchable is false", () => {
|
|
93
|
+
renderField({ searchable: false, searchPlaceholder: "Search countries" })
|
|
94
|
+
openDrawer()
|
|
95
|
+
|
|
96
|
+
expect(
|
|
97
|
+
screen.queryByPlaceholderText("Search countries")
|
|
98
|
+
).not.toBeInTheDocument()
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it("commits the selected value and closes the drawer", () => {
|
|
102
|
+
const onValueChange = vi.fn()
|
|
103
|
+
const input = renderField({ onValueChange })
|
|
104
|
+
openDrawer()
|
|
105
|
+
fireEvent.click(screen.getByText("Canada"))
|
|
106
|
+
|
|
107
|
+
expect(onValueChange).toHaveBeenCalledWith("ca")
|
|
108
|
+
expect(input).toHaveValue("Canada")
|
|
109
|
+
expect(screen.queryByText("United Kingdom")).not.toBeInTheDocument()
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it("does not commit a disabled option", () => {
|
|
113
|
+
const onValueChange = vi.fn()
|
|
114
|
+
renderField({
|
|
115
|
+
onValueChange,
|
|
116
|
+
options: [{ value: "uk", label: "United Kingdom", disabled: true }]
|
|
117
|
+
})
|
|
118
|
+
openDrawer()
|
|
119
|
+
fireEvent.click(screen.getByText("United Kingdom"))
|
|
120
|
+
|
|
121
|
+
expect(onValueChange).not.toHaveBeenCalled()
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it("shows the committed label and clears it", () => {
|
|
125
|
+
const onValueChange = vi.fn()
|
|
126
|
+
const input = renderField({ onValueChange, defaultValue: "ca" })
|
|
127
|
+
|
|
128
|
+
expect(input).toHaveValue("Canada")
|
|
129
|
+
|
|
130
|
+
fireEvent.click(screen.getByLabelText("Clear selection"))
|
|
131
|
+
|
|
132
|
+
expect(input).toHaveValue("")
|
|
133
|
+
expect(onValueChange).toHaveBeenCalledWith(null)
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
it("closes when the overlay is tapped", () => {
|
|
137
|
+
renderField()
|
|
138
|
+
openDrawer()
|
|
139
|
+
|
|
140
|
+
expect(screen.getByText("United Kingdom")).toBeInTheDocument()
|
|
141
|
+
|
|
142
|
+
fireEvent.click(screen.getByLabelText("Close drawer"))
|
|
143
|
+
|
|
144
|
+
expect(screen.queryByText("United Kingdom")).not.toBeInTheDocument()
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
it("shows the label of an option that is not in options", () => {
|
|
148
|
+
const onValueChange = vi.fn()
|
|
149
|
+
renderWithTheme(
|
|
150
|
+
<SelectDrawerField
|
|
151
|
+
label="Breed"
|
|
152
|
+
placeholder="Select a breed"
|
|
153
|
+
options={[]}
|
|
154
|
+
searchable={false}
|
|
155
|
+
onValueChange={onValueChange}
|
|
156
|
+
/>
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
const input = screen.getByRole("combobox") as HTMLInputElement
|
|
160
|
+
|
|
161
|
+
expect(input).toHaveValue("")
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it("keeps a selected label that options cannot resolve", () => {
|
|
165
|
+
const remote: SelectFieldOptionData[] = [
|
|
166
|
+
{ value: "lab", label: "Labrador Retriever" }
|
|
167
|
+
]
|
|
168
|
+
const onValueChange = vi.fn()
|
|
169
|
+
|
|
170
|
+
renderWithTheme(
|
|
171
|
+
<SelectDrawerField
|
|
172
|
+
label="Breed"
|
|
173
|
+
placeholder="Select a breed"
|
|
174
|
+
options={remote}
|
|
175
|
+
onValueChange={onValueChange}
|
|
176
|
+
/>
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
const input = screen.getByRole("combobox") as HTMLInputElement
|
|
180
|
+
const trigger = screen
|
|
181
|
+
.getAllByRole("button")
|
|
182
|
+
.find((candidate) => candidate.querySelector("input"))
|
|
183
|
+
if (!trigger) throw new Error("trigger not found")
|
|
184
|
+
|
|
185
|
+
fireEvent.click(trigger)
|
|
186
|
+
fireEvent.click(screen.getByText("Labrador Retriever"))
|
|
187
|
+
|
|
188
|
+
expect(onValueChange).toHaveBeenCalledWith("lab")
|
|
189
|
+
expect(input).toHaveValue("Labrador Retriever")
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
it("does not open when disabled", () => {
|
|
193
|
+
renderField({ disabled: true })
|
|
194
|
+
const trigger = screen
|
|
195
|
+
.getAllByRole("button")
|
|
196
|
+
.find((candidate) => candidate.querySelector("input"))
|
|
197
|
+
|
|
198
|
+
if (trigger) fireEvent.click(trigger)
|
|
199
|
+
|
|
200
|
+
expect(screen.queryByText("United Kingdom")).not.toBeInTheDocument()
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
it("does not change its own value when controlled", () => {
|
|
204
|
+
const input = renderField({ value: "uk", onValueChange: vi.fn() })
|
|
205
|
+
openDrawer()
|
|
206
|
+
fireEvent.click(screen.getByText("Canada"))
|
|
207
|
+
|
|
208
|
+
expect(input).toHaveValue("United Kingdom")
|
|
209
|
+
})
|
|
210
|
+
})
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { Pressable, useWindowDimensions, View, ViewProps } from "react-native"
|
|
3
|
+
import styled from "@emotion/native"
|
|
4
|
+
import { Cancel, Search } from "@butternutbox/pawprint-icons/core"
|
|
5
|
+
import { Icon } from "../../atoms/Icon"
|
|
6
|
+
import { InputField } from "../../atoms/Input/InputField"
|
|
7
|
+
import type { InputState } from "../../atoms/Input/InputField"
|
|
8
|
+
import { InputLabel } from "../../atoms/Input/InputLabel"
|
|
9
|
+
import { InputDescription } from "../../atoms/Input/InputDescription"
|
|
10
|
+
import { InputError } from "../../atoms/Input/InputError"
|
|
11
|
+
import { parseTokenValue } from "../../../utils"
|
|
12
|
+
import { Drawer } from "../Drawer"
|
|
13
|
+
import { SelectFieldList } from "./SelectFieldList"
|
|
14
|
+
import { drawerListMaxHeight, labelFor, useSelectOptions } from "./core"
|
|
15
|
+
import type { SelectFieldOptionData, SelectOptionsResolver } from "./core"
|
|
16
|
+
|
|
17
|
+
type SelectDrawerFieldOwnProps = {
|
|
18
|
+
options: SelectFieldOptionData[]
|
|
19
|
+
label?: string
|
|
20
|
+
description?: string
|
|
21
|
+
error?: string
|
|
22
|
+
state?: InputState
|
|
23
|
+
optionalText?: string
|
|
24
|
+
placeholder?: string
|
|
25
|
+
leadingIcon?: React.ReactNode
|
|
26
|
+
drawerTitle?: string
|
|
27
|
+
searchable?: boolean
|
|
28
|
+
searchPlaceholder?: string
|
|
29
|
+
noResultsText?: string
|
|
30
|
+
errorText?: string
|
|
31
|
+
onSearchQuery?: SelectOptionsResolver
|
|
32
|
+
debounceMs?: number
|
|
33
|
+
minQueryLength?: number
|
|
34
|
+
searchHintText?: string
|
|
35
|
+
clearAccessibilityLabel?: string
|
|
36
|
+
listMaxHeight?: number
|
|
37
|
+
disabled?: boolean
|
|
38
|
+
value?: string | null
|
|
39
|
+
defaultValue?: string | null
|
|
40
|
+
onValueChange?: (value: string | null) => void
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type SelectDrawerFieldProps = SelectDrawerFieldOwnProps &
|
|
44
|
+
Omit<ViewProps, keyof SelectDrawerFieldOwnProps>
|
|
45
|
+
|
|
46
|
+
const StyledRoot = styled(View)(({ theme }) => ({
|
|
47
|
+
gap: parseTokenValue(theme.tokens.components.inputs.spacing.gap)
|
|
48
|
+
}))
|
|
49
|
+
|
|
50
|
+
const StyledSearchRow = styled(View)(({ theme }) => {
|
|
51
|
+
const { colour } = theme.tokens.semantics
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
backgroundColor: colour.background.container.default,
|
|
55
|
+
paddingBottom: parseTokenValue(theme.tokens.components.inputs.spacing.gap)
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
export const SelectDrawerField = React.forwardRef<View, SelectDrawerFieldProps>(
|
|
60
|
+
(
|
|
61
|
+
{
|
|
62
|
+
options,
|
|
63
|
+
label,
|
|
64
|
+
description,
|
|
65
|
+
error,
|
|
66
|
+
state = "default",
|
|
67
|
+
optionalText,
|
|
68
|
+
placeholder,
|
|
69
|
+
leadingIcon,
|
|
70
|
+
drawerTitle,
|
|
71
|
+
searchable = true,
|
|
72
|
+
searchPlaceholder = "Search",
|
|
73
|
+
noResultsText = "No results found",
|
|
74
|
+
errorText = "Something went wrong",
|
|
75
|
+
onSearchQuery,
|
|
76
|
+
debounceMs = 300,
|
|
77
|
+
minQueryLength = 1,
|
|
78
|
+
searchHintText = "Type to search",
|
|
79
|
+
clearAccessibilityLabel = "Clear selection",
|
|
80
|
+
listMaxHeight,
|
|
81
|
+
disabled = false,
|
|
82
|
+
value,
|
|
83
|
+
defaultValue,
|
|
84
|
+
onValueChange,
|
|
85
|
+
...rest
|
|
86
|
+
},
|
|
87
|
+
ref
|
|
88
|
+
) => {
|
|
89
|
+
const { height: windowHeight } = useWindowDimensions()
|
|
90
|
+
const isControlled = value !== undefined
|
|
91
|
+
const [internalValue, setInternalValue] = React.useState<string | null>(
|
|
92
|
+
defaultValue ?? null
|
|
93
|
+
)
|
|
94
|
+
const selectedValue = isControlled ? value : internalValue
|
|
95
|
+
|
|
96
|
+
const [isOpen, setIsOpen] = React.useState(false)
|
|
97
|
+
const [searchQuery, setSearchQuery] = React.useState("")
|
|
98
|
+
|
|
99
|
+
const [lastSelected, setLastSelected] =
|
|
100
|
+
React.useState<SelectFieldOptionData | null>(null)
|
|
101
|
+
|
|
102
|
+
const selectedLabel =
|
|
103
|
+
labelFor(options, selectedValue) ||
|
|
104
|
+
(lastSelected && lastSelected.value === selectedValue
|
|
105
|
+
? lastSelected.label
|
|
106
|
+
: "")
|
|
107
|
+
|
|
108
|
+
const optionsState = useSelectOptions({
|
|
109
|
+
options,
|
|
110
|
+
query: searchQuery,
|
|
111
|
+
onSearchQuery,
|
|
112
|
+
debounceMs,
|
|
113
|
+
minQueryLength,
|
|
114
|
+
enabled: searchable
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
const commitValue = (nextValue: string | null) => {
|
|
118
|
+
if (!isControlled) setInternalValue(nextValue)
|
|
119
|
+
if (nextValue !== selectedValue) onValueChange?.(nextValue)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const handleOpenChange = (open: boolean) => {
|
|
123
|
+
setIsOpen(open)
|
|
124
|
+
if (!open) setSearchQuery("")
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const handleSelect = (option: SelectFieldOptionData) => {
|
|
128
|
+
setLastSelected(option)
|
|
129
|
+
commitValue(option.value)
|
|
130
|
+
handleOpenChange(false)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const handleClear = () => {
|
|
134
|
+
setLastSelected(null)
|
|
135
|
+
commitValue(null)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const bodyMaxHeight = drawerListMaxHeight(windowHeight, listMaxHeight)
|
|
139
|
+
|
|
140
|
+
const fieldState: InputState = error ? "error" : state
|
|
141
|
+
|
|
142
|
+
const clearAction =
|
|
143
|
+
!disabled && selectedLabel.length > 0 ? (
|
|
144
|
+
<Pressable
|
|
145
|
+
onPress={handleClear}
|
|
146
|
+
accessibilityRole="button"
|
|
147
|
+
accessibilityLabel={clearAccessibilityLabel}
|
|
148
|
+
hitSlop={8}
|
|
149
|
+
>
|
|
150
|
+
<Icon icon={Cancel} size="md" />
|
|
151
|
+
</Pressable>
|
|
152
|
+
) : undefined
|
|
153
|
+
|
|
154
|
+
const searchClearAction =
|
|
155
|
+
searchQuery.length > 0 ? (
|
|
156
|
+
<Pressable
|
|
157
|
+
onPress={() => setSearchQuery("")}
|
|
158
|
+
accessibilityRole="button"
|
|
159
|
+
accessibilityLabel="Clear search"
|
|
160
|
+
hitSlop={8}
|
|
161
|
+
>
|
|
162
|
+
<Icon icon={Cancel} size="md" />
|
|
163
|
+
</Pressable>
|
|
164
|
+
) : undefined
|
|
165
|
+
|
|
166
|
+
return (
|
|
167
|
+
<StyledRoot ref={ref} {...rest}>
|
|
168
|
+
{label && (
|
|
169
|
+
<InputLabel optionalText={optionalText} state={fieldState}>
|
|
170
|
+
{label}
|
|
171
|
+
</InputLabel>
|
|
172
|
+
)}
|
|
173
|
+
<InputField
|
|
174
|
+
value={selectedLabel}
|
|
175
|
+
placeholder={placeholder}
|
|
176
|
+
editable={false}
|
|
177
|
+
onPress={disabled ? undefined : () => setIsOpen(true)}
|
|
178
|
+
state={fieldState}
|
|
179
|
+
hideStateIcons={Boolean(clearAction)}
|
|
180
|
+
leadingIcon={leadingIcon}
|
|
181
|
+
actionIcon={clearAction}
|
|
182
|
+
accessibilityRole="combobox"
|
|
183
|
+
accessibilityState={{ disabled, expanded: isOpen }}
|
|
184
|
+
/>
|
|
185
|
+
{description && (
|
|
186
|
+
<InputDescription state={fieldState}>{description}</InputDescription>
|
|
187
|
+
)}
|
|
188
|
+
{error && <InputError>{error}</InputError>}
|
|
189
|
+
|
|
190
|
+
<Drawer.Root open={isOpen} onOpenChange={handleOpenChange}>
|
|
191
|
+
<Drawer.Portal>
|
|
192
|
+
<Drawer.Overlay />
|
|
193
|
+
<Drawer.Content>
|
|
194
|
+
<Drawer.Header>
|
|
195
|
+
<Drawer.Title>{drawerTitle ?? label ?? "Select"}</Drawer.Title>
|
|
196
|
+
<Drawer.Close />
|
|
197
|
+
</Drawer.Header>
|
|
198
|
+
{/* The host screen's outermost ScrollView must also set
|
|
199
|
+
`keyboardShouldPersistTaps`. Responder capture follows the
|
|
200
|
+
React tree root-downwards, so an outer ScrollView left at the
|
|
201
|
+
default "never" swallows taps on these rows while the search
|
|
202
|
+
input holds focus — and a Modal does not isolate its contents
|
|
203
|
+
from it, because the tree is unchanged. */}
|
|
204
|
+
<Drawer.Body
|
|
205
|
+
style={{ maxHeight: bodyMaxHeight }}
|
|
206
|
+
keyboardShouldPersistTaps="always"
|
|
207
|
+
stickyHeaderIndices={searchable ? [0] : undefined}
|
|
208
|
+
>
|
|
209
|
+
{searchable ? (
|
|
210
|
+
<StyledSearchRow>
|
|
211
|
+
<InputField
|
|
212
|
+
value={searchQuery}
|
|
213
|
+
onChangeText={setSearchQuery}
|
|
214
|
+
placeholder={searchPlaceholder}
|
|
215
|
+
leadingIcon={<Icon icon={Search} size="md" />}
|
|
216
|
+
actionIcon={searchClearAction}
|
|
217
|
+
autoFocus
|
|
218
|
+
selectTextOnFocus
|
|
219
|
+
autoCorrect={false}
|
|
220
|
+
autoCapitalize="none"
|
|
221
|
+
returnKeyType="search"
|
|
222
|
+
accessibilityLabel={searchPlaceholder}
|
|
223
|
+
/>
|
|
224
|
+
</StyledSearchRow>
|
|
225
|
+
) : null}
|
|
226
|
+
<View>
|
|
227
|
+
<SelectFieldList
|
|
228
|
+
state={optionsState}
|
|
229
|
+
selectedValue={selectedValue}
|
|
230
|
+
onSelect={handleSelect}
|
|
231
|
+
noResultsText={noResultsText}
|
|
232
|
+
errorText={errorText}
|
|
233
|
+
searchHintText={searchHintText}
|
|
234
|
+
/>
|
|
235
|
+
</View>
|
|
236
|
+
</Drawer.Body>
|
|
237
|
+
</Drawer.Content>
|
|
238
|
+
</Drawer.Portal>
|
|
239
|
+
</Drawer.Root>
|
|
240
|
+
</StyledRoot>
|
|
241
|
+
)
|
|
242
|
+
}
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
SelectDrawerField.displayName = "SelectDrawerField"
|