Implement standardized skeleton loading placeholders to reduce perceived loading time and prevent layout shift during data fetches. These components match actual content dimensions exactly, improving perceived responsiveness. New skeleton components in src/components/skeletons/: - SkeletonTable: Table/grid loading with customizable rows and cells - SkeletonTableRow: Individual animated skeleton row - SkeletonChart: Chart/graph loading with bars matching dimensions - SkeletonStat: Stat card loading with label and value - SkeletonFormField: Form input loading placeholder - PageLoadingSkeleton: Convenience wrapper for page-level loading states Implementation details: - All skeletons use global 'skeleton-pulse' animation (2s cycle) - Dimensions match real content to prevent layout shift on arrival - Marked with aria-hidden and role=presentation for accessibility - Theme-aware colors using Fluent UI tokens - Respects prefers-reduced-motion setting Updates: - ChartStateWrapper: Uses SkeletonChart instead of spinner - PageFeedback: Added PageLoadingSkeleton component - App.tsx: Injects skeleton styles at startup - Web-Design.md: Added § 8a with loading UX guidance and usage examples All components tested (22 tests, 100% passing) and linted. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
59 lines
1.6 KiB
TypeScript
59 lines
1.6 KiB
TypeScript
/**
|
|
* SkeletonTable component.
|
|
*
|
|
* A placeholder for DataGrid tables showing multiple skeleton rows with
|
|
* animated pulsing. Used to display table structure during data load.
|
|
*/
|
|
|
|
import {
|
|
makeStyles,
|
|
} from "@fluentui/react-components";
|
|
import { SkeletonTableRow } from "./SkeletonTableRow";
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Styles
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const useStyles = makeStyles({
|
|
skeletonTable: {
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
width: "100%",
|
|
},
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Types
|
|
// ---------------------------------------------------------------------------
|
|
|
|
interface SkeletonTableProps {
|
|
/** Number of rows to render. Defaults to 5. */
|
|
rowCount?: number;
|
|
/** Number of cells per row. Defaults to 6. */
|
|
cellCount?: number;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Component
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Render a skeleton table with multiple animated rows.
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* <SkeletonTable rowCount={10} cellCount={5} />
|
|
* ```
|
|
*/
|
|
export function SkeletonTable({ rowCount = 5, cellCount = 6 }: SkeletonTableProps): React.JSX.Element {
|
|
const styles = useStyles();
|
|
|
|
return (
|
|
<div className={styles.skeletonTable} role="presentation">
|
|
{Array.from({ length: rowCount }).map((_, index: number) => (
|
|
<SkeletonTableRow key={`row-${String(index)}`} cellCount={cellCount} />
|
|
))}
|
|
</div>
|
|
);
|
|
}
|