DEV: Load chartjs via npm/webpack (#35249)

This commit is contained in:
David Taylor
2025-10-07 21:20:10 +01:00
committed by GitHub
parent cdf12d3142
commit 843f74e427
17 changed files with 81 additions and 82 deletions
-2
View File
@@ -67,8 +67,6 @@ updates:
open-pull-requests-limit: 20
versioning-strategy: increase
ignore: # These are all vendored so need to be updated manually. See lib/tasks/javascript.rake
- dependency-name: "chart.js"
- dependency-name: "chartjs-plugin-datalabels"
- dependency-name: "magnific-popup"
- dependency-name: "moment"
- dependency-name: "moment-timezone"
@@ -1,29 +1,33 @@
import Component from "@glimmer/component";
import { modifier } from "ember-modifier";
import loadScript from "discourse/lib/load-script";
import loadChartJS, {
loadChartJSDatalabels,
} from "discourse/lib/load-chart-js";
// args:
// chartConfig - object
export default class Chart extends Component {
export default class ChartComponent extends Component {
renderChart = modifier((element) => {
const { chartConfig, loadChartDataLabelsPlugin } = this.args;
loadScript("/javascripts/Chart.min.js")
.then(
() =>
loadChartDataLabelsPlugin &&
loadScript("/javascripts/chartjs-plugin-datalabels.min.js")
)
.then(() => {
if (loadChartDataLabelsPlugin) {
(chartConfig.plugins ??= []).push(window.ChartDataLabels);
}
this.chart = new window.Chart(element.getContext("2d"), chartConfig);
});
this.loadAndInit(element);
return () => this.chart?.destroy();
});
async loadAndInit(element) {
const chartConfig = { ...this.args.chartConfig };
const Chart = await loadChartJS();
if (this.args.loadChartDataLabelsPlugin) {
const ChartDataLabelsPlugin = await loadChartJSDatalabels();
chartConfig.plugins = [
...(chartConfig.plugins || []),
ChartDataLabelsPlugin,
];
}
this.chart = new Chart(element.getContext("2d"), chartConfig);
}
<template>
<div ...attributes>
<div class="chart-canvas-container">
@@ -1,7 +1,7 @@
import Component from "@glimmer/component";
import { action } from "@ember/object";
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
import loadScript from "discourse/lib/load-script";
import loadChartJS from "discourse/lib/load-chart-js";
import I18n, { i18n } from "discourse-i18n";
const oneDay = 86400000; // day in milliseconds
@@ -76,7 +76,7 @@ export default class TopicViewsChart extends Component {
@action
async renderChart(element) {
await loadScript("/javascripts/Chart.min.js");
const Chart = await loadChartJS();
if (!this.args.views?.stats || this.args.views?.stats?.length === 0) {
this.noData = true;
@@ -124,7 +124,7 @@ export default class TopicViewsChart extends Component {
this.chart.destroy();
}
this.chart = new window.Chart(context, {
this.chart = new Chart(context, {
type: "line",
data: {
datasets: [
@@ -0,0 +1,9 @@
import { waitForPromise } from "@ember/test-waiters";
export default async function loadChartJS() {
return (await waitForPromise(import("chart.js/auto"))).default;
}
export async function loadChartJSDatalabels() {
return (await waitForPromise(import("chartjs-plugin-datalabels"))).default;
}
@@ -2,9 +2,6 @@
// Update it by running `rake javascript:update`
export const PUBLIC_JS_VERSIONS = {
"chart.min.js": "chart.js/3.5.1/chart.min.js",
"chartjs-plugin-datalabels.min.js":
"chartjs-plugin-datalabels/2.2.0/chartjs-plugin-datalabels.min.js",
"jquery.magnific-popup.min.js":
"magnific-popup/1.1.0/jquery.magnific-popup.min.js",
};
@@ -35,6 +35,8 @@
"@messageformat/runtime": "^3.0.1",
"ace-builds": "^1.43.3",
"decorator-transforms": "^2.3.0",
"chart.js": "3.5.1",
"chartjs-plugin-datalabels": "2.2.0",
"diff": "^8.0.2",
"discourse-widget-hbs": "workspace:1.0.0",
"ember-curry-component": "^0.3.1",
@@ -19,19 +19,19 @@ module("Unit | Utility | load-script", function (hooks) {
test("generates URLs with version number in the query params", function (assert) {
assert.strictEqual(
cacheBuster("/javascripts/chart.min.js"),
`/javascripts/${jsVersions["chart.min.js"]}`
cacheBuster("/javascripts/jquery.magnific-popup.min.js"),
`/javascripts/${jsVersions["jquery.magnific-popup.min.js"]}`
);
});
test("lookups are case-insensitive", (assert) => {
assert.strictEqual(
cacheBuster("/javascripts/Chart.min.js"),
`/javascripts/${jsVersions["chart.min.js"]}`
cacheBuster("/javascripts/JQuery.magnific-popup.min.js"),
`/javascripts/${jsVersions["jquery.magnific-popup.min.js"]}`
);
assert.strictEqual(
cacheBuster("/javascripts/chart.min.js"),
`/javascripts/${jsVersions["chart.min.js"]}`
cacheBuster("/javascripts/jquery.magnific-popup.min.js"),
`/javascripts/${jsVersions["jquery.magnific-popup.min.js"]}`
);
});
});
+1 -5
View File
@@ -64,11 +64,7 @@ def write_hbs_template(path, task_name, template)
end
def dependencies
[
{ source: "chart.js/dist/chart.min.js", public: true },
{ source: "chartjs-plugin-datalabels/dist/chartjs-plugin-datalabels.min.js", public: true },
{ source: "magnific-popup/dist/jquery.magnific-popup.min.js", public: true },
]
[{ source: "magnific-popup/dist/jquery.magnific-popup.min.js", public: true }]
end
def node_package_name(f)
-2
View File
@@ -15,8 +15,6 @@
"@glint/template": "^1.6.0-alpha.2",
"@rdil/parallel-prettier": "^3.0.0",
"@swc/core": "^1.13.5",
"chart.js": "3.5.1",
"chartjs-plugin-datalabels": "2.2.0",
"chrome-launcher": "^1.2.1",
"chrome-remote-interface": "^0.33.3",
"concurrently": "^9.2.1",
@@ -3,7 +3,7 @@ import { on } from "@ember/modifier";
import { action } from "@ember/object";
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
import { bind } from "discourse/lib/decorators";
import loadScript from "discourse/lib/load-script";
import loadChartJS from "discourse/lib/load-chart-js";
import themeColor from "../lib/themeColor";
export default class DataExplorerBarChart extends Component {
@@ -80,9 +80,8 @@ export default class DataExplorerBarChart extends Component {
@bind
async initChart(canvas) {
await loadScript("/javascripts/Chart.min.js");
const Chart = await loadChartJS();
const context = canvas.getContext("2d");
// eslint-disable-next-line
this.chart = new Chart(context, this.config);
}
@@ -12,7 +12,6 @@ import concatClass from "discourse/helpers/concat-class";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import discourseComputed from "discourse/lib/decorators";
import loadScript from "discourse/lib/load-script";
import { i18n } from "discourse-i18n";
import ComboBox from "select-kit/components/combo-box";
import PollBreakdownChart from "discourse/plugins/poll/discourse/components/poll-breakdown-chart";
@@ -30,11 +29,9 @@ export default class PollBreakdownModal extends Component {
init() {
this.set("groupedBy", this.groupableUserFields[0]?.id);
loadScript("/javascripts/Chart.min.js")
.then(() => loadScript("/javascripts/chartjs-plugin-datalabels.min.js"))
.then(() => {
this.fetchGroupedPollData();
});
this.fetchGroupedPollData();
super.init(...arguments);
}
@@ -5,6 +5,9 @@ import { next } from "@ember/runloop";
import { htmlSafe } from "@ember/template";
import { classNames } from "@ember-decorators/component";
import discourseComputed from "discourse/lib/decorators";
import loadChartJS, {
loadChartJSDatalabels,
} from "discourse/lib/load-chart-js";
import I18n from "discourse-i18n";
import { getColors } from "discourse/plugins/poll/lib/chart-colors";
import { PIE_CHART_TYPE } from "../components/modal/poll-ui-builder";
@@ -37,11 +40,19 @@ export default class PollBreakdownChart extends Component {
}
}
didInsertElement() {
async didInsertElement() {
super.didInsertElement(...arguments);
const canvas = this.element.querySelector("canvas");
this._chart = new window.Chart(canvas.getContext("2d"), this.chartConfig);
const [Chart, ChartDataLabelsPlugin] = await Promise.all([
loadChartJS(),
loadChartJSDatalabels(),
]);
this._chart = new Chart(canvas.getContext("2d"), {
...this.chartConfig,
plugins: [ChartDataLabelsPlugin],
});
}
didReceiveAttrs() {
@@ -79,7 +90,6 @@ export default class PollBreakdownChart extends Component {
return {
type: PIE_CHART_TYPE,
plugins: [window.ChartDataLabels],
data: {
datasets: [
{
@@ -6,6 +6,7 @@ import { htmlSafe } from "@ember/template";
import { tagName } from "@ember-decorators/component";
import { propertyEqual } from "discourse/lib/computed";
import discourseComputed from "discourse/lib/decorators";
import loadChartJS from "discourse/lib/load-chart-js";
import I18n, { i18n } from "discourse-i18n";
import { getColors } from "discourse/plugins/poll/lib/chart-colors";
@@ -22,8 +23,16 @@ export default class PollBreakdownOption extends Component {
onMouseOut = null;
@propertyEqual("highlightedOption", "index") highlighted;
@equal("displayMode", "percentage") showPercentage;
constructor() {
super(...arguments);
loadChartJS().then((Chart) => {
this.set("Chart", Chart);
});
}
@discourseComputed("option.votes", "totalVotes")
percent(votes, total) {
return I18n.toNumber((votes / total) * 100.0, { precision: 1 });
@@ -42,10 +51,10 @@ export default class PollBreakdownOption extends Component {
}
}
@discourseComputed("highlighted", "optionColors", "index")
colorPreviewStyle(highlighted, optionColors, index) {
@discourseComputed("Chart", "highlighted", "optionColors", "index")
colorPreviewStyle(Chart, highlighted, optionColors, index) {
const color = highlighted
? window.Chart.helpers.getHoverColor(optionColors[index])
? Chart?.helpers.getHoverColor(optionColors[index])
: optionColors[index];
return htmlSafe(`background: ${color};`);
@@ -3,7 +3,7 @@ import { action } from "@ember/object";
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
import { htmlSafe } from "@ember/template";
import { modifier } from "ember-modifier";
import loadScript from "discourse/lib/load-script";
import loadChartJS from "discourse/lib/load-chart-js";
import { getColors } from "discourse/plugins/poll/lib/chart-colors";
import { PIE_CHART_TYPE } from "../components/modal/poll-ui-builder";
@@ -115,7 +115,7 @@ export default class PollResultsPieComponent extends Component {
@action
async drawPie() {
await loadScript("/javascripts/Chart.min.js");
const Chart = await loadChartJS();
const data = this.args.options.map((option) => option.votes);
const labels = this.args.options.map((option) => option.html);
@@ -123,7 +123,7 @@ export default class PollResultsPieComponent extends Component {
legendContainerId: this.legendElement.id,
});
const el = this.canvasElement;
// eslint-disable-next-line no-undef
this._chart = new Chart(el.getContext("2d"), config);
}
+6 -6
View File
@@ -61,12 +61,6 @@ importers:
'@swc/core':
specifier: ^1.13.5
version: 1.13.5
chart.js:
specifier: 3.5.1
version: 3.5.1
chartjs-plugin-datalabels:
specifier: 2.2.0
version: 2.2.0(chart.js@3.5.1)
chrome-launcher:
specifier: ^1.2.1
version: 1.2.1
@@ -302,6 +296,12 @@ importers:
ace-builds:
specifier: ^1.43.3
version: 1.43.3
chart.js:
specifier: 3.5.1
version: 3.5.1
chartjs-plugin-datalabels:
specifier: 2.2.0
version: 2.2.0(chart.js@3.5.1)
decorator-transforms:
specifier: ^2.3.0
version: 2.3.0(@babel/core@7.28.4)
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long