Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,4 @@ packages/vchart/__tests__/runtime/node/**.png

*.tsbuildinfo
.github/hooks/copilot-hooks.json
.omx/
85 changes: 85 additions & 0 deletions packages/vchart/__tests__/unit/theme/tooltip.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { mergeSpec } from '@visactor/vutils-extension';
import { TooltipSpecTransformer } from '../../../src/component/tooltip/tooltip-transformer';
import { ComponentTypeEnum } from '../../../src/component/interface/type';
import { tooltip as builtInTooltipTheme } from '../../../src/theme/builtin/common/component/tooltip';

describe('tooltip theme transformer', () => {
const createTransformer = (tooltipTheme: Record<string, any>) =>
new TooltipSpecTransformer({
type: ComponentTypeEnum.tooltip,
mode: 'node',
getTheme: (...keys: string[]) => {
if (keys[0] === 'component' && keys[1] === ComponentTypeEnum.tooltip) {
return mergeSpec({}, builtInTooltipTheme, tooltipTheme);
}
return undefined;
}
});

it('supports tooltip theme fields declared under component.tooltip.style', () => {
const transformer = createTransformer({
style: {
panel: {
backgroundColor: '#123456'
},
titleLabel: {
fill: '#abcdef',
fontSize: 18
},
keyLabel: {
fill: '#ff0000'
}
}
});

const { spec } = transformer.transformSpec({}, {});

expect(spec.style.panel.backgroundColor).toBe('#123456');
expect(spec.style.titleLabel.fill).toBe('#abcdef');
expect(spec.style.titleLabel.fontSize).toBe(18);
expect(spec.style.keyLabel.fill).toBe('#ff0000');
expect(spec.style.style).toBeUndefined();
});

it('keeps root tooltip theme fields at root instead of leaking them into style', () => {
const transformer = createTransformer({
offset: {
x: 24,
y: 16
},
trigger: 'click',
transitionDuration: 0,
panel: {
backgroundColor: '#654321'
}
});

const { spec } = transformer.transformSpec({}, {});

expect(spec.offset).toEqual({ x: 24, y: 16 });
expect(spec.trigger).toBe('click');
expect(spec.transitionDuration).toBe(0);
expect(spec.style.panel.backgroundColor).toBe('#654321');
expect(spec.style.offset).toBeUndefined();
expect(spec.style.trigger).toBeUndefined();
expect(spec.style.transitionDuration).toBeUndefined();
});

it('supports active-type visibility declared in tooltip theme', () => {
const transformer = createTransformer({
mark: {
visible: false
},
dimension: {
visible: true
}
});

const { spec } = transformer.transformSpec({}, {});

expect(spec.mark.visible).toBe(false);
expect(spec.dimension.visible).toBe(true);
expect(spec.activeType).not.toContain('mark');
expect(spec.activeType).toContain('dimension');
});
});
25 changes: 23 additions & 2 deletions packages/vchart/src/component/interface/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type { IMarkLineTheme } from '../marker/mark-line/interface';
import type { IMarkPointTheme } from '../marker/mark-point/interface';
import type { IPlayerTheme } from '../player/interface';
import type { ITitleTheme } from '../title/interface';
import type { ITooltipTheme } from '../tooltip/interface';
import type { ITooltipSpec, ITooltipTheme } from '../tooltip/interface';
import type { ComponentTypeEnum } from './type';
import type { ITotalLabelTheme } from '../label/interface';
import type { IPoptipTheme } from '../poptip/interface';
Expand Down Expand Up @@ -80,7 +80,28 @@ export interface IComponentTheme {
/**
* tooltip 组件配置
*/
[ComponentTypeEnum.tooltip]?: ITooltipTheme<string | IColorKey>;
[ComponentTypeEnum.tooltip]?: ITooltipTheme<string | IColorKey> &
Partial<
Pick<
ITooltipSpec,
| 'visible'
| 'activeType'
| 'mark'
| 'dimension'
| 'group'
| 'trigger'
| 'triggerOff'
| 'showDelay'
| 'hideTimer'
| 'lockAfterClick'
| 'renderMode'
| 'confine'
| 'className'
| 'parentElement'
| 'enterable'
| 'throttleInterval'
>
>;
/**
* crosshair 配置
*/
Expand Down
34 changes: 26 additions & 8 deletions packages/vchart/src/component/tooltip/tooltip-transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,42 @@ import { TOOLTIP_EL_CLASS_NAME } from './constant';
import { getTooltipActualActiveType } from './utils/common';
import { mergeSpec } from '@visactor/vutils-extension';

const TOOLTIP_STYLE_THEME_KEYS = [
'panel',
'shape',
'titleLabel',
'keyLabel',
'valueLabel',
'spaceRow',
'maxContentHeight',
'align'
] as const;

export class TooltipSpecTransformer extends BaseComponentSpecTransformer<any> {
protected _shouldMergeThemeToSpec() {
return false;
}

protected _initTheme(spec: any, chartSpec: any): { spec: any; theme: any } {
const { spec: newSpec, theme } = super._initTheme(spec, chartSpec);
const themeStyle = mergeSpec(
{},
...TOOLTIP_STYLE_THEME_KEYS.map(key => (theme?.[key] !== undefined ? { [key]: theme[key] } : undefined)),
theme?.style
);
const themeSpec = mergeSpec({}, theme);

TOOLTIP_STYLE_THEME_KEYS.forEach(key => {
delete themeSpec[key];
});
delete themeSpec.style;

// 合并样式和配置
newSpec.style = mergeSpec({}, this._theme, newSpec.style);
newSpec.offset = mergeSpec({}, theme.offset, spec.offset);
newSpec.transitionDuration = spec.transitionDuration ?? theme.transitionDuration;
const mergedSpec = mergeSpec({}, themeSpec, newSpec);

// 合并交互相关配置
newSpec.trigger = spec.trigger ?? theme.trigger;
newSpec.triggerOff = spec.triggerOff ?? theme.triggerOff;
// 合并样式配置
mergedSpec.style = mergeSpec({}, themeStyle, mergedSpec.style);

return { spec: newSpec, theme };
return { spec: mergedSpec, theme };
}

protected _transformSpecAfterMergingTheme(spec: any, chartSpec: any, chartSpecInfo?: IChartSpecInfo) {
Expand Down
Loading