chore(storybook): consolidate storybook and enhance plugin stories (#37771)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Evan Rusackas
2026-02-11 19:06:23 -05:00
committed by GitHub
parent b012b63e5b
commit 981b370fe9
173 changed files with 5307 additions and 18230 deletions

View File

@@ -0,0 +1,268 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { SuperChart, getChartTransformPropsRegistry } from '@superset-ui/core';
import { HandlebarsChartPlugin } from '@superset-ui/plugin-chart-handlebars';
import { kpiData, leaderboardData, timelineData } from './data';
import { withResizableChartDemo } from '@storybook-shared';
const VIZ_TYPE = 'handlebars';
new HandlebarsChartPlugin().configure({ key: VIZ_TYPE }).register();
getChartTransformPropsRegistry().registerValue(
VIZ_TYPE,
(chartProps: {
width: number;
height: number;
formData: object;
queriesData: { data: unknown[] }[];
}) => {
const { width, height, formData, queriesData } = chartProps;
const { data } = queriesData[0];
return { width, height, data, formData };
},
);
// KPI Dashboard template - uses inline styles for Storybook compatibility
const kpiTemplate = `
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;">
{{#each data}}
<div style="background: linear-gradient(135deg, {{#if (eq status 'success')}}#11998e 0%, #38ef7d{{else}}{{#if (eq status 'warning')}}#f093fb 0%, #f5576c{{else}}#667eea 0%, #764ba2{{/if}}{{/if}} 100%); border-radius: 16px; padding: 20px; color: white; box-shadow: 0 10px 40px rgba(0,0,0,0.2);">
<div style="font-size: 32px; margin-bottom: 12px;">{{icon}}</div>
<div style="font-size: 12px; text-transform: uppercase; letter-spacing: 1px; opacity: 0.9;">{{metric}}</div>
<div style="font-size: 32px; font-weight: 700; margin: 4px 0;">{{formatNumber value}}</div>
<div style="display: inline-flex; align-items: center; gap: 4px; padding: 4px 8px; border-radius: 12px; font-size: 12px; font-weight: 600; background: {{#if (gt change 0)}}rgba(255,255,255,0.2){{else}}rgba(0,0,0,0.15){{/if}};">
{{#if (gt change 0)}}<span>▲</span>{{else}}<span>▼</span>{{/if}}
{{change}}%
</div>
<div style="margin-top: 8px; font-size: 11px; opacity: 0.8;">Target: {{formatNumber target}}</div>
</div>
{{/each}}
</div>
`;
// Leaderboard template - dark theme with inline styles
const leaderboardTemplate = `
<div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #1a1a2e; border-radius: 16px; padding: 24px; color: #eee;">
<h2 style="margin: 0 0 20px 0; font-size: 18px; color: #fff;">🏆 Top Performers</h2>
{{#each data}}
<div style="display: flex; align-items: center; padding: 12px 16px; margin: 8px 0; background: {{#if (eq rank 1)}}linear-gradient(90deg, rgba(255,215,0,0.2) 0%, transparent 100%){{else}}{{#if (eq rank 2)}}linear-gradient(90deg, rgba(192,192,192,0.2) 0%, transparent 100%){{else}}{{#if (eq rank 3)}}linear-gradient(90deg, rgba(205,127,50,0.2) 0%, transparent 100%){{else}}rgba(255,255,255,0.05){{/if}}{{/if}}{{/if}}; border-radius: 12px; {{#if (eq rank 1)}}border-left: 3px solid #ffd700;{{/if}}{{#if (eq rank 2)}}border-left: 3px solid #c0c0c0;{{/if}}{{#if (eq rank 3)}}border-left: 3px solid #cd7f32;{{/if}}">
<div style="width: 28px; height: 28px; border-radius: 50%; background: {{#if (eq rank 1)}}#ffd700{{else}}{{#if (eq rank 2)}}#c0c0c0{{else}}{{#if (eq rank 3)}}#cd7f32{{else}}rgba(255,255,255,0.1){{/if}}{{/if}}{{/if}}; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 12px; margin-right: 12px; {{#if (lte rank 3)}}color: #1a1a2e;{{/if}}">{{rank}}</div>
<div style="font-size: 28px; margin-right: 12px;">{{avatar}}</div>
<div style="flex: 1;">
<div style="font-weight: 600; font-size: 14px;">{{name}}</div>
<div style="font-size: 11px; color: #888; margin-top: 2px;">{{team}}</div>
</div>
<div style="text-align: right;">
<div style="font-size: 18px; font-weight: 700; color: #fff;">{{formatNumber score}}</div>
<div style="font-size: 12px; margin-top: 2px; color: {{#if (eq trend 'up')}}#38ef7d{{else}}{{#if (eq trend 'down')}}#f5576c{{else}}#888{{/if}}{{/if}};">
{{#if (eq trend 'up')}}↑{{/if}}{{#if (eq trend 'down')}}↓{{/if}}{{#if (eq trend 'same')}}→{{/if}}
</div>
</div>
</div>
{{/each}}
</div>
`;
// Timeline template with inline styles
const timelineTemplate = `
<div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; position: relative; padding-left: 40px;">
<div style="position: absolute; left: 13px; top: 8px; bottom: 8px; width: 3px; background: linear-gradient(180deg, #667eea 0%, #764ba2 100%); border-radius: 2px;"></div>
{{#each data}}
<div style="position: relative; padding: 12px 0;">
<div style="position: absolute; left: -33px; top: 16px; width: 16px; height: 16px; border-radius: 50%; background: {{#if (eq type 'milestone')}}#ffd700{{else}}{{#if (eq type 'success')}}#38ef7d{{else}}{{#if (eq type 'warning')}}#f5576c{{else}}#667eea{{/if}}{{/if}}{{/if}}; border: 3px solid #fff; box-shadow: 0 0 0 3px {{#if (eq type 'milestone')}}rgba(255,215,0,0.3){{else}}{{#if (eq type 'success')}}rgba(56,239,125,0.3){{else}}{{#if (eq type 'warning')}}rgba(245,87,108,0.3){{else}}rgba(102,126,234,0.3){{/if}}{{/if}}{{/if}};"></div>
<div style="background: #f8f9fa; border-radius: 12px; padding: 16px 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.06); margin-left: 8px;">
<div style="font-size: 11px; color: #888; text-transform: uppercase; letter-spacing: 0.5px;">{{date}}</div>
<div style="font-size: 16px; font-weight: 600; color: #333; margin: 4px 0;">{{event}}</div>
<div style="font-size: 13px; color: #666; line-height: 1.4;">{{description}}</div>
</div>
</div>
{{/each}}
</div>
`;
// Simple editable template for the interactive demo
const simpleTemplate = `<div style="font-family: sans-serif; padding: 16px;">
<h2 style="margin: 0 0 16px 0;">{{title}}</h2>
<ul style="list-style: none; padding: 0; margin: 0;">
{{#each data}}
<li style="padding: 8px; margin: 4px 0; background: #f5f5f5; border-radius: 4px;">
<strong>{{metric}}</strong>: {{formatNumber value}}
</li>
{{/each}}
</ul>
</div>`;
// Simple CSS for the interactive demo
const simpleCSS = `.handlebars-container {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.handlebars-container h2 {
color: #333;
margin-bottom: 16px;
}
.handlebars-container ul {
list-style: none;
padding: 0;
}
.handlebars-container li {
padding: 12px;
margin: 8px 0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 8px;
}`;
export default {
title: 'Chart Plugins/plugin-chart-handlebars',
decorators: [withResizableChartDemo],
};
export const InteractiveHandlebars = ({
handlebarsTemplate,
styleTemplate,
width,
height,
}: {
handlebarsTemplate: string;
styleTemplate: string;
width: number;
height: number;
}) => (
<SuperChart
chartType={VIZ_TYPE}
width={width}
height={height}
queriesData={[{ data: kpiData }]}
formData={{
datasource: '1__table',
viz_type: VIZ_TYPE,
handlebars_template: handlebarsTemplate,
style_template: styleTemplate,
}}
/>
);
InteractiveHandlebars.args = {
handlebarsTemplate: simpleTemplate,
styleTemplate: simpleCSS,
};
InteractiveHandlebars.argTypes = {
handlebarsTemplate: {
control: { type: 'text' },
description: 'Handlebars template for rendering data',
},
styleTemplate: {
control: { type: 'text' },
description: 'CSS styles to apply to the chart',
},
};
InteractiveHandlebars.parameters = {
initialSize: {
width: 600,
height: 400,
},
};
export const KPIDashboard = ({
width,
height,
}: {
width: number;
height: number;
}) => (
<SuperChart
chartType={VIZ_TYPE}
width={width}
height={height}
queriesData={[{ data: kpiData }]}
formData={{
datasource: '1__table',
viz_type: VIZ_TYPE,
handlebars_template: kpiTemplate,
}}
/>
);
KPIDashboard.parameters = {
initialSize: {
width: 900,
height: 280,
},
};
export const Leaderboard = ({
width,
height,
}: {
width: number;
height: number;
}) => (
<SuperChart
chartType={VIZ_TYPE}
width={width}
height={height}
queriesData={[{ data: leaderboardData }]}
formData={{
datasource: '1__table',
viz_type: VIZ_TYPE,
handlebars_template: leaderboardTemplate,
}}
/>
);
Leaderboard.parameters = {
initialSize: {
width: 450,
height: 420,
},
};
export const Timeline = ({
width,
height,
}: {
width: number;
height: number;
}) => (
<SuperChart
chartType={VIZ_TYPE}
width={width}
height={height}
queriesData={[{ data: timelineData }]}
formData={{
datasource: '1__table',
viz_type: VIZ_TYPE,
handlebars_template: timelineTemplate,
}}
/>
);
Timeline.parameters = {
initialSize: {
width: 500,
height: 500,
},
};

View File

@@ -0,0 +1,129 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
export const kpiData = [
{
metric: 'Total Revenue',
value: 2847563,
change: 12.5,
target: 2500000,
icon: '💰',
status: 'success',
},
{
metric: 'Active Users',
value: 84521,
change: -3.2,
target: 90000,
icon: '👥',
status: 'warning',
},
{
metric: 'Conversion Rate',
value: 4.7,
change: 0.8,
target: 5.0,
icon: '📈',
status: 'success',
},
{
metric: 'Avg Response Time',
value: 142,
change: -15.3,
target: 150,
icon: '⚡',
status: 'success',
},
];
export const leaderboardData = [
{
rank: 1,
name: 'Sarah Chen',
team: 'Engineering',
score: 2847,
avatar: '👩‍💻',
trend: 'up',
},
{
rank: 2,
name: 'Marcus Johnson',
team: 'Sales',
score: 2654,
avatar: '👨‍💼',
trend: 'up',
},
{
rank: 3,
name: 'Emily Rodriguez',
team: 'Marketing',
score: 2432,
avatar: '👩‍🎨',
trend: 'down',
},
{
rank: 4,
name: 'David Kim',
team: 'Engineering',
score: 2198,
avatar: '👨‍💻',
trend: 'up',
},
{
rank: 5,
name: 'Lisa Thompson',
team: 'Support',
score: 2045,
avatar: '👩‍🔧',
trend: 'same',
},
];
export const timelineData = [
{
date: '2024-01-15',
event: 'Product Launch',
type: 'milestone',
description: 'Successfully launched v2.0 to production',
},
{
date: '2024-01-10',
event: 'Beta Testing Complete',
type: 'success',
description: '500+ users tested with 98% satisfaction',
},
{
date: '2024-01-05',
event: 'Security Audit',
type: 'warning',
description: '2 minor issues found and resolved',
},
{
date: '2023-12-20',
event: 'Development Sprint',
type: 'info',
description: 'Completed 47 story points',
},
{
date: '2023-12-15',
event: 'Design Review',
type: 'info',
description: 'UI/UX approved by stakeholders',
},
];