[WEB-4738] feat: add Skeleton component with respective stories in propel#7704
[WEB-4738] feat: add Skeleton component with respective stories in propel#7704sriramveeraghanta merged 12 commits intopreviewfrom
Conversation
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughAdds a new Skeleton React component with a static Item subcomponent, exposes it via a barrel and package export, adds Storybook stories (Default, Card), and performs minor import-order and duplicate-import cleanups. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Dev as App / Consumer
participant IDX as skeleton index (`index.ts`)
participant Root as Skeleton (`root.tsx`)
participant DOM as Browser DOM
Dev->>IDX: import { Skeleton }
IDX->>Root: re-export resolves to Skeleton
Dev->>Root: <Skeleton className=... ariaLabel=...>
Dev->>Root: <Skeleton.Item height=... width=... />
Root->>DOM: render container (data-slot="skeleton", animate-pulse, role=status)
Root->>DOM: render Item(s) (rounded blocks, inline height/width)
Note right of DOM #DDF2FF: Placeholder UI shown until real content loads
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
|
Pull Request Linked with Plane Work Items
Comment Automatically Generated by Plane |
There was a problem hiding this comment.
Pull Request Overview
This PR introduces a new Skeleton component to the propel package for displaying loading placeholder content. The component provides a flexible structure for creating animated skeleton screens with customizable dimensions and styling.
- Adds a compound Skeleton component with a root container and Item sub-component
- Implements animated pulse effect and proper accessibility attributes
- Provides Storybook stories demonstrating default and card skeleton patterns
Reviewed Changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| packages/propel/src/Skeleton/root.tsx | Main component implementation with root container and Item sub-component |
| packages/propel/src/Skeleton/index.ts | Barrel export for the Skeleton component |
| packages/propel/src/Skeleton/skeleton.stories.tsx | Storybook stories showcasing Default and Card skeleton variants |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (6)
packages/propel/src/Skeleton/index.ts (1)
1-1: Prefer explicit re-export to keep the public API tightLimit surface area and aid tree-shaking by re-exporting only what you intend to expose.
-export * from "./root"; +export { Skeleton } from "./root";packages/propel/src/Skeleton/skeleton.stories.tsx (2)
2-2: Import from the folder root instead of './index'Minor ergonomics/readability.
-import { Skeleton } from "./index"; +import { Skeleton } from ".";
4-10: Usesatisfiesfor stronger meta typing (Storybook 7/8 pattern)Improves type inference without widening.
-const meta: Meta<typeof Skeleton> = { +const meta = { title: "Components/Skeleton", component: Skeleton, parameters: { layout: "centered", }, -}; +} satisfies Meta<typeof Skeleton>;packages/propel/src/Skeleton/root.tsx (3)
16-20: Allow numeric CSS values for dimensionsAccept
number | stringvia CSSProperties for flexibility (e.g., 40 vs "40px").-type ItemProps = { - height?: string; - width?: string; +type ItemProps = { + height?: React.CSSProperties["height"]; + width?: React.CSSProperties["width"]; className?: string; };
10-14: A11y: indicate loading state politely and respect reduced motionAdd
aria-busyand a visually hidden label; prefermotion-safe:animate-pulseto honor user settings.-const SkeletonRoot: React.FC<Props> = ({ children, className = "" }) => ( - <div data-slot="skeleton" className={cn("animate-pulse", className)} role="status"> - {children} - </div> -); +const SkeletonRoot: React.FC<Props> = ({ children, className = "" }) => ( + <div + data-slot="skeleton" + className={cn("motion-safe:animate-pulse", className)} + role="status" + aria-busy="true" + > + <span className="sr-only">Loading</span> + {children} + </div> +);
22-28: A11y: hide decorative blocks from AT and minor cleanupSkeleton blocks are decorative; hide them and simplify the style object.
-const Item: React.FC<ItemProps> = ({ height = "auto", width = "auto", className = "" }) => ( +const Item: React.FC<ItemProps> = ({ height = "auto", width = "auto", className = "" }) => ( <div data-slot="skeleton-item" className={cn("rounded-md bg-custom-background-80", className)} - style={{ height: height, width: width }} + style={{ height, width }} + aria-hidden="true" /> );
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
packages/propel/src/Skeleton/index.ts(1 hunks)packages/propel/src/Skeleton/root.tsx(1 hunks)packages/propel/src/Skeleton/skeleton.stories.tsx(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Analyze (javascript)
- GitHub Check: Build and lint web apps
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
packages/propel/src/accordion/accordion.tsx (1)
1-1: Optional: switch to type-only/named imports (new JSX runtime)If your tsconfig uses the react-jsx runtime, you can avoid a runtime React import and keep only types.
Apply:
-import * as React from "react"; +import type { FC, ReactNode } from "react";Then update usages outside this line (example):
// Replace: // React.FC<AccordionRootProps> -> FC<AccordionRootProps> // React.ReactNode -> ReactNode
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
packages/propel/src/Skeleton/index.ts(1 hunks)packages/propel/src/accordion/accordion.tsx(1 hunks)packages/utils/src/string.ts(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- packages/utils/src/string.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/propel/src/Skeleton/index.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Build and lint web apps
🔇 Additional comments (1)
packages/propel/src/accordion/accordion.tsx (1)
1-1: Duplicate React import removal — LGTMGood cleanup; no behavior change and types still resolve.
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
packages/propel/package.json(2 hunks)
🔇 Additional comments (1)
packages/propel/package.json (1)
58-58: No action needed.Closing brace change only.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (6)
packages/propel/src/skeleton/root.tsx (4)
11-15: A11y: Prefer reduced-motion-friendly pulse and expose aria-busy.Announcing “Loading content” via role=status is fine, but consider:
- Respecting prefers-reduced-motion.
- Exposing
aria-busyso consumers can wire regions correctly.Apply:
-const SkeletonRoot = ({ children, className = "", ariaLabel = "Loading content" }: SkeletonProps) => ( - <div data-slot="skeleton" className={cn("animate-pulse", className)} role="status" aria-label={ariaLabel}> +const SkeletonRoot = ({ children, className = "", ariaLabel = "Loading content", busy = true, ...divProps }: SkeletonProps) => ( + <div + data-slot="skeleton" + className={cn("motion-safe:animate-pulse motion-reduce:animate-none", className)} + role="status" + aria-label={ariaLabel} + aria-busy={busy} + {...divProps} + > {children} </div> )And update the prop type (see next comment).
5-10: Broaden prop surface and forward unknown div props.Allow standard div attributes (id, data-*, onClick) and an optional
busyprop.-type SkeletonProps = { - children: React.ReactNode; - className?: string; - ariaLabel?: string; -}; +type SkeletonProps = React.ComponentPropsWithoutRef<"div"> & { + ariaLabel?: string; + busy?: boolean; +};
23-29: Hide decorative items from screen readers; accept number|string and pass-through props.Items are visual only—mark them
aria-hiddenandrole="presentation". Also acceptnumber|stringfor sizes and merge with incomingstyle.-type ItemProps = { - height?: string; - width?: string; - className?: string; -}; +type ItemProps = React.ComponentPropsWithoutRef<"div"> & { + height?: number | string; + width?: number | string; + className?: string; +}; -const SkeletonItem: React.FC<ItemProps> = ({ height = "auto", width = "auto", className = "" }) => ( +const SkeletonItem: React.FC<ItemProps> = ({ height = "auto", width = "auto", className = "", style, ...divProps }) => ( <div data-slot="skeleton-item" className={cn("rounded-md bg-custom-background-80", className)} - style={{ height, width }} + style={{ height, width, ...style }} + aria-hidden="true" + role="presentation" + {...divProps} /> )
1-1: Optional: forward refs for integration with parent layout/measurements.Forwarding refs improves interoperability (focus/measure/scroll). Short diff:
-import React from "react"; +import React, { forwardRef } from "react"; @@ -const SkeletonRoot = ({ children, className = "", ariaLabel = "Loading content" }: SkeletonProps) => ( +const SkeletonRoot = forwardRef<HTMLDivElement, SkeletonProps>(({ children, className = "", ariaLabel = "Loading content", busy = true, ...divProps }, ref) => ( - <div data-slot="skeleton" className={cn("animate-pulse", className)} role="status" aria-label={ariaLabel}> + <div ref={ref} data-slot="skeleton" className={cn("motion-safe:animate-pulse motion-reduce:animate-none", className)} role="status" aria-label={ariaLabel} aria-busy={busy} {...divProps}> {children} </div> -); +)); @@ -const SkeletonItem: React.FC<ItemProps> = ({ height = "auto", width = "auto", className = "" }) => ( +const SkeletonItem = forwardRef<HTMLDivElement, ItemProps>(({ height = "auto", width = "auto", className = "", style, ...divProps }, ref) => ( <div + ref={ref} data-slot="skeleton-item" className={cn("rounded-md bg-custom-background-80", className)} - style={{ height, width }} + style={{ height, width, ...style }} aria-hidden="true" role="presentation" + {...divProps} /> -); +));Also consider:
export type { SkeletonProps, ItemProps };next to the component export.packages/propel/src/skeleton/skeleton.stories.tsx (2)
15-33: Optional: Add controls via args to showcase sizing quickly.Expose
height,width, andgapas args to make the story interactive.export const Default: Story = { - render: () => ( - <Skeleton className="w-80 flex flex-col gap-2"> - <Skeleton.Item height="40px" width="100%" /> + args: { height: "40px", width: "100%", gap: "2" }, + render: ({ height, width, gap }) => ( + <Skeleton className={`w-80 flex flex-col gap-${gap}`}> + <Skeleton.Item height={height} width={width} /> </Skeleton> ), };
4-10: Ensure unique Storybook titles to avoid collisions
Two stories share the title"Components/Skeleton":
- packages/propel/src/Skeleton/skeleton.stories.tsx
- packages/propel/src/skeleton/skeleton.stories.tsx
Rename one of them (e.g. change to"Components/Feedback/Skeleton") so story IDs remain unique.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
packages/propel/src/skeleton/index.ts(1 hunks)packages/propel/src/skeleton/root.tsx(1 hunks)packages/propel/src/skeleton/skeleton.stories.tsx(1 hunks)
🔇 Additional comments (2)
packages/propel/src/skeleton/index.ts (1)
1-1: Barrel looks good.Simple, tree-shakeable re-export. No issues.
packages/propel/src/skeleton/skeleton.stories.tsx (1)
1-1: Import from react-vite is correct for Storybook 9 + Vite
With Storybook v9 and the Vite builder, types must be imported from@storybook/react-vite(not@storybook/react) as documented in the CSF API and migration guide (storybook.js.org, gist.github.com)Likely an incorrect or invalid review comment.
Description
This PR adds Skeleton component with respective stories in propel package
Type of Change
Screenshots and Media (if applicable)
Summary by CodeRabbit
New Features
Documentation
Chores