@butternutbox/pawprint-native 0.0.1 → 0.1.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 +15 -15
- package/CHANGELOG.md +16 -0
- package/COMPONENT_GUIDELINES.md +111 -4
- package/dist/index.cjs +12370 -1455
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1110 -11
- package/dist/index.d.ts +1110 -11
- package/dist/index.js +12324 -1455
- package/dist/index.js.map +1 -1
- package/package.json +28 -9
- package/src/__mocks__/asset-stub.ts +1 -0
- package/src/__mocks__/emotion-native.tsx +18 -0
- package/src/__mocks__/react-native-gesture-handler.tsx +41 -0
- package/src/__mocks__/react-native-reanimated.tsx +79 -0
- package/src/__mocks__/react-native-safe-area-context.tsx +6 -0
- package/src/__mocks__/react-native-svg.tsx +27 -0
- package/src/__mocks__/react-native-worklets.tsx +11 -0
- package/src/__mocks__/react-native.tsx +338 -0
- package/src/__mocks__/rn-primitives/avatar.tsx +24 -0
- package/src/__mocks__/rn-primitives/checkbox.tsx +19 -0
- package/src/__mocks__/rn-primitives/select.tsx +116 -0
- package/src/__mocks__/rn-primitives/slider.tsx +40 -0
- package/src/__mocks__/rn-primitives/slot.tsx +30 -0
- package/src/__mocks__/rn-primitives/switch.tsx +24 -0
- package/src/__mocks__/rn-primitives/toggle.tsx +16 -0
- package/src/components/atoms/Avatar/Avatar.stories.tsx +57 -49
- package/src/components/atoms/Avatar/Avatar.test.tsx +269 -0
- package/src/components/atoms/Avatar/Avatar.tsx +68 -22
- package/src/components/atoms/Avatar/index.ts +1 -6
- package/src/components/atoms/Badge/Badge.stories.tsx +5 -29
- package/src/components/atoms/Badge/Badge.test.tsx +90 -0
- package/src/components/atoms/Button/Button.test.tsx +123 -0
- package/src/components/atoms/Button/Button.tsx +1 -1
- package/src/components/atoms/CarouselControls/CarouselControls.stories.tsx +217 -0
- package/src/components/atoms/CarouselControls/CarouselControls.tsx +127 -0
- package/src/components/atoms/CarouselControls/index.ts +2 -0
- package/src/components/atoms/Hint/Hint.test.tsx +36 -0
- package/src/components/atoms/Icon/Icon.test.tsx +98 -0
- package/src/components/atoms/Icon/Icon.tsx +5 -1
- package/src/components/atoms/IconButton/IconButton.test.tsx +101 -0
- package/src/components/atoms/Illustration/Illustration.test.tsx +55 -0
- package/src/components/atoms/Input/Input.stories.tsx +129 -86
- package/src/components/atoms/Input/Input.test.tsx +306 -0
- package/src/components/atoms/Input/Input.tsx +9 -1
- package/src/components/atoms/Input/InputField.tsx +226 -74
- package/src/components/atoms/Link/Link.test.tsx +89 -0
- package/src/components/atoms/Logo/Logo.registry.ts +30 -5
- package/src/components/atoms/Logo/Logo.stories.tsx +108 -0
- package/src/components/atoms/Logo/Logo.test.tsx +56 -0
- package/src/components/atoms/Logo/assets/BCorp.tsx +113 -0
- package/src/components/atoms/Logo/assets/ButternutFavicon.tsx +33 -0
- package/src/components/atoms/Logo/assets/ButternutPrimary.tsx +294 -0
- package/src/components/atoms/Logo/assets/ButternutTabbedBottom.tsx +294 -0
- package/src/components/atoms/Logo/assets/ButternutTabbedTop.tsx +294 -0
- package/src/components/atoms/Logo/assets/ButternutWordmark.tsx +274 -0
- package/src/components/atoms/Logo/assets/PsiBufetFavicon.tsx +45 -0
- package/src/components/atoms/Logo/assets/PsiBufetPrimary.tsx +218 -0
- package/src/components/atoms/Logo/assets/PsiBufetTabbedBottom.tsx +218 -0
- package/src/components/atoms/Logo/assets/PsiBufetTabbedTop.tsx +218 -0
- package/src/components/atoms/Logo/assets/PsiBufetWordmark.tsx +195 -0
- package/src/components/atoms/Logo/assets/index.ts +11 -0
- package/src/components/atoms/NumberInput/NumberInput.stories.tsx +183 -0
- package/src/components/atoms/NumberInput/NumberInput.test.tsx +261 -0
- package/src/components/atoms/NumberInput/NumberInput.tsx +129 -0
- package/src/components/atoms/NumberInput/NumberInputField.tsx +77 -0
- package/src/components/atoms/NumberInput/index.ts +4 -0
- package/src/components/atoms/Spinner/Spinner.test.tsx +46 -0
- package/src/components/atoms/Spinner/Spinner.tsx +14 -5
- package/src/components/atoms/Switch/Switch.test.tsx +92 -0
- package/src/components/atoms/Switch/Switch.tsx +16 -13
- package/src/components/atoms/Tag/Tag.test.tsx +70 -0
- package/src/components/atoms/TextArea/TextArea.stories.tsx +303 -0
- package/src/components/atoms/TextArea/TextArea.test.tsx +416 -0
- package/src/components/atoms/TextArea/TextArea.tsx +171 -0
- package/src/components/atoms/TextArea/TextAreaField.tsx +304 -0
- package/src/components/atoms/TextArea/TextAreaLabel.tsx +103 -0
- package/src/components/atoms/TextArea/index.ts +6 -0
- package/src/components/atoms/Typography/Typography.test.tsx +94 -0
- package/src/components/atoms/index.ts +3 -0
- package/src/components/molecules/Accordion/Accordion.stories.tsx +177 -0
- package/src/components/molecules/Accordion/Accordion.test.tsx +185 -0
- package/src/components/molecules/Accordion/Accordion.tsx +284 -0
- package/src/components/molecules/Accordion/index.ts +6 -0
- package/src/components/molecules/Animated/Animated.stories.tsx +254 -0
- package/src/components/molecules/Animated/Animated.tsx +283 -0
- package/src/components/molecules/Animated/index.ts +10 -0
- package/src/components/molecules/ButtonDock/ButtonDock.test.tsx +83 -0
- package/src/components/molecules/ButtonGroup/ButtonGroup.stories.tsx +8 -14
- package/src/components/molecules/ButtonGroup/ButtonGroup.test.tsx +73 -0
- package/src/components/molecules/ButtonGroup/ButtonGroup.tsx +25 -3
- package/src/components/molecules/Checkbox/Checkbox.stories.tsx +72 -0
- package/src/components/molecules/Checkbox/Checkbox.test.tsx +117 -0
- package/src/components/molecules/Checkbox/Checkbox.tsx +101 -95
- package/src/components/molecules/CopyField/CopyField.stories.tsx +313 -0
- package/src/components/molecules/CopyField/CopyField.test.tsx +431 -0
- package/src/components/molecules/CopyField/CopyField.tsx +156 -0
- package/src/components/molecules/CopyField/CopyFieldInput.tsx +127 -0
- package/src/components/molecules/CopyField/hooks/index.ts +1 -0
- package/src/components/molecules/CopyField/hooks/useCopyField.ts +25 -0
- package/src/components/molecules/CopyField/index.ts +4 -0
- package/src/components/molecules/DatePicker/DatePicker.stories.tsx +298 -0
- package/src/components/molecules/DatePicker/DatePicker.test.tsx +201 -0
- package/src/components/molecules/DatePicker/DatePicker.tsx +590 -0
- package/src/components/molecules/DatePicker/index.ts +2 -0
- package/src/components/molecules/Drawer/Drawer.stories.tsx +285 -0
- package/src/components/molecules/Drawer/Drawer.test.tsx +180 -0
- package/src/components/molecules/Drawer/Drawer.tsx +187 -0
- package/src/components/molecules/Drawer/DrawerBody.tsx +80 -0
- package/src/components/molecules/Drawer/DrawerClose.tsx +76 -0
- package/src/components/molecules/Drawer/DrawerContent.tsx +339 -0
- package/src/components/molecules/Drawer/DrawerContext.ts +19 -0
- package/src/components/molecules/Drawer/DrawerDescription.tsx +31 -0
- package/src/components/molecules/Drawer/DrawerDragContext.ts +11 -0
- package/src/components/molecules/Drawer/DrawerFooter.tsx +49 -0
- package/src/components/molecules/Drawer/DrawerFooterContext.ts +6 -0
- package/src/components/molecules/Drawer/DrawerGrabber.tsx +62 -0
- package/src/components/molecules/Drawer/DrawerHeader.tsx +244 -0
- package/src/components/molecules/Drawer/DrawerHeaderContext.ts +13 -0
- package/src/components/molecules/Drawer/DrawerOverlay.tsx +53 -0
- package/src/components/molecules/Drawer/DrawerTitle.tsx +32 -0
- package/src/components/molecules/Drawer/index.ts +12 -0
- package/src/components/molecules/FilterTab/FilterTab.stories.tsx +210 -0
- package/src/components/molecules/FilterTab/FilterTab.tsx +310 -0
- package/src/components/molecules/FilterTab/index.ts +2 -0
- package/src/components/molecules/MessageCard/MessageCard.stories.tsx +169 -0
- package/src/components/molecules/MessageCard/MessageCard.tsx +362 -0
- package/src/components/molecules/MessageCard/index.ts +10 -0
- package/src/components/molecules/Notification/Notification.stories.tsx +219 -0
- package/src/components/molecules/Notification/Notification.tsx +426 -0
- package/src/components/molecules/Notification/index.ts +2 -0
- package/src/components/molecules/NumberField/NumberField.stories.tsx +231 -0
- package/src/components/molecules/NumberField/NumberField.tsx +186 -0
- package/src/components/molecules/NumberField/NumberFieldInput.tsx +287 -0
- package/src/components/molecules/NumberField/index.ts +2 -0
- package/src/components/molecules/PasswordField/PasswordField.stories.tsx +362 -0
- package/src/components/molecules/PasswordField/PasswordField.test.tsx +369 -0
- package/src/components/molecules/PasswordField/PasswordField.tsx +194 -0
- package/src/components/molecules/PasswordField/PasswordFieldError.tsx +52 -0
- package/src/components/molecules/PasswordField/PasswordFieldInput.tsx +73 -0
- package/src/components/molecules/PasswordField/PasswordFieldRequirements.tsx +92 -0
- package/src/components/molecules/PasswordField/hooks/index.ts +2 -0
- package/src/components/molecules/PasswordField/hooks/usePasswordField.ts +113 -0
- package/src/components/molecules/PasswordField/index.ts +10 -0
- package/src/components/molecules/PictureSelector/PictureSelector.stories.tsx +243 -0
- package/src/components/molecules/PictureSelector/PictureSelector.tsx +313 -0
- package/src/components/molecules/PictureSelector/index.ts +5 -0
- package/src/components/molecules/Progress/Progress.stories.tsx +145 -0
- package/src/components/molecules/Progress/Progress.tsx +184 -0
- package/src/components/molecules/Progress/index.ts +2 -0
- package/src/components/molecules/Radio/Radio.test.tsx +104 -0
- package/src/components/molecules/Radio/Radio.tsx +1 -2
- package/src/components/molecules/SearchField/SearchField.stories.tsx +242 -0
- package/src/components/molecules/SearchField/SearchField.test.tsx +318 -0
- package/src/components/molecules/SearchField/SearchField.tsx +143 -0
- package/src/components/molecules/SearchField/SearchFieldInput.tsx +63 -0
- package/src/components/molecules/SearchField/hooks/index.ts +1 -0
- package/src/components/molecules/SearchField/hooks/useSearchField.ts +56 -0
- package/src/components/molecules/SearchField/index.ts +4 -0
- package/src/components/molecules/SegmentedControl/SegmentedControl.stories.tsx +31 -8
- package/src/components/molecules/SegmentedControl/SegmentedControl.test.tsx +141 -0
- package/src/components/molecules/SegmentedControl/SegmentedControl.tsx +237 -23
- package/src/components/molecules/SelectField/SelectField.stories.tsx +320 -0
- package/src/components/molecules/SelectField/SelectField.test.tsx +254 -0
- package/src/components/molecules/SelectField/SelectField.tsx +236 -0
- package/src/components/molecules/SelectField/SelectFieldContent.tsx +85 -0
- package/src/components/molecules/SelectField/SelectFieldItem.tsx +133 -0
- package/src/components/molecules/SelectField/SelectFieldTrigger.tsx +170 -0
- package/src/components/molecules/SelectField/SelectFieldValue.tsx +31 -0
- package/src/components/molecules/SelectField/hooks/index.ts +2 -0
- package/src/components/molecules/SelectField/hooks/useSelectField.ts +84 -0
- package/src/components/molecules/SelectField/index.ts +10 -0
- package/src/components/molecules/Slider/Slider.test.tsx +102 -0
- package/src/components/molecules/Slider/Slider.tsx +293 -180
- package/src/components/molecules/Tooltip/Tooltip.stories.tsx +168 -0
- package/src/components/molecules/Tooltip/Tooltip.tsx +326 -0
- package/src/components/molecules/Tooltip/index.ts +2 -0
- package/src/components/molecules/index.ts +15 -0
- package/src/test-utils.tsx +20 -0
- package/tsconfig.json +1 -1
- package/tsup.config.ts +16 -2
- package/vitest.config.ts +114 -0
- package/vitest.setup.ts +16 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { View } from "react-native"
|
|
3
|
+
import { screen } from "@testing-library/react"
|
|
4
|
+
import { describe, it, expect, vi } from "vitest"
|
|
5
|
+
import { renderWithTheme } from "../../../test-utils"
|
|
6
|
+
import { SelectField } from "./SelectField"
|
|
7
|
+
|
|
8
|
+
describe("SelectField", () => {
|
|
9
|
+
describe("when using simple props API", () => {
|
|
10
|
+
it("renders label when provided", () => {
|
|
11
|
+
renderWithTheme(
|
|
12
|
+
<SelectField label="Country" placeholder="Select a country">
|
|
13
|
+
<SelectField.Item value="uk">United Kingdom</SelectField.Item>
|
|
14
|
+
</SelectField>
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
expect(screen.getByText("Country")).toBeInTheDocument()
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it("renders description when provided", () => {
|
|
21
|
+
renderWithTheme(
|
|
22
|
+
<SelectField
|
|
23
|
+
label="Country"
|
|
24
|
+
placeholder="Select a country"
|
|
25
|
+
description="Choose your country"
|
|
26
|
+
>
|
|
27
|
+
<SelectField.Item value="uk">United Kingdom</SelectField.Item>
|
|
28
|
+
</SelectField>
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
expect(screen.getByText("Choose your country")).toBeInTheDocument()
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it("renders error message when error prop and state='error' are provided", () => {
|
|
35
|
+
renderWithTheme(
|
|
36
|
+
<SelectField
|
|
37
|
+
label="Country"
|
|
38
|
+
placeholder="Select a country"
|
|
39
|
+
state="error"
|
|
40
|
+
error="Country is required"
|
|
41
|
+
>
|
|
42
|
+
<SelectField.Item value="uk">United Kingdom</SelectField.Item>
|
|
43
|
+
</SelectField>
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
expect(screen.getByText("Country is required")).toBeInTheDocument()
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it("does not render error message when error prop is provided without state='error'", () => {
|
|
50
|
+
renderWithTheme(
|
|
51
|
+
<SelectField
|
|
52
|
+
label="Country"
|
|
53
|
+
placeholder="Select a country"
|
|
54
|
+
error="Country is required"
|
|
55
|
+
>
|
|
56
|
+
<SelectField.Item value="uk">United Kingdom</SelectField.Item>
|
|
57
|
+
</SelectField>
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
expect(screen.queryByText("Country is required")).not.toBeInTheDocument()
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it("shows (optional) when optionalText is provided", () => {
|
|
64
|
+
renderWithTheme(
|
|
65
|
+
<SelectField
|
|
66
|
+
label="Country"
|
|
67
|
+
placeholder="Select a country"
|
|
68
|
+
optionalText="(optional)"
|
|
69
|
+
>
|
|
70
|
+
<SelectField.Item value="uk">United Kingdom</SelectField.Item>
|
|
71
|
+
</SelectField>
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
expect(screen.getByText("(optional)")).toBeInTheDocument()
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it("renders the component structure", () => {
|
|
78
|
+
renderWithTheme(
|
|
79
|
+
<SelectField label="Country" placeholder="Select a country">
|
|
80
|
+
<SelectField.Item value="uk">United Kingdom</SelectField.Item>
|
|
81
|
+
</SelectField>
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
expect(screen.getByText("Country")).toBeInTheDocument()
|
|
85
|
+
})
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
describe("when using controlled mode", () => {
|
|
89
|
+
it("renders with controlled value", () => {
|
|
90
|
+
const onValueChange = vi.fn()
|
|
91
|
+
|
|
92
|
+
renderWithTheme(
|
|
93
|
+
<SelectField
|
|
94
|
+
label="Country"
|
|
95
|
+
value="uk"
|
|
96
|
+
onValueChange={onValueChange}
|
|
97
|
+
placeholder="Select a country"
|
|
98
|
+
>
|
|
99
|
+
<SelectField.Item value="uk">United Kingdom</SelectField.Item>
|
|
100
|
+
<SelectField.Item value="us">United States</SelectField.Item>
|
|
101
|
+
</SelectField>
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
expect(screen.getByText("Country")).toBeInTheDocument()
|
|
105
|
+
})
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
describe("when disabled", () => {
|
|
109
|
+
it("renders disabled state", () => {
|
|
110
|
+
renderWithTheme(
|
|
111
|
+
<SelectField label="Country" placeholder="Select a country" disabled>
|
|
112
|
+
<SelectField.Item value="uk">United Kingdom</SelectField.Item>
|
|
113
|
+
</SelectField>
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
expect(screen.getByText("Country")).toBeInTheDocument()
|
|
117
|
+
})
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
describe("validation states", () => {
|
|
121
|
+
it("shows error state with error message", () => {
|
|
122
|
+
renderWithTheme(
|
|
123
|
+
<SelectField
|
|
124
|
+
label="Country"
|
|
125
|
+
placeholder="Select a country"
|
|
126
|
+
state="error"
|
|
127
|
+
error="Country is required"
|
|
128
|
+
>
|
|
129
|
+
<SelectField.Item value="uk">United Kingdom</SelectField.Item>
|
|
130
|
+
</SelectField>
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
expect(screen.getByText("Country is required")).toBeInTheDocument()
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
it("renders success state", () => {
|
|
137
|
+
renderWithTheme(
|
|
138
|
+
<SelectField
|
|
139
|
+
label="Country"
|
|
140
|
+
placeholder="Select a country"
|
|
141
|
+
state="success"
|
|
142
|
+
>
|
|
143
|
+
<SelectField.Item value="uk">United Kingdom</SelectField.Item>
|
|
144
|
+
</SelectField>
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
expect(screen.getByText("Country")).toBeInTheDocument()
|
|
148
|
+
})
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
describe("when using compound component API", () => {
|
|
152
|
+
it("renders compound components", () => {
|
|
153
|
+
renderWithTheme(
|
|
154
|
+
<SelectField.Root>
|
|
155
|
+
<SelectField.Label>Country</SelectField.Label>
|
|
156
|
+
<SelectField.Trigger>
|
|
157
|
+
<SelectField.Value placeholder="Select a country" />
|
|
158
|
+
</SelectField.Trigger>
|
|
159
|
+
<SelectField.Content>
|
|
160
|
+
<SelectField.Item value="uk">United Kingdom</SelectField.Item>
|
|
161
|
+
</SelectField.Content>
|
|
162
|
+
<SelectField.Description>Choose your country</SelectField.Description>
|
|
163
|
+
</SelectField.Root>
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
expect(screen.getByText("Country")).toBeInTheDocument()
|
|
167
|
+
expect(screen.getByText("Choose your country")).toBeInTheDocument()
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
it("renders error in compound mode", () => {
|
|
171
|
+
renderWithTheme(
|
|
172
|
+
<SelectField.Root>
|
|
173
|
+
<SelectField.Label state="error">Country</SelectField.Label>
|
|
174
|
+
<SelectField.Trigger state="error">
|
|
175
|
+
<SelectField.Value placeholder="Select a country" />
|
|
176
|
+
</SelectField.Trigger>
|
|
177
|
+
<SelectField.Content>
|
|
178
|
+
<SelectField.Item value="uk">United Kingdom</SelectField.Item>
|
|
179
|
+
</SelectField.Content>
|
|
180
|
+
<SelectField.Error>Country is required</SelectField.Error>
|
|
181
|
+
</SelectField.Root>
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
expect(screen.getByText("Country is required")).toBeInTheDocument()
|
|
185
|
+
})
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
describe("when using with ref", () => {
|
|
189
|
+
it("forwards ref to root element", () => {
|
|
190
|
+
const ref = React.createRef<View>()
|
|
191
|
+
renderWithTheme(<SelectField ref={ref} label="Test" />)
|
|
192
|
+
expect(ref.current).toBeTruthy()
|
|
193
|
+
})
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
describe("uncontrolled mode with defaultValue", () => {
|
|
197
|
+
it("uses defaultValue as initial value", () => {
|
|
198
|
+
renderWithTheme(
|
|
199
|
+
<SelectField
|
|
200
|
+
label="Country"
|
|
201
|
+
placeholder="Select a country"
|
|
202
|
+
defaultValue="uk"
|
|
203
|
+
>
|
|
204
|
+
<SelectField.Item value="uk">United Kingdom</SelectField.Item>
|
|
205
|
+
<SelectField.Item value="us">United States</SelectField.Item>
|
|
206
|
+
</SelectField>
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
expect(screen.getByText("United Kingdom")).toBeInTheDocument()
|
|
210
|
+
})
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
describe("with items", () => {
|
|
214
|
+
it("renders items with leadingIcon", () => {
|
|
215
|
+
renderWithTheme(
|
|
216
|
+
<SelectField label="Country" placeholder="Select a country">
|
|
217
|
+
<SelectField.Item
|
|
218
|
+
value="uk"
|
|
219
|
+
leadingIcon={<div data-testid="icon">🇬🇧</div>}
|
|
220
|
+
>
|
|
221
|
+
United Kingdom
|
|
222
|
+
</SelectField.Item>
|
|
223
|
+
</SelectField>
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
expect(screen.getByTestId("icon")).toBeInTheDocument()
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
it("renders items with hintText", () => {
|
|
230
|
+
renderWithTheme(
|
|
231
|
+
<SelectField label="Country" placeholder="Select a country">
|
|
232
|
+
<SelectField.Item value="uk" hintText="Europe">
|
|
233
|
+
United Kingdom
|
|
234
|
+
</SelectField.Item>
|
|
235
|
+
</SelectField>
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
expect(screen.getByText("Europe")).toBeInTheDocument()
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
it("renders disabled items", () => {
|
|
242
|
+
renderWithTheme(
|
|
243
|
+
<SelectField label="Country" placeholder="Select a country">
|
|
244
|
+
<SelectField.Item value="uk" disabled>
|
|
245
|
+
United Kingdom
|
|
246
|
+
</SelectField.Item>
|
|
247
|
+
</SelectField>
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
const item = screen.getByText("United Kingdom")
|
|
251
|
+
expect(item).toBeInTheDocument()
|
|
252
|
+
})
|
|
253
|
+
})
|
|
254
|
+
})
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { View, ViewProps } from "react-native"
|
|
3
|
+
import styled from "@emotion/native"
|
|
4
|
+
import * as SelectPrimitive from "@rn-primitives/select"
|
|
5
|
+
import type { Option } from "@rn-primitives/select"
|
|
6
|
+
import type { InputState } from "../../atoms/Input/InputField"
|
|
7
|
+
import { InputError } from "../../atoms/Input/InputError"
|
|
8
|
+
import { InputLabel } from "../../atoms/Input/InputLabel"
|
|
9
|
+
import { InputDescription } from "../../atoms/Input/InputDescription"
|
|
10
|
+
import { SelectFieldTrigger } from "./SelectFieldTrigger"
|
|
11
|
+
import { SelectFieldValue } from "./SelectFieldValue"
|
|
12
|
+
import { SelectFieldContent } from "./SelectFieldContent"
|
|
13
|
+
import { SelectFieldItem } from "./SelectFieldItem"
|
|
14
|
+
|
|
15
|
+
const parseTokenValue = (value: string): number => parseFloat(value)
|
|
16
|
+
|
|
17
|
+
const StyledRoot = styled(View)(({ theme }) => {
|
|
18
|
+
const { spacing } = theme.tokens.components.inputs
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
gap: parseTokenValue(spacing.gap)
|
|
22
|
+
}
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
type SelectFieldOwnProps<Value = Option | string> = {
|
|
26
|
+
label?: string
|
|
27
|
+
description?: string
|
|
28
|
+
error?: string
|
|
29
|
+
state?: InputState
|
|
30
|
+
optionalText?: string
|
|
31
|
+
placeholder?: string
|
|
32
|
+
leadingIcon?: React.ReactNode
|
|
33
|
+
value?: Value | null
|
|
34
|
+
defaultValue?: Value | null
|
|
35
|
+
onValueChange?: (value: Value | null) => void
|
|
36
|
+
children?: React.ReactNode
|
|
37
|
+
disabled?: boolean
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type SelectFieldProps<Value = Option> = SelectFieldOwnProps<Value> &
|
|
41
|
+
Omit<ViewProps, keyof SelectFieldOwnProps<Value>>
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Select field component for choosing a value from a dropdown list.
|
|
45
|
+
* Built on @rn-primitives/select with design system styling.
|
|
46
|
+
* Supports a simple props API, compound component API, and useSelectField hook for state management.
|
|
47
|
+
*
|
|
48
|
+
* **Simple Props API:**
|
|
49
|
+
* @example
|
|
50
|
+
* ```tsx
|
|
51
|
+
* <SelectField
|
|
52
|
+
* label="Country"
|
|
53
|
+
* placeholder="Select a country"
|
|
54
|
+
* description="Choose your country"
|
|
55
|
+
* >
|
|
56
|
+
* <SelectField.Item value="uk">United Kingdom</SelectField.Item>
|
|
57
|
+
* <SelectField.Item value="us">United States</SelectField.Item>
|
|
58
|
+
* <SelectField.Item value="ca">Canada</SelectField.Item>
|
|
59
|
+
* </SelectField>
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* **With useSelectField Hook (Recommended):**
|
|
63
|
+
* @example
|
|
64
|
+
* ```tsx
|
|
65
|
+
* const selectProps = useSelectField({
|
|
66
|
+
* validationRule: {
|
|
67
|
+
* test: (value) => value !== null,
|
|
68
|
+
* message: "Please select an option"
|
|
69
|
+
* }
|
|
70
|
+
* })
|
|
71
|
+
*
|
|
72
|
+
* <SelectField
|
|
73
|
+
* {...selectProps}
|
|
74
|
+
* label="Category"
|
|
75
|
+
* placeholder="Select a category"
|
|
76
|
+
* >
|
|
77
|
+
* <SelectField.Item value="food">Food</SelectField.Item>
|
|
78
|
+
* <SelectField.Item value="toys">Toys</SelectField.Item>
|
|
79
|
+
* </SelectField>
|
|
80
|
+
* ```
|
|
81
|
+
*
|
|
82
|
+
* **Compound Component API:**
|
|
83
|
+
* @example
|
|
84
|
+
* ```tsx
|
|
85
|
+
* <SelectField.Root>
|
|
86
|
+
* <SelectField.Label optionalText="(optional)">Country</SelectField.Label>
|
|
87
|
+
* <SelectField.Trigger>
|
|
88
|
+
* <SelectField.Value placeholder="Select a country" />
|
|
89
|
+
* </SelectField.Trigger>
|
|
90
|
+
* <SelectField.Content>
|
|
91
|
+
* <SelectField.Item value="uk">United Kingdom</SelectField.Item>
|
|
92
|
+
* <SelectField.Item value="us">United States</SelectField.Item>
|
|
93
|
+
* </SelectField.Content>
|
|
94
|
+
* <SelectField.Description>Choose your country</SelectField.Description>
|
|
95
|
+
* <SelectField.Error>Country is required</SelectField.Error>
|
|
96
|
+
* </SelectField.Root>
|
|
97
|
+
* ```
|
|
98
|
+
*
|
|
99
|
+
* @param {string} [label] - Label text (props API)
|
|
100
|
+
* @param {string} [description] - Help text below select (props API)
|
|
101
|
+
* @param {string} [error] - Error message (props API)
|
|
102
|
+
* @param {InputState} [state] - Visual state (default, error, success)
|
|
103
|
+
* @param {string} [optionalText] - Optional text to display next to label (props API)
|
|
104
|
+
* @param {string} [placeholder] - Placeholder text when no value is selected
|
|
105
|
+
* @param {React.ReactNode} [leadingIcon] - Icon to display before the trigger text
|
|
106
|
+
* @param {Value | null} [value] - Controlled value
|
|
107
|
+
* @param {Value | null} [defaultValue] - Default value for uncontrolled mode
|
|
108
|
+
* @param {function} [onValueChange] - Value change handler
|
|
109
|
+
* @param {boolean} [disabled] - Disables the select
|
|
110
|
+
*/
|
|
111
|
+
const SelectFieldRoot = React.forwardRef<View, SelectFieldProps>(
|
|
112
|
+
(
|
|
113
|
+
{
|
|
114
|
+
label,
|
|
115
|
+
placeholder,
|
|
116
|
+
description,
|
|
117
|
+
error,
|
|
118
|
+
state = "default",
|
|
119
|
+
optionalText,
|
|
120
|
+
leadingIcon,
|
|
121
|
+
children,
|
|
122
|
+
value,
|
|
123
|
+
defaultValue,
|
|
124
|
+
onValueChange,
|
|
125
|
+
disabled,
|
|
126
|
+
...rest
|
|
127
|
+
},
|
|
128
|
+
ref
|
|
129
|
+
) => {
|
|
130
|
+
const [isOpen, setIsOpen] = React.useState(false)
|
|
131
|
+
const [internalValue, setInternalValue] = React.useState(
|
|
132
|
+
defaultValue ?? null
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
const isCompound = React.Children.toArray(children).some(
|
|
136
|
+
(child) =>
|
|
137
|
+
React.isValidElement(child) && child.type === SelectFieldContent
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
if (isCompound) {
|
|
141
|
+
return (
|
|
142
|
+
<StyledRoot ref={ref} {...rest}>
|
|
143
|
+
{children}
|
|
144
|
+
</StyledRoot>
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const handleValueChange = (newValue: Option) => {
|
|
149
|
+
const valueToSet = newValue ?? null
|
|
150
|
+
setInternalValue(valueToSet)
|
|
151
|
+
onValueChange?.(valueToSet)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const currentValue = value !== undefined ? value : internalValue
|
|
155
|
+
|
|
156
|
+
return (
|
|
157
|
+
<SelectPrimitive.Root
|
|
158
|
+
value={value ?? undefined}
|
|
159
|
+
defaultValue={defaultValue ?? undefined}
|
|
160
|
+
onValueChange={handleValueChange}
|
|
161
|
+
disabled={disabled}
|
|
162
|
+
onOpenChange={setIsOpen}
|
|
163
|
+
>
|
|
164
|
+
<StyledRoot ref={ref} {...rest}>
|
|
165
|
+
{label && (
|
|
166
|
+
<InputLabel optionalText={optionalText} state={state}>
|
|
167
|
+
{label}
|
|
168
|
+
</InputLabel>
|
|
169
|
+
)}
|
|
170
|
+
<SelectFieldTrigger
|
|
171
|
+
state={state}
|
|
172
|
+
leadingIcon={leadingIcon}
|
|
173
|
+
isOpen={isOpen}
|
|
174
|
+
>
|
|
175
|
+
<SelectFieldValue placeholder={placeholder} />
|
|
176
|
+
</SelectFieldTrigger>
|
|
177
|
+
{description && (
|
|
178
|
+
<InputDescription state={state}>{description}</InputDescription>
|
|
179
|
+
)}
|
|
180
|
+
{error && state === "error" && <InputError>{error}</InputError>}
|
|
181
|
+
</StyledRoot>
|
|
182
|
+
<SelectFieldContent>
|
|
183
|
+
{React.Children.map(children, (child) => {
|
|
184
|
+
if (React.isValidElement(child) && child.type === SelectFieldItem) {
|
|
185
|
+
// Extract value from object if needed
|
|
186
|
+
const selectedValue =
|
|
187
|
+
currentValue &&
|
|
188
|
+
typeof currentValue === "object" &&
|
|
189
|
+
"value" in currentValue
|
|
190
|
+
? (currentValue as { value: string }).value
|
|
191
|
+
: currentValue
|
|
192
|
+
|
|
193
|
+
const childProps = child.props as { value: string }
|
|
194
|
+
return React.cloneElement(
|
|
195
|
+
child as React.ReactElement<{
|
|
196
|
+
value: string
|
|
197
|
+
isSelected?: boolean
|
|
198
|
+
}>,
|
|
199
|
+
{
|
|
200
|
+
isSelected: childProps.value === selectedValue
|
|
201
|
+
}
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
return child
|
|
205
|
+
})}
|
|
206
|
+
</SelectFieldContent>
|
|
207
|
+
</SelectPrimitive.Root>
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
SelectFieldRoot.displayName = "SelectField"
|
|
213
|
+
|
|
214
|
+
type SelectFieldComponent = React.ForwardRefExoticComponent<
|
|
215
|
+
SelectFieldProps<Option | string> & React.RefAttributes<View>
|
|
216
|
+
> & {
|
|
217
|
+
Root: typeof StyledRoot
|
|
218
|
+
Label: typeof InputLabel
|
|
219
|
+
Trigger: typeof SelectFieldTrigger
|
|
220
|
+
Value: typeof SelectFieldValue
|
|
221
|
+
Content: typeof SelectFieldContent
|
|
222
|
+
Item: typeof SelectFieldItem
|
|
223
|
+
Description: typeof InputDescription
|
|
224
|
+
Error: typeof InputError
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export const SelectField = Object.assign(SelectFieldRoot, {
|
|
228
|
+
Root: StyledRoot,
|
|
229
|
+
Label: InputLabel,
|
|
230
|
+
Trigger: SelectFieldTrigger,
|
|
231
|
+
Value: SelectFieldValue,
|
|
232
|
+
Content: SelectFieldContent,
|
|
233
|
+
Item: SelectFieldItem,
|
|
234
|
+
Description: InputDescription,
|
|
235
|
+
Error: InputError
|
|
236
|
+
}) as SelectFieldComponent
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { View, ViewProps, ScrollView } from "react-native"
|
|
3
|
+
import styled from "@emotion/native"
|
|
4
|
+
import { useTheme } from "@emotion/react"
|
|
5
|
+
import * as SelectPrimitive from "@rn-primitives/select"
|
|
6
|
+
import { Slot } from "@rn-primitives/slot"
|
|
7
|
+
|
|
8
|
+
type SelectFieldContentProps = {
|
|
9
|
+
children?: React.ReactNode
|
|
10
|
+
} & Omit<ViewProps, "children">
|
|
11
|
+
|
|
12
|
+
const parseTokenValue = (value: string): number => parseFloat(value)
|
|
13
|
+
|
|
14
|
+
// TODO: Replace with dynamic positioning when @rn-primitives/select supports it on React Native
|
|
15
|
+
// These values position the dropdown below and aligned with the trigger
|
|
16
|
+
const DROPDOWN_OFFSET_X = -32 // Align with trigger left edge
|
|
17
|
+
const DROPDOWN_OFFSET_Y = -94 // Position below trigger with gap
|
|
18
|
+
|
|
19
|
+
const StyledContentShadow = styled(View)(({ theme }) => {
|
|
20
|
+
const { dropdown } = theme.tokens.components.dropdownList
|
|
21
|
+
const shadow = dropdown.list.dropshadow.default
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
borderRadius: parseTokenValue(dropdown.list.borderRadius.default),
|
|
25
|
+
shadowColor: shadow.color,
|
|
26
|
+
shadowOffset: {
|
|
27
|
+
width: parseTokenValue(shadow.offsetX),
|
|
28
|
+
height: parseTokenValue(shadow.offsetY)
|
|
29
|
+
},
|
|
30
|
+
shadowOpacity: 1,
|
|
31
|
+
shadowRadius: parseTokenValue(shadow.blur),
|
|
32
|
+
elevation: 8,
|
|
33
|
+
transform: [
|
|
34
|
+
{ translateX: DROPDOWN_OFFSET_X },
|
|
35
|
+
{ translateY: DROPDOWN_OFFSET_Y }
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
const StyledContentInner = styled(View)(({ theme }) => {
|
|
41
|
+
const { dropdown } = theme.tokens.components.dropdownList
|
|
42
|
+
const { colour } = theme.tokens.components.inputs
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
backgroundColor: colour.field.background.default,
|
|
46
|
+
borderRadius: parseTokenValue(dropdown.list.borderRadius.default),
|
|
47
|
+
minWidth: parseTokenValue(dropdown.list.size.minWidth),
|
|
48
|
+
maxHeight: parseTokenValue(dropdown.list.size.maxHeight),
|
|
49
|
+
overflow: "hidden"
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
export const SelectFieldContent = React.forwardRef<
|
|
54
|
+
View,
|
|
55
|
+
SelectFieldContentProps
|
|
56
|
+
>(({ children, ...rest }, ref) => {
|
|
57
|
+
const theme = useTheme()
|
|
58
|
+
const { spacing } = theme.tokens.components.inputs
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<SelectPrimitive.Portal>
|
|
62
|
+
<SelectPrimitive.Content
|
|
63
|
+
side="bottom"
|
|
64
|
+
align="start"
|
|
65
|
+
sideOffset={parseTokenValue(spacing.gap)}
|
|
66
|
+
alignOffset={0}
|
|
67
|
+
asChild
|
|
68
|
+
>
|
|
69
|
+
<Slot ref={ref}>
|
|
70
|
+
<StyledContentShadow {...rest}>
|
|
71
|
+
<StyledContentInner>
|
|
72
|
+
<SelectPrimitive.Viewport>
|
|
73
|
+
<ScrollView showsVerticalScrollIndicator nestedScrollEnabled>
|
|
74
|
+
{children}
|
|
75
|
+
</ScrollView>
|
|
76
|
+
</SelectPrimitive.Viewport>
|
|
77
|
+
</StyledContentInner>
|
|
78
|
+
</StyledContentShadow>
|
|
79
|
+
</Slot>
|
|
80
|
+
</SelectPrimitive.Content>
|
|
81
|
+
</SelectPrimitive.Portal>
|
|
82
|
+
)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
SelectFieldContent.displayName = "SelectField.Content"
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { Pressable, View, Text, PressableProps } from "react-native"
|
|
3
|
+
import styled from "@emotion/native"
|
|
4
|
+
import { useTheme } from "@emotion/react"
|
|
5
|
+
import * as SelectPrimitive from "@rn-primitives/select"
|
|
6
|
+
import { Slot } from "@rn-primitives/slot"
|
|
7
|
+
|
|
8
|
+
type SelectFieldItemOwnProps = {
|
|
9
|
+
value: string
|
|
10
|
+
leadingIcon?: React.ReactNode
|
|
11
|
+
hintText?: string
|
|
12
|
+
disabled?: boolean
|
|
13
|
+
isSelected?: boolean
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type SelectFieldItemProps = SelectFieldItemOwnProps &
|
|
17
|
+
Omit<PressableProps, keyof SelectFieldItemOwnProps>
|
|
18
|
+
|
|
19
|
+
const parseTokenValue = (value: string): number => parseFloat(value)
|
|
20
|
+
|
|
21
|
+
const StyledItem = styled(Pressable)<{ disabled?: boolean }>(({ theme }) => {
|
|
22
|
+
const { dropdown } = theme.tokens.components.dropdownList
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
flexDirection: "row",
|
|
26
|
+
alignItems: "center",
|
|
27
|
+
gap: parseTokenValue(dropdown.listItem.content.gap),
|
|
28
|
+
height: parseTokenValue(dropdown.listItem.size.height),
|
|
29
|
+
paddingVertical: parseTokenValue(dropdown.listItem.spacing.verticalPadding),
|
|
30
|
+
paddingHorizontal: parseTokenValue(
|
|
31
|
+
dropdown.listItem.spacing.horiztonalPadding
|
|
32
|
+
),
|
|
33
|
+
backgroundColor: dropdown.listItem.colour.background.default
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const StyledIconWrapper = styled(View)({
|
|
38
|
+
flexShrink: 0,
|
|
39
|
+
alignItems: "center",
|
|
40
|
+
justifyContent: "center"
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
const StyledContentWrapper = styled(View)(({ theme }) => {
|
|
44
|
+
const { dropdown } = theme.tokens.components.dropdownList
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
flexDirection: "row",
|
|
48
|
+
alignItems: "center",
|
|
49
|
+
gap: parseTokenValue(dropdown.listItem.content.gap),
|
|
50
|
+
flex: 1,
|
|
51
|
+
minWidth: 0
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
const StyledTextWrapper = styled(View)({
|
|
56
|
+
flex: 1,
|
|
57
|
+
minWidth: 0
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
const StyledItemText = styled(Text)(({ theme }) => {
|
|
61
|
+
const { dropdown } = theme.tokens.components.dropdownList
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
color: dropdown.listItem.colour.text.placeholder.default
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
const StyledHintText = styled(Text)(({ theme }) => {
|
|
69
|
+
const { dropdown } = theme.tokens.components.dropdownList
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
color: dropdown.listItem.colour.text.hint.default,
|
|
73
|
+
flexShrink: 0
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
export const SelectFieldItem = React.forwardRef<View, SelectFieldItemProps>(
|
|
78
|
+
(
|
|
79
|
+
{ value, leadingIcon, hintText, children, disabled, isSelected, ...rest },
|
|
80
|
+
ref
|
|
81
|
+
) => {
|
|
82
|
+
const theme = useTheme()
|
|
83
|
+
const { dropdown } = theme.tokens.components.dropdownList
|
|
84
|
+
const itemText = children as React.ReactNode
|
|
85
|
+
|
|
86
|
+
const backgroundColor = disabled
|
|
87
|
+
? dropdown.listItem.colour.background.disabled
|
|
88
|
+
: isSelected
|
|
89
|
+
? dropdown.listItem.colour.background.hover
|
|
90
|
+
: dropdown.listItem.colour.background.default
|
|
91
|
+
|
|
92
|
+
const textColor = disabled
|
|
93
|
+
? dropdown.listItem.colour.text.placeholder.disabled
|
|
94
|
+
: dropdown.listItem.colour.text.placeholder.default
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<SelectPrimitive.Item
|
|
98
|
+
label={String(children)}
|
|
99
|
+
value={value}
|
|
100
|
+
disabled={disabled}
|
|
101
|
+
asChild
|
|
102
|
+
>
|
|
103
|
+
<Slot ref={ref}>
|
|
104
|
+
<StyledItem disabled={disabled} style={{ backgroundColor }} {...rest}>
|
|
105
|
+
{leadingIcon && (
|
|
106
|
+
<StyledIconWrapper>{leadingIcon}</StyledIconWrapper>
|
|
107
|
+
)}
|
|
108
|
+
<StyledContentWrapper>
|
|
109
|
+
<StyledTextWrapper>
|
|
110
|
+
<StyledItemText style={{ color: textColor }}>
|
|
111
|
+
{itemText}
|
|
112
|
+
</StyledItemText>
|
|
113
|
+
</StyledTextWrapper>
|
|
114
|
+
{hintText && (
|
|
115
|
+
<StyledHintText
|
|
116
|
+
style={{
|
|
117
|
+
color: disabled
|
|
118
|
+
? dropdown.listItem.colour.text.hint.disabled
|
|
119
|
+
: dropdown.listItem.colour.text.hint.default
|
|
120
|
+
}}
|
|
121
|
+
>
|
|
122
|
+
{hintText}
|
|
123
|
+
</StyledHintText>
|
|
124
|
+
)}
|
|
125
|
+
</StyledContentWrapper>
|
|
126
|
+
</StyledItem>
|
|
127
|
+
</Slot>
|
|
128
|
+
</SelectPrimitive.Item>
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
SelectFieldItem.displayName = "SelectField.Item"
|