feat(floating-actions): add FloatingActions component#745
feat(floating-actions): add FloatingActions component#745paanSinghCoder wants to merge 12 commits intomainfrom
Conversation
A floating bar for surfacing contextual actions (bulk-action toolbar, row hover actions, etc.). Position-agnostic visual primitive with a matching vertical separator; composes freely with existing Chip, Button, and IconButton. - Component source, styles (--rs-shadow-lifted), and tests - Docs page with preview, bulk-actions, and icon-only demos - Playground example and examples/page.tsx section Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Rate limit exceeded
To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughAdds a new FloatingActions UI component and exports it from the raystack package. Changes include: a Root component with Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/www/src/app/examples/page.tsx`:
- Around line 2818-2831: The IconButton components inside the FloatingActions
(the icon-only row actions using IconButton with GearIcon, FileTextIcon, and
DotsHorizontalIcon) lack accessible names; update each IconButton to provide an
accessible label (e.g., add an aria-label or aria-labelledby with a clear short
name like "Settings", "View file", "More actions") so screen readers can
identify them, or alternatively include visually hidden text inside the button
and keep the icons for visual users; ensure you update all three IconButton
instances (the ones wrapping GearIcon, FileTextIcon, and DotsHorizontalIcon).
In `@apps/www/src/content/docs/components/floating-actions/demo.ts`:
- Around line 42-47: The IconButton instances inside the FloatingActions demo
lack accessible names; update each icon-only IconButton (the IconButton
components wrapping Pencil2Icon, UploadIcon, and InfoCircledIcon) to provide
accessible labels—e.g., add an aria-label prop or include visually hidden text
describing the action like "Edit", "Upload", and "More info"—so screen readers
can announce their purpose; leave FloatingActions.Separator as-is.
In `@apps/www/src/content/docs/components/floating-actions/props.ts`:
- Around line 1-18: Update the docs types to match the implementation by
importing React types and extending the div passthrough API: add an import for
React (so React.ComponentProps and React.ReactNode are available) and change
FloatingActionsProps to extend React.ComponentProps<'div'> (keeping or removing
explicit props like children if desired) and change
FloatingActionsSeparatorProps to extend React.ComponentProps<'div'> so all
native div attributes (aria-*, data-*, event handlers, style, ref, etc.) are
supported; update references to FloatingActionsProps and
FloatingActionsSeparatorProps accordingly.
In
`@packages/raystack/components/floating-actions/__tests__/floating-actions.test.tsx`:
- Around line 43-75: The Separator component currently allows callers to
override aria-hidden because props are spread after the hardcoded attribute;
update the FloatingActions.Separator implementation in floating-actions.tsx to
spread {...props} first and then set aria-hidden="true" (so the hardcoded value
wins), and add/keep a regression test in floating-actions.test.tsx that renders
<FloatingActions.Separator aria-hidden="false" data-testid="sep"> and asserts
the element has aria-hidden="true"; reference the FloatingActions.Separator
component and ensure ref forwarding and className tests still pass.
In `@packages/raystack/components/floating-actions/floating-actions.tsx`:
- Around line 24-28: The separator div in floating-actions.tsx currently spreads
{...props} after setting aria-hidden='true', allowing callers to override it;
fix by ensuring aria-hidden stays true when props are forwarded—either move
{...props} before aria-hidden or (preferable) keep {...props} but explicitly set
aria-hidden={true} after the spread on the same div (the element using
className={cx(styles.separator, className)}), so aria-hidden cannot be
overridden by incoming props.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: e00c87d3-cd54-40ca-bfcf-9b26aff31d29
📒 Files selected for processing (11)
apps/www/src/app/examples/page.tsxapps/www/src/components/playground/floating-actions-examples.tsxapps/www/src/components/playground/index.tsapps/www/src/content/docs/components/floating-actions/demo.tsapps/www/src/content/docs/components/floating-actions/index.mdxapps/www/src/content/docs/components/floating-actions/props.tspackages/raystack/components/floating-actions/__tests__/floating-actions.test.tsxpackages/raystack/components/floating-actions/floating-actions.module.csspackages/raystack/components/floating-actions/floating-actions.tsxpackages/raystack/components/floating-actions/index.tsxpackages/raystack/index.tsx
| export interface FloatingActionsProps { | ||
| /** | ||
| * The ARIA role of the container. | ||
| * @defaultValue "toolbar" | ||
| */ | ||
| role?: string; | ||
|
|
||
| /** Additional CSS class names. */ | ||
| className?: string; | ||
|
|
||
| /** The contents of the floating bar. */ | ||
| children?: React.ReactNode; | ||
| } | ||
|
|
||
| export interface FloatingActionsSeparatorProps { | ||
| /** Additional CSS class names. */ | ||
| className?: string; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Compare FloatingActions docs props against implementation/docs prop typing patterns.
# Expected: implementation uses div passthrough props; docs should either mirror that or intentionally document native props elsewhere.
rg -n -C3 "FloatingActionsProps|FloatingActionsSeparatorProps|ComponentProps<'div'>|React\\.ReactNode" \
packages/raystack/components/floating-actions \
apps/www/src/content/docs/components/floating-actionsRepository: raystack/apsara
Length of output: 4739
🏁 Script executed:
# Check for docs generator behavior with inherited props
find apps/www -type f -name "*.ts" -o -name "*.tsx" -o -name "*.js" | xargs grep -l "auto-type-table\|type-table" | head -5Repository: raystack/apsara
Length of output: 105
🏁 Script executed:
# Find other components using ComponentProps<'div'> in docs to see if there's a pattern
fd -e "props.ts" "apps/www/src/content/docs/components" | head -10Repository: raystack/apsara
Length of output: 508
🏁 Script executed:
# Check if React is imported in the docs props file
head -20 apps/www/src/content/docs/components/floating-actions/props.tsRepository: raystack/apsara
Length of output: 440
🏁 Script executed:
# Look for an implementation pattern in other components that use ComponentProps<'div'>
find packages/raystack/components -type f -name "*.tsx" -exec grep -l "extends ComponentProps<'div'>" {} \; | head -3Repository: raystack/apsara
Length of output: 453
🏁 Script executed:
# Check other component docs props files for import patterns
ls apps/www/src/content/docs/components/*/props.ts | head -5 | xargs head -3Repository: raystack/apsara
Length of output: 832
🏁 Script executed:
# Check if any docs props files import React or ComponentProps
grep -r "^import\|^export interface" apps/www/src/content/docs/components/*/props.ts | head -20Repository: raystack/apsara
Length of output: 1991
🏁 Script executed:
# Check the auto-type-table implementation in mdx-components
grep -A 20 "auto-type-table" apps/www/src/components/mdx/mdx-components.tsx | head -40Repository: raystack/apsara
Length of output: 41
🏁 Script executed:
# Check tsconfig for global React types
find . -name "tsconfig.json" -o -name "tsconfig.*.json" | xargs grep -l "React\|types" | head -3Repository: raystack/apsara
Length of output: 156
🏁 Script executed:
# Check tsconfig for global React types
cat apps/www/tsconfig.json | grep -A 5 -B 5 "React\|types\|skipLibCheck"Repository: raystack/apsara
Length of output: 530
🏁 Script executed:
# Check the actual implementation interfaces more carefully
cat -n packages/raystack/components/floating-actions/floating-actions.tsx | head -30Repository: raystack/apsara
Length of output: 1083
🏁 Script executed:
# Check if floating-actions docs props.ts actually compiles (check for any import)
cat -n apps/www/src/content/docs/components/floating-actions/props.tsRepository: raystack/apsara
Length of output: 566
🏁 Script executed:
# Look at breadcrumb props.ts (which has imports) for comparison
cat -n apps/www/src/content/docs/components/breadcrumb/props.ts | head -30Repository: raystack/apsara
Length of output: 1050
Align the docs props with the implementation's div passthrough API.
The implementation extends ComponentProps<'div'> and spreads all props, but the docs interface is a manual subset listing only role, className, and children. This hides the supported native div attributes (aria-, data-, event handlers, style, ref, etc.). Also, the docs file uses React.ReactNode without importing React, which is inconsistent with other component docs files like breadcrumb.
Import React types explicitly and extend ComponentProps<'div'> in both interfaces to match the implementation:
📝 Proposed docs type update
+import type { ComponentProps } from 'react';
+
-export interface FloatingActionsProps {
+export interface FloatingActionsProps extends ComponentProps<'div'> {
/**
* The ARIA role of the container.
* `@defaultValue` "toolbar"
*/
- role?: string;
/** Additional CSS class names. */
- className?: string;
/** The contents of the floating bar. */
- children?: React.ReactNode;
}
-export interface FloatingActionsSeparatorProps {
+export interface FloatingActionsSeparatorProps extends ComponentProps<'div'> {
/** Additional CSS class names. */
- className?: string;
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/www/src/content/docs/components/floating-actions/props.ts` around lines
1 - 18, Update the docs types to match the implementation by importing React
types and extending the div passthrough API: add an import for React (so
React.ComponentProps and React.ReactNode are available) and change
FloatingActionsProps to extend React.ComponentProps<'div'> (keeping or removing
explicit props like children if desired) and change
FloatingActionsSeparatorProps to extend React.ComponentProps<'div'> so all
native div attributes (aria-*, data-*, event handlers, style, ref, etc.) are
supported; update references to FloatingActionsProps and
FloatingActionsSeparatorProps accordingly.
| it('renders a separator with the separator class', () => { | ||
| render( | ||
| <FloatingActions> | ||
| <FloatingActions.Separator data-testid='sep' /> | ||
| </FloatingActions> | ||
| ); | ||
| const sep = screen.getByTestId('sep'); | ||
| expect(sep).toBeInTheDocument(); | ||
| expect(sep.className).toContain(styles.separator); | ||
| expect(sep).toHaveAttribute('aria-hidden', 'true'); | ||
| }); | ||
|
|
||
| it('applies custom className', () => { | ||
| render( | ||
| <FloatingActions> | ||
| <FloatingActions.Separator data-testid='sep' className='custom-sep' /> | ||
| </FloatingActions> | ||
| ); | ||
| const sep = screen.getByTestId('sep'); | ||
| expect(sep.className).toContain(styles.separator); | ||
| expect(sep.className).toContain('custom-sep'); | ||
| }); | ||
|
|
||
| it('forwards ref', () => { | ||
| const ref = createRef<HTMLDivElement>(); | ||
| render( | ||
| <FloatingActions> | ||
| <FloatingActions.Separator ref={ref} data-testid='sep' /> | ||
| </FloatingActions> | ||
| ); | ||
| expect(ref.current).toBeInstanceOf(HTMLDivElement); | ||
| expect(ref.current).toBe(screen.getByTestId('sep')); | ||
| }); |
There was a problem hiding this comment.
Lock the separator’s decorative accessibility contract.
The current implementation can let callers override aria-hidden because props are spread after the hardcoded value. Add a regression test here and move aria-hidden='true' after {...props} in floating-actions.tsx.
♿ Proposed regression test and implementation adjustment
it('renders a separator with the separator class', () => {
render(
<FloatingActions>
<FloatingActions.Separator data-testid='sep' />
</FloatingActions>
@@
expect(sep.className).toContain(styles.separator);
expect(sep).toHaveAttribute('aria-hidden', 'true');
});
+
+ it('keeps the separator hidden from assistive technology', () => {
+ render(
+ <FloatingActions>
+ <FloatingActions.Separator data-testid='sep' aria-hidden={false} />
+ </FloatingActions>
+ );
+
+ expect(screen.getByTestId('sep')).toHaveAttribute('aria-hidden', 'true');
+ });Apply this in packages/raystack/components/floating-actions/floating-actions.tsx:
<div
- aria-hidden='true'
className={cx(styles.separator, className)}
{...props}
+ aria-hidden='true'
/>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| it('renders a separator with the separator class', () => { | |
| render( | |
| <FloatingActions> | |
| <FloatingActions.Separator data-testid='sep' /> | |
| </FloatingActions> | |
| ); | |
| const sep = screen.getByTestId('sep'); | |
| expect(sep).toBeInTheDocument(); | |
| expect(sep.className).toContain(styles.separator); | |
| expect(sep).toHaveAttribute('aria-hidden', 'true'); | |
| }); | |
| it('applies custom className', () => { | |
| render( | |
| <FloatingActions> | |
| <FloatingActions.Separator data-testid='sep' className='custom-sep' /> | |
| </FloatingActions> | |
| ); | |
| const sep = screen.getByTestId('sep'); | |
| expect(sep.className).toContain(styles.separator); | |
| expect(sep.className).toContain('custom-sep'); | |
| }); | |
| it('forwards ref', () => { | |
| const ref = createRef<HTMLDivElement>(); | |
| render( | |
| <FloatingActions> | |
| <FloatingActions.Separator ref={ref} data-testid='sep' /> | |
| </FloatingActions> | |
| ); | |
| expect(ref.current).toBeInstanceOf(HTMLDivElement); | |
| expect(ref.current).toBe(screen.getByTestId('sep')); | |
| }); | |
| it('renders a separator with the separator class', () => { | |
| render( | |
| <FloatingActions> | |
| <FloatingActions.Separator data-testid='sep' /> | |
| </FloatingActions> | |
| ); | |
| const sep = screen.getByTestId('sep'); | |
| expect(sep).toBeInTheDocument(); | |
| expect(sep.className).toContain(styles.separator); | |
| expect(sep).toHaveAttribute('aria-hidden', 'true'); | |
| }); | |
| it('keeps the separator hidden from assistive technology', () => { | |
| render( | |
| <FloatingActions> | |
| <FloatingActions.Separator data-testid='sep' aria-hidden={false} /> | |
| </FloatingActions> | |
| ); | |
| expect(screen.getByTestId('sep')).toHaveAttribute('aria-hidden', 'true'); | |
| }); | |
| it('applies custom className', () => { | |
| render( | |
| <FloatingActions> | |
| <FloatingActions.Separator data-testid='sep' className='custom-sep' /> | |
| </FloatingActions> | |
| ); | |
| const sep = screen.getByTestId('sep'); | |
| expect(sep.className).toContain(styles.separator); | |
| expect(sep.className).toContain('custom-sep'); | |
| }); | |
| it('forwards ref', () => { | |
| const ref = createRef<HTMLDivElement>(); | |
| render( | |
| <FloatingActions> | |
| <FloatingActions.Separator ref={ref} data-testid='sep' /> | |
| </FloatingActions> | |
| ); | |
| expect(ref.current).toBeInstanceOf(HTMLDivElement); | |
| expect(ref.current).toBe(screen.getByTestId('sep')); | |
| }); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@packages/raystack/components/floating-actions/__tests__/floating-actions.test.tsx`
around lines 43 - 75, The Separator component currently allows callers to
override aria-hidden because props are spread after the hardcoded attribute;
update the FloatingActions.Separator implementation in floating-actions.tsx to
spread {...props} first and then set aria-hidden="true" (so the hardcoded value
wins), and add/keep a regression test in floating-actions.test.tsx that renders
<FloatingActions.Separator aria-hidden="false" data-testid="sep"> and asserts
the element has aria-hidden="true"; reference the FloatingActions.Separator
component and ensure ref forwarding and className tests still pass.
| <div | ||
| aria-hidden='true' | ||
| className={cx(styles.separator, className)} | ||
| {...props} | ||
| /> |
There was a problem hiding this comment.
Keep the separator hidden even when props are forwarded.
Because {...props} is applied after aria-hidden, a caller can override it with aria-hidden={false}, which breaks the documented separator semantics.
🛡️ Proposed fix
<div
- aria-hidden='true'
- className={cx(styles.separator, className)}
{...props}
+ aria-hidden='true'
+ className={cx(styles.separator, className)}
/>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div | |
| aria-hidden='true' | |
| className={cx(styles.separator, className)} | |
| {...props} | |
| /> | |
| <div | |
| {...props} | |
| aria-hidden='true' | |
| className={cx(styles.separator, className)} | |
| /> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/raystack/components/floating-actions/floating-actions.tsx` around
lines 24 - 28, The separator div in floating-actions.tsx currently spreads
{...props} after setting aria-hidden='true', allowing callers to override it;
fix by ensuring aria-hidden stays true when props are forwarded—either move
{...props} before aria-hidden or (preferable) keep {...props} but explicitly set
aria-hidden={true} after the spread on the same div (the element using
className={cx(styles.separator, className)}), so aria-hidden cannot be
overridden by incoming props.
Keep the component, docs, and playground — the examples/page.tsx scratch page is out of scope for this PR. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
FloatingActions.Separator now wraps `Separator` from the design system with `orientation="vertical"` and `size="full"` defaults, instead of re-implementing a local 1px×16px div. Drops the local `.separator` CSS class and picks up proper `role="separator"` semantics, size/color variants, and Base UI behavior for free. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The floating bar is horizontal-only, so the separator is always vertical. `Omit<..., 'orientation'>` from the prop type and drop the unused horizontal CSS rule. Scoped `.separator` class overrides the Separator primitive's size variant to --rs-space-5 per the Figma spec. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ute for vertical orientation
rohanchkrabrty
left a comment
There was a problem hiding this comment.
- IMO the
FloatingActionscomponent should have position agnostic props, likesideandalign.
There can be two variants - inline and floating(default), inline will be the current one. But in floating the FloatingAction will be actually floating using the side and align position props.
Right now consumer has to write a a block of css just to use this component. The name also is FloatingActions but nowhere it supports that functionality, so it's misleading.
- I think it should also support
orientationfor cases where the component is used on the left or right. But this is can be picked up later too
| export const FloatingActions = Object.assign(FloatingActionsRoot, { | ||
| Separator: FloatingActionsSeparator | ||
| }); |
There was a problem hiding this comment.
Split the object assign into a separate file or do it in index.ts
This pattern with client directive creates some weird client/server issues
There was a problem hiding this comment.
Moved to index.tsx keeping same export pattern as other components.
There was a problem hiding this comment.
File name should be index.ts for barrel export
| role = 'toolbar', | ||
| ...props | ||
| }: FloatingActionsProps) => ( | ||
| <div role={role} className={cx(styles.root, className)} {...props} /> |
There was a problem hiding this comment.
Let' use Toolbar Primitive from Base UI.
Also let's expose a Group subcomponent
There was a problem hiding this comment.
Done. Using the Base UI primitive. Also exposed the Group.
|
|
||
| ## Anatomy | ||
|
|
||
| Compose the bar from existing Apsara primitives. `FloatingActions` provides the container and a matching vertical separator; everything inside is freely composable. |
There was a problem hiding this comment.
This should be a simpler line like - Import and assemble the component along with other primtives
Follows other docs pattern
| children?: React.ReactNode; | ||
| } | ||
|
|
||
| export interface FloatingActionsSeparatorProps { |
There was a problem hiding this comment.
All Separator props should be documented especially color
|
|
||
| ### Separator | ||
|
|
||
| A vertical divider sized to the bar's content. |
There was a problem hiding this comment.
Mention that it's built on top of Separator component
There was a problem hiding this comment.
Added to the docs.
There was a problem hiding this comment.
There is no demo where the component is seen in a scrolling context.
Introduce variant, side, and align props so the component owns its positioning instead of pushing it onto consumers. Floating is the default and pins the bar to the viewport via position: fixed, mirroring Toast's data-position pattern. Inline preserves the prior in-flow behaviour for embedded contexts (cards, tables, docs demos). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Replace hand-rolled <div role="toolbar"> with Base UI Toolbar primitive to inherit roving focus, arrow-key navigation, and disabled cascade. - Expose FloatingActions.Group wrapping Toolbar.Group. - Move Object.assign out of the 'use client' file into a non-client index.ts (matches Field/Sidebar precedent), avoiding client/server boundary issues. Rename index.tsx -> index.ts (no JSX). - Trim Anatomy intro to the standard "Import and assemble the component:" line used by peer docs. - Document Separator's size and render props, and the Toolbar primitive props (disabled, orientation, loopFocus) on Root. - Add a scrolling-context demo using transform: translateZ(0) so the floating bar is scoped to the demo container instead of the page. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
packages/raystack/components/floating-actions/__tests__/floating-actions.test.tsx (2)
27-31: Test name doesn't match its assertions.The test is titled "renders the inline variant without floating positioning attributes" but only asserts
data-variant='inline'. To match the title (and provide actual coverage for the inline behavior), assert the absence of floating-only positioning side-effects or rename the test to something likeapplies data-variant="inline" when variant="inline".♻️ Suggested tightening
it('renders the inline variant without floating positioning attributes', () => { render(<FloatingActions variant='inline'>content</FloatingActions>); const root = screen.getByRole('toolbar'); expect(root).toHaveAttribute('data-variant', 'inline'); + // Inline variant should not be position: fixed; data-side/data-align are + // documented as floating-only and should not influence layout here. + expect(getComputedStyle(root).position).not.toBe('fixed'); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/raystack/components/floating-actions/__tests__/floating-actions.test.tsx` around lines 27 - 31, The test title and assertions are inconsistent: update the test in floating-actions.test.tsx for the FloatingActions component so it either (A) renames the spec to "applies data-variant=\"inline\" when variant=\"inline\"" to match the existing assertion for data-variant, or (B) keep the current title and add assertions that verify inline does not apply floating positioning (for example, assert the toolbar returned by getByRole('toolbar') does not have floating-only attributes/classes such as a data-floating attribute, a CSS class like 'floating', or a computed style position of 'fixed' or 'absolute'). Choose one approach and apply it to the test containing the render(<FloatingActions variant='inline'>...) and the expect(root)... assertion.
87-99: Color prop not actually asserted.The test name promises pass-through of both
colorandclassName, but onlycustom-sepis verified. Consider asserting the color variant class (or at least that the rendered element carries the correspondingSeparatorcolor class) so a regression incolorforwarding is caught.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/raystack/components/floating-actions/__tests__/floating-actions.test.tsx` around lines 87 - 99, The test for FloatingActions.Separator only asserts className forwarding but not color forwarding; update the spec in floating-actions.test.tsx to also assert that the rendered separator reflects the color='secondary' prop by checking for the Separator's color variant (e.g., expect(sep.className).toContain the Separator's secondary color class or expect a data-attribute/style that indicates 'secondary'), targeting FloatingActions.Separator in the existing test so both color and className pass-through are verified.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/www/src/content/docs/components/floating-actions/index.mdx`:
- Line 92: Update the documentation sentence that currently states the separator
renders with role="separator" and aria-orientation="vertical" so it accurately
reflects the Base UI primitive output: change the description to say it emits
role="separator" and data-orientation="vertical" (i.e., replace
aria-orientation="vertical" with data-orientation="vertical" in the separator
description).
- Around line 8-10: The page currently reuses the same demo export `preview` for
both the hero and inline sections, so create a distinct `inlineDemo` export in
demo.ts (clone or adapt from the existing `preview` but set variant="inline" and
any inline-specific props) and export it alongside `floatingDemo`/`preview`;
then update the MDX to render the hero with `preview` or `floatingDemo` and the
inline section with `<Demo data={inlineDemo} />` so the floating and inline
variants are different.
In `@packages/raystack/components/floating-actions/floating-actions.tsx`:
- Around line 1-6: The code uses React.ComponentProps<typeof Separator> but
never imports React; add an explicit import of ComponentProps from 'react'
(e.g., import { ComponentProps } from 'react') and update the type usage to use
ComponentProps<typeof Separator> where referenced (the type on/around the
FloatingActions props definition that currently uses React.ComponentProps<typeof
Separator>) so the file no longer depends on the global React namespace and
matches the package's peer components.
---
Nitpick comments:
In
`@packages/raystack/components/floating-actions/__tests__/floating-actions.test.tsx`:
- Around line 27-31: The test title and assertions are inconsistent: update the
test in floating-actions.test.tsx for the FloatingActions component so it either
(A) renames the spec to "applies data-variant=\"inline\" when
variant=\"inline\"" to match the existing assertion for data-variant, or (B)
keep the current title and add assertions that verify inline does not apply
floating positioning (for example, assert the toolbar returned by
getByRole('toolbar') does not have floating-only attributes/classes such as a
data-floating attribute, a CSS class like 'floating', or a computed style
position of 'fixed' or 'absolute'). Choose one approach and apply it to the test
containing the render(<FloatingActions variant='inline'>...) and the
expect(root)... assertion.
- Around line 87-99: The test for FloatingActions.Separator only asserts
className forwarding but not color forwarding; update the spec in
floating-actions.test.tsx to also assert that the rendered separator reflects
the color='secondary' prop by checking for the Separator's color variant (e.g.,
expect(sep.className).toContain the Separator's secondary color class or expect
a data-attribute/style that indicates 'secondary'), targeting
FloatingActions.Separator in the existing test so both color and className
pass-through are verified.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 1329785d-07f0-4724-a138-ae458c095ac5
📒 Files selected for processing (9)
apps/www/src/components/demo/demo.tsxapps/www/src/components/playground/floating-actions-examples.tsxapps/www/src/content/docs/components/floating-actions/demo.tsapps/www/src/content/docs/components/floating-actions/index.mdxapps/www/src/content/docs/components/floating-actions/props.tspackages/raystack/components/floating-actions/__tests__/floating-actions.test.tsxpackages/raystack/components/floating-actions/floating-actions.module.csspackages/raystack/components/floating-actions/floating-actions.tsxpackages/raystack/components/floating-actions/index.ts
✅ Files skipped from review due to trivial changes (3)
- apps/www/src/components/demo/demo.tsx
- packages/raystack/components/floating-actions/index.ts
- packages/raystack/components/floating-actions/floating-actions.module.css
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/www/src/components/playground/floating-actions-examples.tsx
- apps/www/src/content/docs/components/floating-actions/demo.ts
| ## Accessibility | ||
|
|
||
| - The root uses `role="toolbar"` (enforced by the Base UI Toolbar primitive) and is announced as a group of interactive controls. Keyboard focus moves between toolbar items with the arrow keys. | ||
| - The separator renders with `role="separator"` and `aria-orientation="vertical"` via the Base UI primitive, communicating structural grouping between action clusters. |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Does Base UI @base-ui/react Separator primitive emit aria-orientation attribute when orientation="vertical"?
💡 Result:
No, the Base UI @base-ui/react Separator primitive does not emit the aria-orientation attribute when orientation="vertical". It emits data-orientation="vertical" instead, along with role="separator" on a div element. This follows ARIA practices where aria-orientation is only required for vertical separators if not using the hr element (which Base UI does not), but official docs and sources confirm data-orientation for styling/selectors, not aria-orientation. Secondary docs and CSS examples referencing [aria-orientation="vertical"] appear to be incorrect or illustrative.
Citations:
- 1: https://base-ui.com/react/components/separator
- 2: https://mui-base-ui.mintlify.app/components/separator
- 3: https://cdn.jsdelivr.net/npm/@base-ui/react@1.1.0/separator/Separator.d.ts
- 4: [separator] Implement Separator mui/base-ui#70
- 5: [Menu] Group and Separator components mui/base-ui#535
- 6: separator: fix invisible separator shadcn-ui/ui#10201
Update the documentation to reflect that the separator emits data-orientation="vertical", not aria-orientation="vertical".
The Base UI Separator primitive does not emit aria-orientation. It emits data-orientation="vertical" along with role="separator". Update line 92 to accurately describe the actual rendered attributes.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/www/src/content/docs/components/floating-actions/index.mdx` at line 92,
Update the documentation sentence that currently states the separator renders
with role="separator" and aria-orientation="vertical" so it accurately reflects
the Base UI primitive output: change the description to say it emits
role="separator" and data-orientation="vertical" (i.e., replace
aria-orientation="vertical" with data-orientation="vertical" in the separator
description).
| 'use client'; | ||
|
|
||
| import { Toolbar as ToolbarPrimitive } from '@base-ui/react/toolbar'; | ||
| import { cx } from 'class-variance-authority'; | ||
| import { Separator } from '../separator'; | ||
| import styles from './floating-actions.module.css'; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Confirm whether React is imported anywhere in the file and how peers in the package handle the namespace.
fd -e tsx -e ts . packages/raystack/components -x sh -c 'grep -lE "React\.(ComponentProps|ReactNode|ReactElement|HTMLAttributes)" "$1"' _ {} | head -20
echo "---"
rg -nP "^import .*['\"]react['\"]" packages/raystack/components/floating-actions/floating-actions.tsxRepository: raystack/apsara
Length of output: 794
🏁 Script executed:
cat -n packages/raystack/components/floating-actions/floating-actions.tsxRepository: raystack/apsara
Length of output: 2657
🏁 Script executed:
# Check a couple of the files from the initial output to see if they import React
head -20 packages/raystack/components/avatar/avatar.tsx | grep -E "^import|React"
echo "---"
head -20 packages/raystack/components/data-table/components/virtualized-content.tsx | grep -E "^import|React"Repository: raystack/apsara
Length of output: 994
🏁 Script executed:
# Check tsconfig for React ambient settings
fd tsconfig.json packages/raystack | head -1 | xargs cat | head -50Repository: raystack/apsara
Length of output: 568
🏁 Script executed:
# Check if there's a global React declaration in global.d.ts or similar
cat packages/raystack/global.d.ts 2>/dev/null || echo "File not found or no React globals"
echo "---"
# Also check how avatar.tsx uses React.ComponentProps vs ComponentProps
rg "React\.ComponentProps|ComponentProps" packages/raystack/components/avatar/avatar.tsxRepository: raystack/apsara
Length of output: 443
Add an explicit ComponentProps import.
Line 60 references React.ComponentProps<typeof Separator> but the file never imports React. This relies on the global React namespace, which TypeScript can resolve through ambient types but is fragile and inconsistent with how peer components in the package handle it (e.g., avatar.tsx explicitly imports ComponentProps from 'react'). Replace with a direct import:
♻️ Proposed change
'use client';
+import type { ComponentProps } from 'react';
import { Toolbar as ToolbarPrimitive } from '@base-ui/react/toolbar';
import { cx } from 'class-variance-authority';
import { Separator } from '../separator';
import styles from './floating-actions.module.css';
@@
-export type FloatingActionsSeparatorProps = Omit<
- React.ComponentProps<typeof Separator>,
- 'orientation'
->;
+export type FloatingActionsSeparatorProps = Omit<
+ ComponentProps<typeof Separator>,
+ 'orientation'
+>;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/raystack/components/floating-actions/floating-actions.tsx` around
lines 1 - 6, The code uses React.ComponentProps<typeof Separator> but never
imports React; add an explicit import of ComponentProps from 'react' (e.g.,
import { ComponentProps } from 'react') and update the type usage to use
ComponentProps<typeof Separator> where referenced (the type on/around the
FloatingActions props definition that currently uses React.ComponentProps<typeof
Separator>) so the file no longer depends on the global React namespace and
matches the package's peer components.
# Conflicts: # apps/www/src/components/demo/demo.tsx
…preview edges Wrap each inline demo's bar in a div with paddingBlock so the rendered preview shows visible breathing room above and below. Inline style keeps the change scoped to floating-actions demos without leaking into other component preview areas. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… API FloatingActions accepts arbitrary children (Apsara Button, IconButton, Chip, custom components) rather than wrapping them via a registered Toolbar.Button-style subcomponent, so the Toolbar primitive's disabled cascade only affected items that happened to register with the primitive — not the typical children. Documenting it as "cascades to every focusable item" was misleading. Omit disabled from both FloatingActionsProps and FloatingActionsGroupProps. Consumers can disable individual items explicitly when needed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
packages/raystack/components/floating-actions/floating-actions.tsx (2)
40-47:data-*attributes can be silently overridden by callers.
data-variant,data-side, anddata-aligndrive the floating CSS positioning, but{...props}is spread after them, so a consumer who passes any of these as props (e.g. via type-loose forwarding) will silently break layout. Consider spreadingpropsfirst and pinning the styling-critical attributes last.♻️ Proposed change
<ToolbarPrimitive.Root - data-variant={variant} - data-side={side} - data-align={align} - className={cx(styles.root, className)} {...props} + data-variant={variant} + data-side={side} + data-align={align} + className={cx(styles.root, className)} />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/raystack/components/floating-actions/floating-actions.tsx` around lines 40 - 47, The data-* attributes controlling floating positioning (data-variant, data-side, data-align) are currently set before spreading {...props} into ToolbarPrimitive.Root, which allows callers to override them; move the {...props} spread before those attributes and ensure data-variant, data-side, data-align (and className={cx(styles.root, className)}) are specified last on ToolbarPrimitive.Root so the component's positioning props cannot be silently overridden by consumers.
12-13: Reconsider omittingdisabledfrom the public props.Both
FloatingActionsPropsandFloatingActionsGroupPropsomit thedisabledprop from the underlyingToolbarPrimitive.Root.PropsandToolbarPrimitive.Group.Props. This prevents consumers from using the toolbar's built-indisabledcascade feature—which disables all child controls—that the PR description explicitly identifies as an inherited behavior. Unless there's an intentional design reason (e.g., styling constraints for disabled state), consider removing theOmitrestrictions to allowdisabledto pass through.♻️ Proposed change
-export interface FloatingActionsProps - extends Omit<ToolbarPrimitive.Root.Props, 'disabled'> { +export interface FloatingActionsProps extends ToolbarPrimitive.Root.Props { @@ -export type FloatingActionsGroupProps = Omit< - ToolbarPrimitive.Group.Props, - 'disabled' ->; +export type FloatingActionsGroupProps = ToolbarPrimitive.Group.Props;Also applies to: 50-53
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/raystack/components/floating-actions/floating-actions.tsx` around lines 12 - 13, The props types currently strip the built-in disabled cascade by using Omit<'disabled'>; update FloatingActionsProps and FloatingActionsGroupProps to stop omitting disabled so consumers can pass the toolbar-level disabled flag (i.e., change the extends from Omit<ToolbarPrimitive.Root.Props, 'disabled'> and Omit<ToolbarPrimitive.Group.Props, 'disabled'> to extend the original ToolbarPrimitive.Root.Props and ToolbarPrimitive.Group.Props or include 'disabled' explicitly), and adjust any local usages or tests that assumed disabled was absent to accept and forward the disabled prop to the underlying ToolbarPrimitive components (refer to FloatingActionsProps, FloatingActionsGroupProps, ToolbarPrimitive.Root and ToolbarPrimitive.Group to locate the changes).apps/www/src/components/playground/floating-actions-examples.tsx (1)
83-101: Floating example will overlap the inline examples on this page.Since
variant="floating"(default) rendersposition: fixedat bottom-center, this last instance will sit on top of whatever else is on screen — including the three inline examples above — making the playground page visually noisy and potentially obscuring controls. Consider isolating the floating demo in its own scrolling/section container, or rendering it conditionally (e.g. behind a toggle), as the docs’ scrolling-context demo does.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/www/src/components/playground/floating-actions-examples.tsx` around lines 83 - 101, The floating example (FloatingActions with default floating variant) overlaps the inline examples because it uses position:fixed; update the demo so the floating instance is not always fixed over the page—either render it conditionally behind a toggle or move it into an isolated scrolling/demo container with its own stacking context (e.g., a bounded div with overflow and relative positioning) so it doesn't cover the three inline examples; locate the FloatingActions usage in floating-actions-examples.tsx and apply one of these approaches (or alternatively change this demo instance to variant="inline") to prevent visual overlap.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/www/src/components/playground/floating-actions-examples.tsx`:
- Line 32: Two FloatingActions components share the same aria-label "Bulk
actions", causing duplicate region names for assistive tech; update one of the
FloatingActions instances (the second one used in this component) to a distinct
label such as "Bulk actions (grouped)" or another descriptive string so each
toolbar has a unique aria-label, ensuring the change is applied to the
FloatingActions JSX element(s) in this file (search for the FloatingActions
component usages and update the aria-label prop).
---
Nitpick comments:
In `@apps/www/src/components/playground/floating-actions-examples.tsx`:
- Around line 83-101: The floating example (FloatingActions with default
floating variant) overlaps the inline examples because it uses position:fixed;
update the demo so the floating instance is not always fixed over the
page—either render it conditionally behind a toggle or move it into an isolated
scrolling/demo container with its own stacking context (e.g., a bounded div with
overflow and relative positioning) so it doesn't cover the three inline
examples; locate the FloatingActions usage in floating-actions-examples.tsx and
apply one of these approaches (or alternatively change this demo instance to
variant="inline") to prevent visual overlap.
In `@packages/raystack/components/floating-actions/floating-actions.tsx`:
- Around line 40-47: The data-* attributes controlling floating positioning
(data-variant, data-side, data-align) are currently set before spreading
{...props} into ToolbarPrimitive.Root, which allows callers to override them;
move the {...props} spread before those attributes and ensure data-variant,
data-side, data-align (and className={cx(styles.root, className)}) are specified
last on ToolbarPrimitive.Root so the component's positioning props cannot be
silently overridden by consumers.
- Around line 12-13: The props types currently strip the built-in disabled
cascade by using Omit<'disabled'>; update FloatingActionsProps and
FloatingActionsGroupProps to stop omitting disabled so consumers can pass the
toolbar-level disabled flag (i.e., change the extends from
Omit<ToolbarPrimitive.Root.Props, 'disabled'> and
Omit<ToolbarPrimitive.Group.Props, 'disabled'> to extend the original
ToolbarPrimitive.Root.Props and ToolbarPrimitive.Group.Props or include
'disabled' explicitly), and adjust any local usages or tests that assumed
disabled was absent to accept and forward the disabled prop to the underlying
ToolbarPrimitive components (refer to FloatingActionsProps,
FloatingActionsGroupProps, ToolbarPrimitive.Root and ToolbarPrimitive.Group to
locate the changes).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 5083cfa8-9915-4b5a-9c98-7d28f73d6e9b
📒 Files selected for processing (5)
apps/www/src/components/playground/floating-actions-examples.tsxapps/www/src/content/docs/components/floating-actions/demo.tsapps/www/src/content/docs/components/floating-actions/index.mdxapps/www/src/content/docs/components/floating-actions/props.tspackages/raystack/components/floating-actions/floating-actions.tsx
✅ Files skipped from review due to trivial changes (2)
- apps/www/src/content/docs/components/floating-actions/index.mdx
- apps/www/src/content/docs/components/floating-actions/props.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/www/src/content/docs/components/floating-actions/demo.ts
| </FloatingActions> | ||
|
|
||
| <Text>Inline — multiple action groups:</Text> | ||
| <FloatingActions variant='inline' aria-label='Bulk actions'> |
There was a problem hiding this comment.
Duplicate aria-label="Bulk actions" on two distinct toolbars.
Lines 32 and 56 both label their FloatingActions toolbar "Bulk actions". Assistive tech users navigating by landmarks/regions on this playground page will hear the same name twice. Consider differentiating, e.g. "Bulk actions" and "Bulk actions (grouped)".
Also applies to: 56-56
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/www/src/components/playground/floating-actions-examples.tsx` at line 32,
Two FloatingActions components share the same aria-label "Bulk actions", causing
duplicate region names for assistive tech; update one of the FloatingActions
instances (the second one used in this component) to a distinct label such as
"Bulk actions (grouped)" or another descriptive string so each toolbar has a
unique aria-label, ensuring the change is applied to the FloatingActions JSX
element(s) in this file (search for the FloatingActions component usages and
update the aria-label prop).
Summary
FloatingActionscomponent — a contextual action bar (bulk-action toolbar, row-hover actions) built on Base UI's Toolbar primitive, so it inherits roving focus, arrow-key navigation, and thedisabledcascade.variant:floating(default —position: fixed, anchored viaside/align, defaults to bottom-center) andinline(in-flow). No positioning CSS at the call site.FloatingActions.GroupandFloatingActions.Separator. Composes with existingChip,Button,IconButton. Docs cover variants, grouped actions, icon-only, and a scrolling-context demo; Vitest suite + playground wired up.Usage
Test plan
pnpm --filter @raystack/apsara test components/floating-actions🤖 Generated with Claude Code