@opendata-ai/openchart-engine 6.28.2 → 6.28.5

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opendata-ai/openchart-engine",
3
- "version": "6.28.2",
3
+ "version": "6.28.5",
4
4
  "description": "Headless compiler for openchart: spec validation, data compilation, scales, and layout",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Riley Hilliard",
@@ -48,7 +48,7 @@
48
48
  "typecheck": "tsc --noEmit"
49
49
  },
50
50
  "dependencies": {
51
- "@opendata-ai/openchart-core": "6.28.2",
51
+ "@opendata-ai/openchart-core": "6.28.5",
52
52
  "d3-array": "^3.2.0",
53
53
  "d3-format": "^3.1.2",
54
54
  "d3-interpolate": "^3.0.0",
@@ -112,6 +112,49 @@ describe('computeHeatmapColors', () => {
112
112
  }
113
113
  });
114
114
 
115
+ it('high values are more visually prominent than low values in both modes', () => {
116
+ const col: ColumnConfig = {
117
+ key: 'value',
118
+ heatmap: { palette: ['#fca5a5', '#c44e52'], domain: [0, 100] },
119
+ };
120
+ const lightTheme = getTheme(false);
121
+ const darkTheme = getTheme(true);
122
+ const lightColors = computeHeatmapColors(data, col, lightTheme, false);
123
+ const darkColors = computeHeatmapColors(data, col, darkTheme, true);
124
+
125
+ const lightLowBg = lightColors.get(0)!.backgroundColor!;
126
+ const lightHighBg = lightColors.get(4)!.backgroundColor!;
127
+ const darkLowBg = darkColors.get(0)!.backgroundColor!;
128
+ const darkHighBg = darkColors.get(4)!.backgroundColor!;
129
+
130
+ expect(lightLowBg).not.toBe(lightHighBg);
131
+ expect(darkLowBg).not.toBe(darkHighBg);
132
+
133
+ function parseLum(color: string): number {
134
+ const rgbMatch = /rgb\(\s*(\d+),\s*(\d+),\s*(\d+)\s*\)/.exec(color);
135
+ if (rgbMatch) {
136
+ const [r, g, b] = [rgbMatch[1], rgbMatch[2], rgbMatch[3]].map((c) => {
137
+ const v = Number(c) / 255;
138
+ return v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4;
139
+ });
140
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
141
+ }
142
+ const hexMatch = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(color);
143
+ if (!hexMatch) return 0;
144
+ const [r, g, b] = [hexMatch[1], hexMatch[2], hexMatch[3]].map((c) => {
145
+ const v = Number.parseInt(c, 16) / 255;
146
+ return v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4;
147
+ });
148
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
149
+ }
150
+
151
+ // Light mode: high values get darker/more saturated bg (lower luminance = more prominent)
152
+ expect(parseLum(lightLowBg)).toBeGreaterThan(parseLum(lightHighBg));
153
+
154
+ // Dark mode: high values get brighter bg (higher luminance = more prominent on dark bg)
155
+ expect(parseLum(darkHighBg)).toBeGreaterThan(parseLum(darkLowBg));
156
+ });
157
+
115
158
  it('supports array of color stops as palette', () => {
116
159
  const col: ColumnConfig = {
117
160
  key: 'value',
@@ -297,18 +297,20 @@ describe('compileTileMap', () => {
297
297
  });
298
298
 
299
299
  describe('dimensions', () => {
300
- it('reflects the compile options', () => {
300
+ it('width matches compile options, height fits content', () => {
301
301
  const result = compileTileMap(basicSpec, defaultOptions);
302
302
 
303
303
  expect(result.width).toBe(600);
304
- expect(result.height).toBe(400);
304
+ expect(result.height).toBeGreaterThan(0);
305
+ expect(result.height).toBeLessThanOrEqual(400);
305
306
  });
306
307
 
307
308
  it('works with different container sizes', () => {
308
309
  const result = compileTileMap(basicSpec, { width: 800, height: 600 });
309
310
 
310
311
  expect(result.width).toBe(800);
311
- expect(result.height).toBe(600);
312
+ expect(result.height).toBeGreaterThan(0);
313
+ expect(result.height).toBeLessThanOrEqual(600);
312
314
  });
313
315
  });
314
316
 
@@ -329,6 +329,15 @@ export function compileTileMap(spec: unknown, options: CompileOptions): TileMapL
329
329
  // 15. Resolve animation
330
330
  const resolvedAnimation: ResolvedAnimation | undefined = resolveAnimation(tilemapSpec.animation);
331
331
 
332
+ // Tight content height: tiles + legend + chrome + padding
333
+ const contentHeight =
334
+ tileGridOffsetY +
335
+ tilePositions.gridHeight +
336
+ legendGap +
337
+ legendTotalHeight +
338
+ chrome.bottomHeight +
339
+ padding;
340
+
332
341
  return {
333
342
  area: fullArea,
334
343
  chrome,
@@ -338,7 +347,7 @@ export function compileTileMap(spec: unknown, options: CompileOptions): TileMapL
338
347
  a11y,
339
348
  theme,
340
349
  width: options.width,
341
- height: options.height,
350
+ height: contentHeight,
342
351
  animation: resolvedAnimation,
343
352
  watermark,
344
353
  measureText: