Repository navigation
feat: [select, sidebar, drawer] forward positioner props, unify side #977
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -113,7 +113,7 @@ The button that triggers the Select. | |
|
|
||
| ### Content | ||
|
|
||
| The container that holds the Select items. | ||
| The container that holds the Select items. It passes the Base UI Positioner props, such as `collisionPadding` and `sticky`, to the positioner. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Limit the forwarding claim to the supported positioning props.
🤖 Prompt for AI Agents |
||
|
|
||
| <auto-type-table path="./props.ts" name="SelectContentProps" /> | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -79,6 +79,49 @@ export interface SelectContentProps { | |||||||||||||||||||
| */ | ||||||||||||||||||||
| sideOffset?: number; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /** | ||||||||||||||||||||
| * Additional offset in pixels along the alignment axis. | ||||||||||||||||||||
| * @default 0 | ||||||||||||||||||||
| */ | ||||||||||||||||||||
| alignOffset?: number; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /** Space in pixels to keep from the edge of the collision boundary. */ | ||||||||||||||||||||
| collisionPadding?: number; | ||||||||||||||||||||
|
Comment on lines
+88
to
+89
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: Base UI types
Suggested change
|
||||||||||||||||||||
|
|
||||||||||||||||||||
| /** | ||||||||||||||||||||
| * The element or area that confines the content. | ||||||||||||||||||||
| * @default "clipping-ancestors" | ||||||||||||||||||||
| */ | ||||||||||||||||||||
| collisionBoundary?: Element | Element[] | 'clipping-ancestors'; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /** How the content moves when it does not fit on the chosen side. */ | ||||||||||||||||||||
| collisionAvoidance?: { | ||||||||||||||||||||
| side?: 'flip' | 'shift' | 'none'; | ||||||||||||||||||||
| align?: 'flip' | 'shift' | 'none'; | ||||||||||||||||||||
| fallbackAxisSide?: 'start' | 'end' | 'none'; | ||||||||||||||||||||
| }; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /** | ||||||||||||||||||||
| * Keeps the content in the viewport after the trigger scrolls out of view. | ||||||||||||||||||||
| * @default false | ||||||||||||||||||||
| */ | ||||||||||||||||||||
| sticky?: boolean; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /** | ||||||||||||||||||||
| * The CSS `position` of the content. | ||||||||||||||||||||
| * @default "absolute" | ||||||||||||||||||||
| */ | ||||||||||||||||||||
| positionMethod?: 'absolute' | 'fixed'; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /** An element to place the content against, in place of the trigger. */ | ||||||||||||||||||||
| anchor?: Element | React.RefObject<Element | null> | null; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /** | ||||||||||||||||||||
| * Stops the content from following layout shifts of the anchor. | ||||||||||||||||||||
| * @default false | ||||||||||||||||||||
| */ | ||||||||||||||||||||
| disableAnchorTracking?: boolean; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /** Additional CSS class names. */ | ||||||||||||||||||||
| className?: string; | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,7 +22,7 @@ const BasicDrawer = ({ | |
| } & Record<string, unknown>) => ( | ||
| <Drawer side={side} {...props}> | ||
| <Drawer.Trigger render={<Button />}>{TRIGGER_TEXT}</Drawer.Trigger> | ||
| <Drawer.Content side={side} showCloseButton={showCloseButton}> | ||
| <Drawer.Content showCloseButton={showCloseButton}> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. question: Now that |
||
| <Drawer.Header> | ||
| <Drawer.Title>{DRAWER_TITLE}</Drawer.Title> | ||
| <Drawer.Description>{DRAWER_DESCRIPTION}</Drawer.Description> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,14 @@ | ||
| 'use client'; | ||
|
|
||
| import { Drawer as DrawerPrimitive } from '@base-ui/react/drawer'; | ||
| import { cva, cx, type VariantProps } from 'class-variance-authority'; | ||
| import { cva, cx } from 'class-variance-authority'; | ||
| import { ReactNode } from 'react'; | ||
| import { XIcon } from '~/icons'; | ||
| import { type Radius, radiusVariants } from '../../shared/radius'; | ||
| import { IconButton } from '../icon-button'; | ||
| import { useThemeInjection } from '../theme/portal'; | ||
| import styles from './drawer.module.css'; | ||
| import { useDrawerSide } from './drawer-root'; | ||
|
|
||
| const drawerPopup = cva(styles.drawerPopup, { | ||
| variants: { | ||
|
|
@@ -18,15 +19,11 @@ const drawerPopup = cva(styles.drawerPopup, { | |
| left: styles['drawerPopup-left'], | ||
| right: styles['drawerPopup-right'] | ||
| } | ||
| }, | ||
| defaultVariants: { | ||
| side: 'right' | ||
| } | ||
| }); | ||
|
|
||
| export interface DrawerContentProps | ||
| extends Omit<DrawerPrimitive.Popup.Props, 'children'>, | ||
| VariantProps<typeof drawerPopup> { | ||
| extends Omit<DrawerPrimitive.Popup.Props, 'children'> { | ||
| showCloseButton?: boolean; | ||
| overlayProps?: DrawerPrimitive.Backdrop.Props; | ||
| children?: ReactNode; | ||
|
|
@@ -42,7 +39,6 @@ export interface DrawerContentProps | |
| export function DrawerContent({ | ||
| className, | ||
| children, | ||
| side = 'right', | ||
| showCloseButton = true, | ||
| overlayProps, | ||
| 'aria-label': ariaLabel, | ||
|
|
@@ -51,6 +47,7 @@ export function DrawerContent({ | |
| radius, | ||
| ...props | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: I tried Could we pull |
||
| }: DrawerContentProps) { | ||
| const side = useDrawerSide(); | ||
| const resolvedAriaLabel = | ||
| ariaLabel ?? (ariaLabelledBy ? undefined : 'Drawer'); | ||
| const theme = useThemeInjection(); | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -80,6 +80,30 @@ describe('Select', () => { | |||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| describe('Positioning', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| it.each([ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| true | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ])('forwards positioner props to the positioner (autocomplete: %s)', async autocomplete => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| render( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Select autocomplete={autocomplete}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Select.Trigger> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Select.Value placeholder={TRIGGER_TEXT} /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Select.Trigger> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Select.Content positionMethod='fixed'> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Select.Item value='apple'>Apple</Select.Item> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Select.Content> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Select> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| fireEvent.click(screen.getByLabelText('Select option')); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| await flushMicrotasks(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| document.querySelector('[data-slot="select-positioner"]') | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ).toHaveStyle({ position: 'fixed' }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+93
to
+103
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: This test checks that the positioner gets the prop, but not the bug the PR fixes, which is props reaching the popup. On main,
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| describe('Single Selection', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| it('displays selected value in trigger', async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| render(<BasicSelect defaultValue='apple' />); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
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.
suggestion:
docs/V1-migration.mdstill shows<Drawer.Content side="right" showCloseButton>(line 1709), which won't type-check after this PR, and<Sidebar ... position="left">(line 1805).Could we update both examples in this PR so people following that guide don't hit the error?