@opendata-ai/openchart-core 2.9.1 → 2.11.0

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.
@@ -662,6 +662,8 @@ th[aria-sort="descending"] .viz-table-sort-btn::before {
662
662
  overflow: hidden;
663
663
  background: var(--viz-bg);
664
664
  font-family: var(--viz-font-family);
665
+ width: 100%;
666
+ height: 100%;
665
667
  }
666
668
 
667
669
  .viz-graph-canvas {
@@ -74,3 +74,60 @@ describe('resolveTheme', () => {
74
74
  expect(resolved.spacing.padding).toBe(DEFAULT_THEME.spacing.padding);
75
75
  });
76
76
  });
77
+
78
+ // ---------------------------------------------------------------------------
79
+ // Deep merge behavior hardening
80
+ // ---------------------------------------------------------------------------
81
+
82
+ describe('resolveTheme deep merge edge cases', () => {
83
+ it('replaces categorical array entirely, does not concatenate', () => {
84
+ const custom = ['#aaa', '#bbb'];
85
+ const resolved = resolveTheme({ colors: { categorical: custom } });
86
+ expect(resolved.colors.categorical).toEqual(custom);
87
+ expect(resolved.colors.categorical).toHaveLength(2);
88
+ });
89
+
90
+ it('skips undefined values, preserving defaults', () => {
91
+ const resolved = resolveTheme({ borderRadius: undefined });
92
+ expect(resolved.borderRadius).toBe(DEFAULT_THEME.borderRadius);
93
+ });
94
+
95
+ it('applies multiple overrides at different depths in one call', () => {
96
+ const resolved = resolveTheme({
97
+ colors: { background: '#222222', text: '#eeeeee' },
98
+ fonts: { family: 'Georgia' },
99
+ spacing: { padding: 32 },
100
+ borderRadius: 4,
101
+ });
102
+ expect(resolved.colors.background).toBe('#222222');
103
+ expect(resolved.colors.text).toBe('#eeeeee');
104
+ expect(resolved.fonts.family).toBe('Georgia');
105
+ expect(resolved.spacing.padding).toBe(32);
106
+ expect(resolved.borderRadius).toBe(4);
107
+ // Non-overridden values preserved
108
+ expect(resolved.fonts.mono).toBe(DEFAULT_THEME.fonts.mono);
109
+ expect(resolved.spacing.chromeGap).toBe(DEFAULT_THEME.spacing.chromeGap);
110
+ expect(resolved.colors.categorical).toEqual(DEFAULT_THEME.colors.categorical);
111
+ });
112
+
113
+ it('empty object override returns defaults unchanged', () => {
114
+ const resolved = resolveTheme({});
115
+ expect(resolved.colors).toEqual(expect.objectContaining(DEFAULT_THEME.colors));
116
+ expect(resolved.fonts).toEqual(DEFAULT_THEME.fonts);
117
+ expect(resolved.spacing).toEqual(DEFAULT_THEME.spacing);
118
+ expect(resolved.borderRadius).toBe(DEFAULT_THEME.borderRadius);
119
+ });
120
+
121
+ it('dark background adapts chrome colors without losing chrome structure', () => {
122
+ const resolved = resolveTheme({ colors: { background: '#111111', text: '#ffffff' } });
123
+ expect(resolved.isDark).toBe(true);
124
+ // Chrome structure should still be fully populated
125
+ expect(resolved.chrome.title).toBeDefined();
126
+ expect(resolved.chrome.subtitle).toBeDefined();
127
+ expect(resolved.chrome.source).toBeDefined();
128
+ expect(resolved.chrome.byline).toBeDefined();
129
+ expect(resolved.chrome.footer).toBeDefined();
130
+ // Title color should be adapted (not the light-mode default)
131
+ expect(resolved.chrome.title.color).not.toBe(DEFAULT_THEME.chrome.title.color);
132
+ });
133
+ });
@@ -394,6 +394,8 @@ export interface LegendEntry {
394
394
  shape: 'circle' | 'square' | 'line';
395
395
  /** Whether this entry is currently highlighted/active. */
396
396
  active?: boolean;
397
+ /** True for overflow indicator entries ("+N more"). Not interactive. */
398
+ overflow?: boolean;
397
399
  }
398
400
 
399
401
  /** Resolved legend layout with position and entries. */