migrate(admin): SDK admin and SDK app to Apsara v1 (rc.6)#1600
migrate(admin): SDK admin and SDK app to Apsara v1 (rc.6)#1600paanSinghCoder wants to merge 3 commits into
Conversation
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedReview was skipped due to path filters ⛔ Files ignored due to path filters (1)
CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughMigrates admin app and SDK to ChangesApsara v1 and Frontier client migration
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
|
Coverage Report for CI Build 25781796779Coverage remained the same at 42.291%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
There was a problem hiding this comment.
Actionable comments posted: 11
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
web/sdk/admin/components/multiselect/multiselect/index.tsx (1)
77-84:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdd
data-test-idattribute for the Clear action.Consistent with the options above, the Clear button should also have a
data-test-idattribute for test automation.🧪 Proposed fix
-<Command.Item onClick={onClear}> +<Command.Item onClick={onClear} data-test-id="admin-multiselect-clear"> <Flex justify="center"web/sdk/admin/views/organizations/details/edit/organization.tsx (1)
127-127:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winReview the watch default value for better UX.
The watch call uses
"other"as the default value: whentypeis undefined (which it is initially sincedefaultValuesis{}),watchreturns"other", causingshowOtherTypeFieldto betrueand the "industry (other)" field to display before the user selects an industry.Consider removing the default parameter so the field only appears after the user explicitly selects "Other":
Proposed fix
- const showOtherTypeField = watch("type", "other") === "other"; + const showOtherTypeField = watch("type") === "other";web/sdk/admin/views/users/details/security/sessions/revoke-session-final-confirm.tsx (1)
23-31:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAwait
onConfirmto handle async operations properly.At line 25,
onConfirm()is not awaited inside the asynctry/catch. If the callback is ever async, promise rejections will not be caught and error handling will be bypassed. While the current call sites pass synchronous functions, the component should support async callbacks for better flexibility.Proposed fix
interface RevokeSessionFinalConfirmProps { isOpen: boolean; onOpenChange: (isOpen: boolean) => void; - onConfirm: () => void; + onConfirm: () => void | Promise<void>; isLoading?: boolean; } @@ const handleConfirm = async () => { try { - onConfirm(); + await onConfirm(); onOpenChange(false); } catch (error: any) { toastManager.add({
♻️ Duplicate comments (1)
web/sdk/admin/components/SheetHeader.tsx (1)
3-3:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winVerify the apsara-v1 Text API usage.
Same verification concern as in
web/apps/admin/src/components/sheet/header.tsx: confirm thatsize="regular"andweight="medium"are valid apsara-v1 Text props and produce the expected visual output.Also applies to: 14-16
🧹 Nitpick comments (1)
web/sdk/admin/views/organizations/details/layout/invite-users-dialog.tsx (1)
129-139: 💤 Low valueType assertion could be avoided.
The
as stringcast suggests TypeScript can't infer the type. Since the zod transform (line 36) runs only at validation time,field.valueshould always be a string during rendering. The defensiveArray.isArray()check is likely unnecessary.♻️ Simplify by removing defensive logic
render={({ field }) => { - const { value, ...rest } = field; return ( <TextArea - {...rest} - value={Array.isArray(value) ? value.join(", ") : (value ?? "") as string} + {...field} + value={field.value ?? ""} placeholder="abc@example.com, xyz@example.com" className={styles['invite-users-emails-textarea']} /> ); }}
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 3dfe76d2-419d-425d-b9d0-5016a365c7d8
⛔ Files ignored due to path filters (1)
web/pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (126)
web/apps/admin/package.jsonweb/apps/admin/src/App.tsxweb/apps/admin/src/components/Layout.tsxweb/apps/admin/src/components/Price.tsxweb/apps/admin/src/components/Sidebar/index.tsxweb/apps/admin/src/components/Sidebar/sidebar.module.cssweb/apps/admin/src/components/assign-role.tsxweb/apps/admin/src/components/collapsable-search/index.tsxweb/apps/admin/src/components/dialog/header.tsxweb/apps/admin/src/components/error-boundary.tsxweb/apps/admin/src/components/page-header.tsxweb/apps/admin/src/components/sheet/header.tsxweb/apps/admin/src/components/states/Loading.tsxweb/apps/admin/src/components/states/Unauthorized.tsxweb/apps/admin/src/configs/theme.tsxweb/apps/admin/src/containers/login.tsxweb/apps/admin/src/containers/magiclink.tsxweb/apps/admin/src/hooks/useRQL.tsxweb/apps/admin/src/layout/auth.tsxweb/apps/admin/src/main.tsxweb/apps/admin/src/routes.tsxweb/apps/admin/src/utils/transform-query.tsweb/sdk/admin/components/AssignRole.tsxweb/sdk/admin/components/CustomField.tsxweb/sdk/admin/components/PageHeader.tsxweb/sdk/admin/components/SheetFooter.tsxweb/sdk/admin/components/SheetHeader.tsxweb/sdk/admin/components/multiselect/multiselect/index.tsxweb/sdk/admin/views/admins/columns.tsxweb/sdk/admin/views/admins/index.tsxweb/sdk/admin/views/audit-logs/actor-cell.tsxweb/sdk/admin/views/audit-logs/columns.tsxweb/sdk/admin/views/audit-logs/index.tsxweb/sdk/admin/views/audit-logs/navbar.tsxweb/sdk/admin/views/audit-logs/sidepanel-details.tsxweb/sdk/admin/views/audit-logs/sidepanel-list-id.tsxweb/sdk/admin/views/audit-logs/sidepanel-list-link.tsxweb/sdk/admin/views/audit-logs/sidepanel-log-dialog.tsxweb/sdk/admin/views/invoices/columns.tsxweb/sdk/admin/views/invoices/index.tsxweb/sdk/admin/views/invoices/navbar.tsxweb/sdk/admin/views/organizations/details/apis/columns.tsxweb/sdk/admin/views/organizations/details/apis/details-dialog.tsxweb/sdk/admin/views/organizations/details/apis/index.tsxweb/sdk/admin/views/organizations/details/edit/billing.tsxweb/sdk/admin/views/organizations/details/edit/kyc.tsxweb/sdk/admin/views/organizations/details/edit/organization.tsxweb/sdk/admin/views/organizations/details/invoices/columns.tsxweb/sdk/admin/views/organizations/details/invoices/index.tsxweb/sdk/admin/views/organizations/details/layout/add-tokens-dialog.tsxweb/sdk/admin/views/organizations/details/layout/index.tsxweb/sdk/admin/views/organizations/details/layout/invite-users-dialog.tsxweb/sdk/admin/views/organizations/details/layout/navbar.tsxweb/sdk/admin/views/organizations/details/members/columns.tsxweb/sdk/admin/views/organizations/details/members/index.tsxweb/sdk/admin/views/organizations/details/members/remove-member.tsxweb/sdk/admin/views/organizations/details/pat/columns.tsxweb/sdk/admin/views/organizations/details/pat/components/pat-details-dialog.tsxweb/sdk/admin/views/organizations/details/pat/index.tsxweb/sdk/admin/views/organizations/details/projects/columns.tsxweb/sdk/admin/views/organizations/details/projects/index.tsxweb/sdk/admin/views/organizations/details/projects/members/add-members-dropdown.tsxweb/sdk/admin/views/organizations/details/projects/members/assign-role.tsxweb/sdk/admin/views/organizations/details/projects/members/columns.tsxweb/sdk/admin/views/organizations/details/projects/members/index.tsxweb/sdk/admin/views/organizations/details/projects/members/remove-member.tsxweb/sdk/admin/views/organizations/details/projects/rename-project.tsxweb/sdk/admin/views/organizations/details/projects/use-add-project-members.tsxweb/sdk/admin/views/organizations/details/security/block-organization.tsxweb/sdk/admin/views/organizations/details/security/domains-list.tsxweb/sdk/admin/views/organizations/details/security/index.tsxweb/sdk/admin/views/organizations/details/side-panel/billing-details-section.tsxweb/sdk/admin/views/organizations/details/side-panel/index.tsxweb/sdk/admin/views/organizations/details/side-panel/kyc-section.tsxweb/sdk/admin/views/organizations/details/side-panel/org-details-section.tsxweb/sdk/admin/views/organizations/details/side-panel/plan-details-section.tsxweb/sdk/admin/views/organizations/details/side-panel/tokens-details-section.tsxweb/sdk/admin/views/organizations/details/tokens/columns.tsxweb/sdk/admin/views/organizations/details/tokens/index.tsxweb/sdk/admin/views/organizations/list/columns.tsxweb/sdk/admin/views/organizations/list/create.tsxweb/sdk/admin/views/organizations/list/index.tsxweb/sdk/admin/views/organizations/list/navbar.tsxweb/sdk/admin/views/plans/columns.tsxweb/sdk/admin/views/plans/details.tsxweb/sdk/admin/views/plans/header.tsxweb/sdk/admin/views/plans/index.tsxweb/sdk/admin/views/preferences/PreferencesView.tsxweb/sdk/admin/views/preferences/columns.tsxweb/sdk/admin/views/preferences/details.tsxweb/sdk/admin/views/preferences/index.tsxweb/sdk/admin/views/products/columns.tsxweb/sdk/admin/views/products/details.tsxweb/sdk/admin/views/products/header.tsxweb/sdk/admin/views/products/index.tsxweb/sdk/admin/views/products/prices/columns.tsxweb/sdk/admin/views/products/prices/index.tsxweb/sdk/admin/views/roles/columns.tsxweb/sdk/admin/views/roles/details.tsxweb/sdk/admin/views/roles/header.tsxweb/sdk/admin/views/roles/index.tsxweb/sdk/admin/views/users/details/layout/layout.tsxweb/sdk/admin/views/users/details/layout/membership-dropdown.tsxweb/sdk/admin/views/users/details/layout/navbar.tsxweb/sdk/admin/views/users/details/layout/side-panel-details.tsxweb/sdk/admin/views/users/details/layout/side-panel-membership.tsxweb/sdk/admin/views/users/details/layout/side-panel.tsxweb/sdk/admin/views/users/details/layout/suspend-user.tsxweb/sdk/admin/views/users/details/security/block-user.tsxweb/sdk/admin/views/users/details/security/security.tsxweb/sdk/admin/views/users/details/security/sessions/index.tsxweb/sdk/admin/views/users/details/security/sessions/revoke-session-confirm.tsxweb/sdk/admin/views/users/details/security/sessions/revoke-session-final-confirm.tsxweb/sdk/admin/views/users/details/security/sessions/session-skeleton.tsxweb/sdk/admin/views/users/details/user-details.tsxweb/sdk/admin/views/users/list/columns.tsxweb/sdk/admin/views/users/list/invite-users.tsxweb/sdk/admin/views/users/list/list.tsxweb/sdk/admin/views/users/list/navbar.tsxweb/sdk/admin/views/webhooks/webhooks/columns.tsxweb/sdk/admin/views/webhooks/webhooks/create/index.tsxweb/sdk/admin/views/webhooks/webhooks/delete/index.tsxweb/sdk/admin/views/webhooks/webhooks/header.tsxweb/sdk/admin/views/webhooks/webhooks/index.tsxweb/sdk/admin/views/webhooks/webhooks/update/index.tsxweb/sdk/react/components/auth-header/auth-header.tsx
💤 Files with no reviewable changes (1)
- web/apps/admin/src/routes.tsx
| .sidebar :global(.sidebar-module_main__qDvo4) { | ||
| gap: var(--rs-space-1); | ||
| } |
There was a problem hiding this comment.
Avoid targeting CSS Module hashed class names directly.
The selector .sidebar :global(.sidebar-module_main__qDvo4) relies on a generated hash that may change when the source module is modified or rebuilt, causing the style rule to silently fail.
Consider using a stable selector approach: a data attribute, semantic class, or direct child combinator instead.
♻️ Suggested alternatives
Option 1: Use a data attribute on the target element
-.sidebar :global(.sidebar-module_main__qDvo4) {
+.sidebar [data-sidebar-main] {
gap: var(--rs-space-1);
}Then add data-sidebar-main to the target element in the component.
Option 2: Use a direct child selector (if structure allows)
-.sidebar :global(.sidebar-module_main__qDvo4) {
+.sidebar > .main {
gap: var(--rs-space-1);
}📝 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.
| .sidebar :global(.sidebar-module_main__qDvo4) { | |
| gap: var(--rs-space-1); | |
| } | |
| .sidebar [data-sidebar-main] { | |
| gap: var(--rs-space-1); | |
| } |
🧰 Tools
🪛 Stylelint (17.11.0)
[error] 9-9: Unknown pseudo-class selector ":global" (selector-pseudo-class-no-unknown)
(selector-pseudo-class-no-unknown)
| <Popover.Trigger | ||
| render={ | ||
| <Button data-test-id="admin-selected"> | ||
| {selectedValues.size} selected | ||
| </Button> | ||
| } | ||
| /> |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Does @raystack/apsara v1 Popover.Trigger use a render prop instead of asChild?
💡 Result:
No. In @raystack/apsara’s Popover component, Popover.Trigger is configured via an asChild prop to “merge props with the immediate child element instead of rendering a button,” not via a render prop pattern.[1][2][3] (There is a separate “render” prop used by other components like Tooltip.Trigger, but Popover.Trigger specifically documents the asChild approach.)[1][4]
Citations:
- 1: https://apsara.raystack.org/docs/components/popover
- 2: https://raystack-apsara.mintlify.app/components/popover
- 3: https://mintlify.com/raystack/apsara/components/popover
- 4: https://apsara.raystack.org/docs/components/tooltip
Use asChild prop instead of render for Popover.Trigger.
Apsara v1 Popover.Trigger does not support a render prop. It uses the asChild prop to merge props with its immediate child element. Change to:
<Popover.Trigger asChild>
<Button data-test-id="admin-selected">
{selectedValues.size} selected
</Button>
</Popover.Trigger>
| <Command.Item | ||
| key={option.value} | ||
| onSelect={() => handleSelect(option.value)} | ||
| key={option.value as React.Key} | ||
| onClick={() => handleSelect(option.value)} | ||
| > | ||
| <Flex | ||
| justify="start" | ||
| gap="small" | ||
| gap={3} | ||
| style={{ padding: "4px 0px", cursor: "pointer" }} | ||
| > | ||
| <Checkbox checked={isSelected} /> | ||
| <Flex align="center" gap="small"> | ||
| <Flex align="center" gap={3}> | ||
| <span>{option.label}</span> | ||
| </Flex> | ||
| </Flex> | ||
| </Command.Item> |
There was a problem hiding this comment.
Add data-test-id attribute for testability.
Static analysis warns that interactive elements with onClick handlers should include a data-test-id attribute to support automated testing and E2E test reliability.
🧪 Proposed fix
<Command.Item
key={option.value as React.Key}
onClick={() => handleSelect(option.value)}
+ data-test-id="admin-multiselect-option"
>🧰 Tools
🪛 GitHub Check: JS SDK Lint
[warning] 55-55:
Elements with an onClick handler must have a data-test-id attribute
| <Command.Item | ||
| key={option.value} | ||
| onSelect={() => handleSelect(option.value)} | ||
| key={option.value as React.Key} |
There was a problem hiding this comment.
Unsafe type assertion may cause runtime errors.
The cast option.value as React.Key is unsafe. If the generic type T is an object or other complex type (not string | number | bigint), React will receive an invalid key and may produce warnings or incorrect rendering behavior.
Consider either:
- Constraining the generic:
MultiSelect<T extends React.Key> - Using a key extractor: add a
getKey?: (value: T) => React.Keyprop to the interface - Using the array index (not recommended if order changes)
🔧 Proposed fix using type constraint
-export function MultiSelect<T>({
+export function MultiSelect<T extends React.Key>({
selected,
options = [],
onSelect = () => {},
}: MultiSelectProps<T>) {Or with a key extractor:
interface MultiSelectProps<T> {
selected: T[];
options: Option<T>[];
onSelect: (values: T[]) => void;
+ getKey?: (value: T) => React.Key;
}
export function MultiSelect<T>({
selected,
options = [],
onSelect = () => {},
+ getKey = (v) => String(v),
}: MultiSelectProps<T>) {
// ...
<Command.Item
- key={option.value as React.Key}
+ key={getKey(option.value)}
onClick={() => handleSelect(option.value)}
>| width={600} | ||
| ariaLabel="Basic Dialog" | ||
| ariaDescription="A simple dialog example"> | ||
| aria-label="Basic Dialog"> |
There was a problem hiding this comment.
Use a descriptive aria-label for the dialog.
The aria-label value "Basic Dialog" is generic. For better accessibility, use a label that describes the dialog's purpose, such as "Audit log JSON entry" or "View log entry details".
♿ Proposed fix
<Dialog.Content
width={600}
- aria-label="Basic Dialog">
+ aria-label="Audit log JSON entry">📝 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.
| aria-label="Basic Dialog"> | |
| <Dialog.Content | |
| width={600} | |
| aria-label="Audit log JSON entry"> |
| // @ts-ignore | ||
| portal={false} |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In @raystack/apsara-v1@1.0.0-rc.6, what is the supported prop on Menu.Content to disable portal rendering (if supported)?
💡 Result:
No prop for Menu.Content was found that disables portal rendering. In the Apsara Menu API, Menu.Content exposes only props such as searchPlaceholder, side, align, sideOffset, render, style, and className; there is no documented prop like portal/disablePortal/forceMount for turning off portal behavior in Menu.Content.[1][2] If you need to avoid portal rendering, the supported approach (per the docs shown) would be to use the provided render prop (render) to control what gets rendered, but portal-disable is not surfaced as a Menu.Content prop in the public API docs.[1][2] Answering your exact question: there is no supported Menu.Content prop in @raystack/apsara-v1@1.0.0-rc.6 docs to disable portal rendering.
Citations:
- 1: https://mintlify.com/raystack/apsara/components/menu
- 2: https://apsara.raystack.org/docs/components/menu
Remove the unsupported portal={false} prop and the @ts-ignore suppression.
The portal prop is not supported by Menu.Content in @raystack/apsara-v1@1.0.0-rc.6. The @ts-ignore masks this incompatibility rather than a temporary type mismatch. If portal rendering behavior needs to be controlled, use the supported render prop instead.
| <Tooltip.Trigger | ||
| render={ | ||
| <Button | ||
| variant="outline" | ||
| color="neutral" | ||
| leadingIcon={<PlusIcon />} | ||
| size="small" | ||
| data-test-id="add-domain-button" | ||
| disabled={true} | ||
| > | ||
| Add domain | ||
| </Button> | ||
| } | ||
| /> | ||
| <Tooltip.Content>Coming soon</Tooltip.Content> |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In Radix Tooltip patterns, do disabled <button> elements fire hover/focus events for Tooltip.Trigger? What is the recommended way to show a tooltip for a disabled action button?
💡 Result:
Yes. In Radix UI Tooltip, whether a disabled fires hover/focus events for Tooltip.Trigger depends on how your “disabled” button is implemented/styled. In the common case where the disabled button still renders and the trigger can receive pointer/keyboard events, the tooltip can open on hover/focus; but if your disabled styles effectively remove pointer events (e.g., pointer-events: none) or prevent focus, then Tooltip.Trigger won’t receive those events and the tooltip won’t show. [1][2] Recommended way to show a tooltip for a disabled action button: 1) Don’t rely on the disabled itself as the hover/focus trigger. Instead, wrap it so an enabled wrapper element is the tooltip trigger (Tooltip.Trigger asChild), and render the button with disabled/aria-disabled styling/behavior inside that wrapper. 2) If your disabled CSS sets pointer-events: none, override it so the trigger wrapper can still receive pointer events (e.g., ensure pointer-events is enabled for the element that is actually the trigger). [1][2] Practical pattern (typical): - Tooltip wraps a non-disabled wrapper element (like a or
Citations:
- 1: [Tooltip] tooltips are shown for disabled buttons but documentation says it shouldn't happen radix-ui/primitives#1914
- 2: Hovercard and Tooltip does not work on disabled compnent such as button shadcn-ui/ui#1022
🏁 Script executed:
# Find Button component
fd -t f "Button" --search-path web/sdk | head -20Repository: raystack/frontier
Length of output: 43
🏁 Script executed:
# Look for button-related files
fd -t f -i "button" --search-path web/sdk | head -30Repository: raystack/frontier
Length of output: 194
🏁 Script executed:
# Search for the Button component definition
rg "export.*Button" --type ts --type tsx -A 2 | head -50Repository: raystack/frontier
Length of output: 89
🏁 Script executed:
# Find all Button-related component files
fd "Button" --search-path web/sdk --type fRepository: raystack/frontier
Length of output: 43
🏁 Script executed:
# Search for Button export in typescript files
rg "export.*Button|const Button|function Button" --type ts -A 3 | head -100Repository: raystack/frontier
Length of output: 1312
🏁 Script executed:
# Check the actual file to see where Button is imported from
cat web/sdk/admin/views/organizations/details/security/index.tsx | head -50Repository: raystack/frontier
Length of output: 1535
🏁 Script executed:
# Find apsara-v1 directory
fd -t d "apsara-v1"Repository: raystack/frontier
Length of output: 43
🏁 Script executed:
# Check if it's in node_modules or the repo itself
find . -name "apsara-v1" -type d 2>/dev/null | head -5Repository: raystack/frontier
Length of output: 43
🏁 Script executed:
# Look for Button component in common locations
find . -path "*/apsara*" -name "*.tsx" -o -path "*/apsara*" -name "*.ts" 2>/dev/null | grep -i button | head -10Repository: raystack/frontier
Length of output: 43
🏁 Script executed:
# Check if apsara-v1 is in package.json
rg "@raystack/apsara-v1" --type jsonRepository: raystack/frontier
Length of output: 234
🏁 Script executed:
# Also check yarn.lock or package-lock.json for the package
rg "apsara-v1" --type json | head -20Repository: raystack/frontier
Length of output: 234
🌐 Web query:
@raystack/apsara Button component disabled state CSS pointer-events
💡 Result:
For @raystack/apsara, the Button’s disabled state is handled via the native disabled button semantics (and Apsara communicates it with aria-disabled), rather than relying on pointer-events:none as the mechanism. The component docs say disabled “prevents the button from responding to any user actions” [1], and they note it uses a native button element with disabled state conveyed via aria-disabled [1]. What this means for CSS / pointer-events 1) You generally should not override the disabled behavior with pointer-events:none on the Button itself. Native disabled button behavior already blocks interaction, and Apsara’s own styling guidance for disabled uses disabled-state styling but does not indicate that pointer-events:none is the required/official approach. Apsara’s styling guide shows a disabled-state selector pattern that affects opacity/cursor (and, in that example, sets pointer-events back to initial) [2]. 2) If you see pointer-events issues, it’s more likely due to where/how the Button is rendered or wrapped (e.g., overlays, absolutely-positioned children) rather than a need to add pointer-events:none to the disabled button. If you need “no clicking” while keeping styling - Prefer using the Button’s disabled prop (or, if wrapping, ensure the underlying element actually receives the disabled/aria-disabled semantics). Apsara’s accessibility docs show using the disabled prop directly on Button [3]. Relevant references - Apsara Button API/docs: disabled prop behavior and aria-disabled/native button semantics [1] - Apsara styling guide: example disabled state styling includes cursor/opacity and pointer-events: initial (not none) [2] - MDN for pointer-events: pointer-events:none makes an element the target of no pointer events (click/hover/etc.) but this is separate from native disabled behavior [4] Bottom line: For @raystack/apsara Button, use the provided disabled prop; don’t depend on pointer-events:none as the official disabled mechanism, since Apsara’s docs describe native disabled behavior + aria-disabled for the disabled state [1], and its own styling example doesn’t recommend pointer-events:none [2].
Citations:
- 1: https://apsara.raystack.org/docs/components/button
- 2: https://mintlify.com/raystack/apsara/guides/styling
- 3: https://raystack-apsara.mintlify.app/guides/accessibility
- 4: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/pointer-events
🌐 Web query:
raystack apsara v1 Button disabled tooltip Radix UI
💡 Result:
You’re likely dealing with a “disabled trigger + tooltip” interaction. 1) Apsara (Raystack) side - Apsara’s Button has a built-in disabled state: it “prevents the button from responding to any user actions” and communicates disabled state via aria-disabled [1][2]. - Apsara’s Tooltip is a wrapper with Tooltip.Trigger as the hover/focus trigger and Tooltip.Content as the popup [3][4]. 2) Radix / common root cause (disabled elements often can’t hover) - Radix UI Tooltip has a known class of issues around showing tooltips from disabled buttons because the disabled control may not receive pointer/hover/focus events. There are reports of tooltips being shown or not shown depending on how the disabled trigger is implemented and styled [5][6]. 3) Practical fix pattern (works around disabled-hover issues) - Wrap the disabled button in a non-disabled element (e.g., a span/div) and attach Tooltip.Trigger to that wrapper (so hover/focus events happen on the wrapper, not the disabled button). This is the standard workaround when disabled elements block interaction [6]. - If the tooltip still won’t show, also check for “pointer-events: none” on the disabled button (or inherited styles). One discussion notes that enabling pointer events can make the tooltip appear even when disabled [5]. If you can paste your exact Apsara/Radix tooltip snippet (especially how you render Tooltip.Trigger and whether you use asChild + custom button styles), I can tell you which of the above is happening in your case.
Citations:
- 1: https://apsara.raystack.org/docs/components/button
- 2: https://raystack-apsara.mintlify.app/components/button
- 3: https://apsara.raystack.org/docs/components/tooltip
- 4: https://raystack-apsara.mintlify.app/components/tooltip
- 5: [Tooltip] tooltips are shown for disabled buttons but documentation says it shouldn't happen radix-ui/primitives#1914
- 6: https://medium.com/fredwong-it/disabled-button-doesnt-show-radix-ui-tooltip-8cbd727bfeaf
🏁 Script executed:
# Search for other Tooltip.Trigger usages in the codebase
rg "Tooltip.Trigger" --type ts --type js -B 2 -A 3Repository: raystack/frontier
Length of output: 17975
Wrap the disabled button to make the tooltip accessible to keyboard and pointer users.
Native disabled button behavior can prevent hover and focus events from reaching the Tooltip.Trigger. The codebase establishes a consistent pattern (seen in tokens, teams, projects, members, billing views) where disabled actions use a non-disabled wrapper as the trigger. Apply the same pattern here by wrapping the disabled button in a span or div.
Suggested adjustment
- <Tooltip.Trigger
- render={
- <Button
+ <Tooltip.Trigger
+ render={
+ <span style={{ display: "inline-flex" }}>
+ <Button
variant="outline"
color="neutral"
leadingIcon={<PlusIcon />}
size="small"
data-test-id="add-domain-button"
disabled={true}
>
Add domain
</Button>
+ </span>
}
/>📝 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.
| <Tooltip.Trigger | |
| render={ | |
| <Button | |
| variant="outline" | |
| color="neutral" | |
| leadingIcon={<PlusIcon />} | |
| size="small" | |
| data-test-id="add-domain-button" | |
| disabled={true} | |
| > | |
| Add domain | |
| </Button> | |
| } | |
| /> | |
| <Tooltip.Content>Coming soon</Tooltip.Content> | |
| <Tooltip.Trigger | |
| render={ | |
| <span style={{ display: "inline-flex" }}> | |
| <Button | |
| variant="outline" | |
| color="neutral" | |
| leadingIcon={<PlusIcon />} | |
| size="small" | |
| data-test-id="add-domain-button" | |
| disabled={true} | |
| > | |
| Add domain | |
| </Button> | |
| </span> | |
| } | |
| /> | |
| <Tooltip.Content>Coming soon</Tooltip.Content> |
| <Text className={styles["text-overflow"]} as="p"> | ||
| <Menu> | ||
| <Menu.Trigger className={styles["dropdown-menu-trigger"]}> | ||
| <Text className={styles["text-overflow"]} render={<p />}> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check Text component render prop usage pattern in v1 migrations
# Search for Text render prop usage in migrated files
rg -n -C2 --type=tsx --type=ts -g 'web/sdk/admin/**' -g 'web/apps/admin/**' 'Text.*render='
# Also check if 'as=' prop is still used anywhere (should be migrated)
echo "--- Checking for old 'as=' prop usage ---"
rg -n --type=tsx --type=ts -g 'web/sdk/admin/**' -g 'web/apps/admin/**' '<Text.*\bas=' | head -20Repository: raystack/frontier
Length of output: 177
🏁 Script executed:
#!/bin/bash
# Find and examine the specific file
find . -name "membership-dropdown.tsx" -type fRepository: raystack/frontier
Length of output: 128
🏁 Script executed:
#!/bin/bash
# Once found, let's check the imports and context around line 122
if [ -f "web/sdk/admin/views/users/details/layout/membership-dropdown.tsx" ]; then
echo "=== File content around line 122 ==="
sed -n '1,20p' "web/sdk/admin/views/users/details/layout/membership-dropdown.tsx"
echo ""
echo "=== Imports section ==="
head -30 "web/sdk/admin/views/users/details/layout/membership-dropdown.tsx"
fiRepository: raystack/frontier
Length of output: 2049
🏁 Script executed:
#!/bin/bash
# Search for Text component imports and render prop usage patterns
rg -n 'import.*Text' --type-list | head -5
rg -n 'import.*Text' -A 2 -B 2 | head -50Repository: raystack/frontier
Length of output: 4593
🏁 Script executed:
#!/bin/bash
# Check for render prop usage patterns in tsx files
find . -name "*.tsx" -type f | xargs grep -l "render=<" | head -10Repository: raystack/frontier
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Show lines around 122 in the membership-dropdown file
sed -n '115,130p' "web/sdk/admin/views/users/details/layout/membership-dropdown.tsx"Repository: raystack/frontier
Length of output: 594
🏁 Script executed:
#!/bin/bash
# Search for render prop usage with Text component in tsx files
find . -name "*.tsx" -type f -exec grep -l 'render=<' {} \; | head -20Repository: raystack/frontier
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Search more broadly for Text component with render prop
grep -r 'Text.*render=' --include="*.tsx" --include="*.ts" | head -20Repository: raystack/frontier
Length of output: 194
🏁 Script executed:
#!/bin/bash
# Check apsara-v1 package or find Text component usage patterns
find . -path "*/node_modules" -prune -o -name "*.tsx" -type f -print | xargs grep -l 'render={<' | head -15Repository: raystack/frontier
Length of output: 1003
🏁 Script executed:
#!/bin/bash
# Check other Text component usage from apsara-v1 to see patterns
grep -r 'from.*apsara-v1' --include="*.tsx" --include="*.ts" | head -10Repository: raystack/frontier
Length of output: 987
🏁 Script executed:
#!/bin/bash
# Search for Text component usage with different props (as=, render=)
grep -r '<Text' --include="*.tsx" -A 1 | grep -E '(as=|render=)' | head -30Repository: raystack/frontier
Length of output: 343
🏁 Script executed:
#!/bin/bash
# Look at the git history or PR context to understand as→render migration
if git log -1 --oneline -- "web/sdk/admin/views/users/details/layout/membership-dropdown.tsx" 2>/dev/null; then
git log --oneline -5 -- "web/sdk/admin/views/users/details/layout/membership-dropdown.tsx" 2>/dev/null
fiRepository: raystack/frontier
Length of output: 181
🏁 Script executed:
#!/bin/bash
# Check if there's any documentation or type definitions for Text component
find . -path "*/node_modules" -prune -o -name "*.d.ts" -o -name "*.ts" -o -name "*.tsx" | xargs grep -l 'interface.*Text' 2>/dev/null | grep -v node_modules | head -10Repository: raystack/frontier
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Look for old 'as=' prop usage in Text components to understand migration
grep -r '<Text' --include="*.tsx" -A 1 | grep 'as=' | head -20Repository: raystack/frontier
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Check common Text component usage in the codebase to understand the normal pattern
grep -r '<Text' --include="*.tsx" -A 1 -B 0 | grep -E '(className|children)' | head -30Repository: raystack/frontier
Length of output: 4084
🏁 Script executed:
#!/antml:thinking
I need to verify if render prop is truly the correct v1 syntax. The user mentioned "asChild→render" changes in the PR description. Let me check:
1. If there are other instances of render prop being used
2. What the proper way to customize Text element is in apsara-v1
3. Whether render={<p />} is idiomatic
</thinking>
#!/bin/bash
# Search for any documentation or changelog mentioning asChild/render migration
find . -path "*/node_modules" -prune -o -name "CHANGELOG*" -o -name "README*" -o -name "*.md" | xargs grep -l "asChild\|render" 2>/dev/null | head -10Repository: raystack/frontier
Length of output: 336
🏁 Script executed:
#!/bin/bash
# Look for polymorphic component patterns - check if Text component is used with different elements
grep -r 'render=\|asChild=' --include="*.tsx" --include="*.ts" | grep -v node_modules | head -30Repository: raystack/frontier
Length of output: 2947
🏁 Script executed:
#!/bin/bash
# Check the actual Text component usage with render in context
grep -r 'Text.*render=' --include="*.tsx" -B 2 -A 2 | head -50Repository: raystack/frontier
Length of output: 602
🏁 Script executed:
#!/bin/bash
# Look for the original 'as=' usage in older branches or check if any remain
grep -r '<Text.*as=' --include="*.tsx" | head -20Repository: raystack/frontier
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Check apsara-v1 package in package.json to understand versioning
cat package.json | grep -A 5 -B 5 "apsara-v1"Repository: raystack/frontier
Length of output: 106
🏁 Script executed:
#!/bin/bash
# Find package.json files in the repo
find . -name "package.json" -type f | head -10Repository: raystack/frontier
Length of output: 271
🏁 Script executed:
#!/bin/bash
# Check the workspace package.json or root level
if [ -f "package.json" ]; then
grep "apsara" package.json
elif [ -f "packages/web/package.json" ]; then
grep "apsara" packages/web/package.json
fiRepository: raystack/frontier
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Search for any package.json that contains apsara-v1
find . -name "package.json" -type f -exec grep -l "apsara-v1" {} \;Repository: raystack/frontier
Length of output: 114
🏁 Script executed:
#!/bin/bash
# Check the web directory structure
ls -la web/ | head -20Repository: raystack/frontier
Length of output: 1063
🏁 Script executed:
#!/bin/bash
# Check apsara-v1 version in the packages that use it
grep -A 2 -B 2 "apsara-v1" ./web/apps/admin/package.json
echo "---"
grep -A 2 -B 2 "apsara-v1" ./web/sdk/package.jsonRepository: raystack/frontier
Length of output: 573
🏁 Script executed:
#!/bin/bash
# Search for any documentation about render prop in the repository
find . -path "*/node_modules" -prune -o -name "*.md" -type f -print | xargs grep -l "render\|asChild" 2>/dev/null | head -10Repository: raystack/frontier
Length of output: 43
🏁 Script executed:
#!/antml:thinking>
Let me look for apsara-v1 component docs or check if there are other components using render prop in apsara-v1 to see if this is a consistent pattern. Also want to check if there's any other Text component usage with render prop.
</thinking>
#!/bin/bash
# Look for other apsara-v1 components using render prop to understand the pattern
grep -r 'from.*apsara-v1' --include="*.tsx" -A 5 | grep -E '(Button|Link|Menu).*render=' | head -20Repository: raystack/frontier
Length of output: 248
🏁 Script executed:
#!/bin/bash
# Check if there's any migration guide or comments about asChild→render
grep -r "asChild\|render" --include="*.tsx" --include="*.ts" --include="*.md" | grep -i "migrat\|chang\|upgrad" | head -20Repository: raystack/frontier
Length of output: 625
🏁 Script executed:
#!/bin/bash
# Search for Button or Link components with render prop to understand the pattern better
grep -r 'Button.*render=\|Link.*render=' --include="*.tsx" | head -15Repository: raystack/frontier
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Look at the git diff to see what changed from 'as=' to 'render='
cd web/sdk/admin/views/users/details/layout && git diff HEAD -- membership-dropdown.tsx 2>/dev/null | head -50Repository: raystack/frontier
Length of output: 43
🏁 Script executed:
#!/antml:thinking>
I need to verify if `render={<p />}` is the correct v1 syntax. The PR mentions "asChild→render" changes. Let me search for documentation about this pattern or look for similar usage in the codebase that might give me confidence this is correct.
</thinking>
#!/bin/bash
# Search for more context about how render prop is used with element creation
grep -rn 'render={<' --include="*.tsx" | grep -v 'render={<Text\|render={<Button\|render={<Link\|render={<' | head -20Repository: raystack/frontier
Length of output: 254
🌐 Web query:
apsara-v1 Text component render prop v1.0.0-rc.6 syntax
💡 Result:
The apsara-v1 Text component from @raystack/apsara (latest release v1.0.0-rc.2 as of March 2026, no specific rc.6 found but syntax consistent across docs) does not use a traditional render prop (function prop like render={(props) => <...>}). Instead, it uses standard React children for content and an as prop for polymorphic rendering (e.g., as="span" | "p" | "div").[1][2][3] Basic syntax: import { Text } from '@raystack/apsara'; <Text variant="primary" size="small" weight="medium">Hello World</Text> Key props from official docs:[2][3] - as: string ("span"|"p"|"div"|"label"|"a") - HTML element to render [2] - variant: "primary"|"secondary"|...|"success" - visual style [2] - size: "micro"|"mini"|"small"|"regular"|"large" [2] - weight: "regular"|"medium" [2] - children: ReactNode - text content [3] Example with as prop: <Text as="label" htmlFor="input">Label</Text> Some Apsara components like Button, Dialog.Trigger, Menu use render prop for custom elements (Base UI pattern).[4][5][6] Text does not.[2][3]
Citations:
- 1: https://github.com/raystack/apsara
- 2: https://apsara.raystack.org/docs/components/text
- 3: https://raystack-apsara.mintlify.app/components/text
- 4: https://apsara.raystack.org/docs/components/button
- 5: https://apsara.raystack.org/docs/components/dialog
- 6: https://apsara.raystack.org/docs/components/menu
🏁 Script executed:
#!/bin/bash
# Verify if Text actually accepts render prop by checking actual usage in other parts of codebase
grep -r '<Text' --include="*.tsx" | grep -E '(as=|render=)' | sort | uniq -c | sort -rnRepository: raystack/frontier
Length of output: 359
🏁 Script executed:
#!/an/bash
# Check if there's a custom Text component wrapper in the codebase
find . -path "*/node_modules" -prune -o -name "*.tsx" -o -name "*.ts" | xargs grep -l "export.*Text" 2>/dev/null | grep -v node_modules | head -10Repository: raystack/frontier
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Look at line 122 in context to understand what the actual change was
git show HEAD:web/sdk/admin/views/users/details/layout/membership-dropdown.tsx 2>/dev/null | sed -n '115,130p'Repository: raystack/frontier
Length of output: 594
🏁 Script executed:
#!/bin/bash
# Check the previous version to see what it was before
git log --oneline -n 20 -- web/sdk/admin/views/users/details/layout/membership-dropdown.tsx 2>/dev/nullRepository: raystack/frontier
Length of output: 121
🏁 Script executed:
#!/bin/bash
# Check what the original code looked like before this commit
git show c4beddf6^:web/sdk/admin/views/users/details/layout/membership-dropdown.tsx 2>/dev/null | sed -n '115,130p'Repository: raystack/frontier
Length of output: 43
🏁 Script executed:
#!/antml:thinking>
Wait, I need to be careful. Let me check if this branch existed and if I can get the previous state. Let me also look for the actual commit that made this change.
</thinking>
#!/bin/bash
# Find the commit that introduced the render prop change
git log -p --all -- web/sdk/admin/views/users/details/layout/membership-dropdown.tsx 2>/dev/null | grep -A 5 -B 5 'render=' | head -30Repository: raystack/frontier
Length of output: 210
🏁 Script executed:
#!/an/bash
# Check other files in the codebase to see if as= is still being used anywhere (should indicate old vs new)
grep -r '<Text.*as=' --include="*.tsx" | head -15Repository: raystack/frontier
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Check if there's a TypeScript definition file or type information for Text component
find . -path "*/node_modules" -prune -o -name "*.d.ts" -type f -print | xargs grep -A 10 "interface Text\|type Text" 2>/dev/null | head -40Repository: raystack/frontier
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Look for any comments or migration guides in the code related to this change
grep -r "asChild\|render prop" --include="*.tsx" --include="*.ts" --include="*.md" | head -20Repository: raystack/frontier
Length of output: 1171
🏁 Script executed:
#!/antml/bash
# Check if Text actually has a render prop by examining all Text usages
grep -r '<Text' --include="*.tsx" -A 0 | grep -c 'render='
echo "---"
grep -r '<Text' --include="*.tsx" -A 0 | grep -c 'as='Repository: raystack/frontier
Length of output: 69
🏁 Script executed:
#!/an/bash
# Final verification - check if there are any error logs or type errors related to this
grep -r "render" web/sdk/admin/views/users/details/layout/membership-dropdown.tsxRepository: raystack/frontier
Length of output: 129
🏁 Script executed:
#!/an/bash
# Check if Text is being used as a wrapper component (which would explain the render prop in side-panel-details)
grep -B 5 'render={<Text' web/sdk/admin/views/users/details/layout/side-panel-details.tsx | head -15Repository: raystack/frontier
Length of output: 347
🏁 Script executed:
#!/an/bash
# Examine the exact structure to understand the context
sed -n '1,30p' web/sdk/admin/views/users/details/layout/membership-dropdown.tsxRepository: raystack/frontier
Length of output: 1173
🏁 Script executed:
#!/bin/bash
# Double-check if Text component in apsara-v1 actually accepts render prop by looking at type errors
grep -r "render" web/sdk/admin/views/users/details/layout/membership-dropdown.tsx -C 3Repository: raystack/frontier
Length of output: 310
🏁 Script executed:
#!/an/bash
# Check if this might be a custom wrapper or if Text is re-exported differently
grep -r "export.*as.*Text" --include="*.tsx" --include="*.ts" | grep -v node_modules | head -10Repository: raystack/frontier
Length of output: 43
Change render={<p />} to as="p" — Text component does not accept render prop in v1.
The apsara-v1 Text component uses the as prop for polymorphic element rendering, not a render prop. The correct syntax is <Text className={styles["text-overflow"]} as="p">. The render prop is used by other apsara-v1 components like Button, Dialog.Trigger, and Menu, but not Text.
| toastManager.add({ title: `${t.user({ case: "capital" })} suspended successfully`, type: "success" }); | ||
| onSubmit?.(); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify how SuspendUser.onSubmit is passed at callsites (sync vs async handlers).
rg -nP --type=tsx -C3 '<SuspendUser\b|onSubmit=\{'
rg -nP --type=tsx -C3 'onSubmit=\{async\b|const\s+\w+\s*=\s*async\s*\('Repository: raystack/frontier
Length of output: 117
🏁 Script executed:
# Locate the suspend-user.tsx file and examine its contents
find . -name "suspend-user.tsx" -type fRepository: raystack/frontier
Length of output: 121
🏁 Script executed:
# Search for SuspendUser component usage in the codebase
rg -g '*.tsx' -g '*.ts' 'SuspendUser' -C 3Repository: raystack/frontier
Length of output: 1960
🏁 Script executed:
# Search for onSubmit handlers passed to SuspendUser
rg -g '*.tsx' -g '*.ts' 'onSubmit=' -C 3 | grep -A 3 -B 3 'Suspend'Repository: raystack/frontier
Length of output: 878
🏁 Script executed:
# Read the suspend-user.tsx file to understand the current implementation
cat -n web/sdk/admin/views/users/details/layout/suspend-user.tsxRepository: raystack/frontier
Length of output: 2643
🏁 Script executed:
# Read membership-dropdown.tsx to see how onSuspend is defined
cat -n web/sdk/admin/views/users/details/layout/membership-dropdown.tsx | head -100Repository: raystack/frontier
Length of output: 3706
🏁 Script executed:
# Search for the onSuspend handler definition in membership-dropdown.tsx
rg -g '*.tsx' 'onSuspend\s*=' web/sdk/admin/views/users/details/layout/membership-dropdown.tsx -A 10Repository: raystack/frontier
Length of output: 339
🏁 Script executed:
# Search for all SuspendUser component usages to check if any other callsite uses async handlers
rg -g '*.tsx' -g '*.ts' 'SuspendUser' -B 5 -A 5Repository: raystack/frontier
Length of output: 3039
Await onSubmit before showing success feedback.
On line 22, success toast appears before line 23 executes onSubmit. If onSubmit fails or is async, users still get a success notification despite the operation not completing.
Suggested fix
interface SuspendDropdownProps {
userId: string;
onClose: () => void;
- onSubmit?: () => void;
+ onSubmit?: () => void | Promise<void>;
}
@@
const handleSuspend = async () => {
try {
setIsSubmitting(true);
- toastManager.add({ title: `${t.user({ case: "capital" })} suspended successfully`, type: "success" });
- onSubmit?.();
+ await Promise.resolve(onSubmit?.());
+ toastManager.add({
+ title: `${t.user({ case: "capital" })} suspended successfully`,
+ type: "success",
+ });
} catch (error) {
console.error(error);
} finally {
setIsSubmitting(false);
}
};📝 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.
| toastManager.add({ title: `${t.user({ case: "capital" })} suspended successfully`, type: "success" }); | |
| onSubmit?.(); | |
| interface SuspendDropdownProps { | |
| userId: string; | |
| onClose: () => void; | |
| onSubmit?: () => void | Promise<void>; | |
| } | |
| const handleSuspend = async () => { | |
| try { | |
| setIsSubmitting(true); | |
| await Promise.resolve(onSubmit?.()); | |
| toastManager.add({ | |
| title: `${t.user({ case: "capital" })} suspended successfully`, | |
| type: "success", | |
| }); | |
| } catch (error) { | |
| console.error(error); | |
| } finally { | |
| setIsSubmitting(false); | |
| } | |
| }; |
| <DropdownMenu.Content> | ||
| <DropdownMenu.Group style={{ padding: 0 }}> | ||
| <DropdownMenu.Item style={{ padding: 0 }}> | ||
| <Menu style={{ padding: "0 !important" }}> |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Does React's styleprop support values containing!important(for example{ padding: "0 !important" }), and what is the recommended alternative?
💡 Result:
React’s style prop does not support values that contain !important (e.g. style={{ padding: "0 !important" }}). React does not treat !important as part of the inline-style value serialization; instead, !important needs to be applied via the DOM style API using element.style.setProperty(name, value, priority) (priority = "important"). [1][2] Recommended alternative: use CSS classes (className) and put the !important in your stylesheet; then toggle the class via className rather than trying to encode !important inside the style prop. React’s docs recommend classes for most static styling and for performance, using style only for dynamically-computed values. [3][4] If you truly must do it inline on a specific element, use a ref and setProperty to set the property priority to important rather than embedding !important in the style string. [2][5]
Citations:
- 1: Support !important for styles? facebook/react#1881
- 2: https://stackoverflow.com/questions/67265740/why-important-is-not-working-in-react-js-with-inline-style
- 3: https://reactjs.org/docs/faq-styling.html
- 4: https://beta.reactjs.org/reference/react-dom/components/common
- 5: https://bobbyhadz.com/blog/react-style-important
Remove !important from inline style prop on Line 73.
React's style prop does not support values containing !important; it will be ignored at runtime. If !important override is not needed, use style={{ padding: 0 }}. If override is required, move the style to a CSS class with !important in the stylesheet and toggle via className instead.
Proposed fix
- <Menu style={{ padding: "0 !important" }}>
+ <Menu style={{ padding: 0 }}>📝 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.
| <Menu style={{ padding: "0 !important" }}> | |
| <Menu style={{ padding: 0 }}> |
Summary
sdk/admin/(103 files) andapps/admin/src/(22 files) from Apsara v0 → v1 (rc.6).@raystack/frontier/react(v0) →@raystack/frontier/client(v1). Trims ~680 KB JS / ~80 KB CSS from the admin bundle.AuthHeaderto passalign="center"to the innerHeadline(was defaulting to left).Scope
<Toast.Provider>mounted inmain.tsx; v0ToastContainerandsonnerremoved.sdk/react/views/left on v0 intentionally (out of scope);views-new/is already on v1 and surfaced via the/clientexport.Test plan
pnpm vite buildsucceeds; bundle smaller than mainpnpm exec tsc --noEmitis clean forapps/admin; SDK matches pre-migration baseline (30 pre-existing, 0 net new)