nextui/packages/components/select/stories/select.stories.tsx
WK c59b2a9fe1
feat: 2.8.0 beta changes (#5473)
* ci(changesets): 📦 version packages (beta) (#5254)

* chore(pre-release): enter pre-release mode

* feat: support tailwindcss v4 (#4656)

* feat: upgrade tailwindcssv4

* feat: upgrade tailwindcssv4

* feat: update config

* feat: first init tailwindcss v4

* fix: update shadow xs to sm

* fix: update rounded xs to sm

* fix: variant issues

* fix: variant shadow etc issues

* fix: variant shadow issues

* fix: redundant shadow issues

* fix: redundant blur-sm issues

* fix: redundant blur-sm issues

* fix: redundant blur-sm shadow issues

* fix: remove redundant rename

* fix: role button issues

* fix: role button issues

* fix: font size

* fix: alpha color value

* fix: support text utilities in plugin

* feat: upgrade tailwind-merge

* fix: pkg package scope

(cherry picked from commit 6e823233fca0e920336ec32dda6d1d1d845ed0d2)

* fix: button base ui add cursor pointer

* fix: tailwindcss experimental

* feat: upgrade tailwindcss version

* fix: input step 1

* fix: input transition issue

* fix: theme to var function

* ci: run QA in beta branch

* fix: var to the theme and incorrect var usage MER-3261

* feat: upgrade tailwind-variants

---------

Co-authored-by: WK Wong <wingkwong.code@gmail.com>
Co-authored-by: Junior Garcia <jrgarciadev@gmail.com>

* feat: changeset for tailwindcss v4

* fix: changeset

* fix: changeset

* ci(changesets): version packages (beta) (#5008)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* chore: tw 4 docs

* fix: select tests

* chore: timeout

* ci(changesets): version packages (beta)

* ci(changesets): version packages (beta)

* fix: deployment

* ci(changesets): version packages (beta) (#5009)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* chore: add the cli to upgrade the packages to beta

* fix: cli command

* fix: revert to use previous version (#5012)

* fix: revert to use previous version

* fix: revert to use previous version

* fix: revert

* fix: revert to use previous tailwind-merge version

* ci(changesets): version packages (beta) (#5015)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix: adapt tailwind merger config (#5016)

* Revert "fix: revert to use previous version (#5012)"

This reverts commit 1d01df254d6df9bc524d29d767236823b33a96fd.

* fix: adapt tailwind merge config

* fix: adapt tailwind merge config

* fix: adapt tailwind merge config

* fix: changeset

* ci(changesets): version packages (beta)

* fix(tailwind): add missing values config for bg-grid plugin

* fix: remove useless tw config

* fix: add default styles

* docs: changeset

* docs: typo

* chore: merge origin canary to beta

* ci(changesets): version packages (beta)

* chore: remove changeset & pre.json

* chore: change to minor

* chore(pre-release): enter pre-release mode

* chore(deps): fix versions

* chore(deps): revise changed packages

* chore(deps): update tailwindcss version

* ci(changesets): 📦 version packages (beta) (#5166)

* ci(changesets): version packages (beta)

* fix: add missing delimiter

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: winches <329487092@qq.com>

* fix(docs): add missing semicolon

* fix: animate about skeleton (#5198)

* chore: pre release

* ci(changesets): version packages (beta) (#5199)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* docs: blurred hover button style

* fix: language selector blur (#5208)

* fix: skeleton animate translate value (#5207)

* fix: open in remote repo (#5214)

* chore: upgrade tw version

* fix: open in remote repo

* fix: update lock

* fix: select translate styles (#5219)

* chore: pre release

* ci(changesets): version packages (beta) (#5222)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* chore: remove changeset

* chore(pre-release): enter pre-release mode

* chore(react): continue from 2.8.0-beta.2

* chore(changeset): add changeset

* fix(changeset): use patch

* ci(changesets): version packages (beta)

* fix(docs): beta docs

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: winches <329487092@qq.com>
Co-authored-by: WK Wong <wingkwong.code@gmail.com>
Co-authored-by: Junior Garcia <jrgarciadev@gmail.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Charlotte21110 <hejianer9@163.com>

* fix(workflow): check_if_pre_json_exists order

* chore(changeset): redeploy

* chore(changeset): changeset (#5263)

* ci(changesets): version packages (beta) (#5264)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(docs): mdx in beta (#5261)

* chore(docs): add lint:fix

* fix(docs): slug requires promise in next 15

* fix(docs): useMDXComponent

* fix(docs): typing issue

* docs: typography style error (#5274)

* chore(pre-release): enter pre-release mode

* feat: support tailwindcss v4 (#4656)

* feat: upgrade tailwindcssv4

* feat: upgrade tailwindcssv4

* feat: update config

* feat: first init tailwindcss v4

* fix: update shadow xs to sm

* fix: update rounded xs to sm

* fix: variant issues

* fix: variant shadow etc issues

* fix: variant shadow issues

* fix: redundant shadow issues

* fix: redundant blur-sm issues

* fix: redundant blur-sm issues

* fix: redundant blur-sm shadow issues

* fix: remove redundant rename

* fix: role button issues

* fix: role button issues

* fix: font size

* fix: alpha color value

* fix: support text utilities in plugin

* feat: upgrade tailwind-merge

* fix: pkg package scope

(cherry picked from commit 6e823233fca0e920336ec32dda6d1d1d845ed0d2)

* fix: button base ui add cursor pointer

* fix: tailwindcss experimental

* feat: upgrade tailwindcss version

* fix: input step 1

* fix: input transition issue

* fix: theme to var function

* ci: run QA in beta branch

* fix: var to the theme and incorrect var usage MER-3261

* feat: upgrade tailwind-variants

---------

Co-authored-by: WK Wong <wingkwong.code@gmail.com>
Co-authored-by: Junior Garcia <jrgarciadev@gmail.com>

* feat: changeset for tailwindcss v4

* fix: changeset

* fix: changeset

* ci(changesets): version packages (beta) (#5008)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* chore: tw 4 docs

* fix: select tests

* chore: timeout

* ci(changesets): version packages (beta)

* ci(changesets): version packages (beta)

* fix: deployment

* ci(changesets): version packages (beta) (#5009)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* chore: add the cli to upgrade the packages to beta

* fix: cli command

* fix: revert to use previous version (#5012)

* fix: revert to use previous version

* fix: revert to use previous version

* fix: revert

* fix: revert to use previous tailwind-merge version

* ci(changesets): version packages (beta) (#5015)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix: adapt tailwind merger config (#5016)

* Revert "fix: revert to use previous version (#5012)"

This reverts commit 1d01df254d6df9bc524d29d767236823b33a96fd.

* fix: adapt tailwind merge config

* fix: adapt tailwind merge config

* fix: adapt tailwind merge config

* fix: changeset

* ci(changesets): version packages (beta)

* fix(tailwind): add missing values config for bg-grid plugin

* fix: remove useless tw config

* fix: add default styles

* docs: changeset

* docs: typo

* chore: merge origin canary to beta

* ci(changesets): version packages (beta)

* chore: remove changeset & pre.json

* chore: change to minor

* chore(pre-release): enter pre-release mode

* chore(deps): fix versions

* chore(deps): revise changed packages

* chore(deps): update tailwindcss version

* ci(changesets): 📦 version packages (beta) (#5166)

* ci(changesets): version packages (beta)

* fix: add missing delimiter

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: winches <329487092@qq.com>

* fix(docs): add missing semicolon

* fix: animate about skeleton (#5198)

* chore: pre release

* ci(changesets): version packages (beta) (#5199)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* docs: blurred hover button style

* fix: language selector blur (#5208)

* fix: skeleton animate translate value (#5207)

* fix: open in remote repo (#5214)

* chore: upgrade tw version

* fix: open in remote repo

* fix: update lock

* fix: select translate styles

* Merge branch beta/release-next of github.com:heroui-inc/heroui into beta/release-next-original

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: WK Wong <wingkwong.code@gmail.com>
Co-authored-by: Junior Garcia <jrgarciadev@gmail.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Charlotte21110 <hejianer9@163.com>

* fix: transition scale (#5284)

* feat(toast): enable programatically closing a toast with a specific key (#5125)

* feat(toast): add closeToast method

* docs(toast): add example for programmatically closing toast

* refactor: change button title in doc

* refactor: add type for key

* test: sync storybook example with the one in docs

* chore: add changeset

* refactor: fix grammar

* chore: update toast package version change to patch

* Merge branch 'canary' into pr/5125

* chore(changeset): add ref number

* refactor(toast): export hooks

* chore(docs): use flat buttons

---------

Co-authored-by: WK Wong <wingkwong.code@gmail.com>

* ci(changesets): version packages (beta) (#5285)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(input): prevent accessibility label duplication (#5161)

* fix(input): prevent accessibility label duplication

* chore(chageset): add changeset

* chore(changeset): include issue number

---------

Co-authored-by: WK <wingkwong.code@gmail.com>

* refactor: overlay & interactOutside (#5100)

* fix: remove ariaShouldCloseOnInteractOutside usage

* feat: add `@heroui/use-aria-overlay`

* chore(popover): remove ariaHideOutside

* fix(use-aria-modal-overlay): use useAriaOverlay instead

* fix(use-aira-overlay): revise onInteractOutside

* chore(deps): pnpm-lock.yaml

* feat(modal): add ModalWithAutocompleteTemplate

* chore(modal): remove state.close as handled by useInteractOutside

* fix(use-aria-multiselect): add menuTriggerProps.onPressStart and change domProps to triggerProps

* chore(use-aria-button): support onPressUp

* chore(deps): pnpm-lock.yaml

* chore(use-aria-multiselect): join by commas

* fix(tooltip): use useAriaOverlay instead

* fix(autocomplete): jest timeout issue

* chore(deps): pnpm-lock.yaml

* chore(changset): add changeset

* chore(deps): bump versions

* refactor(tooltip): move to type

* chore(deps): bump RA versions (#5315)

* chore(deps): bump RA versions

* fix(scripts): incorrect docs path

* chore(changeset): add changeset for bumping RA versions

* fix(docs): incorrect import path

* refactor(modal): use a simple example

* ci(changesets): version packages (beta) (#5303)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(input): adding outside-top prop (#4775)

* feat(input): adding outside-top prop

* chore(input): add outside-top to use-label-placement hook

* refactor(input): use old method for computing labelPlacement in 'use-input'

* fix(input): fix focus behaviouir and alignment for 'outside-top'

* Merge branch 'canary' into pr/4775

* chore(changeset): include issue number

* feat(system): useInputLabelPlacement

* refactor(input): use useInputLabelPlacement

* chore(changeset): add changeset

---------

Co-authored-by: WK Wong <wingkwong.code@gmail.com>

* chore: sync latest changes to beta (#5347)

* chore: add herohack announcement

* fix(date-picker): error state (#5317)

* fix(date-range-picker): fixed the error state in preset

* Update giant-sloths-shop.md

* Removed if statement

* chore(date-picker): prettier

---------

Co-authored-by: WK Wong <wingkwong.code@gmail.com>

* chore: banner replacement

* fix(theme): clear button in mobile (#5252)

* fix(toast): fixed close button hover position

* fix(input): fixed the clear button rendering on smaller devices

* Delete .changeset/soft-spoons-march.md

* Update input.ts

* Undo unrelated toast changes

* fix(toast): icons (#5246)

* feat(shared-icons): add loading icon

* fix(toast): icons

* chore(toast): revise types for icons

* chore(changeset): add changeset

* refactor: migrate eslint to v9 (#5267)

* refactor: migrate eslint to v9

* chore: lint

* chore: update eslint command

* chore: fix lint warnings

* chore: separate lint and lint:fix

* chore: exclude contentlayer generated code

* fix(scripts): add missing await

* fix(autocomplete): persist last selected item position (#5286)

* refactor(select): remove unnecessary code

* fix(autocomplete): persist last selected item position

* chore(changeset): add changeset

* chore(deps): bump framer-motion version (#5287)

* chore(deps): bump framer-motion version

* fix: typing issues

* chore(changeset): add changeset

---------

Co-authored-by: Junior Garcia <jrgarciadev@gmail.com>

* chore(docs): supplement onAction & selectionBehavior (#5289)

* fix(autocomplete): ensure focused item matches selected item after filter, selection (#5290)

* fix(autocomplete): ensure focused item matches selected item after filter, selection

* chore: apply type and default value

* chore: add perpose coment in updated code

* test: add focuskey management testcode

* docs: add changeset

* docs: update changeset

* chore: remove comment

* fix: broken components in stories (#5291)

* chore(switch): remove xl size

* chore(docs): remove xl size

* chore(system-rsc): remove xl size

* chore(circular-progress): remove xl size

* chore: undo

* chore(deps): bump RA versions (#5310)

* chore(deps): ra version bump

* chore(changeset): add changeset

* fix(scripts): incorrect docs path

---------

Co-authored-by: Junior Garcia <jrgarciadev@gmail.com>

* chore(docs): update meta data (#5311)

* docs(layout.tsx): added text-foreground (#5316)

* feat(tabs): add click handling for tab items in tests and implementation (#3917)

Co-authored-by: WK Wong <wingkwong.code@gmail.com>

* fix issues in tabs examples (#2405)

Co-authored-by: WK Wong <wingkwong.code@gmail.com>

* chore(docs): add missing onValueChange in CheckboxGroup (#5332)

* ci(changesets): version packages (#5323)

Co-authored-by: Junior Garcia <jrgarciadev@gmail.com>

* chore: remove pre.json

---------

Co-authored-by: Junior Garcia <jrgarciadev@gmail.com>
Co-authored-by: Vishv Salvi <82429084+Vishvsalvi@users.noreply.github.com>
Co-authored-by: KumJungMin <37934668+KumJungMin@users.noreply.github.com>
Co-authored-by: liaoyinglong <vigossliao@gmail.com>
Co-authored-by: zhengjitf <zhengjitf@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* chore(pre-release): enter pre-release mode

* chore: update changesets

* chore: remove changesets

* chore: add changeset

* chore: update `@heroui/react` pre version

* fix(deps): add missing `@heroui/use-aria-overlay` import

* fix: lock file (#5351)

* ci(changesets): 📦 version packages (beta) (#5348)

* ci(changesets): version packages (beta)

* Merge branch 'beta/release-next' into changeset-release/beta/release-next

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: WK Wong <wingkwong.code@gmail.com>

* chore: sync 2.8.0 to beta (#5388)

* chore: remove outdated changeset

* chore(pre-release): enter pre-release mode

* chore(deps): bump RA versions (beta) (#5392)

* chore(deps): bump RA versions

* chore: add changeset

* ci(changesets): version packages (beta) (#5393)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* refactor: remove RA dependencies (overaly & utils) (#5398)

* refactor(system): remove `@react-aria/utils` package

* refactor(system): remove `@react-aria/overlays` package

* ci(changesets): version packages (beta) (#5399)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* refactor: RA optimization (#5401)

* refactor(shared-utils): add common functions from `@react-aria/utils`

* feat(system): add shouldClientNavigate & useRouter

* refactor(deps): remove `@react-aria/utils`

* feat(shared-utils): include common functions from RA

* refactor: import functions from `@heroui/shared-utils` instead

* chore(deps): pnpm-lock.yaml

* chore: add changeset

* chore(shared-utils): update directory

* fix(shared-utils): add use client directive

* feat: add `@heroui/use-viewport-size`

* feat: add `@heroui/use-form-reset`

* feat(use-resize): add hasResizeObserver & RefObject

* feat(form): add useObjectRef

* chore: update import

* chore(deps): update dependencies

* refactor(shared-utils): only keep utils

* chore(deps): pnpm-lock.yaml

* chore(changeset): add changeset

* ci(changesets): version packages (beta) (#5406)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix: transitions (#5409)

* chore(changeset): add changeset

* fix: outline style (#5421)

* fix: outline style

* chore(changeset): add changeset

---------

Co-authored-by: WK Wong <wingkwong.code@gmail.com>

* fix(toast): Renaming the loadingIcon to loadingComponent (#4919)

* fix: toast should be above the modal

* fix: renaming the loadingIcon to loadingComponent

* chore: adding changeset

* chore: fixing conflicts

* chore: adding the region props

* fix: adding Marcus' suggestions

* fix: marcus' suggestions

* Revert all commits except 4c6bf32

* chore(toast): bump `@heroui/theme` in peerDependencies

* chore: nits

* chore: applying marcus' suggestions

* chore(toast): update story

---------

Co-authored-by: WK Wong <wingkwong.code@gmail.com>

* chore: use beta version

* chore: delete pre.json

* chore(pre-release): enter pre-release mode

* chore(changeset): trigger release

* chore(changeset): trigger release

* chore: keep previous changesets

* ci(changesets): version packages (beta) (#5429)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(table): header and isStriped missing radius (#5062)

* fix(table): tableheader and isStriped missing radius

* chore: add changeset

* fix: fix missing radius styles

* chore(changeset): update message

* fix(theme): reorder radius

---------

Co-authored-by: WK Wong <wingkwong.code@gmail.com>

* fix(slider): prevent NaN error for equal min / max values (#5044)

* fix(slider): prevent NaN error for equal min/max values

* fix: restore isdisabled code

* fix(slider): perf prevent NaN error for equal min max values

* refactor: slider code

* fix(slider): prevent NaN error for equal min max values & add test

* fix(slider): perf prevent NaN error for equal min max values

* fix: remove redundant code

* chore: add changeset

* Revert "chore: add changeset"

This reverts commit f31de4ac859e48eb09c8a14a546fed9e5197eccd.

* chore(slider): add missing warn

* refactor(slider): remove unnecessary line

---------

Co-authored-by: WK Wong <wingkwong.code@gmail.com>

* fix: double fetch img src (#5417)

* fix(image): double fetch when using custom image component

* fix(avatar): avoid passing disableAnimation to dom & double fetch source

* chore(changeset): include avatar

* fix(autocomplete): empty button when selectorIcon is null (#5427)

* fix(toast): Toast items close in reverse order (#5405)

* fix(toast): correct closing order to implement proper FIFO behavior

* chore(changeset): add changeset

* fix(changeset): update issue number

* chore(toast): use `!==` instead

* feat(toast): apply exit animation to auto-close timeout

---------

Co-authored-by: WK Wong <wingkwong.code@gmail.com>

* fix(table): remove removeWrapper on virtualized table (#5428)

* fix(table): remove `removeWrapper` for virtualized table

* chore(docs): update description for removeWrapper

* chore(changeset): add changeset

* fix(toast): fixed close button hover position (#5245)

* fix(toast): fixed close button hover position

* Update soft-spoons-march.md

* Update toast.ts

* chore(theme): prettier

* Using "placement" to apply the top and bottom extension

* fix(toast): modified hover for expanded region

* update dependancy array

* lint fix

* Merge branch 'canary' into pr/5245

* chore(theme): remove line breaks

* chore(changeset): add missing package

* static extension size

* fix(toast): static extension fix

---------

Co-authored-by: WK Wong <wingkwong.code@gmail.com>

* feat(table): support custom sort icon (#5243)

* feat(shared-icons): add SortIcon

* feat(table): add CustomSortIcon story

* feat(table): support custom sort icon

* fix(table): handle functional sortIcon

* chore(changeset): add changeset

* chore(table): update type

* feat(docs): add sortIcon to table

* fix(docs): broken object

* chore(shared-icons): lint

* feat(docs): add example for sort icon

* chore: bump pnpm & node version (#5442)

* refactor: bump pnpm & node version

* chore(deps): bump `@types/node`

* fix(calendar): improve month and year picker selection for different zoom levels (#5151)

* fix(calendar): improve month and year picker selection for different zoom levels

* fix(calendar): improve month and year picker selection for different zoom levels

* fix(calendar): prevent stale values while scrolling both year and month pickers simultaneously

* docs: replace invalid yarn command (#5422)

* chore: remove ph banner (#5437)

* chore(changeset): update changeset message

* chore: add line breaks

---------

Co-authored-by: Nicolas Cappabianca <nicolas.cappabianca@gmail.com>
Co-authored-by: WK <wingkwong.code@gmail.com>

* feat(select): adding isClearable to select (#4785)

* feat(select): adding isClearable to select

* chore(select): add changeset

* test(select): add tests for isClearable

* chore(select): add theme package patch, improve the testcase, rename nextui package to heroui

* fix(select): fixing alignment for clear button for different cases

* Merge branch 'canary' into pr/4785

* Merge branch 'canary' into pr/4785

* fix(select): remove component level styling

* fix(select): fix the alignment and focus behaviour

* fix(select): shift clear buttojn close to trigger

* fix(select): fix alignment for different variants

* feat(select): add data-has-end-content

* fix(theme): revise select styles with clear button and end content

* feat(docs): add end content

---------

Co-authored-by: WK Wong <wingkwong.code@gmail.com>

* fix(select): use span instead as trigger is button

* fix(docs): add ToastProvider for blog post

* ci(changesets): version packages (beta) (#5430)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(select): clear button test cases

* fix(modal): draggable modal, scrollable on mobile (#5444)

Co-authored-by: Vishv Salvi <vishvsalvi@Vishvs-MacBook-Air.local>

* chore: trigger release

* ci(changesets): version packages (beta) (#5445)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(theme): table header sorting icon transition (#5449)

* fix: table header sorcting icon transition

* chore(changeset): add changeset

---------

Co-authored-by: WK Wong <wingkwong.code@gmail.com>

* fix(table): allow text selection with cursor in table cells (#5454)

* fix(table): allow text selection with cursor in table cells

* chore(changeset): add changeset

---------

Co-authored-by: WK Wong <wingkwong.code@gmail.com>

* ci(changesets): version packages (beta) (#5450)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* chore: add back RA deps (#5466)

* chore(deps): bump testing-library & jest versions (#5468)

* refactor: revise test cases

* chore(deps): bump testing-library & jest versions

* ci(changesets): version packages (beta) (#5467)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* refactor(autocomplete): test cases

* fix(use-theme): incorrect target theme

* ci(changesets): version packages (beta) (#5470)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(use-theme): remove all theme values and add the new one

* ci(changesets): version packages (beta) (#5472)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* docs: 2.8.0 (#5443)

* chore(docs): update routes.json

* chore(docs): bump versions

* chore(docs): initial draft

* chore(docs): add examples

* chore(Docs): update credits and community

* chore(docs): update date & what's next

* chore(docs): update meta data

* chore(docs): add update tags

* chore(docs): add tableSortIcon example

* chore(docs): revise sidebar width

* chore(docs): remove package size optimization

* chore(docs): update meta

* ci(changesets): exit pre-release mode

* chore: update package versions

* chore(changeset): remove unrelated changeset

* chore(deps): remove unused autoprefixer

* chore(docs): update tailwind v4 content

* chore(docs): update template doc content

* chore(docs): update formatting

* chore(toast): export getToastQueue (#5476)

* chore(docs): add hydration issue handling to page router example (#5474)

* docs(dark-mode): add hydration handling to page router example

* chore(docs): format

---------

Co-authored-by: WK Wong <wingkwong.code@gmail.com>

* chore(deps): bump tw4 versions

* fix: handle files with paths containing empty space (#5478)

* chore(docs): revise wordings in 2.8.0 blog

* feat(slider): add getTooltipValue prop for custom tooltip value (#5384)

* feat(slider): add getTooltipValue prop for custom tooltip value

* feat(slider): fix coderabbit highlights

* feat(slider): fixed wingkwong highlights

* feat(slider): added custom-tooltip.raw.tsx?raw

* feat(slider): improved custom-tooltip.raw.tsx

* chore: undo README.md

* chore(slider): use normal $ sign

* chore(changeset): add changeset

* chore(docs): formatting

---------

Co-authored-by: andartadev1 <andartadev@gmail.com>
Co-authored-by: WK Wong <wingkwong.code@gmail.com>

* chore(docs): revise custom tooltip example

* chore(docs): add custom tooltip example for slider

* chore(deps): bump contentlayer2 & next-contentlayer2

* chore(docs): fix formatting

* chore(docs): update metadata

* chore: add v2.8.0 image

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: winches <329487092@qq.com>
Co-authored-by: Junior Garcia <jrgarciadev@gmail.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Charlotte21110 <hejianer9@163.com>
Co-authored-by: Feiyu Zheng <67126194+ChaserZ98@users.noreply.github.com>
Co-authored-by: Dmytro Klymenko <dmytro@klymenko.xyz>
Co-authored-by: Abhinav Agarwal <78839973+abhinav700@users.noreply.github.com>
Co-authored-by: Vishv Salvi <82429084+Vishvsalvi@users.noreply.github.com>
Co-authored-by: KumJungMin <37934668+KumJungMin@users.noreply.github.com>
Co-authored-by: liaoyinglong <vigossliao@gmail.com>
Co-authored-by: zhengjitf <zhengjitf@gmail.com>
Co-authored-by: Maharshi Alpesh <maharshialpesh@gmail.com>
Co-authored-by: Yohan <58714229+kaishuige@users.noreply.github.com>
Co-authored-by: Aditya Ray <96347576+adi-ray@users.noreply.github.com>
Co-authored-by: Aakash Patel <ap0524@srmist.edu.in>
Co-authored-by: Nicolas Cappabianca <nicolas.cappabianca@gmail.com>
Co-authored-by: Vishv Salvi <vishvsalvi@Vishvs-MacBook-Air.local>
Co-authored-by: Priyadharshini S <priyadharshinis5102@gmail.com>
Co-authored-by: arar <77303253+ararTP@users.noreply.github.com>
Co-authored-by: andartadev1 <andartadev@gmail.com>
2025-07-14 17:50:05 -03:00

1532 lines
38 KiB
TypeScript

import type {ValidationResult} from "@react-types/shared";
import type {ChangeEvent} from "react";
import type {Meta} from "@storybook/react";
import type {Selection} from "@react-types/shared";
import type {Pokemon, Animal, User} from "@heroui/stories-utils";
import type {SelectedItems, SelectProps} from "../src";
import React from "react";
import {useForm} from "react-hook-form";
import {select, button} from "@heroui/theme";
import {PetBoldIcon, SelectorIcon} from "@heroui/shared-icons";
import {Avatar} from "@heroui/avatar";
import {Chip} from "@heroui/chip";
import {Button} from "@heroui/button";
import {useInfiniteScroll} from "@heroui/use-infinite-scroll";
import {usePokemonList, animalsData, usersData} from "@heroui/stories-utils";
import {Form} from "@heroui/form";
import {Select, SelectItem, SelectSection} from "../src";
export default {
title: "Components/Select",
component: Select,
argTypes: {
variant: {
control: {
type: "select",
},
options: ["flat", "faded", "bordered", "underlined"],
},
color: {
control: {
type: "select",
},
options: ["default", "primary", "secondary", "success", "warning", "danger"],
},
radius: {
control: {
type: "select",
},
options: ["none", "sm", "md", "lg", "full"],
},
size: {
control: {
type: "select",
},
options: ["sm", "md", "lg"],
},
labelPlacement: {
control: {
type: "select",
},
options: ["inside", "outside", "outside-left"],
},
isDisabled: {
control: {
type: "boolean",
},
},
isClearable: {
control: {
type: "boolean",
},
},
},
decorators: [
(Story) => (
<div className="flex items-start justify-center w-screen h-screen">
<Story />
</div>
),
],
} as Meta<typeof Select>;
const defaultProps = {
...select.defaultVariants,
};
const items = animalsData.map((item) => <SelectItem key={item.value}>{item.label}</SelectItem>);
const Template = ({color, variant, ...args}: SelectProps) => (
<Select className="max-w-xs" color={color} label="Favorite Animal" variant={variant} {...args}>
{items}
</Select>
);
const DynamicTemplate = ({color, variant, ...args}: SelectProps<Animal>) => (
<Select
className="max-w-xs"
color={color}
items={animalsData}
label="Favorite Animal"
variant={variant}
{...args}
>
{(item) => <SelectItem key={item.value}>{item.label}</SelectItem>}
</Select>
);
const DynamicTemplateWithDescriptions = ({color, variant, ...args}: SelectProps<Animal>) => (
<Select
className="max-w-xs"
color={color}
items={animalsData}
label="Favorite Animal"
variant={variant}
{...args}
>
{(item) => (
<SelectItem key={item.value} description={item.description}>
{item.label}
</SelectItem>
)}
</Select>
);
const ItemStartContentTemplate = ({color, variant, ...args}: SelectProps<Animal>) => (
<Select className="max-w-xs" color={color} label="Select country" variant={variant} {...args}>
<SelectItem
key="argentina"
startContent={<Avatar alt="Argentina" className="w-6 h-6" src="https://flagcdn.com/ar.svg" />}
>
Argentina
</SelectItem>
<SelectItem
key="venezuela"
startContent={<Avatar alt="Venezuela" className="w-6 h-6" src="https://flagcdn.com/ve.svg" />}
>
Venezuela
</SelectItem>
<SelectItem
key="brazil"
startContent={<Avatar alt="Brazil" className="w-6 h-6" src="https://flagcdn.com/br.svg" />}
>
Brazil
</SelectItem>
<SelectItem
key="switzerland"
startContent={
<Avatar alt="Switzerland" className="w-6 h-6" src="https://flagcdn.com/ch.svg" />
}
>
Switzerland
</SelectItem>
<SelectItem
key="germany"
startContent={<Avatar alt="Germany" className="w-6 h-6" src="https://flagcdn.com/de.svg" />}
>
Germany
</SelectItem>
<SelectItem
key="spain"
startContent={<Avatar alt="Spain" className="w-6 h-6" src="https://flagcdn.com/es.svg" />}
>
Spain
</SelectItem>
<SelectItem
key="france"
startContent={<Avatar alt="France" className="w-6 h-6" src="https://flagcdn.com/fr.svg" />}
>
France
</SelectItem>
<SelectItem
key="italy"
startContent={<Avatar alt="Italy" className="w-6 h-6" src="https://flagcdn.com/it.svg" />}
>
Italy
</SelectItem>
<SelectItem
key="mexico"
startContent={<Avatar alt="Mexico" className="w-6 h-6" src="https://flagcdn.com/mx.svg" />}
>
Mexico
</SelectItem>
</Select>
);
const ControlledTemplate = ({color, variant, ...args}: SelectProps<Animal>) => {
const [value, setValue] = React.useState<Selection>(new Set(["cat"]));
const handleSelectionChange = (e: ChangeEvent<HTMLSelectElement>) => {
setValue(new Set([e.target.value]));
};
return (
<div className="flex w-full max-w-xs flex-col gap-2">
<Select
fullWidth
color={color}
items={animalsData}
label="Favorite Animal"
selectedKeys={value}
variant={variant}
onChange={handleSelectionChange}
{...args}
>
{(item) => <SelectItem key={item.value}>{item.label}</SelectItem>}
</Select>
<p className="text-default-500">Selected: {value}</p>
</div>
);
};
const ControlledOpenTemplate = ({color, variant, ...args}: SelectProps<Animal>) => {
const [isOpen, setIsOpen] = React.useState(false);
return (
<div className="flex w-full max-w-xs items-center gap-2">
<Select
className="max-w-xs"
color={color}
isOpen={isOpen}
label="Favorite Animal"
variant={variant}
onOpenChange={(open) => open !== isOpen && setIsOpen(open)}
{...args}
>
{items}
</Select>
<Button onPress={() => setIsOpen(!isOpen)}>{isOpen ? "Close" : "Open"}</Button>
</div>
);
};
const ControlledMultipleTemplate = ({color, variant, ...args}: SelectProps<Animal>) => {
const [values, setValues] = React.useState<Selection>(new Set(["cat", "dog"]));
const handleSelectionChange = (items: Selection) => {
setValues(items);
};
return (
<div className="flex w-full max-w-xs flex-col gap-2">
<Select
fullWidth
color={color}
items={animalsData}
label="Favorite Animal"
selectedKeys={values}
selectionMode="multiple"
variant={variant}
onSelectionChange={handleSelectionChange}
{...args}
>
{(item) => <SelectItem key={item.value}>{item.label}</SelectItem>}
</Select>
<p className="text-default-500">Selected: {[...values].join(", ")}</p>
</div>
);
};
const FormTemplate = ({color, variant, ...args}: SelectProps) => {
return (
<form
className="w-full max-w-xs items-end flex flex-col gap-4"
onSubmit={(e) => {
alert(`Submitted value: ${e.target["favorite-animal"].value}`);
e.preventDefault();
}}
>
<Select
color={color}
label="Favorite Animal"
name="favorite-animal"
variant={variant}
{...args}
>
{items}
</Select>
<button className={button({className: "max-w-fit"})} type="submit">
Submit
</button>
</form>
);
};
const ServerValidationTemplate = (args: SelectProps) => {
const [submittedData, setSubmittedData] = React.useState<{animal: string} | null>(null);
const [serverErrors, setServerErrors] = React.useState({});
const onSubmit = (e) => {
e.preventDefault();
const formData = new FormData(e.target as HTMLFormElement);
const value = formData.get("animal");
if (!value) {
setServerErrors({
animal: "Please select a valid value",
});
return;
}
if (!value || (value !== "cat" && value !== "dog")) {
setServerErrors({
animal: "Please select a cat or dog",
});
} else {
setServerErrors({});
setSubmittedData({animal: value});
}
};
return (
<Form
className="w-full flex flex-col items-start gap-2"
validationErrors={serverErrors}
onSubmit={onSubmit}
>
<Select isRequired {...args} className="max-w-xs" label="Favorite Animal" name="animal">
{items}
</Select>
<button className={button({color: "primary"})} type="submit">
Submit
</button>
{submittedData && (
<div className="text-small text-default-500">
You submitted: <code>{JSON.stringify(submittedData)}</code>
</div>
)}
</Form>
);
};
const ServerValidationTemplateWithMultiple = (args: SelectProps) => {
const [submittedData, setSubmittedData] = React.useState<{animals: string[]} | null>(null);
const [serverErrors, setServerErrors] = React.useState({});
const onSubmit = (e) => {
e.preventDefault();
const formData = new FormData(e.target as HTMLFormElement);
const values = formData.getAll("animals");
if (!values.length || !values.every((v) => v === "cat" || v === "dog")) {
setServerErrors({
animals: "Please select only cats and/or dogs",
});
} else {
setServerErrors({});
setSubmittedData({animals: values as string[]});
}
};
return (
<Form
className="w-full flex flex-col items-start gap-2"
validationErrors={serverErrors}
onSubmit={onSubmit}
>
<Select
{...args}
className="max-w-xs"
label="Favorite Animals"
name="animals"
selectionMode="multiple"
>
{items}
</Select>
<button className={button({color: "primary"})} type="submit">
Submit
</button>
{submittedData && (
<div className="text-small text-default-500">
You submitted: <code>{JSON.stringify(submittedData)}</code>
</div>
)}
</Form>
);
};
const MirrorTemplate = ({color, variant, ...args}: SelectProps) => (
<div className="w-full max-w-xl flex flex-row gap-4">
<Select className="max-w-xs" color={color} label="Select an animal" variant={variant} {...args}>
{items}
</Select>
<Select
className="max-w-xs"
color={color}
label="Favorite Animal"
placeholder="Select an animal"
variant={variant}
{...args}
>
{items}
</Select>
</div>
);
const LabelPlacementTemplate = ({color, variant, ...args}: SelectProps) => (
<div className="w-full flex flex-col items-center gap-12">
<div className="w-full max-w-5xl flex flex-col gap-3">
<h3>Without placeholder</h3>
<div className="w-full flex flex-row items-end gap-4">
<Select color={color} label="Select an animal" variant={variant} {...args}>
{items}
</Select>
<Select
color={color}
label="Select an animal"
variant={variant}
{...args}
labelPlacement="outside"
>
{items}
</Select>
<Select
color={color}
label="Select an animal"
variant={variant}
{...args}
labelPlacement="outside-left"
>
{items}
</Select>
</div>
</div>
<div className="w-full max-w-5xl flex flex-col gap-3">
<h3>With placeholder</h3>
<div className="w-full flex flex-row items-end gap-4">
<Select
color={color}
label="Favorite Animal"
placeholder="Select an animal"
variant={variant}
{...args}
>
{items}
</Select>
<Select
color={color}
label="Favorite Animal"
placeholder="Select an animal"
variant={variant}
{...args}
labelPlacement="outside"
>
{items}
</Select>
<Select
color={color}
label="Favorite Animal"
placeholder="Select an animal"
variant={variant}
{...args}
labelPlacement="outside-left"
>
{items}
</Select>
</div>
</div>
<div className="w-full max-w-5xl flex flex-col gap-3">
<h3>With placeholder and description</h3>
<div className="w-full flex flex-row items-end gap-4">
<Select
color={color}
description="Select your favorite animal"
label="Favorite Animal"
placeholder="Select an animal"
variant={variant}
{...args}
>
{items}
</Select>
<Select
color={color}
description="Select your favorite animal"
label="Favorite Animal"
placeholder="Select an animal"
variant={variant}
{...args}
labelPlacement="outside"
>
{items}
</Select>
<Select
color={color}
description="Select your favorite animal"
label="Favorite Animal"
placeholder="Select an animal"
variant={variant}
{...args}
labelPlacement="outside-left"
>
{items}
</Select>
</div>
</div>
</div>
);
const StartContentTemplate = ({color, variant, ...args}: SelectProps) => (
<Select
className="max-w-xs"
color={color}
defaultSelectedKeys={["cat"]}
label="Favorite Animal"
startContent={<PetBoldIcon />}
variant={variant}
{...args}
>
{items}
</Select>
);
const EndContentTemplate = ({color, variant, ...args}: SelectProps) => (
<Select
className="max-w-xs"
color={color}
defaultSelectedKeys={["cat"]}
endContent={<PetBoldIcon />}
label="Favorite Animal"
variant={variant}
{...args}
>
{items}
</Select>
);
const EmptyTemplate = ({color, variant, ...args}: SelectProps) => (
<div className="w-full justify-center flex gap-2">
<Select
hideEmptyContent
className="max-w-xs"
color={color}
label="Hide empty content"
variant={variant}
{...args}
>
{[]}
</Select>
<Select
className="max-w-xs"
color={color}
hideEmptyContent={false}
label="Show empty content"
variant={variant}
{...args}
>
{[]}
</Select>
</div>
);
const CustomItemsTemplate = ({color, variant, ...args}: SelectProps<User>) => (
<div className="w-full justify-center flex gap-2">
<Select
className="max-w-xs mt-8"
color={color}
items={usersData}
label="Assigned to"
variant={variant}
{...args}
>
{(item) => (
<SelectItem key={item.id} textValue={item.name}>
<div className="flex gap-2 items-center">
<Avatar alt={item.name} className="shrink-0" size="sm" src={item.avatar} />
<div className="flex flex-col">
<span className="text-small">{item.name}</span>
<span className="text-tiny text-default-400">{item.email}</span>
</div>
</div>
</SelectItem>
)}
</Select>
<Select
className="max-w-xs mt-8"
color={color}
items={usersData}
label="Assigned to"
placeholder="Assigned to"
variant={variant}
{...args}
>
{(item) => (
<SelectItem key={item.id} textValue={item.name}>
<div className="flex gap-2 items-center">
<Avatar alt={item.name} className="shrink-0" size="sm" src={item.avatar} />
<div className="flex flex-col">
<span className="text-small">{item.name}</span>
<span className="text-tiny text-default-400">{item.email}</span>
</div>
</div>
</SelectItem>
)}
</Select>
</div>
);
const WithSectionsTemplate = ({color, variant, ...args}: SelectProps<User>) => (
<Select className="max-w-xs" color={color} label="Favorite Animal" variant={variant} {...args}>
<SelectSection showDivider title="Mammals">
<SelectItem key="Lion">Lion</SelectItem>
<SelectItem key="Tiger">Tiger</SelectItem>
<SelectItem key="Elephant">Elephant</SelectItem>
<SelectItem key="Kangaroo">Kangaroo</SelectItem>
<SelectItem key="Panda">Panda</SelectItem>
<SelectItem key="Giraffe">Giraffe</SelectItem>
<SelectItem key="Zebra">Zebra</SelectItem>
<SelectItem key="Cheetah">Cheetah</SelectItem>
</SelectSection>
<SelectSection title="Birds">
<SelectItem key="Eagle">Eagle</SelectItem>
<SelectItem key="Parrot">Parrot</SelectItem>
<SelectItem key="Penguin">Penguin</SelectItem>
<SelectItem key="Ostrich">Ostrich</SelectItem>
<SelectItem key="Peacock">Peacock</SelectItem>
<SelectItem key="Swan">Swan</SelectItem>
<SelectItem key="Falcon">Falcon</SelectItem>
<SelectItem key="Flamingo">Flamingo</SelectItem>
</SelectSection>
</Select>
);
const WithCustomSectionsStylesTemplate = ({color, variant, ...args}: SelectProps<User>) => {
const headingClasses =
"flex w-full sticky top-1 z-20 py-1.5 px-2 bg-default-100 shadow-small rounded-small";
return (
<Select
className="max-w-xs"
color={color}
label="Favorite Animal"
scrollShadowProps={{
isEnabled: false,
}}
variant={variant}
{...args}
>
<SelectSection
classNames={{
heading: headingClasses,
}}
title="Mammals"
>
<SelectItem key="Lion">Lion</SelectItem>
<SelectItem key="Tiger">Tiger</SelectItem>
<SelectItem key="Elephant">Elephant</SelectItem>
<SelectItem key="Kangaroo">Kangaroo</SelectItem>
<SelectItem key="Panda">Panda</SelectItem>
<SelectItem key="Giraffe">Giraffe</SelectItem>
<SelectItem key="Zebra">Zebra</SelectItem>
<SelectItem key="Cheetah">Cheetah</SelectItem>
</SelectSection>
<SelectSection
classNames={{
heading: headingClasses,
}}
title="Birds"
>
<SelectItem key="Eagle">Eagle</SelectItem>
<SelectItem key="Parrot">Parrot</SelectItem>
<SelectItem key="Penguin">Penguin</SelectItem>
<SelectItem key="Ostrich">Ostrich</SelectItem>
<SelectItem key="Peacock">Peacock</SelectItem>
<SelectItem key="Swan">Swan</SelectItem>
<SelectItem key="Falcon">Falcon</SelectItem>
<SelectItem key="Flamingo">Flamingo</SelectItem>
</SelectSection>
</Select>
);
};
const WithAriaLabelTemplate = ({color, variant, ...args}: SelectProps) => (
<Select className="max-w-xs" color={color} label="Favorite Animal" variant={variant} {...args}>
{items}
</Select>
);
const CustomStylesTemplate = ({color, variant, ...args}: SelectProps<User>) => {
return (
<Select
className="max-w-xs"
classNames={{
label: "group-data-[filled=true]:-translate-y-5",
trigger: "min-h-16",
listboxWrapper: "max-h-[400px]",
}}
color={color}
items={usersData}
label="Assigned to"
listboxProps={{
itemClasses: {
base: [
"rounded-md",
"text-default-500",
"transition-opacity",
"data-[hover=true]:text-foreground",
"data-[hover=true]:bg-default-100",
"dark:data-[hover=true]:bg-default-50",
"data-[selectable=true]:focus:bg-default-50",
"data-[pressed=true]:opacity-70",
"data-[focus-visible=true]:ring-default-500",
],
},
}}
popoverProps={{
classNames: {
base: "before:bg-default-200",
content: "p-0 border-small border-divider bg-background",
},
}}
variant={variant}
{...args}
>
{(item) => (
<SelectItem key={item.id} textValue={item.name}>
<div className="flex gap-2 items-center">
<Avatar alt={item.name} className="shrink-0" size="sm" src={item.avatar} />
<div className="flex flex-col">
<span className="text-small">{item.name}</span>
<span className="text-tiny text-default-400">{item.email}</span>
</div>
</div>
</SelectItem>
)}
</Select>
);
};
const AsyncLoadingTemplate = ({color, variant, ...args}: SelectProps<Pokemon>) => {
const [isOpen, setIsOpen] = React.useState(false);
const {items, hasMore, isLoading, onLoadMore} = usePokemonList({fetchDelay: 1500});
const [, scrollerRef] = useInfiniteScroll({
hasMore,
distance: 20,
isEnabled: isOpen,
shouldUseLoader: false, // We don't want to show the loader at the bottom of the list
onLoadMore,
});
return (
<Select
className="max-w-xs"
color={color}
isLoading={isLoading}
items={items}
label="Pick a Pokemon"
placeholder="Select a Pokemon"
scrollRef={scrollerRef}
selectionMode="single"
variant={variant}
onOpenChange={setIsOpen}
{...args}
>
{(item) => (
<SelectItem key={item.name} className="capitalize">
{item.name}
</SelectItem>
)}
</Select>
);
};
const WithReactHookFormTemplate = (args: SelectProps) => {
const {
register,
formState: {errors},
handleSubmit,
} = useForm({
defaultValues: {
withDefaultValue: "cat",
withoutDefaultValue: "",
requiredField: "",
},
});
const onSubmit = (data: any) => {
// eslint-disable-next-line no-console
console.log(data);
alert("Submitted value: " + JSON.stringify(data));
};
return (
<form className="flex w-full max-w-xs flex-col gap-2" onSubmit={handleSubmit(onSubmit)}>
<Select data-testid="select-1" {...args} {...register("withDefaultValue")}>
{items}
</Select>
<Select data-testid="select-2" {...args} {...register("withoutDefaultValue")}>
{items}
</Select>
<Select data-testid="select-3" {...args} {...register("requiredField", {required: true})}>
{items}
</Select>
{errors.requiredField && <span className="text-danger">This field is required</span>}
<button className={button({class: "w-fit"})} type="submit">
Submit
</button>
</form>
);
};
const ScrollableContainerTemplate = (args: SelectProps) => {
const categories = [
{
target: "Animals",
items: [
{name: "Lion", emoji: "🦁"},
{name: "Tiger", emoji: "🐅"},
{name: "Elephant", emoji: "🐘"},
{name: "Kangaroo", emoji: "🦘"},
{name: "Panda", emoji: "🐼"},
{name: "Giraffe", emoji: "🦒"},
{name: "Zebra", emoji: "🦓"},
{name: "Cheetah", emoji: "🐆"},
],
},
{
target: "Birds",
items: [
{name: "Eagle", emoji: "🦅"},
{name: "Parrot", emoji: "🦜"},
{name: "Penguin", emoji: "🐧"},
{name: "Ostrich", emoji: "🦢"},
{name: "Peacock", emoji: "🦚"},
{name: "Swan", emoji: "🦢"},
{name: "Falcon", emoji: "🦅"},
{name: "Flamingo", emoji: "🦩"},
],
},
];
const DEFAULT_CATEGORY = "Animals";
return (
<>
<form className="h-full overflow-auto">
<div className="flex justify-between h-[1500px]">
<div className="flex items-center gap-2">
<div className="flex w-full flex-wrap gap-4 md:flex-nowrap">
<Select
aria-label="Favourite Animals"
className="w-52"
defaultSelectedKeys={[DEFAULT_CATEGORY]}
label="Category"
name="Category"
{...args}
>
{categories.map((category, idx, arr) => (
<SelectSection
key={category.target}
showDivider={idx !== arr.length - 1}
title={category.target}
>
{category.items.map((item) => (
<SelectItem key={item.name}>{`${item.emoji} ${item.name}`}</SelectItem>
))}
</SelectSection>
))}
</Select>
</div>
</div>
</div>
</form>
</>
);
};
interface LargeDatasetSchema {
label: string;
value: string;
description: string;
}
function generateLargeDataset(n: number): LargeDatasetSchema[] {
const dataset: LargeDatasetSchema[] = [];
const items = [
"Cat",
"Dog",
"Elephant",
"Lion",
"Tiger",
"Giraffe",
"Dolphin",
"Penguin",
"Zebra",
"Shark",
"Whale",
"Otter",
"Crocodile",
];
for (let i = 0; i < n; i++) {
const item = items[i % items.length];
dataset.push({
label: `${item}${i}`,
value: `${item.toLowerCase()}${i}`,
description: "Sample description",
});
}
return dataset;
}
const LargeDatasetTemplate = (args: SelectProps & {numItems: number}) => {
const largeDataset = generateLargeDataset(args.numItems);
return (
<div className="flex w-full max-w-full py-20 xl:px-32 lg:px-20 px-20">
<Select label={`Select from ${args.numItems} items`} {...args}>
{largeDataset.map((item) => (
<SelectItem key={item.value}>{item.label}</SelectItem>
))}
</Select>
</div>
);
};
const ValidationBehaviorAriaTemplate = (args: SelectProps) => {
// Custom validation example
const CustomValidationExample = () => {
return (
<Select
{...args}
className="max-w-xs"
label="Favorite Animal"
placeholder="Select an animal"
validate={(value) => {
if (typeof value === "string" && value === "penguin") {
return "Penguins are not allowed";
}
return null;
}}
validationBehavior="aria"
>
<SelectItem key="penguin">Penguin</SelectItem>
<SelectItem key="zebra">Zebra</SelectItem>
<SelectItem key="shark">Shark</SelectItem>
</Select>
);
};
//Custom validation example multiple
const CustomValidationExampleMultiple = () => {
return (
<Select
{...args}
className="max-w-xs"
label="Favorite Animal"
placeholder="Select an animal"
selectionMode="multiple"
validate={(value) => {
if (Array.isArray(value) && value.includes("penguin")) {
return "Penguins are not allowed";
}
return null;
}}
validationBehavior="aria"
>
<SelectItem key="penguin">Penguin</SelectItem>
<SelectItem key="zebra">Zebra</SelectItem>
<SelectItem key="shark">Shark</SelectItem>
</Select>
);
};
// Server validation example
const ServerValidationExample = () => {
const [serverErrors, setServerErrors] = React.useState({});
const onSubmit = (e: React.FormEvent) => {
e.preventDefault();
const formData = new FormData(e.target as HTMLFormElement);
const value = formData.get("animal");
if (value === "penguin") {
setServerErrors({
animal: "Server says: No penguins allowed!",
});
} else {
setServerErrors({});
}
};
return (
<Form
className="w-full flex flex-col items-start gap-2"
validationErrors={serverErrors}
onSubmit={onSubmit}
>
<Select className="max-w-xs" label="Select Animal" name="animal" validationBehavior="aria">
<SelectItem key="penguin">Penguin</SelectItem>
<SelectItem key="zebra">Zebra</SelectItem>
<SelectItem key="shark">Shark</SelectItem>
</Select>
<button className={button({color: "primary"})} type="submit">
Validate
</button>
</Form>
);
};
return (
<div className="flex flex-col gap-8">
<div className="flex flex-col gap-2">
<h3 className="text-default-500">Custom Validation</h3>
<p className="text-small text-default-400">Try selecting a penguin</p>
<CustomValidationExample />
</div>
<div className="flex flex-col gap-2">
<h3 className="text-default-500">Custom Validation Multiple</h3>
<p className="text-small text-default-400">Try selecting a penguin</p>
<CustomValidationExampleMultiple />
</div>
<div className="flex flex-col gap-2">
<h3 className="text-default-500">Server Validation</h3>
<p className="text-small text-default-400">Select a penguin and click validate</p>
<ServerValidationExample />
</div>
</div>
);
};
export const Default = {
render: MirrorTemplate,
args: {
...defaultProps,
},
};
export const Multiple = {
render: Template,
args: {
...defaultProps,
selectionMode: "multiple",
},
};
export const Required = {
render: FormTemplate,
args: {
...defaultProps,
isRequired: true,
},
};
export const Disabled = {
render: Template,
args: {
...defaultProps,
selectedKey: "cat",
variant: "faded",
isDisabled: true,
},
};
export const DisabledOptions = {
render: Template,
args: {
...defaultProps,
disabledKeys: ["zebra", "tiger", "lion", "elephant", "crocodile", "whale"],
},
};
export const IsInvalid = {
render: Template,
args: {
...defaultProps,
isInvalid: true,
variant: "bordered",
defaultSelectedKeys: ["dog"],
errorMessage: "Please select a valid animal",
},
};
export const LabelPlacement = {
render: LabelPlacementTemplate,
args: {
...defaultProps,
},
};
export const AsyncLoading = {
render: AsyncLoadingTemplate,
args: {
...defaultProps,
},
};
export const StartContent = {
render: StartContentTemplate,
args: {
...defaultProps,
},
};
export const EndContent = {
render: EndContentTemplate,
args: {
...defaultProps,
},
};
export const EmptyContent = {
render: EmptyTemplate,
args: {
...defaultProps,
},
};
export const WithDescription = {
render: MirrorTemplate,
args: {
...defaultProps,
description: "Select your favorite animal",
},
};
export const WithoutLabel = {
render: Template,
args: {
...defaultProps,
label: null,
"aria-label": "Select an animal",
placeholder: "Select an animal",
},
};
export const WithoutScrollShadow = {
render: Template,
args: {
...defaultProps,
scrollShadowProps: {
isEnabled: false,
},
},
};
export const WithItemDescriptions = {
render: DynamicTemplateWithDescriptions,
args: {
...defaultProps,
},
};
export const WithItemStartContent = {
render: ItemStartContentTemplate,
args: {
...defaultProps,
},
};
export const WithErrorMessage = {
render: DynamicTemplate,
args: {
...defaultProps,
isInvalid: true,
errorMessage: "Please select an animal",
},
};
export const WithErrorMessageFunction = {
render: DynamicTemplate,
args: {
...defaultProps,
isInvalid: true,
errorMessage: (value: ValidationResult) => {
if (value.isInvalid) {
return "Please select an animal";
}
},
},
};
export const WithChips = {
render: CustomItemsTemplate,
args: {
...defaultProps,
variant: "bordered",
selectionMode: "multiple",
isMultiline: true,
labelPlacement: "outside",
classNames: {
base: "max-w-xs",
trigger: "min-h-12 py-2",
},
renderValue: (items: SelectedItems<User>) => {
return (
<div className="flex flex-wrap gap-2">
{items.map((item) => (
<Chip key={item.key}>{item.data?.name}</Chip>
))}
</div>
);
},
},
};
export const WithSections = {
render: WithSectionsTemplate,
args: {
...defaultProps,
},
};
export const WithCustomSectionsStyles = {
render: WithCustomSectionsStylesTemplate,
args: {
...defaultProps,
},
};
export const WithAriaLabel = {
render: WithAriaLabelTemplate,
args: {
...defaultProps,
label: "Select an animal 🐹",
"aria-label": "Select an animal",
},
};
export const WithReactHookForm = {
render: WithReactHookFormTemplate,
args: {
...defaultProps,
},
};
export const WithServerValidation = {
render: ServerValidationTemplate,
args: {
...defaultProps,
},
};
export const WithServerValidationMultiple = {
render: ServerValidationTemplateWithMultiple,
args: {
...defaultProps,
},
};
export const WithScrollableContainer = {
render: ScrollableContainerTemplate,
args: {
...defaultProps,
},
};
export const Controlled = {
render: ControlledTemplate,
args: {
...defaultProps,
},
};
export const ControlledMultiple = {
render: ControlledMultipleTemplate,
args: {
...defaultProps,
},
};
export const ControlledOpen = {
render: ControlledOpenTemplate,
args: {
...defaultProps,
},
};
export const CustomSelectorIcon = {
render: Template,
args: {
...defaultProps,
disableSelectorIconRotation: true,
selectorIcon: <SelectorIcon />,
},
};
export const CustomItems = {
render: CustomItemsTemplate,
args: {
...defaultProps,
},
};
export const CustomRenderValue = {
render: CustomItemsTemplate,
args: {
...defaultProps,
labelPlacement: "outside",
classNames: {
trigger: "h-12",
},
renderValue: (items: SelectedItems<User>) => {
return items.map((item) => (
<div key={item.key} className="flex items-center gap-2">
<Avatar alt={item.data?.name} className="shrink-0" size="sm" src={item.data?.avatar} />
<div className="flex flex-col">
<span>{item.data?.name}</span>
<span className="text-default-500 text-tiny">({item.data?.email})</span>
</div>
</div>
));
},
},
};
export const CustomStyles = {
render: CustomStylesTemplate,
args: {
...defaultProps,
variant: "bordered",
renderValue: (items: SelectedItems<User>) => {
return items.map((item) => (
<div key={item.key} className="flex items-center gap-2">
<Avatar alt={item.data?.name} className="shrink-0" size="sm" src={item.data?.avatar} />
<div className="flex flex-col">
<span>{item.data?.name}</span>
<span className="text-default-500 text-tiny">({item.data?.email})</span>
</div>
</div>
));
},
},
};
export const OneThousandList = {
render: LargeDatasetTemplate,
args: {
...defaultProps,
placeholder: "Select an item...",
numItems: 1000,
isVirtualized: true,
},
};
export const TenThousandList = {
render: LargeDatasetTemplate,
args: {
...defaultProps,
placeholder: "Select an item...",
numItems: 10000,
isVirtualized: true,
},
};
export const CustomMaxListboxHeight = {
render: LargeDatasetTemplate,
args: {
...defaultProps,
placeholder: "Select an item...",
numItems: 1000,
isVirtualized: true,
maxListboxHeight: 400,
},
};
export const CustomItemHeight = {
render: LargeDatasetTemplate,
args: {
...defaultProps,
placeholder: "Select an item...",
numItems: 1000,
isVirtualized: true,
maxListboxHeight: 400,
itemHeight: 40,
},
};
export const Clearable = {
render: Template,
args: {
...defaultProps,
isClearable: true,
},
};
const AVATAR_DECORATIONS: {[key: string]: string[]} = {
arcane: ["jinx", "atlas-gauntlets", "flame-chompers", "fishbones", "hexcore", "shimmer"],
anime: ["cat-ears", "heart-bloom", "in-love", "in-tears", "soul-leaving-body", "starry-eyed"],
"lofi-vibes": ["chromawave", "cozy-cat", "cozy-headphones", "doodling", "rainy-mood"],
valorant: [
"a-hint-of-clove",
"blade-storm",
"cypher",
"frag-out",
"omen-cowl",
"reyna-leer",
"vct-supernova",
"viper",
"yoru",
"carnalito2",
"a-hint-of-clove2",
"blade-storm2",
"cypher2",
"frag-out2",
"omen-cowl2",
"reyna-leer2",
"vct-supernova2",
"viper2",
"yoru2",
"carnalito3",
"a-hint-of-clove3",
"blade-storm3",
"cypher3",
"frag-out3",
"omen-cowl3",
"reyna-leer3",
"vct-supernova3",
"viper3",
"yoru3",
"carnalito4",
"a-hint-of-clove4",
"blade-storm4",
"cypher4",
"frag-out4",
"omen-cowl4",
"reyna-leer4",
"vct-supernova4",
"viper4",
"yoru4",
],
spongebob: [
"flower-clouds",
"gary-the-snail",
"imagination",
"musclebob",
"sandy-cheeks",
"spongebob",
],
arcade: ["clyde-invaders", "hot-shot", "joystick", "mallow-jump", "pipedream", "snake"],
"street-fighter": ["akuma", "cammy", "chun-li", "guile", "juri", "ken", "m.bison", "ryu"],
};
export const NonVirtualizedVsVirtualizedWithSections = {
render: () => {
const SelectComponent = ({isVirtualized}: {isVirtualized: boolean}) => (
<Select
disallowEmptySelection
className="max-w-xs"
color="secondary"
defaultSelectedKeys={["jinx"]}
isVirtualized={isVirtualized}
label={`Avatar Decoration ${isVirtualized ? "(Virtualized)" : "(Non-virtualized)"}`}
selectedKeys={["jinx"]}
selectionMode="single"
variant="bordered"
>
{Object.keys(AVATAR_DECORATIONS).map((key) => (
<SelectSection
key={key}
classNames={{
heading: "uppercase text-secondary",
}}
title={key}
>
{AVATAR_DECORATIONS[key].map((item) => (
<SelectItem key={item} className="capitalize" color="secondary" variant="bordered">
{item.replace(/-/g, " ")}
</SelectItem>
))}
</SelectSection>
))}
</Select>
);
return (
<div className="flex gap-4 w-full">
<SelectComponent isVirtualized={false} />
<SelectComponent isVirtualized={true} />
</div>
);
},
};
export const ValidationBehaviorAria = {
render: ValidationBehaviorAriaTemplate,
args: {
...defaultProps,
},
};
export const PopoverTopOrBottom = {
args: {
...defaultProps,
},
render: (args) => (
<div className="relative h-screen w-screen">
<div className="absolute top-0 p-8">
<div className="w-48">
<Template {...args} />
</div>
</div>
<div className="absolute top-1/2 p-8">
<div className="w-48">
<Template {...args} />
</div>
</div>
</div>
),
};