@dnax/ui 0.0.66 → 0.0.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,232 @@
1
+ // Vérifie que la liste de modules **enregistrés par `QChart.vue`** suffit à rendre chaque
2
+ // famille de marque. Le SFC charge ECharts en dynamique (`echarts/core` + `use([...])`) : un
3
+ // module oublié n'échoue pas — il est **silencieusement ignoré**. Ce test rejoue exactement les
4
+ // mêmes imports tree-shakés (avec le rendu SVG) pour que l'oubli devienne visible.
5
+ //
6
+ // ⚠️ Toute nouvelle série / composant doit être ajouté ici **et** dans `QChart.vue`.
7
+ import { describe, expect, it } from "bun:test"
8
+ import * as echarts from "echarts/core"
9
+ import {
10
+ BarChart,
11
+ BoxplotChart,
12
+ FunnelChart,
13
+ GaugeChart,
14
+ HeatmapChart,
15
+ LineChart,
16
+ PieChart,
17
+ RadarChart,
18
+ SankeyChart,
19
+ ScatterChart,
20
+ SunburstChart,
21
+ TreemapChart,
22
+ } from "echarts/charts"
23
+ import {
24
+ GridComponent,
25
+ LegendComponent,
26
+ MarkAreaComponent,
27
+ MarkLineComponent,
28
+ RadarComponent,
29
+ TitleComponent,
30
+ TooltipComponent,
31
+ VisualMapComponent,
32
+ } from "echarts/components"
33
+ import { SVGRenderer } from "echarts/renderers"
34
+ import { LegacyGridContainLabel } from "echarts/features"
35
+ import { chartToECharts } from "./chart"
36
+ import type { QChartConfig } from "./chart"
37
+
38
+ echarts.use([
39
+ BarChart,
40
+ LineChart,
41
+ ScatterChart,
42
+ PieChart,
43
+ HeatmapChart,
44
+ BoxplotChart,
45
+ GaugeChart,
46
+ FunnelChart,
47
+ RadarChart,
48
+ TreemapChart,
49
+ SunburstChart,
50
+ SankeyChart,
51
+ GridComponent,
52
+ RadarComponent,
53
+ TooltipComponent,
54
+ LegendComponent,
55
+ TitleComponent,
56
+ MarkLineComponent,
57
+ MarkAreaComponent,
58
+ VisualMapComponent,
59
+ SVGRenderer,
60
+ LegacyGridContainLabel,
61
+ ])
62
+
63
+ const THEME = { text: "#111111", muted: "#666666", grid: "#e5e5e5" }
64
+
65
+ const render = (config: QChartConfig) => {
66
+ const chart = echarts.init(null as any, null, { renderer: "svg", ssr: true, width: 420, height: 280 })
67
+ chart.setOption(chartToECharts({ theme: THEME, ...config, marks: config.marks ?? [] }))
68
+ const svg = chart.renderToSVGString()
69
+ chart.dispose()
70
+ return svg
71
+ }
72
+
73
+ const monthly = [
74
+ { month: "Jan", value: 42 },
75
+ { month: "Feb", value: 51 },
76
+ { month: "Mar", value: 47 },
77
+ ]
78
+
79
+ describe("modules ECharts enregistrés par QChart", () => {
80
+ it("les marques cartésiennes et les annotations se dessinent", () => {
81
+ const svg = render({
82
+ marks: [
83
+ { type: "bar", data: monthly, x: "month", y: "value", name: "Bars", labels: true },
84
+ { type: "line", data: monthly, x: "month", y: "value", name: "Line" },
85
+ { type: "band", y0: 45, y1: 55, fill: "warning" },
86
+ { type: "rule", y: [50], label: "Target" },
87
+ ],
88
+ })
89
+ expect(svg).toContain("<rect")
90
+ expect(svg).toContain("<path")
91
+ })
92
+
93
+ it("les familles hors axes se dessinent", () => {
94
+ const cases: [string, QChartConfig][] = [
95
+ ["pie", { marks: [{ type: "pie", data: monthly, x: "month", y: "value" }] }],
96
+ [
97
+ "heatmap",
98
+ {
99
+ marks: [
100
+ {
101
+ type: "heatmap",
102
+ data: [
103
+ { day: "Mon", hour: "9h", v: 1 },
104
+ { day: "Tue", hour: "9h", v: 2 },
105
+ ],
106
+ x: "hour",
107
+ y: "day",
108
+ fill: "v",
109
+ },
110
+ ],
111
+ },
112
+ ],
113
+ [
114
+ "gauge",
115
+ {
116
+ marks: [
117
+ {
118
+ type: "gauge",
119
+ data: [{ label: "Uptime", value: 99 }],
120
+ x: "label",
121
+ y: "value",
122
+ target: 99.5,
123
+ },
124
+ ],
125
+ },
126
+ ],
127
+ ["funnel", { marks: [{ type: "funnel", data: monthly, x: "month", y: "value" }] }],
128
+ [
129
+ "radar",
130
+ {
131
+ marks: [
132
+ {
133
+ type: "radar",
134
+ data: [
135
+ { skill: "Speed", value: 8 },
136
+ { skill: "Power", value: 6 },
137
+ ],
138
+ x: "skill",
139
+ y: "value",
140
+ },
141
+ ],
142
+ },
143
+ ],
144
+ [
145
+ "treemap",
146
+ {
147
+ marks: [
148
+ {
149
+ type: "treemap",
150
+ data: [
151
+ { name: "Root", parent: "", value: 0 },
152
+ { name: "Leaf", parent: "Root", value: 10 },
153
+ ],
154
+ x: "name",
155
+ parent: "parent",
156
+ value: "value",
157
+ },
158
+ ],
159
+ },
160
+ ],
161
+ [
162
+ "sunburst",
163
+ {
164
+ marks: [
165
+ {
166
+ type: "sunburst",
167
+ data: [
168
+ { name: "Root", parent: "", value: 0 },
169
+ { name: "Leaf", parent: "Root", value: 10 },
170
+ ],
171
+ x: "name",
172
+ parent: "parent",
173
+ value: "value",
174
+ },
175
+ ],
176
+ },
177
+ ],
178
+ [
179
+ "sankey",
180
+ {
181
+ marks: [
182
+ {
183
+ type: "sankey",
184
+ data: [{ source: "A", target: "B", value: 10 }],
185
+ },
186
+ ],
187
+ },
188
+ ],
189
+ [
190
+ "boxplot",
191
+ {
192
+ marks: [{ type: "boxplot", data: [{ team: "A", s: [1, 2, 3, 4, 5] }], x: "team", values: "s" }],
193
+ },
194
+ ],
195
+ [
196
+ "histogram",
197
+ { marks: [{ type: "histogram", data: [1, 2, 2, 3, 5, 8], x: (d: any) => d, bins: 3 }] },
198
+ ],
199
+ [
200
+ "waterfall",
201
+ {
202
+ marks: [
203
+ {
204
+ type: "waterfall",
205
+ data: [
206
+ { step: "A", delta: 10 },
207
+ { step: "B", delta: -4 },
208
+ ],
209
+ x: "step",
210
+ y: "delta",
211
+ },
212
+ ],
213
+ },
214
+ ],
215
+ ]
216
+ for (const [label, config] of cases) {
217
+ const svg = render(config)
218
+ expect(svg.startsWith("<svg"), label).toBe(true)
219
+ expect(svg.length, label).toBeGreaterThan(200)
220
+ }
221
+ })
222
+
223
+ it("le second axe Y se dessine", () => {
224
+ const svg = render({
225
+ marks: [
226
+ { type: "bar", data: monthly, x: "month", y: "value", name: "Volume" },
227
+ { type: "line", data: monthly, x: "month", y: "value", yAxis: "right", name: "Rate" },
228
+ ],
229
+ })
230
+ expect(svg).toContain("Volume")
231
+ })
232
+ })
package/lib/chart.test.ts CHANGED
@@ -12,7 +12,6 @@ import {
12
12
  pickFromEvent,
13
13
  sameSelection,
14
14
  tableModel,
15
- tableModel,
16
15
  textValue,
17
16
  } from "./chart"
