fix(button): add theme-aware preset color hover/active tokens (#56872)

* fix(button): add theme-aware preset color hover/active tokens

Add ${colorKey}Hover and ${colorKey}Active tokens that swap values based on dark/light mode for improved contrast and user experience.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: add preset color hover/active tokens for consistent button interaction

- Add xxxHover and xxxActive tokens for preset colors in genColorMapToken
- Override these tokens in dark mode to swap hover/active values
- Update ButtonToken type to include PresetColorHoverActiveMap
- Update button variant styles to use new hover/active tokens
- Fix #56656: button hover/active state inconsistency in dark mode

* test: add @csstools to compileModules

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test: support .mjs files in Jest configuration

- Updated .jest.js transform pattern to include .mjs files
- Simplified .jest.node.js transform patterns
- Added jest-mjs-transformer.js for babel-jest mjs handling

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test: remove unused jest-mjs-transformer.js

- Removed jest-mjs-transformer.js as it's no longer used
- .mjs files are now handled by the updated transform patterns in Jest configs

* test: add .mjs support to .jest.image.js

- Updated .jest.image.js transform pattern to include .mjs files
- fixes image test failures due to ES module parsing errors

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
二货爱吃白萝卜
2026-02-06 11:10:41 +08:00
committed by GitHub
parent 99eb877828
commit bbb0683ab6
7 changed files with 45 additions and 11 deletions

View File

@@ -1,13 +1,11 @@
const { moduleNameMapper, transformIgnorePatterns } = require('./.jest');
// jest config for image snapshots
module.exports = {
setupFiles: ['./tests/setup.ts'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'md'],
moduleNameMapper,
transform: {
'\\.tsx?$': './node_modules/@ant-design/tools/lib/jest/codePreprocessor',
'\\.js$': './node_modules/@ant-design/tools/lib/jest/codePreprocessor',
'^.+\\.(ts|tsx|js|mjs)$': './node_modules/@ant-design/tools/lib/jest/codePreprocessor',
'\\.md$': './node_modules/@ant-design/tools/lib/jest/demoPreprocessor',
'\\.(jpg|png|gif|svg)$': './node_modules/@ant-design/tools/lib/jest/imagePreprocessor',
},

View File

@@ -11,6 +11,7 @@ const compileModules = [
'parse5',
'@exodus',
'jsdom',
'@csstools',
];
// cnpm use `_` as prefix
@@ -62,7 +63,7 @@ module.exports = {
],
transform: {
'\\.tsx?$': './node_modules/@ant-design/tools/lib/jest/codePreprocessor',
'\\.(m?)js$': './node_modules/@ant-design/tools/lib/jest/codePreprocessor',
'\\.(m?)js(m)?$': './node_modules/@ant-design/tools/lib/jest/codePreprocessor',
'\\.md$': './node_modules/@ant-design/tools/lib/jest/demoPreprocessor',
'\\.(jpg|png|gif|svg)$': './node_modules/@ant-design/tools/lib/jest/imagePreprocessor',
},

View File

@@ -1,14 +1,12 @@
const { moduleNameMapper, transformIgnorePatterns } = require('./.jest');
// jest config for server render environment
module.exports = {
setupFiles: ['./tests/setup.ts'],
setupFilesAfterEnv: ['./tests/setupAfterEnv.ts'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'md'],
moduleNameMapper,
transform: {
'\\.tsx?$': './node_modules/@ant-design/tools/lib/jest/codePreprocessor',
'\\.js$': './node_modules/@ant-design/tools/lib/jest/codePreprocessor',
'^.+\\.(ts|tsx|js|mjs)$': './node_modules/@ant-design/tools/lib/jest/codePreprocessor',
'\\.md$': './node_modules/@ant-design/tools/lib/jest/demoPreprocessor',
'\\.(jpg|png|gif|svg)$': './node_modules/@ant-design/tools/lib/jest/imagePreprocessor',
},

View File

@@ -4,9 +4,9 @@ import { unit } from '@ant-design/cssinjs';
import { AggregationColor } from '../../color-picker/color';
import { isBright } from '../../color-picker/components/ColorPresets';
import { PresetColors } from '../../theme/interface';
import type { FullToken, GenStyleFn, GetDefaultToken, PresetColorKey } from '../../theme/internal';
import { getLineHeight, mergeToken } from '../../theme/internal';
import { PresetColors } from '../../theme/interface';
import getAlphaColor from '../../theme/util/getAlphaColor';
/** Component only token. Which will handle additional calculation of alias token */
@@ -247,6 +247,10 @@ type ShadowColorMap = {
[Key in `${PresetColorKey}ShadowColor`]: string;
};
type PresetColorHoverActiveMap = {
[Key in `${PresetColorKey}Hover` | `${PresetColorKey}Active`]: string;
};
type GroupToken = {
/**
* @desc 按钮组边框颜色
@@ -257,7 +261,11 @@ type GroupToken = {
groupBorderColor: string;
};
export interface ButtonToken extends FullToken<'Button'>, ShadowColorMap, GroupToken {
export interface ButtonToken
extends FullToken<'Button'>,
ShadowColorMap,
PresetColorHoverActiveMap,
GroupToken {
/**
* @desc 按钮横向内边距
* @descEN Horizontal padding of button

View File

@@ -268,10 +268,10 @@ const genVariantStyle: GenerateStyle<ButtonToken> = (token) => {
PresetColors.map((colorKey) => {
const darkColor = token[`${colorKey}6`];
const lightColor = token[`${colorKey}1`];
const hoverColor = token[`${colorKey}5`];
const hoverColor = token[`${colorKey}Hover`];
const lightHoverColor = token[`${colorKey}2`];
const lightActiveColor = token[`${colorKey}3`];
const activeColor = token[`${colorKey}7`];
const activeColor = token[`${colorKey}Active`];
const shadowColor = token[`${colorKey}ShadowColor`];

View File

@@ -2,6 +2,7 @@ import { generate } from '@ant-design/colors';
import type { DerivativeFunc } from '@ant-design/cssinjs';
import type { MapToken, PresetColorType, SeedToken } from '../../interface';
import { PresetColors } from '../../interface/presetColors';
import defaultAlgorithm from '../default';
import { defaultPresetColors } from '../seed';
import genColorMapToken from '../shared/genColorMapToken';
@@ -29,6 +30,19 @@ const derivative: DerivativeFunc<SeedToken, MapToken> = (token, mapToken) => {
generateNeutralColorPalettes,
});
const presetColorHoverActiveTokens = PresetColors.reduce<Record<string, string>>(
(prev, colorKey) => {
const colorBase = token[colorKey as keyof PresetColorType];
if (colorBase) {
const colorPalette = generateColorPalettes(colorBase);
prev[`${colorKey}Hover`] = colorPalette[7];
prev[`${colorKey}Active`] = colorPalette[5];
}
return prev;
},
{},
);
return {
...mergedMapToken,
@@ -38,6 +52,8 @@ const derivative: DerivativeFunc<SeedToken, MapToken> = (token, mapToken) => {
// Colors
...colorMapToken,
...presetColorHoverActiveTokens,
// Customize selected item background color
// https://github.com/ant-design/ant-design/issues/30524#issuecomment-871961867
colorPrimaryBg: colorMapToken.colorPrimaryBorder,

View File

@@ -1,6 +1,7 @@
import { FastColor } from '@ant-design/fast-color';
import type { ColorMapToken, SeedToken } from '../../interface';
import { PresetColors } from '../../interface/presetColors';
import type { GenerateColorMap, GenerateNeutralColorMap } from '../ColorMap';
interface PaletteGenerators {
@@ -37,6 +38,16 @@ export default function genColorMapToken(
.mix(new FastColor(errorColors[3]), 50)
.toHexString();
const presetColorTokens: Record<string, string> = {};
PresetColors.forEach((colorKey) => {
const colorBase = seed[colorKey];
if (colorBase) {
const colorPalette = generateColorPalettes(colorBase);
presetColorTokens[`${colorKey}Hover`] = colorPalette[5];
presetColorTokens[`${colorKey}Active`] = colorPalette[7];
}
});
return {
...neutralColors,
@@ -101,6 +112,8 @@ export default function genColorMapToken(
colorLink: linkColors[6],
colorLinkActive: linkColors[7],
...presetColorTokens,
colorBgMask: new FastColor('#000').setA(0.45).toRgbString(),
colorWhite: '#fff',
};