Skip to content

Commit 86a0abb

Browse files
feat(echarts): implement lollipop-basic (#9607)
## Implementation: `lollipop-basic` - javascript/echarts Implements the **javascript/echarts** version of `lollipop-basic`. **File:** `plots/lollipop-basic/implementations/javascript/echarts.js` **Parent Issue:** #934 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/anyplot/actions/runs/28497356876)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: Markus Neusinger <2921697+MarkusNeusinger@users.noreply.github.com>
1 parent 113303e commit 86a0abb

2 files changed

Lines changed: 354 additions & 0 deletions

File tree

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// anyplot.ai
2+
// lollipop-basic: Basic Lollipop Chart
3+
// Library: echarts 5.5.1 | JavaScript 22.23.0
4+
// Quality: 88/100 | Created: 2026-07-01
5+
6+
const t = window.ANYPLOT_TOKENS;
7+
8+
// --- Data (average annual salary by profession, USD thousands) --------------
9+
const professions = [
10+
"Software Eng.", "Data Scientist", "Physician",
11+
"Financial Analyst", "UX Designer", "Project Manager",
12+
"Mechanical Eng.", "Marketing Mgr.", "Nurse", "Teacher"
13+
];
14+
const salaries = [125, 118, 115, 95, 88, 85, 82, 75, 68, 58];
15+
const avg = Math.round(salaries.reduce((a, b) => a + b, 0) / salaries.length);
16+
17+
// --- Init -------------------------------------------------------------------
18+
const chart = echarts.init(document.getElementById("container"));
19+
20+
// --- Option -----------------------------------------------------------------
21+
chart.setOption({
22+
animation: false,
23+
color: t.palette,
24+
backgroundColor: "transparent",
25+
title: {
26+
text: "lollipop-basic · javascript · echarts · anyplot.ai",
27+
left: "center",
28+
top: 28,
29+
textStyle: { color: t.ink, fontSize: 22, fontWeight: "normal" }
30+
},
31+
grid: { left: 90, right: 80, top: 90, bottom: 120 },
32+
xAxis: {
33+
type: "category",
34+
data: professions,
35+
axisLabel: {
36+
color: t.inkSoft,
37+
fontSize: 14,
38+
rotate: 30,
39+
interval: 0
40+
},
41+
axisLine: { show: false },
42+
axisTick: { show: false },
43+
splitLine: { show: false }
44+
},
45+
yAxis: {
46+
type: "value",
47+
name: "Avg. Annual Salary (USD thousands)",
48+
nameLocation: "middle",
49+
nameGap: 65,
50+
nameTextStyle: { color: t.inkSoft, fontSize: 14 },
51+
min: 0,
52+
max: 150,
53+
axisLabel: { color: t.inkSoft, fontSize: 14 },
54+
axisLine: { show: false },
55+
axisTick: { show: false },
56+
splitLine: { lineStyle: { color: t.grid } }
57+
},
58+
series: [
59+
{
60+
type: "bar",
61+
data: salaries,
62+
barWidth: 2,
63+
itemStyle: { color: t.palette[0] },
64+
markLine: {
65+
symbol: "none",
66+
silent: true,
67+
lineStyle: { color: t.inkSoft, type: "dashed", width: 1.5 },
68+
label: {
69+
position: "end",
70+
formatter: "Avg: " + avg + "K",
71+
color: t.inkSoft,
72+
fontSize: 13
73+
},
74+
data: [{ yAxis: avg, name: "Average" }]
75+
},
76+
z: 1
77+
},
78+
{
79+
type: "scatter",
80+
data: salaries,
81+
symbolSize: 16,
82+
itemStyle: { color: t.palette[0] },
83+
label: {
84+
show: true,
85+
position: "top",
86+
formatter: "{c}K",
87+
color: t.inkSoft,
88+
fontSize: 13
89+
},
90+
z: 2
91+
}
92+
]
93+
});
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
library: echarts
2+
language: javascript
3+
specification_id: lollipop-basic
4+
created: '2026-07-01T06:14:41Z'
5+
updated: '2026-07-01T06:34:23Z'
6+
generated_by: claude-sonnet
7+
workflow_run: 28497356876
8+
issue: 934
9+
language_version: 22.23.0
10+
library_version: 5.5.1
11+
preview_url_light: https://storage.googleapis.com/anyplot-images/plots/lollipop-basic/javascript/echarts/plot-light.png
12+
preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/lollipop-basic/javascript/echarts/plot-dark.png
13+
preview_html_light: https://storage.googleapis.com/anyplot-images/plots/lollipop-basic/javascript/echarts/plot-light.html
14+
preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/lollipop-basic/javascript/echarts/plot-dark.html
15+
quality_score: 88
16+
review:
17+
strengths:
18+
- Clean lollipop via ECharts bar+scatter composition with correct barWidth:2 stems
19+
and symbolSize:16 dots
20+
- All theme tokens correctly mapped (t.ink, t.inkSoft, t.grid, t.palette[0])
21+
- Average reference line via markLine adds storytelling context with a clear benchmark
22+
- All font sizes explicitly set, theme-adaptive chrome works correctly in both light
23+
and dark renders
24+
- 'Perfect spec compliance: vertical orientation, sorted data, thin stems, circular
25+
markers'
26+
- Data sorted descending for clean ranking readability
27+
weaknesses:
28+
- Title font size 22 CSS px produces a title spanning ~42% of canvas width, slightly
29+
below the 50–70% target for this ~51-char mandated title — consider increasing
30+
to 26px
31+
- Physician salary at 115K USD is notably lower than typical US physician averages
32+
(~200–400K), making the salary comparison somewhat factually inaccurate relative
33+
to the other professions
34+
- 'VQ-01: minor proportional concern — title appears slightly small at fontsize
35+
22 in the 1600px CSS mount'
36+
image_description: |-
37+
Light render (plot-light.png):
38+
Background: warm off-white (#FAF8F1) — correct theme surface, not pure white
39+
Chrome: Title "lollipop-basic · javascript · echarts · anyplot.ai" centered at top in dark ink, all readable; Y-axis label "Avg. Annual Salary (USD thousands)" in t.inkSoft; X-axis categorical labels rotated 30°, all 10 professions clearly legible; Y-axis tick labels (0, 30, 60, 90, 120, 150) readable; value labels (e.g. "125K", "118K") above each scatter dot in t.inkSoft
40+
Data: 10 lollipops in brand green (#009E73) — thin 2px stems (bar) rising from 0 baseline to symbolSize:16 circular scatter dots. Dashed average line at ~91K with "Avg: 91K" label at right edge. Data sorted descending from Software Eng (125K) to Teacher (58K).
41+
Legibility verdict: PASS — all text readable, no light-on-light failures, proper contrast throughout
42+
43+
Dark render (plot-dark.png):
44+
Background: warm near-black (#1A1A17) — correct dark surface, not pure black
45+
Chrome: Title in light-colored ink readable against dark background; Y-axis label visible in t.inkSoft (warm light gray); X-axis categorical labels remain readable with light text; Y-axis tick labels legible; value labels above dots remain visible; dashed average line and "Avg: 91K" label both visible against dark background
46+
Data: Same brand green (#009E73) lollipop stems and dots — data colors identical to light render (only chrome flipped). Grid lines subtle and visible.
47+
Legibility verdict: PASS — no dark-on-dark failures; all chrome elements correctly use theme-adaptive tokens (t.ink / t.inkSoft); data green remains visible and distinguishable on the dark surface
48+
criteria_checklist:
49+
visual_quality:
50+
score: 29
51+
max: 30
52+
items:
53+
- id: VQ-01
54+
name: Text Legibility
55+
score: 7
56+
max: 8
57+
passed: true
58+
comment: All font sizes explicitly set (title:22, axis/tick/labels:14, data
59+
labels:13). Title at fontSize 22 CSS px spans ~42% of canvas width — slightly
60+
below the 50-70% target for this 51-char mandated title. All text readable
61+
in both themes.
62+
- id: VQ-02
63+
name: No Overlap
64+
score: 6
65+
max: 6
66+
passed: true
67+
comment: X-axis labels rotated 30° at interval:0 — no visible overlap. Value
68+
labels above dots well-separated. No text collision with data elements.
69+
- id: VQ-03
70+
name: Element Visibility
71+
score: 6
72+
max: 6
73+
passed: true
74+
comment: barWidth:2 stems clearly visible as thin lines, symbolSize:16 dots
75+
prominent — ideal density-adapted sizing for 10 sparse data points.
76+
- id: VQ-04
77+
name: Color Accessibility
78+
score: 2
79+
max: 2
80+
passed: true
81+
comment: 'Single-series chart using brand green #009E73. CVD-safe by palette
82+
design. Adequate contrast in both themes.'
83+
- id: VQ-05
84+
name: Layout & Canvas
85+
score: 4
86+
max: 4
87+
passed: true
88+
comment: Good canvas utilization (~65-70%). Margins left:90 right:80 top:90
89+
bottom:120 well-balanced. 30° rotation with bottom:120 provides enough room
90+
for rotated labels.
91+
- id: VQ-06
92+
name: Axis Labels & Title
93+
score: 2
94+
max: 2
95+
passed: true
96+
comment: 'Y-axis: ''Avg. Annual Salary (USD thousands)'' — descriptive with
97+
units. X-axis categorical labels are self-explanatory professions. Title
98+
format correct.'
99+
- id: VQ-07
100+
name: Palette Compliance
101+
score: 2
102+
max: 2
103+
passed: true
104+
comment: 'color:t.palette set; both series use t.palette[0] (#009E73); backgroundColor:transparent
105+
with page-level #FAF8F1/#1A1A17; all chrome uses t.ink/t.inkSoft/t.grid
106+
tokens. Both renders theme-correct.'
107+
design_excellence:
108+
score: 13
109+
max: 20
110+
items:
111+
- id: DE-01
112+
name: Aesthetic Sophistication
113+
score: 5
114+
max: 8
115+
passed: true
116+
comment: 'Clean minimal look above library defaults: axis lines and tick marks
117+
removed, brand green applied, value labels provide precise reading. Not
118+
yet publication-ready but clearly above generic defaults.'
119+
- id: DE-02
120+
name: Visual Refinement
121+
score: 4
122+
max: 6
123+
passed: true
124+
comment: 'axisLine:show:false and axisTick:show:false on both axes creates
125+
a clean frameless chart. Y-axis split lines only with t.grid color. Good
126+
whitespace. Missing: legend styling, no box frames.'
127+
- id: DE-03
128+
name: Data Storytelling
129+
score: 4
130+
max: 6
131+
passed: true
132+
comment: Average reference line at 91K creates a meaningful focal point —
133+
viewer immediately sees which professions are above/below average. Data
134+
sorted descending reinforces the ranking narrative.
135+
spec_compliance:
136+
score: 15
137+
max: 15
138+
items:
139+
- id: SC-01
140+
name: Plot Type
141+
score: 5
142+
max: 5
143+
passed: true
144+
comment: Correct lollipop chart via bar+scatter composition. Thin stems (barWidth:2)
145+
+ circular markers (scatter symbolSize:16) in vertical orientation.
146+
- id: SC-02
147+
name: Required Features
148+
score: 4
149+
max: 4
150+
passed: true
151+
comment: Stems from baseline to marker, circular dots at values, vertical
152+
orientation (categories on x-axis), data sorted by value descending — all
153+
spec requirements met.
154+
- id: SC-03
155+
name: Data Mapping
156+
score: 3
157+
max: 3
158+
passed: true
159+
comment: 'X-axis: profession categories; Y-axis: salary values. All 10 data
160+
points correctly mapped and visible.'
161+
- id: SC-04
162+
name: Title & Legend
163+
score: 3
164+
max: 3
165+
passed: true
166+
comment: Title exactly 'lollipop-basic · javascript · echarts · anyplot.ai'.
167+
No legend needed for single-series chart (correct omission).
168+
data_quality:
169+
score: 14
170+
max: 15
171+
items:
172+
- id: DQ-01
173+
name: Feature Coverage
174+
score: 6
175+
max: 6
176+
passed: true
177+
comment: 10 professions with varying salary values (58K-125K range) demonstrating
178+
the chart's ranking and comparison strengths. Good variation across the
179+
range.
180+
- id: DQ-02
181+
name: Realistic Context
182+
score: 5
183+
max: 5
184+
passed: true
185+
comment: Average annual salary by profession — real-world, comprehensible,
186+
neutral scenario. No controversial content.
187+
- id: DQ-03
188+
name: Appropriate Scale
189+
score: 3
190+
max: 4
191+
passed: true
192+
comment: Most salary values are plausible for the US market. However, Physician
193+
at 115K USD is notably below typical US physician averages ($200K-$400K),
194+
making the relative ranking between Physician and Software Engineer feel
195+
factually inverted vs. real-world expectations.
196+
code_quality:
197+
score: 10
198+
max: 10
199+
items:
200+
- id: CQ-01
201+
name: KISS Structure
202+
score: 3
203+
max: 3
204+
passed: true
205+
comment: Clear linear Data→Init→Option structure. No functions or classes.
206+
- id: CQ-02
207+
name: Reproducibility
208+
score: 2
209+
max: 2
210+
passed: true
211+
comment: All data hardcoded deterministically. Appropriate for browser ECharts
212+
environment.
213+
- id: CQ-03
214+
name: Clean Imports
215+
score: 2
216+
max: 2
217+
passed: true
218+
comment: ECharts available as global — no imports needed. Code is clean.
219+
- id: CQ-04
220+
name: Code Elegance
221+
score: 2
222+
max: 2
223+
passed: true
224+
comment: Clean bar+scatter combination for lollipop. No over-engineering.
225+
Correct use of z-ordering for layering.
226+
- id: CQ-05
227+
name: Output & API
228+
score: 1
229+
max: 1
230+
passed: true
231+
comment: 'Correct harness contract: echarts.init(#container), animation:false,
232+
no devicePixelRatio. Harness handles plot-light.png/plot-dark.png output.'
233+
library_mastery:
234+
score: 7
235+
max: 10
236+
items:
237+
- id: LM-01
238+
name: Idiomatic Usage
239+
score: 4
240+
max: 5
241+
passed: true
242+
comment: Standard ECharts lollipop pattern (bar+scatter) with correct markLine
243+
usage for reference annotations. Proper token integration. Good but could
244+
leverage more ECharts-specific idioms.
245+
- id: LM-02
246+
name: Distinctive Features
247+
score: 3
248+
max: 5
249+
passed: true
250+
comment: Uses ECharts markLine for average reference line with custom label
251+
formatter — this is a distinctively ECharts API feature. layer-composition
252+
of bar+scatter series is the canonical ECharts lollipop approach.
253+
verdict: APPROVED
254+
impl_tags:
255+
dependencies: []
256+
techniques:
257+
- layer-composition
258+
patterns: []
259+
dataprep: []
260+
styling:
261+
- minimal-chrome

0 commit comments

Comments
 (0)