diff --git a/.changeset/10021-legend-swatch-custom-property.md b/.changeset/10021-legend-swatch-custom-property.md new file mode 100644 index 0000000000..37e1c97a1c --- /dev/null +++ b/.changeset/10021-legend-swatch-custom-property.md @@ -0,0 +1,12 @@ +--- +'@object-ui/plugin-charts': patch +--- + +fix(plugin-charts): the legend swatch carries its series colour as a custom property + +`ChartLegendContent` painted each swatch with an inline `backgroundColor`, the +colour-bearing property AGENTS.md's styling carve-out names as forbidden. The +swatch now publishes the author-declared colour as `--color-bg` and a static +`bg-(--color-bg)` utility consumes it, the same shape the tooltip indicator in +the same file already uses, so the theme keeps control of the rule. The +rendered colour is unchanged. diff --git a/packages/plugin-charts/src/ChartContainerImpl.legendSwatchColour.test.tsx b/packages/plugin-charts/src/ChartContainerImpl.legendSwatchColour.test.tsx new file mode 100644 index 0000000000..9f1263c1d1 --- /dev/null +++ b/packages/plugin-charts/src/ChartContainerImpl.legendSwatchColour.test.tsx @@ -0,0 +1,67 @@ +/** + * ObjectUI + * Copyright (c) 2024-present ObjectStack Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/** + * The legend swatch carries the series colour the way AGENTS.md's styling + * carve-out prescribes (objectui#10021). + * + * A series colour is author-declared, so it may reach the DOM through + * `style={{}}` — but only as a CSS custom property that a STATIC Tailwind + * utility consumes, never as a colour-bearing property (`backgroundColor`) + * written inline. The swatch used to write `backgroundColor: item.color`; the + * tooltip indicator in the same file already used the compliant shape + * (`bg-(--color-bg)` + `"--color-bg"`), and the swatch now copies it. + */ + +import React from 'react'; +import { describe, it, expect, vi, afterEach } from 'vitest'; +import { render, cleanup } from '@testing-library/react'; + +// ResponsiveContainer measures 0x0 under happy-dom; a passthrough keeps the +// legend content (rendered as a plain child) in the tree. +vi.mock('recharts', async () => { + const actual = await vi.importActual>('recharts'); + return { + ...actual, + ResponsiveContainer: ({ children }: { children: React.ReactElement }) => children, + }; +}); + +import { ChartContainer, ChartLegendContent } from './ChartContainerImpl'; + +afterEach(() => { + cleanup(); +}); + +function renderSwatch(color: string): HTMLElement { + const { container } = render( + + + , + ); + const swatch = container.querySelector('.rounded-\\[2px\\]'); + expect(swatch).not.toBeNull(); + return swatch!; +} + +describe('ChartLegendContent swatch colour (objectui#10021)', () => { + it('writes no colour-bearing property inline', () => { + const swatch = renderSwatch('#ff0000'); + expect(swatch.style.backgroundColor).toBe(''); + expect(swatch.style.color).toBe(''); + expect(swatch.getAttribute('style') ?? '').not.toMatch(/background|(^|;)\s*color\s*:/i); + }); + + it('publishes the author colour as a custom property a static utility consumes', () => { + const swatch = renderSwatch('#ff0000'); + expect(swatch.style.getPropertyValue('--color-bg')).toBe('#ff0000'); + expect(swatch.classList.contains('bg-(--color-bg)')).toBe(true); + }); +}); diff --git a/packages/plugin-charts/src/ChartContainerImpl.tsx b/packages/plugin-charts/src/ChartContainerImpl.tsx index e423bc6bd3..9fbd59c616 100644 --- a/packages/plugin-charts/src/ChartContainerImpl.tsx +++ b/packages/plugin-charts/src/ChartContainerImpl.tsx @@ -482,11 +482,17 @@ function ChartLegendContent({ {itemConfig?.icon && !hideIcon ? ( ) : ( + // The series colour is author-declared, so it reaches the DOM + // only as a custom property a static utility consumes — the + // same shape as the tooltip indicator above (AGENTS.md styling + // carve-out; objectui#10021).
)} {/* Fall back to the series name recharts itself put on the