@butternutbox/pawprint-native 0.9.0 → 0.10.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@butternutbox/pawprint-native",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "type": "module",
5
5
  "description": "ButternutBox Pawprint Design System - React Native Components",
6
6
  "main": "./dist/index.cjs",
@@ -324,6 +324,13 @@ export const Platform = {
324
324
  select: (obj: any) => obj.ios ?? obj.default
325
325
  }
326
326
 
327
+ export const Keyboard = {
328
+ addListener: (_event: string, _handler: () => void) => ({
329
+ remove: () => {}
330
+ }),
331
+ dismiss: () => {}
332
+ }
333
+
327
334
  // ─── Type exports (re-exported as empty for TS compat) ───────────────────
328
335
  export type ViewProps = any
329
336
  export type TextProps = any
@@ -1,10 +1,24 @@
1
1
  import React from "react"
2
2
 
3
+ const SelectCtx = React.createContext<{
4
+ open: boolean
5
+ setOpen: (open: boolean) => void
6
+ }>({ open: false, setOpen: () => {} })
7
+
3
8
  export const Root = React.forwardRef<any, any>(
4
9
  (
5
- { value, defaultValue, onValueChange, disabled, children, ...rest },
10
+ {
11
+ value,
12
+ defaultValue,
13
+ onValueChange,
14
+ disabled,
15
+ children,
16
+ onOpenChange,
17
+ ...rest
18
+ },
6
19
  ref
7
20
  ) => {
21
+ const [open, setOpenState] = React.useState(false)
8
22
  const [internalValue, setInternalValue] = React.useState(
9
23
  value ?? defaultValue ?? null
10
24
  )
@@ -17,12 +31,19 @@ export const Root = React.forwardRef<any, any>(
17
31
  }
18
32
  }
19
33
 
34
+ const setOpen = (nextOpen: boolean) => {
35
+ setOpenState(nextOpen)
36
+ onOpenChange?.(nextOpen)
37
+ }
38
+
20
39
  return (
21
- <div ref={ref} data-disabled={disabled} {...rest}>
22
- {typeof children === "function"
23
- ? children({ value: currentValue, onChange: handleChange })
24
- : children}
25
- </div>
40
+ <SelectCtx.Provider value={{ open, setOpen }}>
41
+ <div ref={ref} data-disabled={disabled} {...rest}>
42
+ {typeof children === "function"
43
+ ? children({ value: currentValue, onChange: handleChange })
44
+ : children}
45
+ </div>
46
+ </SelectCtx.Provider>
26
47
  )
27
48
  }
28
49
  )
@@ -30,11 +51,18 @@ Root.displayName = "SelectPrimitive.Root"
30
51
 
