import type { Meta, StoryObj } from "@storybook/react-native-web-vite"; import React from "react"; import { View } from "react-native"; import { SafeAreaProvider } from "react-native-safe-area-context"; import { PortalHost } from "@rn-primitives/portal"; import { Select } from "@jobber/components-native"; import { SelectComposableBasic, SelectComposableControlledWithRef, SelectComposableCustomMarker, SelectComposableDependentOptions, SelectComposableDescription, SelectComposableDisabled, SelectComposableDisabledOptions, SelectComposableGroupedOptions, SelectComposableItemPrefixSuffix, SelectComposableLabelAbove, SelectComposableLabelInside, SelectComposableReadOnly, SelectComposableValidationFlow, } from "./docs"; const meta = { title: "Components/Selections/Select/SelectComposable", component: Select.Root, parameters: { viewport: { defaultViewport: "mobile1" }, }, decorators: [ // PortalHost outside SafeAreaProvider so the portal-based dropdown renders // and resolves its position against the page origin (see the SelectPrimitive // stories for the rationale). Apps mount one PortalHost at their root. (Story: React.ComponentType) => ( <> ), ], } satisfies Meta; export default meta; type Story = StoryObj; // ── Field & label placement ──────────────────────────────────────────────── /** Default placement: the composed `Select.Label` renders inside the trigger. */ export const Basic: Story = { render: SelectComposableBasic }; /** `labelPlacement="above"` renders the same `Select.Label` above the trigger. */ export const LabelAbove: Story = { render: SelectComposableLabelAbove }; /** `labelPlacement="inside"` renders the label as a small label inside the trigger. */ export const LabelInside: Story = { render: SelectComposableLabelInside }; /** Supporting `description` rendered below the field. */ export const Description: Story = { render: SelectComposableDescription }; /** Validation: the `error` message and critical styling clear once selected. */ export const ValidationFlow: Story = { render: SelectComposableValidationFlow }; // ── Options & content ────────────────────────────────────────────────────── /** Grouped options with group labels and a separator between groups. */ export const GroupedOptions: Story = { render: SelectComposableGroupedOptions }; /** `Select.ItemPrefix` / `Select.ItemSuffix` slots passed into `Select.Item`. */ export const ItemPrefixSuffix: Story = { render: SelectComposableItemPrefixSuffix, }; /** Override the default checkmark via the `indicator` slot prop. */ export const CustomMarker: Story = { render: SelectComposableCustomMarker }; /** Individual options can be disabled. */ export const DisabledOptions: Story = { render: SelectComposableDisabledOptions, }; // ── Behaviour & field states ─────────────────────────────────────────────── /** The trigger ref exposes imperative `open()` / `close()`. */ export const ControlledWithRef: Story = { render: SelectComposableControlledWithRef, }; /** Changing a parent selection clears a now-invalid controlled child value. */ export const ControlledValueReset: Story = { render: SelectComposableDependentOptions, }; /** Read-only: non-interactive, value rendered as text with no chevron. */ export const ReadOnly: Story = { render: SelectComposableReadOnly }; /** Disabled field. */ export const Disabled: Story = { render: SelectComposableDisabled };