+
+---
+
+## Component Library
+
+Interactive documentation for Superset's UI component library. **${totalComponents} components** documented across ${Object.keys(categories).filter(k => categories[k].length > 0).length} categories.
+
+${categoryList}
+
+## Usage
+
+All components are exported from \`@superset-ui/core/components\`:
+
+\`\`\`tsx
+import { Button, Modal, Select } from '@superset-ui/core/components';
+\`\`\`
+
+## Contributing
+
+This documentation is auto-generated from Storybook stories. To add or update component documentation:
+
+1. Create or update the component's \`.stories.tsx\` file
+2. Add a descriptive \`title\` and \`description\` in the story meta
+3. Export an interactive story with \`args\` for configurable props
+4. Run \`yarn generate:superset-components\` in the \`docs/\` directory
+
+:::info Work in Progress
+This component library is actively being documented. See the [Components TODO](./TODO) page for a list of components awaiting documentation.
+:::
+
+---
+
+*Auto-generated from Storybook stories in the [Design System/Introduction](https://github.com/apache/superset/blob/master/superset-frontend/packages/superset-ui-core/src/components/DesignSystem.stories.tsx) story.*
+`;
+}
+
+/**
+ * Generate TODO.md tracking skipped components
+ */
+function generateTodoMd(skippedFiles) {
+ const disabledSources = SOURCES.filter(s => !s.enabled);
+ const grouped = {};
+ for (const file of skippedFiles) {
+ const source = disabledSources.find(s => file.includes(s.path));
+ const sourceName = source ? source.name : 'unknown';
+ if (!grouped[sourceName]) grouped[sourceName] = [];
+ grouped[sourceName].push(file);
+ }
+
+ const sections = Object.entries(grouped)
+ .map(([source, files]) => {
+ const fileList = files.map(f => `- [ ] \`${path.relative(ROOT_DIR, f)}\``).join('\n');
+ return `### ${source}\n\n${files.length} components\n\n${fileList}`;
+ })
+ .join('\n\n');
+
+ return `---
+title: Components TODO
+sidebar_class_name: hidden
+---
+
+# Components TODO
+
+These components were found but not yet supported for documentation generation.
+Future phases will add support for these sources.
+
+## Summary
+
+- **Total skipped:** ${skippedFiles.length} story files
+- **Reason:** Import path resolution not yet implemented
+
+## Skipped by Source
+
+${sections}
+
+## How to Add Support
+
+1. Determine the correct import path for the source
+2. Update \`generate-superset-components.mjs\` to handle the source
+3. Add source to \`SUPPORTED_SOURCES\` array
+4. Re-run the generator
+
+---
+
+*Auto-generated by generate-superset-components.mjs*
+`;
+}
+
+/**
+ * Main function
+ */
+async function main() {
+ console.log('Generating Superset Components documentation...\n');
+
+ // Find enabled story files
+ const enabledFiles = findEnabledStoryFiles();
+ console.log(`Found ${enabledFiles.length} story files from enabled sources\n`);
+
+ // Find disabled story files (for tracking)
+ const disabledFiles = findDisabledStoryFiles();
+ console.log(`Found ${disabledFiles.length} story files from disabled sources (tracking only)\n`);
+
+ // Parse enabled files
+ const components = [];
+ for (const { file, source } of enabledFiles) {
+ const parsed = parseStoryFile(file, source);
+ if (parsed && parsed.componentName) {
+ components.push(parsed);
+ }
+ }
+
+ console.log(`Parsed ${components.length} components\n`);
+
+ // Group by category
+ const categories = {};
+ for (const component of components) {
+ if (!categories[component.category]) {
+ categories[component.category] = [];
+ }
+ categories[component.category].push(component);
+ }
+
+ // Ensure output directory exists
+ if (!fs.existsSync(OUTPUT_DIR)) {
+ fs.mkdirSync(OUTPUT_DIR, { recursive: true });
+ }
+
+ // Generate MDX files by category
+ let generatedCount = 0;
+ for (const [category, categoryComponents] of Object.entries(categories)) {
+ const categoryDir = path.join(OUTPUT_DIR, category);
+ if (!fs.existsSync(categoryDir)) {
+ fs.mkdirSync(categoryDir, { recursive: true });
+ }
+
+ // Generate component pages
+ for (const component of categoryComponents) {
+ const storyContent = fs.readFileSync(component.filePath, 'utf-8');
+ const mdxContent = generateMDX(component, storyContent);
+ const outputPath = path.join(categoryDir, `${component.componentName.toLowerCase()}.mdx`);
+ fs.writeFileSync(outputPath, mdxContent);
+ console.log(` ✓ ${category}/${component.componentName}`);
+ generatedCount++;
+ }
+
+ // Generate category index
+ const indexContent = generateCategoryIndex(category, categoryComponents);
+ const indexPath = path.join(categoryDir, 'index.mdx');
+ fs.writeFileSync(indexPath, indexContent);
+ console.log(` ✓ ${category}/index`);
+ }
+
+ // Generate main overview
+ const overviewContent = generateOverviewIndex(categories);
+ const overviewPath = path.join(OUTPUT_DIR, 'index.mdx');
+ fs.writeFileSync(overviewPath, overviewContent);
+ console.log(` ✓ index (overview)`);
+
+ // Generate TODO.md
+ const todoContent = generateTodoMd(disabledFiles);
+ const todoPath = path.join(OUTPUT_DIR, 'TODO.md');
+ fs.writeFileSync(todoPath, todoContent);
+ console.log(` ✓ TODO.md`);
+
+ console.log(`\nDone! Generated ${generatedCount} component pages.`);
+ console.log(`Tracked ${disabledFiles.length} components for future implementation.`);
+}
+
+main().catch(console.error);
diff --git a/docs/sidebarTutorials.js b/docs/sidebarTutorials.js
index 6e0eff12247..b527517a954 100644
--- a/docs/sidebarTutorials.js
+++ b/docs/sidebarTutorials.js
@@ -110,9 +110,21 @@ const sidebars = {
'testing/frontend-testing',
'testing/backend-testing',
'testing/e2e-testing',
+ 'testing/storybook',
'testing/ci-cd',
],
},
+ {
+ type: 'category',
+ label: 'UI Components',
+ collapsed: true,
+ items: [
+ {
+ type: 'autogenerated',
+ dirName: 'components',
+ },
+ ],
+ },
{
type: 'link',
label: 'API Reference',
diff --git a/docs/src/components/BlurredSection.tsx b/docs/src/components/BlurredSection.tsx
index d502cadcb3d..712a36fa8c5 100644
--- a/docs/src/components/BlurredSection.tsx
+++ b/docs/src/components/BlurredSection.tsx
@@ -39,11 +39,12 @@ const StyledBlurredSection = styled('section')`
interface BlurredSectionProps {
children: ReactNode;
+ id?: string;
}
-const BlurredSection = ({ children }: BlurredSectionProps) => {
+const BlurredSection = ({ children, id }: BlurredSectionProps) => {
return (
-
Horizontal divider with title (orientationMargin applies here):
+Vertical divider (use container gap for spacing):
+Item
+ {SAMPLE_ITEMS.map((item, i) => ( +