31
52
  export const Trigger = React.forwardRef<any, any>(
32
53
  ({ asChild, children, ...rest }, ref) => {
54
+ const { open, setOpen } = React.useContext(SelectCtx)
55
+ const handlePress = () => setOpen(!open)
56
+
33
57
  if (asChild && React.isValidElement(children)) {
34
- return React.cloneElement(children, { ref, ...rest } as any)
58
+ return React.cloneElement(children, {
59
+ ref,
60
+ onPress: handlePress,
61
+ ...rest
62
+ } as any)
35
63
  }
36
64
  return (
37
- <button ref={ref} role="button" {...rest}>
65
+ <button ref={ref} role="button" onClick={handlePress} {...rest}>
38
66
  {children}
39
67
  </button>
40
68
  )
@@ -59,22 +87,20 @@ Value.displayName = "SelectPrimitive.Value"
59
87
  export const Portal = ({ children }: any) => <>{children}</>
60
88
  Portal.displayName = "SelectPrimitive.Portal"
61
89
 
62
- export const Viewport = React.forwardRef<any, any>(
63
- ({ children, ...rest }, ref) => (
64
- <div ref={ref} {...rest}>
65
- {children}
66
- </div>
67
- )
68
- )
69
- Viewport.displayName = "SelectPrimitive.Viewport"
70
-
71
90
  export const Overlay = React.forwardRef<any, any>(
72
- ({ asChild, children, ...rest }, ref) => {
73
- if (asChild && React.isValidElement(children)) {
74
- return React.cloneElement(children, { ref, ...rest } as any)
91
+ ({ onPress, closeOnPress = true, children, ...rest }, ref) => {
92
+ const { setOpen } = React.useContext(SelectCtx)
93
+ const handlePress = () => {
94
+ if (closeOnPress) setOpen(false)
95
+ onPress?.()
75
96
  }
76
97
  return (
77
- <div ref={ref} {...rest}>
98
+ <div
99
+ ref={ref}
100
+ data-testid="select-overlay"
101
+ onClick={handlePress}
102
+ {...rest}
103
+ >
78
104
  {children}
79
105
  </div>
80
106
  )
@@ -82,8 +108,19 @@ export const Overlay = React.forwardRef<any, any>(
82
108
  )
83
109
  Overlay.displayName = "SelectPrimitive.Overlay"
84
110
 
111
+ export const Viewport = React.forwardRef<any, any>(
112
+ ({ children, ...rest }, ref) => (
113
+ <div ref={ref} {...rest}>
114
+ {children}
115
+ </div>
116
+ )
117
+ )
118
+ Viewport.displayName = "SelectPrimitive.Viewport"
119
+
85
120
  export const Content = React.forwardRef<any, any>(
86
121
  ({ asChild, children, ...rest }, ref) => {
122
+ const { open } = React.useContext(SelectCtx)
123
+ if (!open) return null
87
124
  if (asChild && React.isValidElement(children)) {
88
125
  return React.cloneElement(children, { ref, ...rest } as any)
89
126
  }
@@ -13,6 +13,7 @@ import {
13
13
  Error as ErrorIcon,
14
14
  CheckCircle as SuccessIcon
15
15
  } from "@butternutbox/pawprint-icons/core"
16
+ import { resolveFont } from "../../../fonts"
16
17
  import { Icon } from "../Icon"
17
18
 
18
19
  export type InputState = "default" | "error" | "success"
@@ -61,18 +62,24 @@ const StyledInputWrapper = styled(Animated.View)<{
61
62
 
62
63
  const StyledInput = styled(TextInput)(({ theme }) => {
63
64
  const { colour, field } = theme.tokens.components.inputs
64
- const placeholderTypography = field.placeholder.default
65
+ const typography = field.placeholder.default
66
+ const { fontFamily } = resolveFont(
67
+ typography.fontFamily,
68
+ typography.fontWeight
69
+ )
70
+ const fontSize = parseTokenValue(typography.fontSize)
71
+ const letterSpacing = typography.letterSpacing?.endsWith("%")
72
+ ? (parseFloat(typography.letterSpacing) / 100) * fontSize
73
+ : parseFloat(typography.letterSpacing ?? "0")
65
74
  return {
66
75
  flex: 1,
67
76
  minWidth: 0,
68
77
  minHeight: 0,
69
78
  padding: 0,
70
79
  color: colour.field.text.default,
71
- fontSize: parseTokenValue(placeholderTypography.fontSize),
72
- fontFamily: placeholderTypography.fontFamily,
73
- fontWeight: placeholderTypography.fontWeight,
74
- lineHeight: parseTokenValue(placeholderTypography.lineHeight),
75
- letterSpacing: parseTokenValue(placeholderTypography.letterSpacing),
80
+ fontFamily,
81
+ fontSize,
82
+ letterSpacing,
76
83
  // Suppress the browser default focus outline on React Native Web —
77
84
  // the focus indicator is rendered on the wrapper instead.
78
85
  ...({
@@ -275,3 +282,27 @@ export const InputField = React.forwardRef<TextInput, InputFieldProps>(
275
282
  )
276
283
 
277
284
  InputField.displayName = "Input.Field"
285
+
286
+ /**
287
+ * Bare text input with design-system typography and colors applied.
288
+ * No container, border, or padding — use inside components that provide
289
+ * their own visual wrapper (e.g. SelectField trigger).
290
+ */
291
+ export const InputText = React.forwardRef<TextInput, TextInputProps>(
292
+ ({ placeholderTextColor, ...rest }, ref) => {
293
+ const theme = useTheme()
294
+ const { colour } = theme.tokens.components.inputs
295
+
296
+ return (
297
+ <StyledInput
298
+ ref={ref}
299
+ placeholderTextColor={
300
+ placeholderTextColor ?? colour.field.text.placeholder
301
+ }
302
+ {...rest}
303
+ />
304
+ )
305
+ }
306
+ )
307
+
308
+ InputText.displayName = "Input.Text"
@@ -2,7 +2,7 @@ export { Input } from "./Input"
2
2
  export type { InputProps } from "./Input"
3
3
  export { InputLabel } from "./InputLabel"
4
4
  export type { InputLabelProps } from "./InputLabel"
5
- export { InputField } from "./InputField"
5
+ export { InputField, InputText } from "./InputField"
6
6
  export type { InputFieldProps } from "./InputField"
7
7
  export { InputDescription } from "./InputDescription"
8
8
  export type { InputDescriptionProps } from "./InputDescription"
@@ -205,3 +205,41 @@ export const AllVariants = () => (
205
205
  />
206
206
  </View>
207
207
  )
208
+
209
+ export const WithMinQuantity = () => (
210
+ <View style={styles.column}>
211
+ <ProductDisplayCard
212
+ title="Bulk Recipe Pack"
213
+ subtext="+£2.50/pouch"
214
+ showSubtext
215
+ quantity={5}
216
+ minQuantity={5}
217
+ showQuantityPicker
218
+ showBanner
219
+ bannerType="info"
220
+ showBannerIcon={false}
221
+ banner="Minimum quantity: 5 pouches required"
222
+ image={placeholderImageUrl}
223
+ />
224
+ </View>
225
+ )
226
+
227
+ export const WithImagePress = () => {
228
+ const [pressed, setPressed] = React.useState(false)
229
+ return (
230
+ <View style={styles.column}>
231
+ <ProductDisplayCard
232
+ title="Recipe Name"
233
+ subtext="+£0.00/pouch"
234
+ showSubtext
235
+ quantity={1}
236
+ showQuantityPicker
237
+ onImagePress={() => setPressed(true)}
238
+ showBanner={pressed}
239
+ bannerType="success"
240
+ banner="Image pressed!"
241
+ image={placeholderImageUrl}
242
+ />
243
+ </View>
244
+ )
245
+ }
@@ -1,5 +1,5 @@
1
1
  import React from "react"
2
- import { View, ViewProps, Image } from "react-native"
2
+ import { View, ViewProps, Image, Pressable } from "react-native"
3
3
  import styled from "@emotion/native"
4
4
  import { Typography } from "../../atoms/Typography"
5
5
  import { NumberField } from "../../molecules/NumberField"
@@ -17,25 +17,28 @@ export type ProductDisplayCardProps = ViewProps & {
17
17
  hideQuantityButtons?: boolean
18
18
  incrementDisabled?: boolean
19
19
  decrementDisabled?: boolean
20
+ minQuantity?: number
20
21
  image?: string | React.ReactNode
21
22
  imageBackgroundColor?: string
22
23
  thumbnailWidth?: number
24
+ onImagePress?: () => void
23
25
  banner?: React.ReactNode
24
26
  showBanner?: boolean
25
27
  bannerType?: "error" | "success" | "warning" | "info"
26
28
  showBannerIcon?: boolean
27
29
  }
28
30
 
29
- const StyledCardContainer = styled(View)(({ theme }) => {
31
+ const StyledCardContainer = styled(View, {
32
+ shouldForwardProp: (prop) => prop !== "showBanner"
33
+ })<{ showBanner?: boolean }>(({ theme }) => {
30
34
  const { spacing, borderRadius } = theme.tokens.semantics.dimensions
31
35
  const { colour } = theme.tokens.semantics
32
36
  const spacingMd = parseTokenValue(spacing.md)
33
37
  const spacingXl = parseTokenValue(spacing.xl)
38
+ const radiusMd = parseTokenValue(borderRadius.md)
34
39
  return {
35
40
  flexDirection: "row",
36
41
  width: "100%",
37
- // Cap at the Figma mobile card width (can be narrower — responsive).
38
- maxWidth: 358,
39
42
  // The 148px image sets the baseline height; the card grows if the content
40
43
  // is taller (e.g. a long or translated title wrapping to multiple lines)
41
44
  // so nothing is clipped.
@@ -48,9 +51,12 @@ const StyledCardContainer = styled(View)(({ theme }) => {
48
51
  shadowOpacity: 0.05,
49
52
  shadowRadius: spacingXl - 4,
50
53
  elevation: 5,
51
- borderRadius: parseTokenValue(borderRadius.md),
54
+ borderRadius: radiusMd,
52
55
  overflow: "hidden",
53
- zIndex: 1
56
+ zIndex: 1,
57
+ // Don't apply negative margin when banner is shown; banner wrapper handles positioning
58
+ marginBottom: 0,
59
+ flex: 1
54
60
  }
55
61
  })
56
62
 
@@ -88,12 +94,16 @@ const StyledTextContainer = styled(View)(() => ({
88
94
 
89
95
  const StyledBannerWrapper = styled(View)(({ theme }) => {
90
96
  const { spacing, borderRadius } = theme.tokens.semantics.dimensions
97
+ const radiusMd = parseTokenValue(borderRadius.md)
98
+ const spacingMd = parseTokenValue(spacing.md)
91
99
  return {
92
100
  width: "100%",
93
- // Tuck the banner up behind the card by the corner radius so its square top
94
- // corners hide under the card's rounded bottom; the rest sits below.
95
- marginTop: -parseTokenValue(borderRadius.md),
96
- paddingBottom: parseTokenValue(spacing.md)
101
+ // Tuck the banner up to overlap with card bottom; the 27px notification padding
102
+ // shows the cream reveal under the card's rounded corners
103
+ marginTop: -(radiusMd + spacingMd),
104
+ marginBottom: 0,
105
+ paddingTop: spacingMd,
106
+ paddingBottom: spacingMd
97
107
  }
98
108
  })
99
109
 
@@ -166,9 +176,11 @@ export const ProductDisplayCard = React.forwardRef<
166
176
  hideQuantityButtons = false,
167
177
  incrementDisabled = false,
168
178
  decrementDisabled = false,
179
+ minQuantity = 1,
169
180
  image,
170
181
  imageBackgroundColor,
171
182
  thumbnailWidth,
183
+ onImagePress,
172
184
  banner,
173
185
  showBanner = false,
174
186
  bannerType = "info",
@@ -178,28 +190,34 @@ export const ProductDisplayCard = React.forwardRef<
178
190
  ref
179
191
  ) => {
180
192
  const handleQuantityChange = (newQuantity: number) => {
181
- if (newQuantity >= 1) {
193
+ if (newQuantity >= minQuantity) {
182
194
  onQuantityChange?.(newQuantity)
183
195
  }
184
196
  }
185
197
 
186
198
  const cardContent = (
187
- <StyledCardContainer ref={ref} {...rest}>
199
+ <StyledCardContainer
200
+ ref={ref}
201
+ showBanner={showBanner && !!banner}
202
+ {...rest}
203
+ >
188
204
  {image && (
189
- <StyledImage
190
- imageBackgroundColor={imageBackgroundColor}
191
- thumbnailWidth={thumbnailWidth}
192
- >
193
- {typeof image === "string" ? (
194
- <Image
195
- source={{ uri: image }}
196
- resizeMode="cover"
197
- style={{ flex: 1, width: "100%" }}
198
- />
199
- ) : (
200
- image
201
- )}
202
- </StyledImage>
205
+ <Pressable disabled={!onImagePress} onPress={onImagePress}>
206
+ <StyledImage
207
+ imageBackgroundColor={imageBackgroundColor}
208
+ thumbnailWidth={thumbnailWidth}
209
+ >
210
+ {typeof image === "string" ? (
211
+ <Image
212
+ source={{ uri: image }}
213
+ resizeMode="cover"
214
+ style={{ flex: 1, width: "100%" }}
215
+ />
216
+ ) : (
217
+ image
218
+ )}
219
+ </StyledImage>
220
+ </Pressable>
203
221
  )}
204
222
 
205
223
  <StyledContentArea>
@@ -228,7 +246,7 @@ export const ProductDisplayCard = React.forwardRef<
228
246
  }}
229
247
  onIncrement={() => handleQuantityChange(quantity + 1)}
230
248
  onDecrement={() => handleQuantityChange(quantity - 1)}
231
- min={1}
249
+ min={minQuantity}
232
250
  accessible
233
251
  accessibilityLabel={`Quantity: ${quantity}`}
234
252
  showIncrementButton={!hideQuantityButtons}
@@ -1,5 +1,5 @@
1
1
  import React from "react"
2
- import { View } from "react-native"
2
+ import { View, Text } from "react-native"
3
3
  import { ProductListingCard } from "./ProductListingCard"
4
4
  import { Badge } from "./Badge"
5
5
  import { ThumbsUpFilledPrimary } from "@butternutbox/pawprint-icons/marketing"
@@ -207,3 +207,67 @@ export const WithWasPrice = {
207
207
  </View>
208
208
  )
209
209
  }
210
+
211
+ export const WithStringImage = {
212
+ render: () => (
213
+ <View style={{ width: 232, alignSelf: "flex-start" }}>
214
+ <ProductListingCard
215
+ image="https://placehold.co/232x232/E8E8E8/666?text=Product"
216
+ imageAlt="Product Image"
217
+ badge={defaultBadge}
218
+ category="Daily supplements"
219
+ title="Fish Oil"
220
+ size="200ml"
221
+ price="From £7.49"
222
+ showBadge={true}
223
+ showCategory={true}
224
+ showTitle={true}
225
+ showSize={true}
226
+ showPrice={true}
227
+ />
228
+ </View>
229
+ )
230
+ }
231
+
232
+ export const WithCustomComponent = {
233
+ render: () => (
234
+ <View style={{ width: 232, alignSelf: "flex-start" }}>
235
+ <ProductListingCard
236
+ image={
237
+ <View
238
+ style={{
239
+ flex: 1,
240
+ backgroundColor: "#F5E6D3",
241
+ justifyContent: "center",
242
+ alignItems: "center"
243
+ }}
244
+ >
245
+ <View
246
+ style={{
247
+ width: 80,
248
+ height: 80,
249
+ backgroundColor: "#D4A574",
250
+ borderRadius: 40,
251
+ justifyContent: "center",
252
+ alignItems: "center"
253
+ }}
254
+ >
255
+ <Text style={{ fontSize: 40 }}>🐟</Text>
256
+ </View>
257
+ </View>
258
+ }
259
+ imageAlt="Custom component"
260
+ badge={defaultBadge}
261
+ category="Daily supplements"
262
+ title="Fish Oil"
263
+ size="200ml"
264
+ price="From £7.49"
265
+ showBadge={true}
266
+ showCategory={true}
267
+ showTitle={true}
268
+ showSize={true}
269
+ showPrice={true}
270
+ />
271
+ </View>
272
+ )
273
+ }
@@ -12,7 +12,7 @@ import { Badge } from "./Badge"
12
12
  import { Grid } from "./Grid"
13
13
 
14
14
  type ProductListingCardOwnProps = {
15
- image?: ImageSourcePropType
15
+ image?: string | React.ReactNode | ImageSourcePropType
16
16
  imageAlt?: string
17
17
  badge?: React.ReactNode
18
18
  category?: string
@@ -145,11 +145,21 @@ const _ProductListingCard = React.forwardRef<View, ProductListingCardProps>(
145
145
  <ImageContainer>
146
146
  {image && (
147
147
  <View style={{ flex: 1, width: "100%", height: "100%" }}>
148
- <StyledImage
149
- source={image}
150
- accessibilityLabel={imageAlt}
151
- accessible
152
- />
148
+ {typeof image === "string" ? (
149
+ <StyledImage
150
+ source={{ uri: image }}
151
+ accessibilityLabel={imageAlt}
152
+ accessible
153
+ />
154
+ ) : React.isValidElement(image) ? (
155
+ image
156
+ ) : (
157
+ <StyledImage
158
+ source={image as ImageSourcePropType}
159
+ accessibilityLabel={imageAlt}
160
+ accessible
161
+ />
162
+ )}
153
163
  </View>
154
164
  )}
155
165
  {showBadge && badge && <BadgeContainer>{badge}</BadgeContainer>}
@@ -1,6 +1,5 @@
1
1
  import React, { useState } from "react"
2
2
  import { View, StyleSheet } from "react-native"
3
- import { PortalHost } from "@rn-primitives/portal"
4
3
  import { SelectField, useSelectField } from "."
5
4
  import { Typography } from "../../atoms/Typography"
6
5
  import { Icon } from "../../atoms/Icon"
@@ -8,10 +7,7 @@ import { Search, Settings, Lock, Info } from "@butternutbox/pawprint-icons/core"
8
7
  import type { Option } from "@rn-primitives/select"
9
8
 
10
9
  const StoryWrapper = ({ children }: { children: React.ReactNode }) => (
11
- <>
12
- {children}
13
- <PortalHost />
14
- </>
10
+ <>{children}</>
15
11
  )
16
12
 
17
13
  export default {
@@ -304,6 +300,115 @@ export const Controlled = () => {
304
300
  )
305
301
  }
306
302
 
303
+ export const Searchable = () => (
304
+ <StoryWrapper>
305
+ <View style={styles.column}>
306
+ <View style={styles.section}>
307
+ <Typography size="sm" weight="semiBold" color="tertiary">
308
+ Searchable Select
309
+ </Typography>
310
+ <SelectField
311
+ label="Country"
312
+ placeholder="Select a country"
313
+ description="Type to filter countries"
314
+ searchable
315
+ searchPlaceholder="Search countries..."
316
+ >
317
+ <SelectField.Item value="au">Australia</SelectField.Item>
318
+ <SelectField.Item value="br">Brazil</SelectField.Item>
319
+ <SelectField.Item value="ca">Canada</SelectField.Item>
320
+ <SelectField.Item value="dk">Denmark</SelectField.Item>
321
+ <SelectField.Item value="eg">Egypt</SelectField.Item>
322
+ <SelectField.Item value="fr">France</SelectField.Item>
323
+ <SelectField.Item value="de">Germany</SelectField.Item>
324
+ <SelectField.Item value="in">India</SelectField.Item>
325
+ <SelectField.Item value="jp">Japan</SelectField.Item>
326
+ <SelectField.Item value="mx">Mexico</SelectField.Item>
327
+ <SelectField.Item value="nl">Netherlands</SelectField.Item>
328
+ <SelectField.Item value="nz">New Zealand</SelectField.Item>
329
+ <SelectField.Item value="no">Norway</SelectField.Item>
330
+ <SelectField.Item value="pl">Poland</SelectField.Item>
331
+ <SelectField.Item value="pt">Portugal</SelectField.Item>
332
+ <SelectField.Item value="es">Spain</SelectField.Item>
333
+ <SelectField.Item value="se">Sweden</SelectField.Item>
334
+ <SelectField.Item value="ch">Switzerland</SelectField.Item>
335
+ <SelectField.Item value="uk">United Kingdom</SelectField.Item>
336
+ <SelectField.Item value="us">United States</SelectField.Item>
337
+ </SelectField>
338
+ </View>
339
+ </View>
340
+ </StoryWrapper>
341
+ )
342
+
343
+ export const SearchableControlled = () => {
344
+ const [value, setValue] = useState<Option | string | null>(null)
345
+
346
+ const displayValue = (() => {
347
+ if (!value) return "(none)"
348
+ if (typeof value === "object" && "value" in value) return value.value
349
+ return String(value)
350
+ })()
351
+
352
+ return (
353
+ <StoryWrapper>
354
+ <View style={styles.column}>
355
+ <View style={styles.section}>
356
+ <Typography size="sm" weight="semiBold" color="tertiary">
357
+ Controlled value: {displayValue}
358
+ </Typography>
359
+ <SelectField
360
+ label="Country"
361
+ placeholder="Select a country"
362
+ description="Select a country — the clear button resets the controlled value"
363
+ searchable
364
+ searchPlaceholder="Search countries..."
365
+ value={value}
366
+ onValueChange={(newValue) => setValue(newValue)}
367
+ >
368
+ <SelectField.Item value="au">Australia</SelectField.Item>
369
+ <SelectField.Item value="ca">Canada</SelectField.Item>
370
+ <SelectField.Item value="fr">France</SelectField.Item>
371
+ <SelectField.Item value="de">Germany</SelectField.Item>
372
+ <SelectField.Item value="jp">Japan</SelectField.Item>
373
+ <SelectField.Item value="uk">United Kingdom</SelectField.Item>
374
+ <SelectField.Item value="us">United States</SelectField.Item>
375
+ </SelectField>
376
+ </View>
377
+ </View>
378
+ </StoryWrapper>
379
+ )
380
+ }
381
+
382
+ export const SearchableDefaultPlaceholder = () => (
383
+ <StoryWrapper>
384
+ <View style={styles.column}>
385
+ <View style={styles.section}>
386
+ <Typography size="sm" weight="semiBold" color="tertiary">
387
+ Default Search Placeholder
388
+ </Typography>
389
+ <SelectField label="Pet breed" placeholder="Select a breed" searchable>
390
+ <SelectField.Item value="labrador">
391
+ Labrador Retriever
392
+ </SelectField.Item>
393
+ <SelectField.Item value="german-shepherd">
394
+ German Shepherd
395
+ </SelectField.Item>
396
+ <SelectField.Item value="golden-retriever">
397
+ Golden Retriever
398
+ </SelectField.Item>
399
+ <SelectField.Item value="bulldog">Bulldog</SelectField.Item>
400
+ <SelectField.Item value="poodle">Poodle</SelectField.Item>
401
+ <SelectField.Item value="beagle">Beagle</SelectField.Item>
402
+ <SelectField.Item value="rottweiler">Rottweiler</SelectField.Item>
403
+ <SelectField.Item value="yorkshire-terrier">
404
+ Yorkshire Terrier
405
+ </SelectField.Item>
406
+ </SelectField>
407
+ </View>
408
+ </View>
409
+ </StoryWrapper>
410
+ )
411
+
307
412
  const styles = StyleSheet.create({
308
413
  container: {
309
414
  width: 320