bito-code-review[bot] commented on code in PR #37771: URL: https://github.com/apache/superset/pull/37771#discussion_r2779121582
########## superset-frontend/plugins/plugin-chart-handlebars/src/stories/Handlebars.stories.tsx: ########## @@ -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;"> Review Comment: <div> <div id="suggestion"> <div id="issue"><b>Unused CSS Class</b></div> <div id="fix"> Add `class="handlebars-container"` to the top-level `<div>` in `simpleTemplate` so the defined CSS rules apply. </div> </div> <small><i>Code Review Run #3b6483</i></small> </div> --- Should Bito avoid suggestions like this for future reviews? (<a href=https://alpha.bito.ai/home/ai-agents/review-rules>Manage Rules</a>) - [ ] Yes, avoid them ########## superset-frontend/plugins/plugin-chart-handlebars/src/stories/Handlebars.stories.tsx: ########## @@ -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> Review Comment: <div> <div id="suggestion"> <div id="issue"><b>Missing Data Field</b></div> <div id="fix"> Update the Handlebars template to use a data field that exists (for example, replace `{{title}}` with `{{metric}}`) or add a `title` property to kpiData. </div> </div> <small><i>Code Review Run #3b6483</i></small> </div> --- Should Bito avoid suggestions like this for future reviews? (<a href=https://alpha.bito.ai/home/ai-agents/review-rules>Manage Rules</a>) - [ ] Yes, avoid them ########## superset-frontend/plugins/legacy-plugin-chart-country-map/src/stories/CountryMap.stories.tsx: ########## @@ -95,9 +96,9 @@ export const BasicCountryMapStory = ( height={height} queriesData={[{ data }]} formData={{ - linearColorScheme: colorSchema, - numberFormat: '.3s', - selectCountry: country, + linear_color_scheme: colorSchema, + number_format: '.3s', + select_country: country, Review Comment: <div> <div id="suggestion"> <div id="issue"><b>Inconsistent formData key destructuring</b></div> <div id="fix"> The formData properties are correctly updated to snake_case to match the control panel definitions, but transformProps.ts still destructures camelCase keys, which will result in undefined values and broken chart functionality. </div> <details> <summary> <b>Code suggestion</b> </summary> <blockquote>Check the AI-generated fix before applying</blockquote> <div id="code"> ``` - const { - linearColorScheme, - numberFormat, - selectCountry, - colorScheme, - sliceId, - } = formData; + const { + linear_color_scheme: linearColorScheme, + number_format: numberFormat, + select_country: selectCountry, + colorScheme, + sliceId, + } = formData; ``` </div> </details> </div> <small><i>Code Review Run #3b6483</i></small> </div> --- Should Bito avoid suggestions like this for future reviews? (<a href=https://alpha.bito.ai/home/ai-agents/review-rules>Manage Rules</a>) - [ ] Yes, avoid them ########## superset-frontend/plugins/plugin-chart-handlebars/src/stories/Handlebars.stories.tsx: ########## @@ -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, Review Comment: <div> <div id="suggestion"> <div id="issue"><b>FormData Key Mismatch</b></div> <div id="fix"> FormData key 'style_template' mismatches component's `formData.styleTemplate` access, preventing CSS application in stories. </div> <details> <summary> <b>Code suggestion</b> </summary> <blockquote>Check the AI-generated fix before applying</blockquote> <div id="code"> ``` --- a/superset-frontend/plugins/plugin-chart-handlebars/src/stories/Handlebars.stories.tsx +++ b/superset-frontend/plugins/plugin-chart-handlebars/src/stories/Handlebars.stories.tsx @@ -157,7 +157,7 @@ export const InteractiveHandlebars = ({ formData={{ datasource: '1__table', viz_type: VIZ_TYPE, handlebars_template: handlebarsTemplate, - style_template: styleTemplate, + styleTemplate: styleTemplate, }} /> ); ``` </div> </details> </div> <small><i>Code Review Run #3b6483</i></small> </div> --- Should Bito avoid suggestions like this for future reviews? (<a href=https://alpha.bito.ai/home/ai-agents/review-rules>Manage Rules</a>) - [ ] Yes, avoid them ########## superset-frontend/plugins/plugin-chart-handlebars/src/stories/Handlebars.stories.tsx: ########## @@ -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, Review Comment: <div> <div id="suggestion"> <div id="issue"><b>FormData Key Mismatch</b></div> <div id="fix"> FormData key 'handlebars_template' mismatches component's `formData.handlebarsTemplate` access, breaking template rendering in stories. </div> <details> <summary> <b>Code suggestion</b> </summary> <blockquote>Check the AI-generated fix before applying</blockquote> <div id="code"> ``` --- a/superset-frontend/plugins/plugin-chart-handlebars/src/stories/Handlebars.stories.tsx +++ b/superset-frontend/plugins/plugin-chart-handlebars/src/stories/Handlebars.stories.tsx @@ -157,7 +157,7 @@ export const InteractiveHandlebars = ({ formData={{ datasource: '1__table', viz_type: VIZ_TYPE, - handlebars_template: handlebarsTemplate, + handlebarsTemplate: handlebarsTemplate, style_template: styleTemplate, }} /> ); @@ -201,7 +201,7 @@ export const KPIDashboard = ({ formData={{ datasource: '1__table', viz_type: VIZ_TYPE, - handlebars_template: kpiTemplate, + handlebarsTemplate: kpiTemplate, }} /> ); @@ -228,7 +228,7 @@ export const Leaderboard = ({ formData={{ datasource: '1__table', viz_type: VIZ_TYPE, - handlebars_template: leaderboardTemplate, + handlebarsTemplate: leaderboardTemplate, }} /> ); @@ -255,7 +255,7 @@ export const Timeline = ({ formData={{ datasource: '1__table', viz_type: VIZ_TYPE, - handlebars_template: timelineTemplate, + handlebarsTemplate: timelineTemplate, }} /> ); ``` </div> </details> </div> <small><i>Code Review Run #3b6483</i></small> </div> --- Should Bito avoid suggestions like this for future reviews? (<a href=https://alpha.bito.ai/home/ai-agents/review-rules>Manage Rules</a>) - [ ] Yes, avoid them -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