18
17
  import type { QChartConfig, QChartMark } from "./chart"
@@ -523,7 +522,17 @@ describe("marque `table`", () => {
523
522
  })
524
523
 
525
524
  it("`separator` est bien une option de la marque `table` (table d'API générée)", () => {
526
- expect(MARK_OPTIONS.table).toEqual(["data", "link", "columns", "separator"])
525
+ expect(MARK_OPTIONS.table).toEqual([
526
+ "data",
527
+ "link",
528
+ "columns",
529
+ "separator",
530
+ "sortBy",
531
+ "sortDesc",
532
+ "pageSize",
533
+ "page",
534
+ "totals",
535
+ ])
527
536
  })
528
537
  })
529
538
 
@@ -693,7 +702,7 @@ describe("marque `table` (`tableModel`)", () => {
693
702
  { field: "month", label: "Month" },
694
703
  { field: "revenue", label: "Revenue", align: "end", format: expect.any(Function) },
695
704
  ])
696
- expect(model.columns[1].format?.(42, rows[0])).toBe("42 €")
705
+ expect(model.columns[1]!.format?.(42, rows[0])).toBe("42 €")
697
706
  })
698
707
 
699
708
  it("accepte les colonnes en abrégé (`'month'`)", () => {
@@ -816,7 +825,7 @@ describe("clic de légende en mode `select` (`nextLegendPick`)", () => {
816
825
  it("**désélectionne** quand on reclique le nom courant (`null`)", () => {
817
826
  expect(nextLegendPick({ name: "Jan" }, "Jan")).toBeNull()
818
827
  // la comparaison est textuelle : 42 et « 42 » désignent la même part
819
- expect(nextLegendPick({ name: 42 }, "42")).toBeNull()
828
+ expect(nextLegendPick({ name: 42 } as any, "42")).toBeNull()
820
829
  })
821
830
 
822
831
  it("une sélection sans nom (ou absente) laisse sélectionner", () => {