[AGILE-360] Review suggestions: signal inputs and scoped chart plugins - #24892
Conversation
Add a `showGroupByOptions` input to WorkPackageOverviewGraphComponent so embedding widgets (e.g. the sprint report work package graph widget) can hide the group-by <select>.
Otherwise the first chart rendered on a page sets the plugins for all charts of that page. This can cause bugs, depending on which chart loads first. This happened for sprint reports.
1. a quick smoke test to check that the correct options are passed to the chart component 2. a feature spec that inspects the DOM to see if the correct data is actually rendered
Replaces the manual getAttribute parsing in ngOnInit with signal inputs. The component is registered via createCustomElement, and Angular Elements already maps dasherised attributes onto inputs and applies input transforms, so booleanAttribute and a JSON transform cover the attribute-string case. groupBy becomes a model() as the select writes to it. Rails callers now pass plain attributes instead of the data-* inputs hash, which Angular Elements never observes, matching how the burndown widget passes chart-data. https://community.openproject.org/wp/AGILE-360
Registers chartjs-plugin-datalabels inline on the embedded graph's canvas instead of in the global Chart.js registry, so it no longer leaks into the burndown and budget charts and the display default flip becomes unnecessary. Provides the shared registerables and PrimerColorsPlugin once at the root via provideCharts, and drops init-chartjs.ts: importing it from main.ts made chart.js a hard dependency of the bootstrap path, pinning it to the main chunk even once the chart components load lazily. https://community.openproject.org/wp/AGILE-360
|
Caution The provided work package version does not match the core version Details:
Please make sure that:
|
There was a problem hiding this comment.
Pull request overview
This PR refines the AGILE-360 work package overview graph integration by (1) switching the custom element to Angular signal-based inputs (so Rails callers can pass plain attributes instead of data-* inputs:) and (2) scoping Chart.js datalabels usage to only the relevant chart instances (removing the prior global Chart.js side-effect initialization).
Changes:
- Update
WorkPackageOverviewGraphComponentto useinput()/model()signals and consume attributes directly (global-scope,initial-filters,show-group-by-options). - Remove global Chart.js initialization (
init-chartjs.ts) and register shared Chart.js pieces viaprovideCharts, while passing datalabels as an inline per-chart plugin. - Adjust Rails widget renderers + specs to pass/read plain attributes instead of
data-*dataset attributes.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| modules/backlogs/spec/components/backlogs/sprint_reports/widgets/work_package_graph_spec.rb | Updates spec expectations to read plain attributes (global-scope, initial-filters, etc.). |
| modules/backlogs/app/components/backlogs/sprint_reports/widgets/work_package_graph.rb | Switches widget rendering to plain attributes and JSON-serializes initial-filters. |
| frontend/src/main.ts | Drops the global side-effect import of Chart.js initialization. |
| frontend/src/app/shared/components/work-package-graphs/overview/wp-overview-graph.template.html | Updates template bindings to call signal inputs (showGroupByOptions(), chartOptions()). |
| frontend/src/app/shared/components/work-package-graphs/overview/wp-overview-graph.component.ts | Migrates inputs to signals and removes manual attribute parsing in ngOnInit. |
| frontend/src/app/shared/components/work-package-graphs/embedded/wp-embedded-graph.html | Passes plugins into the chart directive for per-chart plugin scoping. |
| frontend/src/app/shared/components/work-package-graphs/embedded/wp-embedded-graph.component.ts | Scopes chartjs-plugin-datalabels to this chart instance via a local plugins array. |
| frontend/src/app/core/setup/init-chartjs.ts | Deletes the prior global Chart.js registration/defaults side-effect module. |
| frontend/src/app/app.module.ts | Registers Chart.js registerables + Primer colors plugin via provideCharts(...). |
| app/components/projects/settings/versions/work_packages_graph_widget_component.rb | Switches widget rendering to plain attributes and JSON-serializes initial-filters. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| // Rendered as a custom element, so inputs may arrive as attribute strings. | ||
| readonly initialFilters = input<GraphFilter[]|null, string|GraphFilter[]|null>(null, { | ||
| transform: (value) => (typeof value === 'string' ? JSON.parse(value) as GraphFilter[] : value), | ||
| }); |
|
Warning Flaky specs
🤖 Ask Copilot to investigateCopy the prompt below into a new comment on this PR to delegate the investigation to GitHub Copilot. It will look into the flakiness and open a separate pull request with you as reviewer. |
82e4993 to
fb60262
Compare
Note
👨🏻 For @EinLama's consideration: merge/cherry-pick or throw away as necesasary.
Ticket
https://community.openproject.org/wp/AGILE-360
What are you trying to accomplish?
Companion PR to #24796 with two concrete suggestions from the review, offered as commits to cherry-pick or merge into the feature branch if you agree with them.
1. Signal inputs on
WorkPackageOverviewGraphComponent(dc42957bfb6)The component is registered via
createCustomElement, and Angular Elements already maps dasherised attribute names onto inputs and applies input transforms. So the manualgetAttribute(...) ?? getAttribute('data-...')parsing inngOnInit(including the block added forshow-group-by-options) can go entirely:booleanAttributecoversglobal-scope/show-group-by-options, a small JSON transform coversinitial-filters, andgroupBybecomes amodel()since the select writes to it.On the Rails side the callers now pass plain attributes instead of
inputs:(which JSON-encodes intodata-*attributes that Angular Elements never observes) — the same way the burndown widget in #24796 already passeschart-data.2. Chart-scoped datalabels instead of global registration (
b8489ccd3cd)The plugin leak fixed by
38a2dbbc94bis thatwithDefaultRegisterables(ChartDataLabels, ...)put datalabels into Chart.js's global registry, so every chart on the page got it. Registering everything globally ininit-chartjs.tsand then flippingChart.defaults.set('plugins.datalabels', { display: false })plus an explicitdisplay: trueopt-in is two global overrides to cure one global leak.ng2-charts'
BaseChartDirectivehas apluginsinput for exactly this: inline plugins are scoped to that chart instance and never touch the registry. The genuinely shared pieces (registerables,PrimerColorsPlugin) are provided once at the root viaprovideCharts, which is how ng2-charts expects to be configured — registration still runs lazily in the firstBaseChartDirectiveconstructor.This also removes the
init-chartjs.tsimport frommain.ts: esbuild only splits on dynamicimport(), so a static side-effect import from the entry file pins chart.js to the main chunk for good. Today chart.js lands inmainanyway becauseapp.module.tsstatically imports every chart component, but that is an accident we want to undo, and chart setup should live inside the chart dependency graph rather than the app entry point.What approach did you choose and why?
See above — each commit message carries the reasoning. Verified with
ngc(AOT template check),tsc, ESLint, and the widget component specs. One thing worth a visual check before merging: datalabels still rendering on the WP graph and absent on the burndown chart, since the inline[plugins]binding is the one behavioural change not covered by compile or specs.Merge checklist