-
Notifications
You must be signed in to change notification settings - Fork 0
Select primitive #38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
kawikabader
wants to merge
55
commits into
main
Choose a base branch
from
select-primitive
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+4,464
−424
Open
Select primitive #38
Changes from all commits
Commits
Show all changes
55 commits
Select commit
Hold shift + click to select a range
63b3170
docs(select): add Select Primitive PDR with purpose, patterns, access…
kawikabader 6a10d3b
docs(select): update PDR with naming conventions, implementation patt…
kawikabader 3b65f52
docs(select): clarify implementation pattern and update core concepts…
kawikabader 236ef88
Merge branch 'main' into select-primitive-pdr
kbader-godaddy 784b785
Merge branch 'main' into select-primitive-pdr
kbader-godaddy 00b872a
docs(select): update Select primitive pdr
kawikabader 29cd16d
docs(select): refine Select primitive documentation for clarity and a…
kawikabader 72a05c6
Merge branch 'main' into select-primitive-pdr
kawikabader e099926
docs(select): align PDR with orchestration pattern and slot composition
kawikabader cbbc969
feat(select): add Select component with slot composition and examples
kawikabader 44506c6
refactor(select): simplify hidden select option rendering for readabi…
kawikabader 95f5d6c
docs(select): streamline Select primitive documentation for clarity a…
kawikabader d08cf1b
refactor(select): adopt container-based composition and enhance docs,…
kawikabader 540c637
chore(select): remove unused dependency on @react-aria/utils from pac…
kawikabader a3aebe4
feat(select): introduce @bento/select component
kawikabader 10128c5
feat(select): add multi-select support, new examples, and improved st…
kawikabader 28b32e6
chore(select): update configuration files and improve test coverage s…
kawikabader 6006ad2
refactor(select): consolidate examples and migrate from SelectOption …
kawikabader 928b386
docs(select): refine Select primitive documentation, clarify attribut…
kawikabader 1d7f97f
feat(select): add @bento/select with dynamic/grouped examples, improv…
kawikabader 787e22f
fix(use-props): handle falsy values in renderProp function
kawikabader aaf4a86
refactor(pressable): extract interaction state before useProps
kawikabader 9348b1a
refactor(button): use React Aria hooks directly instead of Pressable
kawikabader 1e5bc94
chore(listbox): simplify example CSS and remove unused value prop
kawikabader db41418
refactor(select): update data-open attribute handling for improved co…
kawikabader e900637
refactor(button, select): remove unused React imports and enhance but…
kawikabader 7e4d04c
Merge branch 'main' into select-primitive
kawikabader ffdffb0
Merge branch 'main' into select-primitive
kawikabader 45347ab
docs: changeset
kawikabader d036dad
refactor(select, button): remove unused imports and update button pro…
kawikabader 10d01c2
feat(use-props): add type-safe function overloads
kawikabader 02eeeca
test(use-props): add coverage for falsy value handling in renderProp
kawikabader 1aa03bc
refactor(button): single useProps call with explicit slot passthrough
kawikabader e32e552
refactor(select): use usePopover directly, remove wrapper hook
kawikabader bae2e48
fix(overlay): update example to use standard ref pattern
kawikabader 5da142b
fix(text): use React.ElementType for as prop
kawikabader b991e42
chore: normalize package.json formatting
kawikabader 3ab564c
fix(button): add aria-labelledby to slot passthrough props
kawikabader 3e31e9c
test(select): add 100% coverage for example components
kawikabader cb65324
fix(select): improve ref handling in Popover component
kawikabader 9952b25
refactor(pressable): merge slot props for improved React Aria hook in…
kawikabader 576bd49
feat(button): improve examples and enhance prop handling in Button co…
kawikabader 5a88b02
refactor(button): remove unused ButtonInFormExample from test suite
kawikabader 47b3b13
Merge main into select-primitive
kawikabader 17079f5
fix(button): improve test coverage for render prop example
kawikabader 160d9e2
refactor(button): streamline Button implementation and improve prop h…
kawikabader 8c40328
refactor(pressable): enhance Pressable component with forwarded ref a…
kawikabader 412343b
refactor(select): improve type handling and add ref forwarding to Select
kawikabader 8127c4d
refactor: update package imports and add watch script to multiple pac…
kawikabader 78add36
Merge branch 'main' into select-primitive
kawikabader 63e436d
refactor(select): add renderItem for dynamic collections and improve …
kawikabader 32b3dad
refactor(listbox): update prop handling for render prop support in Li…
kawikabader 91901fe
Merge branch 'main' into select-primitive
kbader-godaddy 7bc29e9
refactor(select): streamline dynamic collection handling in examples …
kawikabader 62d6ff2
refactor(button): enhance Button component with internal ref handling…
kawikabader File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| --- | ||
| "@bento/visually-hidden": minor | ||
| "@bento/use-svg-sprite": minor | ||
| "@bento/illustration": minor | ||
| "@bento/field-error": minor | ||
| "@bento/scroll-lock": minor | ||
| "@bento/focus-lock": minor | ||
| "@bento/container": minor | ||
| "@bento/pressable": minor | ||
| "@bento/use-props": minor | ||
| "@bento/checkbox": minor | ||
| "@bento/dismiss": minor | ||
| "@bento/divider": minor | ||
| "@bento/forward": minor | ||
| "@bento/heading": minor | ||
| "@bento/listbox": minor | ||
| "@bento/overlay": minor | ||
| "@bento/button": minor | ||
| "@bento/portal": minor | ||
| "@bento/select": minor | ||
| "@bento/error": minor | ||
| "@bento/radio": minor | ||
| "@bento/slots": minor | ||
| "@bento/icon": minor | ||
| "@bento/text": minor | ||
| "@bento/box": minor | ||
| --- | ||
|
|
||
| Add @bento/select primitive. Includes Button refactor for forwardRef support and renderProp falsy value fix. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,43 @@ | ||
| import { Button } from '@bento/button'; | ||
| /* v8 ignore next */ | ||
| import React from 'react'; | ||
| import { Button, type ButtonProps } from '@bento/button'; | ||
|
|
||
| export function ButtonExample() { | ||
| export function ButtonExample(props: ButtonProps) { | ||
| return <Button {...props}>{props.children || 'Click me'}</Button>; | ||
| } | ||
|
|
||
| export function ButtonWithAriaExample() { | ||
| return ( | ||
| <Button | ||
| onPress={function handlePress() { | ||
| console.log('button pressed!'); | ||
| }} | ||
| > | ||
| Click me! | ||
| <Button aria-label="Close dialog" aria-expanded="false" aria-haspopup="dialog"> | ||
| Close | ||
| </Button> | ||
| ); | ||
| } | ||
|
|
||
| export function ButtonWithDataAttributesExample() { | ||
| return ( | ||
| <Button data-testid="my-button" data-foo="bar" data-select-trigger="true"> | ||
| Trigger | ||
| </Button> | ||
| ); | ||
| } | ||
|
|
||
| export function ButtonInFormExample() { | ||
| return ( | ||
| <form id="test-form"> | ||
| <Button type="submit" form="test-form" name="action" value="submit"> | ||
| Submit | ||
| </Button> | ||
| </form> | ||
| ); | ||
| } | ||
|
|
||
| export function DisabledButtonExample() { | ||
| return <Button isDisabled>Disabled Button</Button>; | ||
| } | ||
|
|
||
| export function ButtonWithRenderPropExample() { | ||
| return ( | ||
| <Button onPress={() => console.log('pressed')}>{({ isHovered }) => (isHovered ? 'Hover!' : 'Click me')}</Button> | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,38 +1,102 @@ | ||
| import { useProps } from '@bento/use-props'; | ||
| import { withSlots } from '@bento/slots'; | ||
| import { Pressable, type PressableProps } from '@bento/pressable'; | ||
| import { useButton } from 'react-aria'; | ||
| import React, { ComponentProps } from 'react'; | ||
| import { useProps } from '@bento/use-props'; | ||
| import { useDataAttributes } from '@bento/use-data-attributes'; | ||
| import { useButton, useFocusRing, useHover, mergeProps, type AriaButtonProps, type HoverEvents } from 'react-aria'; | ||
| import { mergeRefs } from '@react-aria/utils'; | ||
| /* v8 ignore next */ | ||
| import React from 'react'; | ||
|
|
||
| export interface ButtonProps | ||
| extends Omit<PressableProps, 'children'>, | ||
| Omit<ComponentProps<'button'>, keyof PressableProps> { | ||
| /** A ref to the button element. This is useful if you want to access the button element directly. */ | ||
| childRef?: React.Ref<HTMLButtonElement>; | ||
|
|
||
| extends Omit<AriaButtonProps, 'children' | 'href' | 'target' | 'rel' | 'elementType'>, | ||
| HoverEvents, | ||
| Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, keyof AriaButtonProps | 'children'> { | ||
| /** The content to display inside the button. */ | ||
| children: React.ReactNode; | ||
| children: React.ReactNode | ((props: ButtonRenderProps) => React.ReactNode); | ||
| } | ||
|
|
||
| export interface ButtonRenderProps { | ||
| /** Whether the button is currently pressed. */ | ||
| isPressed: boolean; | ||
| /** Whether the button is currently hovered. */ | ||
| isHovered: boolean; | ||
| /** Whether the button is focused. */ | ||
| isFocused: boolean; | ||
| /** Whether the button is keyboard focused. */ | ||
| isFocusVisible: boolean; | ||
| } | ||
|
|
||
| /** | ||
| * A complete button component built on top of the Pressable primitive. | ||
| * Renders as a native button element with all accessibility and interaction features. | ||
| * A button component built on React Aria's useButton. | ||
| * | ||
| * @example | ||
| * <Button onPress={() => console.log('pressed')}>Click me</Button> | ||
| * | ||
| * @example | ||
| * ```tsx | ||
| * <Button onPress={() => console.log('Button pressed!')}>Click me</Button> | ||
| * ``` | ||
| * <Button>{({ isPressed }) => isPressed ? 'Pressing...' : 'Click me'}</Button> | ||
| */ | ||
| export const Button = withSlots('BentoButton', function Button(args: ButtonProps) { | ||
| const { props } = useProps(args); | ||
| const { children, childRef, ...restProps } = props; | ||
| const { buttonProps } = useButton(restProps, childRef); | ||
|
|
||
| return ( | ||
| <Pressable {...restProps} slot="pressable"> | ||
| <button {...buttonProps} ref={childRef}> | ||
| {children} | ||
| export const Button = withSlots( | ||
| 'BentoButton', | ||
| function Button(args: ButtonProps, forwardedRef: React.Ref<HTMLButtonElement>) { | ||
| // First pass: merge slot props so React Aria hooks see complete props | ||
| const { props: mergedProps, ref: mergedRef } = useProps(args, {}, forwardedRef); | ||
| const buttonRef = React.useRef<HTMLButtonElement>(null); | ||
|
|
||
| // React Aria hooks receive slot-merged props and internal ref | ||
| const { buttonProps, isPressed } = useButton(mergedProps, buttonRef); | ||
| const { focusProps, isFocused, isFocusVisible } = useFocusRing(mergedProps); | ||
| const { hoverProps, isHovered } = useHover(mergedProps); | ||
|
|
||
| // Render state for children render function | ||
| const renderState: ButtonRenderProps = { | ||
| isPressed, | ||
| isHovered, | ||
| isFocused, | ||
| isFocusVisible | ||
| }; | ||
|
|
||
| // Execute children render prop explicitly (before useProps to prevent incorrect execution) | ||
| const content = typeof args.children === 'function' ? args.children(renderState) : args.children; | ||
|
|
||
| // Second pass: apply user props with render state via apply() | ||
| // Apply merges React Aria props as defaults that slots can override | ||
| const { apply } = useProps(args, renderState); | ||
|
|
||
| const dataAttrs = useDataAttributes({ | ||
| pressed: isPressed, | ||
| hovered: isHovered, | ||
| focused: isFocused, | ||
| focusVisible: isFocusVisible, | ||
| disabled: mergedProps.isDisabled | ||
| }); | ||
|
|
||
| return ( | ||
| <button | ||
| {...apply( | ||
| { | ||
| ...mergeProps(buttonProps, focusProps, hoverProps) | ||
| }, | ||
| [ | ||
| 'ref', | ||
| 'children', | ||
| 'isDisabled', | ||
| 'excludeFromTabOrder', | ||
| 'preventFocusOnPress', | ||
| 'onPress', | ||
| 'onPressStart', | ||
| 'onPressEnd', | ||
| 'onPressUp', | ||
| 'onPressChange', | ||
| 'onHoverStart', | ||
| 'onHoverEnd', | ||
| 'onHoverChange', | ||
| 'onFocusChange' | ||
| ] | ||
| )} | ||
| {...dataAttrs} | ||
| ref={mergeRefs(buttonRef, mergedRef)} | ||
| > | ||
| {content} | ||
| </button> | ||
| </Pressable> | ||
| ); | ||
| }); | ||
| ); | ||
| } | ||
| ); | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So here we are deviating from how Bento handles the "function" props, no? https://github.com/godaddy/bento/blob/main/packages/use-props/src/index.ts#L221-L226
Not sure if exposing those states in the second argument of
usePropsis the right call but I'd look into it: