Compare commits

..

3 Commits

Author SHA1 Message Date
lijianan
d85e0e0163 Merge branch 'master' into chore/scripts 2025-10-19 11:08:45 +08:00
lijianan
1ab5e38e70 Merge branch 'master' into chore/scripts 2025-08-24 15:31:56 +08:00
MadCcc
52a701b497 chore: add inquirer to skip CI 2025-08-11 17:04:37 +08:00
364 changed files with 3617 additions and 11257 deletions

View File

@@ -20,9 +20,6 @@
html {
scrollbar-width: thin;
scrollbar-color: #eaeaea transparent;
@supports (text-autospace: normal) {
text-autospace: normal;
}
}
.rc-footer {

View File

@@ -1,107 +0,0 @@
import React, { useCallback, useEffect } from 'react';
const ANT_SYNC_STORAGE_EVENT_KEY = 'ANT_SYNC_STORAGE_EVENT_KEY';
const isFunction = (val: any): val is (...args: any[]) => any => {
return typeof val === 'function';
};
interface Options<T> {
defaultValue?: T;
serializer?: (value: T) => string;
deserializer?: (value: string) => T;
onError?: (error: unknown) => void;
}
const useLocalStorage = <T>(key: string, options: Options<T> = {}) => {
const storage = typeof window !== 'undefined' ? localStorage : null;
const { serializer, deserializer, onError, defaultValue } = options;
const mergedSerializer = typeof serializer === 'function' ? serializer : JSON.stringify;
const mergedDeserializer = typeof deserializer === 'function' ? deserializer : JSON.parse;
const handleError = typeof onError === 'function' ? onError : console.error;
const getStoredValue = () => {
try {
const rawData = storage?.getItem(key);
if (rawData) {
return mergedDeserializer(rawData);
}
} catch (e) {
if (process.env.NODE_ENV !== 'production') {
handleError(e);
}
return defaultValue;
}
return defaultValue;
};
const [state, setState] = React.useState<T>(getStoredValue);
useEffect(() => {
setState(getStoredValue());
}, [key]);
const updateState: React.Dispatch<React.SetStateAction<T>> = (value) => {
const currentState = isFunction(value) ? value(state) : value;
setState(currentState);
try {
let newValue: string | null;
const oldValue = storage?.getItem(key);
if (typeof currentState === 'undefined') {
newValue = null;
storage?.removeItem(key);
} else {
newValue = mergedSerializer(currentState);
storage?.setItem(key, newValue);
}
dispatchEvent(
new CustomEvent(ANT_SYNC_STORAGE_EVENT_KEY, {
detail: { key, newValue, oldValue, storageArea: storage },
}),
);
} catch (e) {
if (process.env.NODE_ENV !== 'production') {
handleError(e);
}
}
};
const shouldSync = (ev: StorageEvent) => {
return ev && ev.key === key && ev.storageArea === storage;
};
const onNativeStorage = useCallback(
(event: StorageEvent) => {
if (shouldSync(event)) {
setState(getStoredValue());
}
},
[key],
);
const onCustomStorage = useCallback(
(event: Event) => {
if (shouldSync(event as StorageEvent)) {
setState(getStoredValue());
}
},
[key],
);
useEffect(() => {
window?.addEventListener('storage', onNativeStorage);
window?.addEventListener(ANT_SYNC_STORAGE_EVENT_KEY, onCustomStorage);
return () => {
window?.removeEventListener('storage', onNativeStorage);
window?.removeEventListener(ANT_SYNC_STORAGE_EVENT_KEY, onCustomStorage);
};
}, [key, onNativeStorage, onCustomStorage]);
return [state, updateState] as const;
};
export default useLocalStorage;

View File

@@ -85,6 +85,10 @@ const useThemeAnimation = () => {
);
document
.startViewTransition(async () => {
// wait for theme change end
while (colorBgElevated === animateRef.current.colorBgElevated) {
await new Promise<void>((resolve) => setTimeout(resolve, 1000 / 60));
}
const root = document.documentElement;
root.classList.remove(isDark ? 'dark' : 'light');
root.classList.add(isDark ? 'light' : 'dark');

View File

@@ -31,17 +31,15 @@ const locales = {
const useStyle = createStyles(({ token, css, cx }, siteConfig: SiteContextProps) => {
const textShadow = `0 0 4px ${token.colorBgContainer}`;
const isDark = siteConfig.theme.includes('dark');
const mask = cx(css`
position: absolute;
inset: 0;
backdrop-filter: blur(2px);
opacity: 1;
background-color: rgba(255, 255, 255, 0.2);
background-color: ${isDark ? 'rgba(0, 0, 0, 0.2)' : 'rgba(255, 255, 255, 0.2)'};
transition: all 1s ease;
pointer-events: none;
[data-prefers-color='dark'] & {
background-color: rgba(0, 0, 0, 0.2);
}
`);
const block = cx(css`

View File

@@ -389,9 +389,10 @@ const Theme: React.FC = () => {
themeType,
...ThemesInfo[themeType],
};
setThemeData(mergedData);
form.setFieldsValue(mergedData);
}, [form, themeType]);
}, [themeType]);
const isDark = React.use(DarkContext);
@@ -432,14 +433,23 @@ const Theme: React.FC = () => {
token: { ...themeToken, colorPrimary: colorPrimaryValue },
algorithm: algorithmFn,
components: {
Layout: isLight ? { headerBg: 'transparent', bodyBg: 'transparent' } : {},
Layout: isLight
? {
headerBg: 'transparent',
bodyBg: 'transparent',
}
: {},
Menu: isLight
? { itemBg: 'transparent', subMenuItemBg: 'transparent', activeBarBorderWidth: 0 }
? {
itemBg: 'transparent',
subMenuItemBg: 'transparent',
activeBarBorderWidth: 0,
}
: {},
...(themeType === 'v4' ? defaultTheme.components : {}),
},
}),
[themeToken, colorPrimaryValue, algorithmFn, isLight, themeType],
[themeToken, colorPrimaryValue, algorithmFn, themeType],
);
// ================================ Render ================================

View File

@@ -60,15 +60,7 @@ export type Extras = {
export type Icons = Icon[];
export type HeadingBanner = {
[key in 'cn' | 'en']: {
title?: string;
href?: string;
};
};
export type SiteData = {
headingBanner: HeadingBanner;
articles: Articles;
authors: Authors;
recommendations: Recommendations;
@@ -90,27 +82,10 @@ export function preLoad(list: string[]) {
}
}
// Banner 硬编码,以防止页面闪烁问题
export const getBannerData = (): null | {
title: string;
href: string;
} => {
return {
title: 'See Conf 2025 震撼来袭 - 探索 AI 时代的用户体验与工程实践',
href: 'https://seeconf.antfin.com/',
};
};
export const useAntdSiteConfig = () => {
const { data, error, isLoading } = useSWR<Partial<SiteData>, Error>(
`https://render.alipay.com/p/h5data/antd4-config_website-h5data.json`,
(url: string) => fetch(url).then((res) => res.json()),
{
suspense: false,
// revalidateOnMount: false,
revalidateIfStale: false,
revalidateOnFocus: false,
},
);
return { data, error, isLoading };
};

View File

@@ -1,11 +1,10 @@
import React, { Suspense } from 'react';
import React, { Suspense, useEffect } from 'react';
import { App, Button, ConfigProvider, Skeleton } from 'antd';
import { enUS, zhCN } from 'antd-token-previewer';
import type { ThemeConfig } from 'antd/es/config-provider/context';
import { Helmet } from 'dumi';
import useLocale from '../../hooks/useLocale';
import useLocalStorage from '../../hooks/useLocalStorage';
const ThemeEditor = React.lazy(() => import('antd-token-previewer/lib/ThemeEditor'));
@@ -34,18 +33,24 @@ const locales = {
},
};
const ANT_THEME_EDITOR_THEME = 'ant-theme-editor-theme';
const ANT_DESIGN_V5_THEME_EDITOR_THEME = 'ant-design-v5-theme-editor-theme';
const CustomTheme: React.FC = () => {
const { message } = App.useApp();
const [locale, lang] = useLocale(locales);
const [themeConfig, setThemeConfig] = useLocalStorage<ThemeConfig>(ANT_THEME_EDITOR_THEME, {
defaultValue: {},
});
const [theme, setTheme] = React.useState<ThemeConfig>({});
useEffect(() => {
const storedConfig = localStorage.getItem(ANT_DESIGN_V5_THEME_EDITOR_THEME);
if (storedConfig) {
const themeConfig = JSON.parse(storedConfig);
setTheme(themeConfig);
}
}, []);
const handleSave = () => {
setThemeConfig(themeConfig);
localStorage.setItem(ANT_DESIGN_V5_THEME_EDITOR_THEME, JSON.stringify(theme));
message.success(locale.saveSuccessfully);
};
@@ -60,9 +65,11 @@ const CustomTheme: React.FC = () => {
<ThemeEditor
advanced
hideAdvancedSwitcher
theme={{ name: 'Custom Theme', key: 'test', config: themeConfig }}
theme={{ name: 'Custom Theme', key: 'test', config: theme }}
style={{ height: 'calc(100vh - 64px)' }}
onThemeChange={(newTheme) => setThemeConfig(newTheme.config)}
onThemeChange={(newTheme) => {
setTheme(newTheme.config);
}}
locale={lang === 'cn' ? zhCN : enUS}
actions={
<Button type="primary" onClick={handleSave}>

View File

@@ -20,9 +20,7 @@
const isEnabled = always || enabledCondition.every(Boolean);
if (!isEnabled) {
return;
}
if (!isEnabled) return;
const prefixCls = 'antd-mirror-notify';
const primaryColor = '#1677ff';

View File

@@ -139,10 +139,7 @@ const ComponentMeta: React.FC<ComponentMetaProps> = (props) => {
}, [component, repo, source]);
// ======================== Render ========================
const importCode =
component === 'Icon'
? `import { AntDesignOutlined } from '@ant-design/icons';`
: `import { ${transformComponentName(component)} } from 'antd';`;
const importList = `import { ${transformComponentName(component)} } from "antd";`;
return (
<Descriptions
@@ -156,14 +153,14 @@ const ComponentMeta: React.FC<ComponentMetaProps> = (props) => {
{
label: locale.import,
children: (
<CopyToClipboard text={importCode} onCopy={onCopy}>
<CopyToClipboard text={`import { ${component} } from "antd";`} onCopy={onCopy}>
<Tooltip
placement="right"
title={copied ? locale.copied : locale.copy}
onOpenChange={onOpenChange}
>
<Typography.Text className={styles.code} onClick={onCopy}>
{importCode}
{importList}
</Typography.Text>
</Tooltip>
</CopyToClipboard>

View File

@@ -7,8 +7,8 @@ import classNames from 'classnames';
import ImagePreview from '../ImagePreview';
import type { ImagePreviewProps } from '../ImagePreview';
const isNonNullable = <T,>(val: T): val is NonNullable<T> => {
return val !== undefined && val !== null;
const isNotEmpty = (val: any) => {
return typeof val !== 'undefined' && val !== null && val !== '';
};
const useStyle = createStyles(({ css, token }) => {
@@ -51,8 +51,8 @@ const FlexWithImagePreview: React.FC<
return (
<Flex className={classNames(styles.wrapper, className)} style={style} {...rest}>
<Flex align="flex-start" justify="flex-start" vertical>
{isNonNullable(title) && <div className={styles.title}>{title}</div>}
{isNonNullable(description) && <div className={styles.description}>{description}</div>}
{isNotEmpty(title) && <div className={styles.title}>{title}</div>}
{isNotEmpty(description) && <div className={styles.description}>{description}</div>}
</Flex>
<ImagePreview {...imagePreviewProps}>{children}</ImagePreview>
</Flex>

View File

@@ -48,8 +48,6 @@ const IconSearch: React.FC = () => {
const handleSearchIcon = debounce((e: React.ChangeEvent<HTMLInputElement>) => {
setDisplayState((prevState) => ({ ...prevState, searchKey: e.target.value }));
document.getElementById('list-of-icons')?.scrollIntoView({ behavior: 'smooth' });
}, 300);
const handleChangeTheme = useCallback((value: ThemeType) => {

View File

@@ -164,6 +164,7 @@ const CodePreview: React.FC<CodePreviewProps> = ({
</div>
),
})),
// eslint-disable-next-line react-hooks/exhaustive-deps
[
entryName,
error,

View File

@@ -72,10 +72,10 @@ const Palette: React.FC<PaletteProps> = (props) => {
key={i}
ref={(node) => {
if (node) {
colorNodesRef.current[`${name}-${i + 1}`] = node;
colorNodesRef.current[`${name}-${i}`] = node;
}
}}
className={`main-color-item palette-${name}-${i + 1}`}
className={`main-color-item palette-${name}-${i}`}
style={{
color: (name === 'yellow' ? i > 6 : i > 5) ? firstColor : lastColor,
fontWeight: i === 6 ? 'bold' : 'normal',

View File

@@ -16,7 +16,6 @@ export interface SemanticPreviewInjectionProps {
const useStyle = createStyles(({ token }) => ({
container: css`
position: relative;
z-index: 0;
`,
colWrap: css`
border-inline-end: 1px solid ${token.colorBorderSecondary};

View File

@@ -11,17 +11,17 @@ import type { MenuProps } from 'antd';
import { CompactTheme, DarkTheme } from 'antd-token-previewer/es/icons';
import { FormattedMessage, useLocation } from 'dumi';
import useLocalStorage from '../../../hooks/useLocalStorage';
import useThemeAnimation from '../../../hooks/useThemeAnimation';
import type { SiteContextProps } from '../../slots/SiteContext';
import SiteContext from '../../slots/SiteContext';
import { getLocalizedPathname, isZhCN } from '../../utils';
import { getLocalizedPathname, isZhCN, isLocalStorageNameSupported } from '../../utils';
import Link from '../Link';
import ThemeIcon from './ThemeIcon';
export type ThemeName = 'light' | 'dark' | 'auto' | 'compact' | 'motion-off' | 'happy-work';
export const ANT_DESIGN_SITE_THEME = 'ant-design-site-theme';
// 主题持久化存储键名
const ANT_DESIGN_SITE_THEME = 'ant-design-site-theme';
export interface ThemeSwitchProps {
value?: ThemeName[];
@@ -33,10 +33,6 @@ const ThemeSwitch: React.FC<ThemeSwitchProps> = () => {
const toggleAnimationTheme = useThemeAnimation();
const lastThemeKey = useRef<string>(theme.includes('dark') ? 'dark' : 'light');
const [, setTheme] = useLocalStorage<ThemeName>(ANT_DESIGN_SITE_THEME, {
defaultValue: undefined,
});
const badge = <Badge color="blue" style={{ marginTop: -1 }} />;
// 主题选项配置
@@ -137,9 +133,14 @@ const ThemeSwitch: React.FC<ThemeSwitchProps> = () => {
const filteredTheme = theme.filter((t) => !['light', 'dark', 'auto'].includes(t));
const newTheme = [...filteredTheme, themeKey];
setTheme(themeKey);
updateSiteConfig({
theme: newTheme,
});
updateSiteConfig({ theme: newTheme });
// 持久化到 localStorage
if (isLocalStorageNameSupported()) {
localStorage.setItem(ANT_DESIGN_SITE_THEME, themeKey);
}
} else {
// 其他主题选项是开关式的
const hasTheme = theme.includes(themeKey);

View File

@@ -12,18 +12,15 @@ import { getSandpackCssText } from '@codesandbox/sandpack-react';
import { theme as antdTheme, App } from 'antd';
import type { MappingAlgorithm } from 'antd';
import type { DirectionType, ThemeConfig } from 'antd/es/config-provider';
import dayjs from 'dayjs';
import { createSearchParams, useOutlet, useSearchParams, useServerInsertedHTML } from 'dumi';
import { DarkContext } from '../../hooks/useDark';
import useLayoutState from '../../hooks/useLayoutState';
import useLocalStorage from '../../hooks/useLocalStorage';
import { getBannerData } from '../../pages/index/components/util';
import { ANT_DESIGN_SITE_THEME } from '../common/ThemeSwitch';
import type { ThemeName } from '../common/ThemeSwitch';
import SiteThemeProvider from '../SiteThemeProvider';
import type { SiteContextProps } from '../slots/SiteContext';
import SiteContext from '../slots/SiteContext';
import { isLocalStorageNameSupported } from '../utils';
import '@ant-design/v5-patch-for-react-19';
@@ -31,9 +28,11 @@ type Entries<T> = { [K in keyof T]: [K, T[K]] }[keyof T][];
type SiteState = Partial<Omit<SiteContextProps, 'updateSiteConfig'>>;
const RESPONSIVE_MOBILE = 768;
export const ANT_DESIGN_NOT_SHOW_BANNER = 'ANT_DESIGN_NOT_SHOW_BANNER';
// 主题持久化存储键名
const ANT_DESIGN_SITE_THEME = 'ant-design-site-theme';
// Compatible with old anchors
if (typeof window !== 'undefined') {
const hashId = location.hash.slice(1);
@@ -44,13 +43,9 @@ if (typeof window !== 'undefined') {
}
}
const getAlgorithm = (themes: ThemeName[] = [], systemTheme: 'dark' | 'light') =>
const getAlgorithm = (themes: ThemeName[] = []) =>
themes
.map((theme) => {
// auto 模式下根据系统主题切换
if (theme === 'auto' && systemTheme === 'dark') {
return antdTheme.darkAlgorithm;
}
if (theme === 'dark') {
return antdTheme.darkAlgorithm;
}
@@ -61,11 +56,21 @@ const getAlgorithm = (themes: ThemeName[] = [], systemTheme: 'dark' | 'light') =
})
.filter(Boolean);
const getSystemTheme = (): 'light' | 'dark' => {
if (typeof window === 'undefined') {
return 'light';
// 获取最终主题优先级URL Query > Local Storage > Site (Memory)
const getFinalTheme = (urlTheme: ThemeName[]): ThemeName[] => {
// 只认 light/dark
const baseTheme = urlTheme.filter((t) => !['light', 'dark', 'auto'].includes(t));
const urlColor = urlTheme.find((t) => t === 'light' || t === 'dark');
if (urlColor) return [...baseTheme, urlColor];
if (isLocalStorageNameSupported()) {
const stored = localStorage.getItem(ANT_DESIGN_SITE_THEME) as ThemeName;
if (stored && ['light', 'dark', 'auto'].includes(stored)) {
return [...baseTheme, stored];
}
}
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
// 默认 auto
return [...baseTheme, 'auto'];
};
const GlobalLayout: React.FC = () => {
@@ -79,32 +84,6 @@ const GlobalLayout: React.FC = () => {
bannerVisible: false,
});
const [storedTheme] = useLocalStorage<ThemeName>(ANT_DESIGN_SITE_THEME, {
defaultValue: undefined,
});
const [bannerLastTime] = useLocalStorage<string>(ANT_DESIGN_NOT_SHOW_BANNER, {
defaultValue: undefined,
});
// 获取最终主题优先级URL Query > Local Storage > Site (Memory)
const getFinalTheme = (urlTheme: ThemeName[]): ThemeName[] => {
// 只认 light/dark
const baseTheme = urlTheme.filter((t) => !['light', 'dark', 'auto'].includes(t));
const urlColor = urlTheme.find((t) => t === 'light' || t === 'dark');
if (urlColor) {
return [...baseTheme, urlColor];
}
if (['light', 'dark', 'auto'].includes(storedTheme)) {
return [...baseTheme, storedTheme];
}
return [...baseTheme, 'auto'];
};
const [systemTheme, setSystemTheme] = React.useState<'light' | 'dark'>(() => getSystemTheme());
const bannerData = getBannerData();
// TODO: This can be remove in v6
const useCssVar = searchParams.get('cssVar') !== 'false';
@@ -132,6 +111,10 @@ const GlobalLayout: React.FC = () => {
} else {
nextSearchParams.delete('theme');
}
// 设置 data-prefers-color
if (color) {
document.querySelector('html')?.setAttribute('data-prefers-color', color);
}
}
});
@@ -139,6 +122,7 @@ const GlobalLayout: React.FC = () => {
setSearchParams(nextSearchParams);
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[searchParams, setSearchParams],
);
@@ -146,17 +130,6 @@ const GlobalLayout: React.FC = () => {
updateSiteConfig({ isMobile: window.innerWidth < RESPONSIVE_MOBILE });
}, [updateSiteConfig]);
// 设置 data-prefers-color 属性
useEffect(() => {
const color = theme.find((t) => t === 'light' || t === 'dark');
const html = document.querySelector<HTMLHtmlElement>('html');
if (theme.includes('auto') && systemTheme) {
html?.setAttribute('data-prefers-color', systemTheme);
} else if (color) {
html?.setAttribute('data-prefers-color', color);
}
}, [systemTheme, theme]);
// 监听系统主题变化
useEffect(() => {
if (typeof window === 'undefined') {
@@ -165,10 +138,7 @@ const GlobalLayout: React.FC = () => {
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
const handleSystemThemeChange = (e: MediaQueryListEvent) => {
const newSystemTheme = e.matches ? 'dark' : 'light';
setSystemTheme(newSystemTheme);
};
const handleSystemThemeChange = () => {};
mediaQuery.addEventListener('change', handleSystemThemeChange);
@@ -183,18 +153,17 @@ const GlobalLayout: React.FC = () => {
const finalTheme = getFinalTheme(urlTheme);
const _direction = searchParams.get('direction') as DirectionType;
const storedBannerVisible = bannerLastTime && dayjs().diff(dayjs(bannerLastTime), 'day') >= 1;
const isZhCN = typeof window !== 'undefined' && window.location.pathname.includes('-cn');
const hasBannerContent = isZhCN && !!bannerData;
setSiteState({
theme: finalTheme,
direction: _direction === 'rtl' ? 'rtl' : 'ltr',
bannerVisible: hasBannerContent && (bannerLastTime ? !!storedBannerVisible : true),
});
// 设置 data-prefers-color 属性
const colorTheme = finalTheme.find((t) => ['light', 'dark'].includes(t));
if (colorTheme) {
document.documentElement.setAttribute('data-prefers-color', colorTheme);
}
// Handle isMobile
updateMobileMode();
@@ -208,6 +177,7 @@ const GlobalLayout: React.FC = () => {
return () => {
window.removeEventListener('resize', updateMobileMode);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [searchParams, updateMobileMode]);
const siteContextValue = React.useMemo<SiteContextProps>(
@@ -225,12 +195,12 @@ const GlobalLayout: React.FC = () => {
// 算法优先级auto 时用系统主题算法
const themeForAlgorithm = theme;
return {
algorithm: getAlgorithm(themeForAlgorithm, systemTheme),
algorithm: getAlgorithm(themeForAlgorithm),
token: { motion: !theme.includes('motion-off') },
cssVar: useCssVar,
hashed: !useCssVar,
};
}, [theme, useCssVar, systemTheme]);
}, [theme, useCssVar]);
const styleCache = React.useMemo(() => createCache(), []);
@@ -269,9 +239,7 @@ const GlobalLayout: React.FC = () => {
));
return (
<DarkContext
value={theme.includes('dark') || (theme.includes('auto') && systemTheme === 'dark')}
>
<DarkContext value={theme.includes('dark')}>
<StyleProvider
cache={styleCache}
linters={[legacyNotSelectorLinter, parentSelectorLinter, NaNLinter]}

View File

@@ -40,10 +40,12 @@ const Content: React.FC<React.PropsWithChildren> = ({ children }) => {
useLayoutEffect(() => {
setShowDebug(process.env.NODE_ENV === 'development' || isDebugDemo);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isDebugDemo]);
const contextValue = useMemo<DemoContextProps>(
() => ({ showDebug, setShowDebug, codeType, setCodeType }),
// eslint-disable-next-line react-hooks/exhaustive-deps
[showDebug, codeType],
);

View File

@@ -8,8 +8,6 @@ import { useLocation, useSiteData } from 'dumi';
import DumiSearchBar from 'dumi/theme-default/slots/SearchBar';
import useLocale from '../../../hooks/useLocale';
import useLocalStorage from '../../../hooks/useLocalStorage';
import { getBannerData } from '../../../pages/index/components/util';
import ThemeSwitch from '../../common/ThemeSwitch';
import DirectionIcon from '../../icons/DirectionIcon';
import { ANT_DESIGN_NOT_SHOW_BANNER } from '../../layouts/GlobalLayout';
@@ -24,7 +22,20 @@ import SwitchBtn from './SwitchBtn';
const RESPONSIVE_XS = 1120;
const RESPONSIVE_SM = 1200;
export const ANT_LOCAL_TYPE_KEY = 'ANT_LOCAL_TYPE_KEY';
const locales = {
cn: {
message: '语雀征文 · 说说你和开源的故事,赢取 Ant Design 精美周边 🎁',
shortMessage: '语雀征文 · 说说你和开源的故事,赢取 Ant Design 精美周边 🎁',
more: '前往了解',
link: 'https://www.yuque.com/opensource2023',
},
en: {
message: '',
shortMessage: '',
more: '',
link: '',
},
};
const useStyle = createStyles(({ token, css }) => {
const searchIconColor = '#ced4d9';
@@ -151,12 +162,11 @@ interface HeaderState {
// ================================= Header =================================
const Header: React.FC = () => {
const [, lang] = useLocale();
const [locale, lang] = useLocale(locales);
const { pkg } = useSiteData();
const themeConfig = getThemeConfig();
const [headerState, setHeaderState] = useState<HeaderState>({
menuVisible: false,
windowWidth: 1400,
@@ -169,33 +179,24 @@ const Header: React.FC = () => {
const { styles } = useStyle();
const [, setTopBannerDay] = useLocalStorage<string>(ANT_DESIGN_NOT_SHOW_BANNER, {
defaultValue: undefined,
});
const [, setLocalType] = useLocalStorage<string>(ANT_LOCAL_TYPE_KEY, {
defaultValue: undefined,
});
const handleHideMenu = useCallback(() => {
setHeaderState((prev) => ({ ...prev, menuVisible: false }));
}, []);
const onWindowResize = useCallback(() => {
setHeaderState((prev) => ({ ...prev, windowWidth: window.innerWidth }));
}, []);
const onMenuVisibleChange = useCallback((visible: boolean) => {
setHeaderState((prev) => ({ ...prev, menuVisible: visible }));
}, []);
const onDirectionChange = () => {
updateSiteConfig({ direction: direction !== 'rtl' ? 'rtl' : 'ltr' });
};
const onBannerClose = () => {
updateSiteConfig({ bannerVisible: false });
setTopBannerDay(dayjs().toISOString());
if (utils.isLocalStorageNameSupported()) {
localStorage.setItem(ANT_DESIGN_NOT_SHOW_BANNER, dayjs().toISOString());
}
};
useEffect(() => {
@@ -237,8 +238,9 @@ const Header: React.FC = () => {
const currentProtocol = `${window.location.protocol}//`;
const currentHref = window.location.href.slice(currentProtocol.length);
setLocalType(utils.isZhCN(pathname) ? 'en-US' : 'zh-CN');
if (utils.isLocalStorageNameSupported()) {
localStorage.setItem('locale', utils.isZhCN(pathname) ? 'en-US' : 'zh-CN');
}
window.location.href =
currentProtocol +
currentHref.replace(
@@ -270,12 +272,6 @@ const Header: React.FC = () => {
const isHome = ['', 'index', 'index-cn'].includes(pathname);
const isZhCN = lang === 'cn';
const isRTL = direction === 'rtl';
// Get banner data from site config
const bannerData = getBannerData();
const bannerTitle = bannerData?.title || '';
const bannerHref = bannerData?.href || '';
let responsive: null | 'narrow' | 'crowded' = null;
if (windowWidth < RESPONSIVE_XS) {
responsive = 'crowded';
@@ -379,7 +375,7 @@ const Header: React.FC = () => {
<MenuOutlined className="nav-phone-icon" />
</Popover>
)}
{isZhCN && bannerVisible && bannerTitle && bannerHref && (
{isZhCN && bannerVisible && (
<ConfigProvider
theme={{
token: {
@@ -391,25 +387,23 @@ const Header: React.FC = () => {
<Alert
className={styles.banner}
message={
bannerTitle && bannerHref ? (
<>
<span>{bannerTitle}</span>
<a
className={styles.link}
href={bannerHref}
target="_blank"
rel="noreferrer"
onClick={() => {
window.gtag?.('event', '点击', {
event_category: 'top_banner',
event_label: bannerHref,
});
}}
>
</a>
</>
) : null
<>
<span>{isMobile ? locale.shortMessage : locale.message}</span>
<a
className={styles.link}
href={locale.link}
target="_blank"
rel="noreferrer"
onClick={() => {
window.gtag?.('event', '点击', {
event_category: 'top_banner',
event_label: locale.link,
});
}}
>
{locale.more}
</a>
</>
}
type="info"
banner

View File

@@ -2,7 +2,6 @@ import * as React from 'react';
import type { DirectionType } from 'antd/es/config-provider';
import type { ThemeName } from '../common/ThemeSwitch';
import { getBannerData } from '../../pages/index/components/util';
export interface SiteContextProps {
isMobile: boolean;
@@ -14,7 +13,7 @@ export interface SiteContextProps {
const SiteContext = React.createContext<SiteContextProps>({
isMobile: false,
bannerVisible: !!getBannerData(),
bannerVisible: false,
direction: 'ltr',
theme: ['light'],
updateSiteConfig: () => {},

View File

@@ -1,7 +1,6 @@
import semver from 'semver';
import flatten from 'lodash/flatten';
import flattenDeep from 'lodash/flattenDeep';
import semver from 'semver';
import deprecatedVersions from '../../../BUG_VERSIONS.json';
import themeConfig from '../themeConfig';
@@ -158,6 +157,19 @@ export function getLocalizedPathname(
return { pathname: fullPath, search };
}
export function isLocalStorageNameSupported() {
const testKey = 'test';
const storage = window.localStorage;
try {
storage.setItem(testKey, '1');
storage.removeItem(testKey);
return true;
} catch (error) {
console.error('Your web browser does not support storing settings locally.', error);
return false;
}
}
export function loadScript(src: string) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
@@ -165,7 +177,7 @@ export function loadScript(src: string) {
script.src = src;
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
document.head!.appendChild(script);
});
}

View File

@@ -131,6 +131,17 @@ export default defineConfig({
headScripts: [
`
(function () {
function isLocalStorageNameSupported() {
const testKey = 'test';
const storage = window.localStorage;
try {
storage.setItem(testKey, '1');
storage.removeItem(testKey);
return true;
} catch (error) {
return false;
}
}
// 优先级提高到所有静态资源的前面,语言不对,加载其他静态资源没意义
const pathname = location.pathname;
@@ -160,7 +171,7 @@ export default defineConfig({
}
// 首页无视链接里面的语言设置 https://github.com/ant-design/ant-design/issues/4552
if (pathname === '/' || pathname === '/index-cn') {
if (isLocalStorageNameSupported() && (pathname === '/' || pathname === '/index-cn')) {
const lang =
(window.localStorage && localStorage.getItem('locale')) ||
((navigator.language || navigator.browserLanguage).toLowerCase() === 'zh-cn'

View File

@@ -39,9 +39,7 @@ jobs:
const now = new Date();
for (const issue of issues) {
if (issue.pull_request) {
continue;
}
if (issue.pull_request) continue;
const updatedAt = new Date(issue.updated_at);
const daysInactive = (now - updatedAt) / (1000 * 60 * 60 * 24);

View File

@@ -41,9 +41,7 @@ jobs:
}, {});
const total = Object.keys(jobs).length;
if (total === 0) {
core.setFailed('no jobs found');
}
if(total === 0) core.setFailed('no jobs found');
// the name here must be the same as `jobs.xxx.{name}` in preview-build.yml
// set output

View File

@@ -63,7 +63,7 @@ jobs:
- uses: oven-sh/setup-bun@v2
- name: download site artifact
uses: actions/download-artifact@v6
uses: actions/download-artifact@v5
with:
name: real-site
path: _site
@@ -105,7 +105,7 @@ jobs:
needs: build-site
steps:
- name: download site artifact
uses: actions/download-artifact@v6
uses: actions/download-artifact@v5
with:
name: real-site
path: _site
@@ -117,7 +117,7 @@ jobs:
cd ..
- name: Upload to Release
uses: softprops/action-gh-release@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe # v2.4.2
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1
with:
fail_on_unmatched_files: true
files: website.tar.gz

View File

@@ -123,7 +123,7 @@ jobs:
- uses: oven-sh/setup-bun@v2
- run: bun install
- uses: actions/download-artifact@v6
- uses: actions/download-artifact@v5
with:
pattern: coverage-artifacts-*
merge-multiple: true

View File

@@ -126,7 +126,7 @@ jobs:
- uses: oven-sh/setup-bun@v2
- run: bun install
- uses: actions/download-artifact@v6
- uses: actions/download-artifact@v5
with:
pattern: coverage-artifacts-*
merge-multiple: true

View File

@@ -54,7 +54,7 @@ jobs:
- uses: oven-sh/setup-bun@v2
- run: bun install
- uses: actions/download-artifact@v6
- uses: actions/download-artifact@v5
with:
pattern: snapshot-artifacts-*
merge-multiple: true

View File

@@ -43,9 +43,7 @@ jobs:
}, {});
const total = Object.keys(jobs).length;
if (total === 0) {
core.setFailed('no jobs found');
}
if(total === 0) core.setFailed('no jobs found');
// the name here must be the same as `jobs.xxx.{name}`
console.log('visual-diff report job status: %s', jobs['visual-diff report'].status);

View File

@@ -40,9 +40,7 @@ jobs:
}, {});
const total = Object.keys(jobs).length;
if (total === 0) {
core.setFailed('no jobs found');
}
if(total === 0) core.setFailed('no jobs found');
// the name here must be the same as `jobs.xxx.{name}`
console.log('visual-diff report job status: %s', jobs['test image']);

View File

@@ -15,66 +15,6 @@ tag: vVERSION
---
## 5.29.0
`2025-11-17`
- 🆕 InputNumber component deprecates `addonAfter` and `addonBefore` props, use Space.Compact instead. [#55505](https://github.com/ant-design/ant-design/pull/55505) [@EmilyyyLiu](https://github.com/EmilyyyLiu)
- ⌨️ Improve Drawer accessibility by adding `aria-labelledby` when a title is present. [#55697](https://github.com/ant-design/ant-design/pull/55697) [@rxy001](https://github.com/rxy001)
- 🌐 Fixed spacing in Korean localization for “복사됨” message. [#55672](https://github.com/ant-design/ant-design/pull/55672) [@rapsealk](https://github.com/rapsealk)
## 5.28.1
`2025-11-11`
- Carousel
- 🐞 Fix Carousel style issue in vertical mode. [#55615](https://github.com/ant-design/ant-design/pull/55615) [@wanpan11](https://github.com/wanpan11)
- 🐞 Fix Carousel dots animation missing when first render. [#55589](https://github.com/ant-design/ant-design/pull/55589) [@wanpan11](https://github.com/wanpan11)
- 🐞 Fix Descriptions where content style wrongly used `labelStyle`. [#55572](https://github.com/ant-design/ant-design/pull/55572) [@li-jia-nan](https://github.com/li-jia-nan)
- 💄 Adjust the height of the Select component to 32px when `variant="underlined"`. [#55607](https://github.com/ant-design/ant-design/pull/55607) [@ustcfury](https://github.com/ustcfury)
- 💄 When the `underlined` property is enabled for the Input component, the border color changes on mouse hover. [#55609](https://github.com/ant-design/ant-design/pull/55609) [@ustcfury](https://github.com/ustcfury)
- 🌐 Add missing TimePicker translations for locales: ar_EG, en_GB, gl_ES, bg_BG, ca_ES, cs_CZ, el_GR, es_ES, eu_ES, fi_FI, he_IL, hu_HU, is_IS, kn_IN, kmr_IQ, lv_LV, mk_MK, mn_MN, ms_MY, pl_PL, pt_BR, pt_PT, ro_RO, sk_SK, sl_SI, sv_SE, ta_IN, th_TH, zh_TW, et_EE. [#55656](https://github.com/ant-design/ant-design/pull/55656) [@li-jia-nan](https://github.com/li-jia-nan)
- TypeScript
- 🤖 Improve Flex component `gap` prop typing, for better IDE autocomplete and type inference. [#55591](https://github.com/ant-design/ant-design/pull/55591) [@ayangweb](https://github.com/ayangweb)
## 5.28.0
`2025-11-01`
- 🆕 Drawer supports `closable.placement` prop to specify the position of the close button. [#54067](https://github.com/ant-design/ant-design/pull/54067) [@davidhsing](https://github.com/davidhsing)
- 🆕 Image component supports `fallback` global configuration. [#54702](https://github.com/ant-design/ant-design/pull/54702) [@Jiyur](https://github.com/Jiyur)
- 🆕 QRCode component supports `boostLevel` prop. [#55063](https://github.com/ant-design/ant-design/pull/55063) [@li-jia-nan](https://github.com/li-jia-nan)
- 🆕 Splitter supports `onCollapse` prop. [#54673](https://github.com/ant-design/ant-design/pull/54673) [@ug-hero](https://github.com/ug-hero)
- 🆕 Statistic displays animation effect by default when set to `loading`. [#55398](https://github.com/ant-design/ant-design/pull/55398) [@afc163](https://github.com/afc163)
- 🆕 TreeSelect supports global configuration for switcher icon. [#54821](https://github.com/ant-design/ant-design/pull/54821) [@Jiyur](https://github.com/Jiyur)
- Segmented
- 💄 Segmented theme variable `itemSelectedBg` supports background gradient. [#55391](https://github.com/ant-design/ant-design/pull/55391) [@zancheng](https://github.com/zancheng)
- 🐞 Fix Segmented abnormal animation after React DevTools upgrade. [#55438](https://github.com/ant-design/ant-design/pull/55438) [@afc163](https://github.com/afc163)
- 🐞 Fix Tree and Transfer components `disabled` inheritance issue. [#54831](https://github.com/ant-design/ant-design/pull/54831) [@cactuser-Lu](https://github.com/cactuser-Lu)
- 🐞 Fix Tree.DirectoryTree `defaultExpandAll` not working when `fieldNames` is defined. [#55420](https://github.com/ant-design/ant-design/pull/55420) [@Wxh16144](https://github.com/Wxh16144)
- Upload
- 🆕 Upload component adds `info.defaultRequest` in `customRequest` parameters. [#55146](https://github.com/ant-design/ant-design/pull/55146) [@EmilyyyLiu](https://github.com/EmilyyyLiu)
- 💄 Upload component supports `pictureCardSize` token. [#54756](https://github.com/ant-design/ant-design/pull/54756) [@wanpan11](https://github.com/wanpan11)
- 💄 Notification supports configuring background color token. [#54802](https://github.com/ant-design/ant-design/pull/54802) [@thinkasany](https://github.com/thinkasany)
- 💄 Pagination supports modifying the text color of active items through `itemActiveColor` and `itemActiveColorHover` tokens. [#55195](https://github.com/ant-design/ant-design/pull/55195) [@Renderz](https://github.com/Renderz)
- 🐞 Fix Select, DatePicker, TreeSelect, Cascader and other components not showing default suffix icon when `suffixIcon` is configured as undefined. [#54790](https://github.com/ant-design/ant-design/pull/54790) [@EmilyyyLiu](https://github.com/EmilyyyLiu)
- 🐞 Fix Mentions component not inheriting `disabled` from external Form. [#54829](https://github.com/ant-design/ant-design/pull/54829) [@EmilyyyLiu](https://github.com/EmilyyyLiu)
- 🐞 Fix Watermark component crashing when wrapping Modal with `modalRender`. [#55435](https://github.com/ant-design/ant-design/pull/55435) [@ug-hero](https://github.com/ug-hero)
- 🗑 Input component deprecates `addonAfter` and `addonBefore` props, use Space.Compact instead. [#55315](https://github.com/ant-design/ant-design/pull/55315) [@EmilyyyLiu](https://github.com/EmilyyyLiu)
- 🤖 Row component `gutter` prop supports string type definition. [#54628](https://github.com/ant-design/ant-design/pull/54628) [@ug-hero](https://github.com/ug-hero)
## 5.27.6
`2025-10-20`
- 🐞 Fix Table `pagination.align` is not working. [#55316](https://github.com/ant-design/ant-design/pull/55316)
- 🛠 Refactor Modal useMemo of ConfirmDialog to resolve useMemo invalid where Object.values generates a new array. [#55376](https://github.com/ant-design/ant-design/pull/55376)
- TypeScript
- 🤖 Add ConfigProvider the `Window` type definition in `getTargetContainer` of . [#55313](https://github.com/ant-design/ant-design/pull/55313)
- 🤖 Add ConfigProvider the `ShadowRoot` type definition in `getTargetContainer` and `getPopupContainer`. [#55278](https://github.com/ant-design/ant-design/pull/55278) [@leshalv](https://github.com/leshalv)
- 🤖 Improve Modal type definition. [#55371](https://github.com/ant-design/ant-design/pull/55371)
## 5.27.5
`2025-10-14`

View File

@@ -15,65 +15,6 @@ tag: vVERSION
---
## 5.29.0
`2025-11-17`
- 🆕 InputNumber 组件废弃 `addonAfter``addonBefore` 属性,使用 Space.Compact 替换。 [#55505](https://github.com/ant-design/ant-design/pull/55505) [@EmilyyyLiu](https://github.com/EmilyyyLiu)
- ⌨️ 优化 Drawer 组件的可访问性,存在标题时的属性 `aria-labelledby`。[#55697](https://github.com/ant-design/ant-design/pull/55697) [@rxy001](https://github.com/rxy001)
- 🌐 修复韩语本地化中“복사됨”文本的空格规则问题。[#55672](https://github.com/ant-design/ant-design/pull/55672) [@rapsealk](https://github.com/rapsealk)
## 5.28.1
`2025-11-11`
- Carousel
- 🐞 修复 Carousel 组件纵向样式问题。[#55615](https://github.com/ant-design/ant-design/pull/55615) [@wanpan11](https://github.com/wanpan11)
- 🐞 修复 Carousel 首次渲染时指示点动画丢失。[#55589](https://github.com/ant-design/ant-design/pull/55589) [@wanpan11](https://github.com/wanpan11)
- 🐞 修复 Descriptions 组件内容样式错误使用 `labelStyle` 的问题。[#55572](https://github.com/ant-design/ant-design/pull/55572) [@li-jia-nan](https://github.com/li-jia-nan)
- 💄 修正 `underlined` 变体的 Select 组件高度为 32px。[#55607](https://github.com/ant-design/ant-design/pull/55607) [@ustcfury](https://github.com/ustcfury)
- 💄 为 `underlined` 变体的 Input 组件补全 hover 边框颜色样式。[#55609](https://github.com/ant-design/ant-design/pull/55609) [@ustcfury](https://github.com/ustcfury)
- 🌐 补充 TimePicker 多语言翻译覆盖以下语言ar_EG、en_GB、gl_ES、bg_BG、ca_ES、cs_CZ、el_GR、es_ES、eu_ES、fi_FI、he_IL、hu_HU、is_IS、kn_IN、kmr_IQ、lv_LV、mk_MK、mn_MN、ms_MY、pl_PL、pt_BR、pt_PT、ro_RO、sk_SK、sl_SI、sv_SE、ta_IN、th_TH、zh_TW、et_EE。[#55656](https://github.com/ant-design/ant-design/pull/55656) [@li-jia-nan](https://github.com/li-jia-nan)
- TypeScript
- 🤖 优化 Flex 组件 `gap` 属性类型定义,支持更友好的 IDE 类型提示 [#55591](https://github.com/ant-design/ant-design/pull/55591) [@ayangweb](https://github.com/ayangweb)
## 5.28.0
`2025-11-01`
- 🆕 Drawer 支持 `closable.placement` 属性,用于指定关闭按钮的位置。[#54067](https://github.com/ant-design/ant-design/pull/54067) [@davidhsing](https://github.com/davidhsing)
- 🆕 Image 组件支持 `fallback` 全局配置。[#54702](https://github.com/ant-design/ant-design/pull/54702) [@Jiyur](https://github.com/Jiyur)
- 🆕 QRCode 组件支持 `boostLevel` 属性。[#55063](https://github.com/ant-design/ant-design/pull/55063) [@li-jia-nan](https://github.com/li-jia-nan)
- 🆕 Splitter 支持 `onCollapse` 属性。[#54673](https://github.com/ant-design/ant-design/pull/54673) [@ug-hero](https://github.com/ug-hero)
- 🆕 Statistic 设置为 `loading` 时默认展现动画效果。[#55398](https://github.com/ant-design/ant-design/pull/55398) [@afc163](https://github.com/afc163)
- 🆕 TreeSelect 支持切换器图标的全局配置。[#54821](https://github.com/ant-design/ant-design/pull/54821) [@Jiyur](https://github.com/Jiyur)
- Segmented
- 💄 Segmented 的主题变量 `itemSelectedBg` 支持背景渐变。[#55391](https://github.com/ant-design/ant-design/pull/55391) [@zancheng](https://github.com/zancheng)
- 🐞 修复 Segmented 切换动画总是从第一项移动闪烁的问题。[#55438](https://github.com/ant-design/ant-design/pull/55438) [@afc163](https://github.com/afc163)
- 🐞 修复 Tree 和 Transfer 组件的 `disabled` 继承问题。[#54831](https://github.com/ant-design/ant-design/pull/54831) [@cactuser-Lu](https://github.com/cactuser-Lu)
- 🐞 修复 Tree.DirectoryTree 定义 `fieldNames``defaultExpandAll` 不生效的问题。[#55420](https://github.com/ant-design/ant-design/pull/55420) [@Wxh16144](https://github.com/Wxh16144)
- Upload
- 🆕 Upload 组件 `customRequest` 参数中增加 `info.defaultRequest`。[#55146](https://github.com/ant-design/ant-design/pull/55146) [@EmilyyyLiu](https://github.com/EmilyyyLiu)
- 💄 Upload 组件支持 `pictureCardSize` token。[#54756](https://github.com/ant-design/ant-design/pull/54756) [@wanpan11](https://github.com/wanpan11)
- 💄 Notification 支持配置背景颜色的 token。[#54802](https://github.com/ant-design/ant-design/pull/54802) [@thinkasany](https://github.com/thinkasany)
- 💄 Pagination 支持通过 `itemActiveColor``itemActiveColorHover` token 修改高亮项的文字颜色。[#55195](https://github.com/ant-design/ant-design/pull/55195) [@Renderz](https://github.com/Renderz)
- 🐞 修复 Select、DatePicker、TreeSelect、Cascader 等组件 `suffixIcon` 配置值为 undefined 时没有默认后缀图标的问题。[#54790](https://github.com/ant-design/ant-design/pull/54790) [@EmilyyyLiu](https://github.com/EmilyyyLiu)
- 🐞 修复 Mentions 组件没有继承外部 Form 的 `disabled` 的问题。[#54829](https://github.com/ant-design/ant-design/pull/54829) [@EmilyyyLiu](https://github.com/EmilyyyLiu)
- 🐞 修复 Watermark 组件内包裹 Modal 使用 `modalRender` 会导致崩溃的问题。[#55435](https://github.com/ant-design/ant-design/pull/55435) [@ug-hero](https://github.com/ug-hero)
- 🗑 Input 组件废弃 `addonAfter``addonBefore` 属性,使用 Space.Compact 替换。[#55315](https://github.com/ant-design/ant-design/pull/55315) [@EmilyyyLiu](https://github.com/EmilyyyLiu)
- 🤖 Row 组件 `gutter` 属性支持 string 类型定义。[#54628](https://github.com/ant-design/ant-design/pull/54628) [@ug-hero](https://github.com/ug-hero)
## 5.27.6
`2025-10-20`
- 🐞 修复 Table `pagination.align` 属性失效的问题。[#55316](https://github.com/ant-design/ant-design/pull/55316)
- 🛠 重构 Modal 中 ConfirmDialog 的 useMemo以解决 Object.values 生成新数组导致 useMemo 失效的问题。[#55376](https://github.com/ant-design/ant-design/pull/55376)
- TypeScript
- 🤖 补充 ConfigProvider `getTargetContainer``Window` 类型定义。[#55313](https://github.com/ant-design/ant-design/pull/55313)
- 🤖 补充 ConfigProvider 的 `getTargetContainer``getPopupContainer``ShadowRoot` 类型定义。[#55278](https://github.com/ant-design/ant-design/pull/55278) [@leshalv](https://github.com/leshalv)
- 🤖 优化 Modal 中类型定义。[#55371](https://github.com/ant-design/ant-design/pull/55371)
## 5.27.5
`2025-10-14`
@@ -95,7 +36,7 @@ tag: vVERSION
- 🐞 修复 Table 在使 `sticky` 表头或设置 `scroll.y` 时,筛选下拉与 Tooltip 重复显示的问题。[#54910](https://github.com/ant-design/ant-design/pull/54910) [@afc163](https://github.com/afc163)
- 🐞 修复 Table 表头在首次加载时未正确渲染的问题。[#54910](https://github.com/ant-design/ant-design/pull/54910) [@afc163](https://github.com/afc163)
- 🐞 修复 Table 在启用 `scroll.x` 时,固定列的对齐问题。[#54899](https://github.com/ant-design/ant-design/pull/54899) [@afc163](https://github.com/afc163)
- 🐞 修复 Button 仅图标icon-only按钮的内边距受主题影响的问题。[#54970](https://github.com/ant-design/ant-design/pull/54970) [@guoyunhe](https://github.com/guoyunhe)
- 🐞 修复 Button 仅图标icon-only按钮的内边距受主题影响的问题。 [#54970](https://github.com/ant-design/ant-design/pull/54970) [@guoyunhe](https://github.com/guoyunhe)
- 🐞 修复 Splitter 在非受控模式下初次挂载时,`minSize``maxSize` 未生效的问题。[#54939](https://github.com/ant-design/ant-design/pull/54939) [@zombieJ](https://github.com/zombieJ)
- 🐞 修复 Switch 波纹效果与 Tailwind CSS disabled 变体的兼容性问题。[#54933](https://github.com/ant-design/ant-design/pull/54933) [@Jiyur](https://github.com/Jiyur)
- 🐞 修复 Input.Search 在搜索按钮为 `disabled` 时,悬停仍会导致边框和图标变色的问题。[#54892](https://github.com/ant-design/ant-design/pull/54892) [@Jiyur](https://github.com/Jiyur)

View File

@@ -15,8 +15,7 @@
"!scripts/previewEditor/**/*",
"!**/*.tmp",
"!package.json",
"!components/style/antd.css",
"!scripts/visual-regression/report-template.html"
"!components/style/antd.css"
]
},
"formatter": {
@@ -58,7 +57,6 @@
"noExplicitAny": "off",
"noArrayIndexKey": "off",
"noConfusingVoidType": "off",
"noNonNullAssertedOptionalChain": "off",
"noThenProperty": "off",
"noTemplateCurlyInString": "off",
"useIterableCallbackReturn": "off",

View File

@@ -67,7 +67,7 @@ const genPurePanel = <ComponentProps extends BaseProps = BaseProps>(
resizeObserver.disconnect();
};
}
}, [prefixCls]);
}, []);
let mergedProps: WrapProps = {
...props,

View File

@@ -2,8 +2,8 @@ import React, { useEffect } from 'react';
import { CloseOutlined } from '@ant-design/icons';
import { render } from '@testing-library/react';
import { useClosable } from '../hooks';
import type { UseClosableParams } from '../hooks/useClosable';
import useClosable from '../hooks/useClosable';
type ParamsOfUseClosable = [
closable: UseClosableParams['closable'],

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { fireEvent, render } from '../../../tests/utils';
import { useSyncState } from '../hooks';
import useSyncState from '../hooks/useSyncState';
describe('Table', () => {
it('useSyncState', () => {

View File

@@ -0,0 +1,56 @@
import React from 'react';
import { act, render } from '../../../tests/utils';
import useUniqueMemo from '../hooks/useUniqueMemo';
describe('Table', () => {
beforeEach(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.clearAllTimers();
jest.useRealTimers();
});
it('useSyncState', () => {
const sharedObjDeps1 = {};
const sharedObjDeps2 = {};
let calledTimes = 0;
const Test: React.FC<{ depName?: string }> = ({ depName }) => {
useUniqueMemo(() => {
calledTimes += 1;
return depName;
}, [depName, sharedObjDeps1, 'bamboo', sharedObjDeps2]);
return null;
};
// Reuse the same memo
const { rerender } = render(
<>
<Test depName="light" />
<Test depName="light" />
<Test depName="light" />
</>,
);
expect(calledTimes).toBe(1);
// Different deps should clean up the cache
act(() => {
jest.advanceTimersByTime(1000 * 60 * 20);
});
for (let i = 0; i < 20000; i += 1) {
rerender(<Test depName="diff" key={i} />);
}
rerender(<Test depName="clear" />);
calledTimes = 0;
// Back should recompute
rerender(<Test depName="light" />);
expect(calledTimes).toBe(1);
});
});

View File

@@ -300,7 +300,7 @@ describe('Test useZIndex hooks', () => {
if (['SelectLike', 'DatePicker', 'ImagePreview'].includes(key)) {
let comps = document.querySelectorAll<HTMLElement>(selector1);
comps.forEach((comp) => {
expect(comp).toHaveStyle({ zIndex: '' });
expect(comp?.style.zIndex).toBeFalsy();
});
comps = document.querySelectorAll<HTMLElement>(selector2);
comps.forEach((comp) => {
@@ -311,9 +311,9 @@ describe('Test useZIndex hooks', () => {
const operOffset = comp.classList.contains('ant-image-preview-operations-wrapper')
? 1
: 0;
expect(comp).toHaveStyle({
zIndex: 1000 + containerZIndexValue + consumerOffset + operOffset,
});
expect(comp?.style.zIndex).toBe(
String(1000 + containerZIndexValue + consumerOffset + operOffset),
);
});
comps = document.querySelectorAll<HTMLElement>(selector3);
@@ -325,21 +325,21 @@ describe('Test useZIndex hooks', () => {
const operOffset = comp.classList.contains('ant-image-preview-operations-wrapper')
? 1
: 0;
expect(comp).toHaveStyle({
zIndex: 1000 + containerZIndexValue * 2 + consumerOffset + operOffset,
});
expect(comp?.style.zIndex).toBe(
String(1000 + containerZIndexValue * 2 + consumerOffset + operOffset),
);
});
} else {
const element1 = document.querySelector<HTMLElement>(selector1);
const element2 = document.querySelector<HTMLElement>(selector2);
const element3 = document.querySelector<HTMLElement>(selector3);
expect(element1).toHaveStyle({ zIndex: key === 'Tour' ? 1001 : '' });
expect(element2).toHaveStyle({
zIndex: 1000 + containerZIndexValue + consumerZIndexValue,
});
expect(element3).toHaveStyle({
zIndex: 1000 + containerZIndexValue * 2 + consumerZIndexValue,
});
expect(element1?.style.zIndex).toBe(key === 'Tour' ? '1001' : '');
expect(element2?.style.zIndex).toBe(
String(1000 + containerZIndexValue + consumerZIndexValue),
);
expect(element3?.style.zIndex).toBe(
String(1000 + containerZIndexValue * 2 + consumerZIndexValue),
);
}
unmount();
}, 20000);
@@ -399,14 +399,16 @@ describe('Test useZIndex hooks', () => {
<FloatButton />
</WrapWithProvider>,
);
const ele = container.querySelector<HTMLElement>('.ant-float-btn');
expect(ele).toHaveStyle({ zIndex: 1100 + containerBaseZIndexOffset.FloatButton });
expect(container.querySelector<HTMLElement>('.ant-float-btn')?.style.zIndex).toBe(
// parentZIndex + containerBaseZIndexOffset["FloatButton"]
String(1100 + containerBaseZIndexOffset.FloatButton),
);
rerender(
<WrapWithProvider container="FloatButton">
<FloatButton style={{ zIndex: 666 }} />
</WrapWithProvider>,
);
expect(ele).toHaveStyle({ zIndex: 666 });
expect(container.querySelector<HTMLElement>('.ant-float-btn')?.style.zIndex).toBe(String(666));
});
it('not warning for static func', () => {

View File

@@ -1,8 +0,0 @@
export * from './useClosable';
export * from './useForceUpdate';
export * from './useMergeSemantic';
export * from './useMultipleSelect';
export * from './usePatchElement';
export * from './useProxyImperativeHandle';
export * from './useSyncState';
export * from './useZIndex';

View File

@@ -56,7 +56,10 @@ function useClosableConfig(closableCollection?: ClosableCollection | null) {
closeIcon: typeof closeIcon !== 'boolean' && closeIcon !== null ? closeIcon : undefined,
};
if (closable && typeof closable === 'object') {
closableConfig = { ...closableConfig, ...closable };
closableConfig = {
...closableConfig,
...closable,
};
}
return closableConfig;
}, [closable, closeIcon]);
@@ -79,7 +82,7 @@ interface FallbackCloseCollection extends ClosableCollection {
/** Use same object to support `useMemo` optimization */
const EmptyFallbackCloseCollection: FallbackCloseCollection = {};
export const useClosable = (
export default function useClosable(
propCloseCollection?: ClosableCollection,
contextCloseCollection?: ClosableCollection | null,
fallbackCloseCollection: FallbackCloseCollection = EmptyFallbackCloseCollection,
@@ -88,7 +91,7 @@ export const useClosable = (
closeIcon: React.ReactNode,
closeBtnIsDisabled: boolean,
ariaOrDataProps?: HTMLAriaDataAttributes,
] => {
] {
// Align the `props`, `context` `fallback` to config object first
const propCloseConfig = useClosableConfig(propCloseCollection);
const contextCloseConfig = useClosableConfig(contextCloseCollection);
@@ -169,4 +172,4 @@ export const useClosable = (
mergedClosableConfig,
mergedFallbackCloseCollection,
]);
};
}

View File

@@ -1,5 +1,6 @@
import React from 'react';
import * as React from 'react';
export const useForceUpdate = () => {
return React.useReducer((ori) => ori + 1, 0);
};
export default function useForceUpdate() {
const [, forceUpdate] = React.useReducer((x) => x + 1, 0);
return forceUpdate;
}

View File

@@ -1,11 +1,13 @@
import * as React from 'react';
import classnames from 'classnames';
import type { ValidChar } from '../type';
import type { ValidChar } from './interface';
type TemplateSemanticClassNames<T extends string> = Partial<Record<T, string>>;
export type SemanticSchema = { _default?: string } & {
export type SemanticSchema = {
_default?: string;
} & {
[key: `${ValidChar}${string}`]: SemanticSchema;
};
@@ -13,7 +15,7 @@ export type SemanticSchema = { _default?: string } & {
export function mergeClassNames<
T extends string,
SemanticClassNames extends Partial<Record<T, any>> = TemplateSemanticClassNames<T>,
>(schema?: SemanticSchema, ...classNames: (SemanticClassNames | undefined)[]) {
>(schema: SemanticSchema | undefined, ...classNames: (SemanticClassNames | undefined)[]) {
const mergedSchema = schema || {};
return classNames.reduce((acc: any, cur) => {
@@ -29,10 +31,8 @@ export function mergeClassNames<
} else {
// Covert string to object structure
const { _default: defaultField } = keySchema;
if (defaultField) {
acc[key] = acc[key] || {};
acc[key][defaultField] = classnames(acc[key][defaultField], curVal);
}
acc[key] = acc[key] || {};
acc[key][defaultField!] = classnames(acc[key][defaultField!], curVal);
}
} else {
// Flatten fill
@@ -40,16 +40,16 @@ export function mergeClassNames<
}
});
return acc;
}, {} as SemanticClassNames);
}, {} as SemanticClassNames) as SemanticClassNames;
}
function useSemanticClassNames<ClassNamesType extends object>(
schema?: SemanticSchema,
schema: SemanticSchema | undefined,
...classNames: (Partial<ClassNamesType> | undefined)[]
): Partial<ClassNamesType> {
return React.useMemo(
() => mergeClassNames(schema, ...classNames),
[classNames, schema],
[classNames],
) as ClassNamesType;
}
@@ -58,25 +58,31 @@ function useSemanticStyles<StylesType extends object>(
...styles: (Partial<StylesType> | undefined)[]
) {
return React.useMemo(() => {
return styles.reduce<Record<string, React.CSSProperties>>((acc, cur = {}) => {
Object.keys(cur).forEach((key) => {
acc[key] = { ...acc[key], ...(cur as Record<string, React.CSSProperties>)[key] };
});
return acc;
}, {});
return styles.reduce(
(acc, cur = {}) => {
Object.keys(cur).forEach((key) => {
acc[key] = { ...acc[key], ...(cur as Record<string, React.CSSProperties>)[key] };
});
return acc;
},
{} as Record<string, React.CSSProperties>,
);
}, [styles]) as StylesType;
}
// =========================== Export ===========================
function fillObjectBySchema<T extends object>(obj: T, schema: SemanticSchema): T {
const newObj: any = { ...obj };
Object.keys(schema).forEach((key) => {
if (key !== '_default') {
const nestSchema = (schema as any)[key] as SemanticSchema;
const nextValue = newObj[key] || {};
newObj[key] = nestSchema ? fillObjectBySchema(nextValue, nestSchema) : nextValue;
}
});
return newObj;
}
@@ -84,17 +90,18 @@ function fillObjectBySchema<T extends object>(obj: T, schema: SemanticSchema): T
* Merge classNames and styles from multiple sources.
* When `schema` is provided, it will **must** provide the nest object structure.
*/
export const useMergeSemantic = <ClassNamesType extends object, StylesType extends object>(
export default function useMergeSemantic<ClassNamesType extends object, StylesType extends object>(
classNamesList: (ClassNamesType | undefined)[],
stylesList: (StylesType | undefined)[],
schema?: SemanticSchema,
) => {
) {
const mergedClassNames = useSemanticClassNames(schema, ...classNamesList) as ClassNamesType;
const mergedStyles = useSemanticStyles(...stylesList) as StylesType;
return React.useMemo(() => {
return [
fillObjectBySchema(mergedClassNames, schema!) as ClassNamesType,
fillObjectBySchema(mergedStyles, schema!) as StylesType,
] as const;
}, [mergedClassNames, mergedStyles, schema]);
};
}, [mergedClassNames, mergedStyles]);
}

View File

@@ -0,0 +1,27 @@
export type ValidChar =
| 'a'
| 'b'
| 'c'
| 'd'
| 'e'
| 'f'
| 'g'
| 'h'
| 'i'
| 'j'
| 'k'
| 'l'
| 'm'
| 'n'
| 'o'
| 'p'
| 'q'
| 'r'
| 's'
| 't'
| 'u'
| 'v'
| 'w'
| 'x'
| 'y'
| 'z';

View File

@@ -6,7 +6,7 @@ export type PrevSelectedIndex = null | number;
* @title multipleSelect hooks
* @description multipleSelect by hold down shift key
*/
export const useMultipleSelect = <T, K>(getKey: (item: T, index: number, array: T[]) => K) => {
export default function useMultipleSelect<T, K>(getKey: (item: T) => K) {
const [prevSelectedIndex, setPrevSelectedIndex] = useState<PrevSelectedIndex>(null);
const multipleSelect = useCallback(
@@ -16,7 +16,7 @@ export const useMultipleSelect = <T, K>(getKey: (item: T, index: number, array:
// add/delete the selected range
const startIndex = Math.min(configPrevSelectedIndex || 0, currentSelectedIndex);
const endIndex = Math.max(configPrevSelectedIndex || 0, currentSelectedIndex);
const rangeKeys = data.slice(startIndex, endIndex + 1).map<K>(getKey);
const rangeKeys = data.slice(startIndex, endIndex + 1).map((item) => getKey(item));
const shouldSelected = rangeKeys.some((rangeKey) => !selectedKeys.has(rangeKey));
const changedKeys: K[] = [];
@@ -39,5 +39,9 @@ export const useMultipleSelect = <T, K>(getKey: (item: T, index: number, array:
[prevSelectedIndex],
);
return [multipleSelect, setPrevSelectedIndex] as const;
};
const updatePrevSelectedIndex = (val: PrevSelectedIndex) => {
setPrevSelectedIndex(val);
};
return [multipleSelect, updatePrevSelectedIndex] as const;
}

View File

@@ -1,9 +1,9 @@
import * as React from 'react';
export const usePatchElement = (): [
export default function usePatchElement(): [
React.ReactElement[],
(element: React.ReactElement) => () => void,
] => {
] {
const [elements, setElements] = React.useState<React.ReactElement[]>([]);
const patchElement = React.useCallback((element: React.ReactElement) => {
@@ -18,4 +18,4 @@ export const usePatchElement = (): [
}, []);
return [elements, patchElement];
};
}

View File

@@ -22,13 +22,10 @@ function fillProxy(
return element;
}
export const useProxyImperativeHandle = <
export default function useProxyImperativeHandle<
NativeELementType extends HTMLElement,
ReturnRefType extends { nativeElement: NativeELementType },
>(
ref: Ref<any> | undefined,
init: () => ReturnRefType,
) => {
>(ref: Ref<any> | undefined, init: () => ReturnRefType) {
return useImperativeHandle(ref, () => {
const refObj = init();
const { nativeElement } = refObj;
@@ -48,4 +45,4 @@ export const useProxyImperativeHandle = <
// Fallback of IE
return fillProxy(nativeElement, refObj);
});
};
}

View File

@@ -1,12 +1,13 @@
import * as React from 'react';
import { useForceUpdate } from './useForceUpdate';
import useForceUpdate from './useForceUpdate';
type UseSyncStateProps<T> = readonly [() => T, (newValue: T) => void];
export const useSyncState = <T>(initialValue: T): UseSyncStateProps<T> => {
export default function useSyncState<T>(initialValue: T): UseSyncStateProps<T> {
const ref = React.useRef<T>(initialValue);
const [, forceUpdate] = useForceUpdate();
const forceUpdate = useForceUpdate();
return [
() => ref.current,
(newValue: T) => {
@@ -15,4 +16,4 @@ export const useSyncState = <T>(initialValue: T): UseSyncStateProps<T> => {
forceUpdate();
},
] as const;
};
}

View File

@@ -0,0 +1,97 @@
import React from 'react';
const BEAT_LIMIT = 1000 * 60 * 10;
/**
* A helper class to map keys to values.
* It supports both primitive keys and object keys.
*/
class ArrayKeyMap<T> {
map = new Map<string, T>();
// Use WeakMap to avoid memory leak
objectIDMap = new WeakMap<object, number>();
nextID = 0;
lastAccessBeat = new Map<string, number>();
// We will clean up the cache when reach the limit
accessBeat = 0;
set(keys: React.DependencyList, value: any) {
// New set will trigger clear
this.clear();
// Set logic
const compositeKey = this.getCompositeKey(keys);
this.map.set(compositeKey, value);
this.lastAccessBeat.set(compositeKey, Date.now());
}
get(keys: React.DependencyList) {
const compositeKey = this.getCompositeKey(keys);
const cache = this.map.get(compositeKey);
this.lastAccessBeat.set(compositeKey, Date.now());
this.accessBeat += 1;
return cache;
}
getCompositeKey(keys: React.DependencyList) {
const ids = keys.map<string>((key) => {
if (key && typeof key === 'object') {
return `obj_${this.getObjectID(key)}`;
}
return `${typeof key}_${key}`;
});
return ids.join('|');
}
getObjectID(obj: object) {
if (this.objectIDMap.has(obj)) {
return this.objectIDMap.get(obj);
}
const id = this.nextID;
this.objectIDMap.set(obj, id);
this.nextID += 1;
return id;
}
clear() {
if (this.accessBeat > 10000) {
const now = Date.now();
this.lastAccessBeat.forEach((beat, key) => {
if (now - beat > BEAT_LIMIT) {
this.map.delete(key);
this.lastAccessBeat.delete(key);
}
});
this.accessBeat = 0;
}
}
}
const uniqueMap = new ArrayKeyMap();
/**
* Like `useMemo`, but this hook result will be shared across all instances.
*/
function useUniqueMemo<T>(memoFn: () => T, deps: React.DependencyList) {
return React.useMemo<T>(() => {
const cachedValue = uniqueMap.get(deps);
if (cachedValue) {
return cachedValue as T;
}
const newValue = memoFn();
uniqueMap.set(deps, newValue);
return newValue;
}, deps);
}
export default useUniqueMemo;

View File

@@ -140,7 +140,7 @@ const useResponsiveObserver = () => {
subscribers.clear();
},
};
}, [responsiveMap]);
}, [token]);
};
export default useResponsiveObserver;

View File

@@ -1,14 +1,10 @@
import type React from 'react';
export type Primitive = null | undefined | string | number | boolean | symbol | bigint;
/** https://github.com/Microsoft/TypeScript/issues/29729 */
export type LiteralUnion<T, U extends Primitive = string> = T | (U & Record<never, never>);
export type LiteralUnion<T extends string> = T | (string & {});
export type AnyObject = Record<PropertyKey, any>;
export type EmptyObject = Record<never, never>;
export type CustomComponent<P = AnyObject> = React.ComponentType<P> | string;
/**
@@ -82,31 +78,3 @@ export type GetContextProp<
T extends React.Context<any>,
PropName extends keyof GetContextProps<T>,
> = NonNullable<GetContextProps<T>[PropName]>;
export type ValidChar =
| 'a'
| 'b'
| 'c'
| 'd'
| 'e'
| 'f'
| 'g'
| 'h'
| 'i'
| 'j'
| 'k'
| 'l'
| 'm'
| 'n'
| 'o'
| 'p'
| 'q'
| 'r'
| 's'
| 't'
| 'u'
| 'v'
| 'w'
| 'x'
| 'y'
| 'z';

View File

@@ -55,10 +55,7 @@ export interface AffixRef {
updatePosition: ReturnType<typeof throttleByAnimationFrame>;
}
interface InternalAffixProps extends AffixProps {
onTestUpdatePosition?: () => void;
}
type InternalAffixProps = AffixProps & { onTestUpdatePosition?: any };
const Affix = React.forwardRef<AffixRef, InternalAffixProps>((props, ref) => {
const {
style,

View File

@@ -10,7 +10,7 @@ import CSSMotion from 'rc-motion';
import pickAttrs from 'rc-util/lib/pickAttrs';
import { composeRef } from 'rc-util/lib/ref';
import type { ClosableType } from '../_util/hooks';
import type { ClosableType } from '../_util/hooks/useClosable';
import { replaceElement } from '../_util/reactNode';
import { devUseWarning } from '../_util/warning';
import { useComponentConfig } from '../config-provider/context';
@@ -176,9 +176,7 @@ const Alert = React.forwardRef<AlertRef, AlertProps>((props, ref) => {
// closeable when closeText or closeIcon is assigned
const isClosable = React.useMemo<boolean>(() => {
if (typeof closable === 'object' && closable.closeIcon) {
return true;
}
if (typeof closable === 'object' && closable.closeIcon) return true;
if (closeText) {
return true;
}
@@ -228,7 +226,7 @@ const Alert = React.forwardRef<AlertRef, AlertProps>((props, ref) => {
return contextClosable.closeIcon;
}
return contextCloseIcon;
}, [closeIcon, closable, contextClosable, closeText, contextCloseIcon]);
}, [closeIcon, closable, closeText, contextCloseIcon]);
const mergedAriaProps = React.useMemo<React.AriaAttributes>(() => {
const merged = closable ?? contextClosable;

View File

@@ -1,7 +1,7 @@
## zh-CN
友好的 [React 错误处理](https://zh-hans.react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary) 包裹组件。
友好的 [React 错误处理](https://reactjs.org/blog/2017/07/26/error-handling-in-react-16.html) 包裹组件。
## en-US
ErrorBoundary Component for making error handling easier in [React](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary).
ErrorBoundary Component for making error handling easier in [React](https://reactjs.org/blog/2017/07/26/error-handling-in-react-16.html).

View File

@@ -246,7 +246,7 @@ const Anchor: React.FC<AnchorProps> = (props) => {
);
setCurrentActiveLink(currentActiveLink);
}, [links, targetOffset, offsetTop, bounds]);
}, [dependencyListItem, targetOffset, offsetTop]);
const handleScrollTo = React.useCallback<(link: string) => void>(
(link) => {

View File

@@ -185,7 +185,7 @@ describe('Anchor Render', () => {
const link = container.querySelector(`a[href="http://www.example.com/#${hash}"]`)!;
fireEvent.click(link);
await waitFakeTimer();
expect(link).toHaveClass('ant-anchor-link-title-active');
expect(link.classList).toContain('ant-anchor-link-title-active');
});
it('scrolls the page when clicking a link', async () => {
@@ -737,9 +737,11 @@ describe('Anchor Render', () => {
/>,
);
expect(container.querySelectorAll('.ant-anchor-ink').length).toBe(1);
expect(container.querySelector('.ant-anchor-wrapper')).toHaveClass(
'ant-anchor-wrapper-horizontal',
);
expect(
container
.querySelector('.ant-anchor-wrapper')
?.classList.contains('ant-anchor-wrapper-horizontal'),
).toBeTruthy();
});
it('nested children via items should be filtered out when direction is horizontal', () => {
@@ -816,7 +818,7 @@ describe('Anchor Render', () => {
const link = container.querySelector(`a[href="http://www.example.com/#${hash}"]`)!;
fireEvent.click(link);
await waitFakeTimer();
expect(link).toHaveClass('ant-anchor-link-title-active');
expect(link.classList).toContain('ant-anchor-link-title-active');
});
it('scrolls the page when clicking a link', async () => {
@@ -1024,29 +1026,25 @@ describe('Anchor Render', () => {
</div>
);
};
const { container, findByText } = await render(<Foo />);
(await findByText('part-1')).click();
const wrapper = await render(<Foo />);
(await wrapper.findByText('part-1')).click();
await waitFakeTimer();
const inkElement = container.querySelector<HTMLSpanElement>('.ant-anchor-ink');
const toggleButton = container.querySelector<HTMLElement>('button');
const ink = wrapper.container.querySelector<HTMLSpanElement>('.ant-anchor-ink')!;
const toggleButton = wrapper.container.querySelector('button')!;
expect(toggleButton).toBeInTheDocument();
fireEvent.click(toggleButton!);
fireEvent.click(toggleButton);
act(() => jest.runAllTimers());
expect(!!ink.style.left).toBe(true);
expect(!!ink.style.width).toBe(true);
expect(ink.style.top).toBe('');
expect(ink.style.height).toBe('');
expect(inkElement).toHaveStyle({
left: '0px',
width: '0px',
});
fireEvent.click(toggleButton!);
fireEvent.click(toggleButton);
act(() => jest.runAllTimers());
expect(inkElement).toHaveStyle({
top: '0px',
height: '0px',
});
expect(!!ink.style.top).toBe(true);
expect(!!ink.style.height).toBe(true);
expect(ink.style.left).toBe('');
expect(ink.style.width).toBe('');
});
});
});

View File

@@ -4,7 +4,7 @@ import type { BaseSelectRef } from 'rc-select';
import toArray from 'rc-util/lib/Children/toArray';
import omit from 'rc-util/lib/omit';
import { useZIndex } from '../_util/hooks';
import { useZIndex } from '../_util/hooks/useZIndex';
import type { InputStatus } from '../_util/statusUtils';
import { devUseWarning } from '../_util/warning';
import type { ConfigConsumerProps } from '../config-provider';
@@ -24,7 +24,6 @@ export interface DataSourceItemObject {
value: string;
text: string;
}
export type DataSourceItemType = DataSourceItemObject | React.ReactNode;
export interface AutoCompleteProps<

View File

@@ -1096,11 +1096,7 @@ exports[`renders components/auto-complete/demo/certain-category.tsx extend conte
</div>
`;
exports[`renders components/auto-complete/demo/certain-category.tsx extend context correctly 2`] = `
[
"Warning: [antd: Input] \`addonAfter\` is deprecated. Please use \`Space.Compact\` instead.",
]
`;
exports[`renders components/auto-complete/demo/certain-category.tsx extend context correctly 2`] = `[]`;
exports[`renders components/auto-complete/demo/custom.tsx extend context correctly 1`] = `
<div
@@ -2428,7 +2424,6 @@ exports[`renders components/auto-complete/demo/form-debug.tsx extend context cor
exports[`renders components/auto-complete/demo/form-debug.tsx extend context correctly 2`] = `
[
"Warning: [antd: Input.Group] \`Input.Group\` is deprecated. Please use \`Space.Compact\` instead.",
"Warning: [antd: Input] \`addonAfter\` is deprecated. Please use \`Space.Compact\` instead.",
]
`;
@@ -2987,11 +2982,7 @@ exports[`renders components/auto-complete/demo/uncertain-category.tsx extend con
</div>
`;
exports[`renders components/auto-complete/demo/uncertain-category.tsx extend context correctly 2`] = `
[
"Warning: [antd: Input] \`addonAfter\` is deprecated. Please use \`Space.Compact\` instead.",
]
`;
exports[`renders components/auto-complete/demo/uncertain-category.tsx extend context correctly 2`] = `[]`;
exports[`renders components/auto-complete/demo/variant.tsx extend context correctly 1`] = `
<div

View File

@@ -46,7 +46,8 @@ Common props ref[Common props](/docs/react/common-props)
| allowClear | Show clear button | boolean \| { clearIcon?: ReactNode } | false | 5.8.0: Support Object type |
| autoFocus | If get focus when component mounted | boolean | false | |
| backfill | If backfill selected item the input when using keyboard | boolean | false | |
| children | Customize input element | HTMLInputElement \| HTMLTextAreaElement \| React.ReactElement&lt;InputProps> | &lt;Input /> | |
| children (for customize input element) | Customize input element | HTMLInputElement \| HTMLTextAreaElement \| React.ReactElement&lt;InputProps> | &lt;Input /> | |
| children (for dataSource) | Data source to auto complete | React.ReactElement&lt;OptionProps> \| Array&lt;React.ReactElement&lt;OptionProps>> | - | |
| classNames | Semantic DOM class | [Record<SemanticDOM, string>](#semantic-dom) | - | 5.25.0 |
| defaultActiveFirstOption | Whether active first option by default | boolean | true | |
| defaultOpen | Initial open state of dropdown | boolean | - | |

View File

@@ -47,7 +47,8 @@ demo:
| allowClear | 支持清除 | boolean \| { clearIcon?: ReactNode } | false | 5.8.0: 支持对象形式 |
| autoFocus | 自动获取焦点 | boolean | false | |
| backfill | 使用键盘选择选项的时候把选中项回填到输入框中 | boolean | false | |
| children | 自定义输入框 | HTMLInputElement \| HTMLTextAreaElement \| React.ReactElement&lt;InputProps> | &lt;Input /> | |
| children (自动完成的数据源) | 自动完成的数据源,不能和自定义输入框同时配置 | React.ReactElement&lt;OptionProps> \| Array&lt;React.ReactElement&lt;OptionProps>> | - | |
| children (自定义输入框) | 自定义输入框,不能和自动完成的数据源同时配置 | HTMLInputElement \| HTMLTextAreaElement \| React.ReactElement&lt;InputProps> | &lt;Input /> | |
| classNames | 语义化结构 class | [Record<SemanticDOM, string>](#semantic-dom) | - | 5.25.0 |
| defaultActiveFirstOption | 是否默认高亮第一个选项 | boolean | true | |
| defaultOpen | 是否默认展开下拉菜单 | boolean | - | |

View File

@@ -129,7 +129,7 @@ const Avatar = React.forwardRef<HTMLSpanElement, AvatarProps>((props, ref) => {
fontSize: currentSize && (icon || children) ? currentSize / 2 : 18,
}
: {};
}, [screens, size, icon, children]);
}, [screens, size]);
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning('Avatar');
@@ -205,7 +205,11 @@ const Avatar = React.forwardRef<HTMLSpanElement, AvatarProps>((props, ref) => {
childrenToRender = (
<ResizeObserver onResize={setScaleParam}>
<span className={`${prefixCls}-string`} ref={avatarChildrenRef} style={childrenStyle}>
<span
className={`${prefixCls}-string`}
ref={avatarChildrenRef}
style={{ ...childrenStyle }}
>
{children}
</span>
</ResizeObserver>

View File

@@ -146,11 +146,11 @@ describe('Badge', () => {
// https://github.com/ant-design/ant-design/issues/15349
it('should color style works on Badge', () => {
const { container } = render(
<Badge style={{ color: 'rgb(255, 0, 0)' }} status="success" text="Success" />,
<Badge style={{ color: 'red' }} status="success" text="Success" />,
);
expect((container.querySelector('.ant-badge-status-text')! as HTMLElement).style.color).toEqual(
'red',
);
expect(container.querySelector<HTMLElement>('.ant-badge-status-text')).toHaveStyle({
color: 'rgb(255, 0, 0)',
});
});
// https://github.com/ant-design/ant-design/issues/15799
@@ -247,8 +247,14 @@ describe('Badge', () => {
const { container } = render(
<Badge
count={10}
classNames={{ root: 'test-root', indicator: 'test-indicator' }}
styles={{ root: { padding: 10 }, indicator: { padding: 20 } }}
classNames={{
root: 'test-root',
indicator: 'test-indicator',
}}
styles={{
root: { backgroundColor: 'yellow' },
indicator: { backgroundColor: 'blue' },
}}
>
test
</Badge>,
@@ -261,7 +267,9 @@ describe('Badge', () => {
expect(element?.querySelector<HTMLElement>('sup')).toHaveClass('test-indicator');
// styles
expect(element).toHaveStyle({ padding: '10px' });
expect(element?.querySelector<HTMLElement>('sup')).toHaveStyle({ padding: '20px' });
expect(element).toHaveStyle({ backgroundColor: 'rgb(255, 255, 0)' });
expect(element?.querySelector<HTMLElement>('sup')).toHaveStyle({
backgroundColor: 'rgb(0, 0, 255)',
});
});
});

View File

@@ -37,28 +37,28 @@ describe('Ribbon', () => {
expect(container.querySelectorAll('.ant-ribbon-color-green').length).toEqual(1);
});
it('works with custom color', () => {
const { container, rerender } = render(
<Badge.Ribbon color="rgb(136, 136, 136)" placement="start">
const { container: wrapperLeft } = render(
<Badge.Ribbon color="#888" placement="start">
<div />
</Badge.Ribbon>,
);
expect(container.querySelector<HTMLElement>('.ant-ribbon')).toHaveStyle({
backgroundColor: 'rgb(136, 136, 136)',
});
expect(container.querySelector<HTMLElement>('.ant-ribbon-corner')).toHaveStyle({
color: 'rgb(136, 136, 136)',
});
rerender(
<Badge.Ribbon color="rgb(136, 136, 136)" placement="end">
expect((wrapperLeft.querySelector('.ant-ribbon')! as HTMLElement).style.background).toEqual(
'rgb(136, 136, 136)',
);
expect((wrapperLeft.querySelector('.ant-ribbon-corner')! as HTMLElement).style.color).toEqual(
'rgb(136, 136, 136)',
);
const { container: wrapperRight } = render(
<Badge.Ribbon color="#888" placement="end">
<div />
</Badge.Ribbon>,
);
expect(container.querySelector<HTMLElement>('.ant-ribbon')).toHaveStyle({
backgroundColor: 'rgb(136, 136, 136)',
});
expect(container.querySelector<HTMLElement>('.ant-ribbon-corner')).toHaveStyle({
color: 'rgb(136, 136, 136)',
});
expect((wrapperRight.querySelector('.ant-ribbon')! as HTMLElement).style.background).toEqual(
'rgb(136, 136, 136)',
);
expect(
(wrapperRight.querySelector('.ant-ribbon-corner')! as HTMLElement).style.color,
).toEqual('rgb(136, 136, 136)');
});
});

View File

@@ -2014,11 +2014,7 @@ Array [
]
`;
exports[`renders components/button/demo/debug-icon.tsx extend context correctly 2`] = `
[
"Warning: [antd: Input] \`addonAfter\` is deprecated. Please use \`Space.Compact\` instead.",
]
`;
exports[`renders components/button/demo/debug-icon.tsx extend context correctly 2`] = `[]`;
exports[`renders components/button/demo/disabled.tsx extend context correctly 1`] = `
<div

View File

@@ -1,8 +1,8 @@
import React, { Children, useContext, useEffect, useMemo, useRef, useState } from 'react';
import classNames from 'classnames';
import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect';
import omit from 'rc-util/lib/omit';
import { useComposeRef } from 'rc-util/lib/ref';
import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect';
import { devUseWarning } from '../_util/warning';
import Wave from '../_util/wave';
@@ -154,7 +154,7 @@ const InternalCompoundedButton = React.forwardRef<
}
return ['default', 'outlined'];
}, [color, variant, type, danger, button?.color, button?.variant, mergedType]);
}, [type, color, variant, danger, button?.variant, button?.color]);
const isDanger = mergedColor === 'danger';
const mergedColorText = isDanger ? 'dangerous' : mergedColor;

View File

@@ -1,5 +1,8 @@
import React from 'react';
import Dayjs from 'dayjs';
import 'dayjs/locale/zh-cn';
import React from 'react';
import MockDate from 'mockdate';
import type { PickerPanelProps } from 'rc-picker';
import dayjsGenerateConfig from 'rc-picker/lib/generate/dayjs';
@@ -14,12 +17,9 @@ import ConfigProvider from '../../config-provider';
import Group from '../../radio/group';
import Button from '../../radio/radioButton';
import Select from '../../select';
import type { DefaultOptionType } from '../../select';
import Header from '../Header';
import type { CalendarHeaderProps } from '../Header';
import 'dayjs/locale/zh-cn';
const ref: {
calendarProps?: PickerPanelProps;
calendarHeaderProps?: CalendarHeaderProps<unknown>;
@@ -375,10 +375,15 @@ describe('Calendar', () => {
// Year
const headerRender = jest.fn(({ value }) => {
const year = value.year();
const options: DefaultOptionType[] = [];
const options = [];
for (let i = year - 100; i < year + 100; i += 1) {
options.push({ label: i, value: i });
options.push(
<Select.Option className="year-item" key={i} value={i}>
{i}
</Select.Option>,
);
}
return (
<Select
size="small"
@@ -386,8 +391,9 @@ describe('Calendar', () => {
className="my-year-select"
onChange={onYearChange}
value={String(year)}
options={options}
/>
>
{options}
</Select>
);
});
const uiWithYear = <Calendar fullscreen={false} headerRender={headerRender} />;
@@ -406,17 +412,23 @@ describe('Calendar', () => {
const headerRenderWithMonth = jest.fn(({ value }) => {
const start = 0;
const end = 12;
const months: string[] = [];
const monthOptions: DefaultOptionType[] = [];
const monthOptions = [];
const current = value.clone();
const localeData = value.localeData();
const months = [];
for (let i = 0; i < 12; i += 1) {
current.month(i);
months.push(localeData.monthsShort(current));
}
for (let index = start; index < end; index += 1) {
monthOptions.push({ label: months[index], value: index });
monthOptions.push(
<Select.Option className="month-item" key={index} value={index}>
{months[index]}
</Select.Option>,
);
}
const month = value.month();
return (
<Select
@@ -425,8 +437,9 @@ describe('Calendar', () => {
className="my-month-select"
onChange={onMonthChange}
value={String(month)}
options={monthOptions}
/>
>
{monthOptions}
</Select>
);
});
const uiWithMonth = <Calendar fullscreen={false} headerRender={headerRenderWithMonth} />;

View File

@@ -65,12 +65,8 @@ const App: React.FC = () => {
};
const cellRender: CalendarProps<Dayjs>['cellRender'] = (current, info) => {
if (info.type === 'date') {
return dateCellRender(current);
}
if (info.type === 'month') {
return monthCellRender(current);
}
if (info.type === 'date') return dateCellRender(current);
if (info.type === 'month') return monthCellRender(current);
return info.originNode;
};

View File

@@ -327,7 +327,7 @@ exports[`renders components/carousel/demo/autoplay.tsx extend context correctly
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
4
</h3>
@@ -347,7 +347,7 @@ exports[`renders components/carousel/demo/autoplay.tsx extend context correctly
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
1
</h3>
@@ -367,7 +367,7 @@ exports[`renders components/carousel/demo/autoplay.tsx extend context correctly
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
2
</h3>
@@ -387,7 +387,7 @@ exports[`renders components/carousel/demo/autoplay.tsx extend context correctly
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
3
</h3>
@@ -407,7 +407,7 @@ exports[`renders components/carousel/demo/autoplay.tsx extend context correctly
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
4
</h3>
@@ -427,7 +427,7 @@ exports[`renders components/carousel/demo/autoplay.tsx extend context correctly
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
1
</h3>
@@ -447,7 +447,7 @@ exports[`renders components/carousel/demo/autoplay.tsx extend context correctly
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
2
</h3>
@@ -467,7 +467,7 @@ exports[`renders components/carousel/demo/autoplay.tsx extend context correctly
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
3
</h3>
@@ -487,7 +487,7 @@ exports[`renders components/carousel/demo/autoplay.tsx extend context correctly
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
4
</h3>
@@ -1036,7 +1036,7 @@ exports[`renders components/carousel/demo/dot-duration.tsx extend context correc
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
4
</h3>
@@ -1056,7 +1056,7 @@ exports[`renders components/carousel/demo/dot-duration.tsx extend context correc
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
1
</h3>
@@ -1076,7 +1076,7 @@ exports[`renders components/carousel/demo/dot-duration.tsx extend context correc
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
2
</h3>
@@ -1096,7 +1096,7 @@ exports[`renders components/carousel/demo/dot-duration.tsx extend context correc
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
3
</h3>
@@ -1116,7 +1116,7 @@ exports[`renders components/carousel/demo/dot-duration.tsx extend context correc
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
4
</h3>
@@ -1136,7 +1136,7 @@ exports[`renders components/carousel/demo/dot-duration.tsx extend context correc
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
1
</h3>
@@ -1156,7 +1156,7 @@ exports[`renders components/carousel/demo/dot-duration.tsx extend context correc
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
2
</h3>
@@ -1176,7 +1176,7 @@ exports[`renders components/carousel/demo/dot-duration.tsx extend context correc
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
3
</h3>
@@ -1196,7 +1196,7 @@ exports[`renders components/carousel/demo/dot-duration.tsx extend context correc
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
4
</h3>
@@ -1272,7 +1272,7 @@ exports[`renders components/carousel/demo/fade.tsx extend context correctly 1`]
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
1
</h3>
@@ -1292,7 +1292,7 @@ exports[`renders components/carousel/demo/fade.tsx extend context correctly 1`]
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
2
</h3>
@@ -1312,7 +1312,7 @@ exports[`renders components/carousel/demo/fade.tsx extend context correctly 1`]
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
3
</h3>
@@ -1332,7 +1332,7 @@ exports[`renders components/carousel/demo/fade.tsx extend context correctly 1`]
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
4
</h3>
@@ -1503,7 +1503,7 @@ Array [
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
4
</h3>
@@ -1523,7 +1523,7 @@ Array [
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
1
</h3>
@@ -1543,7 +1543,7 @@ Array [
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
2
</h3>
@@ -1563,7 +1563,7 @@ Array [
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
3
</h3>
@@ -1583,7 +1583,7 @@ Array [
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
4
</h3>
@@ -1603,7 +1603,7 @@ Array [
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
1
</h3>
@@ -1623,7 +1623,7 @@ Array [
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
2
</h3>
@@ -1643,7 +1643,7 @@ Array [
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
3
</h3>
@@ -1663,7 +1663,7 @@ Array [
tabindex="-1"
>
<h3
style="margin: 0px; height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
style="height: 160px; color: rgb(255, 255, 255); line-height: 160px; text-align: center; background: rgb(54, 77, 121);"
>
4
</h3>

View File

@@ -324,7 +324,7 @@ exports[`renders components/carousel/demo/autoplay.tsx correctly 1`] = `
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
4
</h3>
@@ -344,7 +344,7 @@ exports[`renders components/carousel/demo/autoplay.tsx correctly 1`] = `
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
1
</h3>
@@ -364,7 +364,7 @@ exports[`renders components/carousel/demo/autoplay.tsx correctly 1`] = `
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
2
</h3>
@@ -384,7 +384,7 @@ exports[`renders components/carousel/demo/autoplay.tsx correctly 1`] = `
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
3
</h3>
@@ -404,7 +404,7 @@ exports[`renders components/carousel/demo/autoplay.tsx correctly 1`] = `
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
4
</h3>
@@ -424,7 +424,7 @@ exports[`renders components/carousel/demo/autoplay.tsx correctly 1`] = `
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
1
</h3>
@@ -444,7 +444,7 @@ exports[`renders components/carousel/demo/autoplay.tsx correctly 1`] = `
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
2
</h3>
@@ -464,7 +464,7 @@ exports[`renders components/carousel/demo/autoplay.tsx correctly 1`] = `
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
3
</h3>
@@ -484,7 +484,7 @@ exports[`renders components/carousel/demo/autoplay.tsx correctly 1`] = `
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
4
</h3>
@@ -1027,7 +1027,7 @@ exports[`renders components/carousel/demo/dot-duration.tsx correctly 1`] = `
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
4
</h3>
@@ -1047,7 +1047,7 @@ exports[`renders components/carousel/demo/dot-duration.tsx correctly 1`] = `
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
1
</h3>
@@ -1067,7 +1067,7 @@ exports[`renders components/carousel/demo/dot-duration.tsx correctly 1`] = `
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
2
</h3>
@@ -1087,7 +1087,7 @@ exports[`renders components/carousel/demo/dot-duration.tsx correctly 1`] = `
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
3
</h3>
@@ -1107,7 +1107,7 @@ exports[`renders components/carousel/demo/dot-duration.tsx correctly 1`] = `
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
4
</h3>
@@ -1127,7 +1127,7 @@ exports[`renders components/carousel/demo/dot-duration.tsx correctly 1`] = `
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
1
</h3>
@@ -1147,7 +1147,7 @@ exports[`renders components/carousel/demo/dot-duration.tsx correctly 1`] = `
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
2
</h3>
@@ -1167,7 +1167,7 @@ exports[`renders components/carousel/demo/dot-duration.tsx correctly 1`] = `
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
3
</h3>
@@ -1187,7 +1187,7 @@ exports[`renders components/carousel/demo/dot-duration.tsx correctly 1`] = `
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
4
</h3>
@@ -1261,7 +1261,7 @@ exports[`renders components/carousel/demo/fade.tsx correctly 1`] = `
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
1
</h3>
@@ -1281,7 +1281,7 @@ exports[`renders components/carousel/demo/fade.tsx correctly 1`] = `
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
2
</h3>
@@ -1301,7 +1301,7 @@ exports[`renders components/carousel/demo/fade.tsx correctly 1`] = `
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
3
</h3>
@@ -1321,7 +1321,7 @@ exports[`renders components/carousel/demo/fade.tsx correctly 1`] = `
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
4
</h3>
@@ -1490,7 +1490,7 @@ Array [
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
4
</h3>
@@ -1510,7 +1510,7 @@ Array [
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
1
</h3>
@@ -1530,7 +1530,7 @@ Array [
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
2
</h3>
@@ -1550,7 +1550,7 @@ Array [
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
3
</h3>
@@ -1570,7 +1570,7 @@ Array [
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
4
</h3>
@@ -1590,7 +1590,7 @@ Array [
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
1
</h3>
@@ -1610,7 +1610,7 @@ Array [
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
2
</h3>
@@ -1630,7 +1630,7 @@ Array [
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
3
</h3>
@@ -1650,7 +1650,7 @@ Array [
tabindex="-1"
>
<h3
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
>
4
</h3>

View File

@@ -2,7 +2,6 @@ import React from 'react';
import { Carousel } from 'antd';
const contentStyle: React.CSSProperties = {
margin: 0,
height: '160px',
color: '#fff',
lineHeight: '160px',

View File

@@ -2,7 +2,6 @@ import React from 'react';
import { Carousel } from 'antd';
const contentStyle: React.CSSProperties = {
margin: 0,
height: '160px',
color: '#fff',
lineHeight: '160px',

View File

@@ -2,7 +2,6 @@ import React from 'react';
import { Carousel } from 'antd';
const contentStyle: React.CSSProperties = {
margin: 0,
height: '160px',
color: '#fff',
lineHeight: '160px',

View File

@@ -5,7 +5,6 @@ import { Carousel, Radio } from 'antd';
type DotPosition = CarouselProps['dotPosition'];
const contentStyle: React.CSSProperties = {
margin: 0,
height: '160px',
color: '#fff',
lineHeight: '160px',

View File

@@ -1,4 +1,4 @@
import { Keyframes, unit } from '@ant-design/cssinjs';
import { unit } from '@ant-design/cssinjs';
import { resetComponent } from '../../style';
import type { FullToken, GenerateStyle, GetDefaultToken } from '../../theme/internal';
@@ -230,16 +230,6 @@ const genDotsStyle: GenerateStyle<CarouselToken> = (token) => {
colorBgContainer,
motionDurationSlow,
} = token;
const animation = new Keyframes(`${token.prefixCls}-dot-animation`, {
from: {
transform: `translate3d(-100%, 0, 0)`,
},
to: {
transform: `translate3d(0%, 0, 0)`,
},
});
return {
[componentCls]: {
'.slick-dots': {
@@ -293,6 +283,7 @@ const genDotsStyle: GenerateStyle<CarouselToken> = (token) => {
outline: 'none',
cursor: 'pointer',
overflow: 'hidden',
transform: 'translate3d(-100%, 0, 0)',
},
button: {
@@ -331,10 +322,8 @@ const genDotsStyle: GenerateStyle<CarouselToken> = (token) => {
},
'&::after': {
background: colorBgContainer,
animationName: animation,
animationDuration: `var(${DotDuration})`,
animationTimingFunction: 'ease-out',
animationFillMode: 'forwards',
transform: 'translate3d(0, 0, 0)',
transition: `transform var(${DotDuration}) ease-out`,
},
},
},
@@ -346,15 +335,6 @@ const genDotsStyle: GenerateStyle<CarouselToken> = (token) => {
const genCarouselVerticalStyle: GenerateStyle<CarouselToken> = (token) => {
const { componentCls, dotOffset, arrowOffset, marginXXS } = token;
const animation = new Keyframes(`${token.prefixCls}-dot-vertical-animation`, {
from: {
height: 0,
},
to: {
height: token.dotActiveWidth,
},
});
const reverseSizeOfDot = {
width: token.dotHeight,
height: token.dotWidth,
@@ -416,19 +396,12 @@ const genCarouselVerticalStyle: GenerateStyle<CarouselToken> = (token) => {
'&.slick-active': {
...reverseSizeOfDot,
height: token.dotActiveWidth,
button: {
...reverseSizeOfDot,
height: token.dotActiveWidth,
},
button: reverseSizeOfDot,
'&::after': {
...reverseSizeOfDot,
animationName: animation,
animationDuration: `var(${DotDuration})`,
animationTimingFunction: 'ease-out',
animationFillMode: 'forwards',
transition: `height var(${DotDuration}) ease-out`,
},
},
},

View File

@@ -558,14 +558,14 @@ describe('Cascader', () => {
resetWarned();
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
const { container } = render(<Cascader dropdownStyle={{ padding: 10 }} open />);
const customStyle = { background: 'red' };
const { container } = render(<Cascader dropdownStyle={customStyle} open />);
expect(errSpy).toHaveBeenCalledWith(
'Warning: [antd: Cascader] `dropdownStyle` is deprecated. Please use `styles.popup.root` instead.',
);
expect(container.querySelector<HTMLElement>('.ant-select-dropdown')).toHaveStyle({
padding: '10px',
});
expect(container.querySelector('.ant-select-dropdown')?.getAttribute('style')).toContain(
'background: red',
);
errSpy.mockRestore();
});
@@ -595,11 +595,11 @@ describe('Cascader', () => {
resetWarned();
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
const columnStyle = { background: 'red' };
const { getByRole } = render(
<Cascader
options={[{ label: 'test', value: 1 }]}
dropdownMenuColumnStyle={{ padding: 10 }}
dropdownMenuColumnStyle={columnStyle}
open
/>,
);
@@ -607,7 +607,7 @@ describe('Cascader', () => {
'Warning: [antd: Cascader] `dropdownMenuColumnStyle` is deprecated. Please use `popupMenuColumnStyle` instead.',
);
const menuColumn = getByRole('menuitemcheckbox');
expect(menuColumn).toHaveStyle({ padding: '10px' });
expect(menuColumn.style.background).toBe('red');
errSpy.mockRestore();
});

View File

@@ -11,7 +11,7 @@ import RcCascader from 'rc-cascader';
import type { Placement } from 'rc-select/lib/BaseSelect';
import omit from 'rc-util/lib/omit';
import { useZIndex } from '../_util/hooks';
import { useZIndex } from '../_util/hooks/useZIndex';
import type { SelectCommonPlacement } from '../_util/motion';
import { getTransitionName } from '../_util/motion';
import genPurePanel from '../_util/PurePanel';

View File

@@ -82,12 +82,14 @@ describe('Collapse', () => {
</Collapse.Panel>
</Collapse>,
);
expect(container.querySelector('.ant-collapse-item')).not.toHaveClass(
'ant-collapse-item-active',
);
expect(
container.querySelector('.ant-collapse-item')?.classList.contains('ant-collapse-item-active'),
).toBe(false);
fireEvent.click(container.querySelector('.ant-collapse-header')!);
await waitFakeTimer();
expect(container.querySelector('.ant-collapse-item')).toHaveClass('ant-collapse-item-active');
expect(
container.querySelector('.ant-collapse-item')?.classList.contains('ant-collapse-item-active'),
).toBe(true);
jest.useRealTimers();
});

View File

@@ -3,6 +3,8 @@ import { SettingOutlined } from '@ant-design/icons';
import type { CollapseProps } from 'antd';
import { Collapse, Select } from 'antd';
const { Option } = Select;
const text = `
A dog is a type of domesticated animal.
Known for its loyalty and faithfulness,
@@ -62,15 +64,10 @@ const App: React.FC = () => {
/>
<br />
<span>Expand Icon Position: </span>
<Select
value={expandIconPosition}
style={{ margin: '0 8px' }}
onChange={onPositionChange}
options={[
{ label: 'start', value: 'start' },
{ label: 'end', value: 'end' },
]}
/>
<Select value={expandIconPosition} style={{ margin: '0 8px' }} onChange={onPositionChange}>
<Option value="start">start</Option>
<Option value="end">end</Option>
</Select>
</>
);
};

View File

@@ -205,7 +205,7 @@ describe('ColorPicker', () => {
fireEvent.click(container.querySelector('.ant-color-picker-trigger')!);
await waitFakeTimer();
const presetsColors = container
?.querySelector('.ant-collapse-content')
.querySelector('.ant-collapse-content')
?.querySelectorAll('.ant-color-picker-presets-color')!;
expect(container.querySelector('.ant-color-picker-presets')).toBeTruthy();
@@ -217,7 +217,9 @@ describe('ColorPicker', () => {
).toBeTruthy();
fireEvent.click(presetsColors[0]);
expect(presetsColors[0]).not.toHaveClass('ant-color-picker-presets-color-bright');
expect(
presetsColors[0].classList.contains('ant-color-picker-presets-color-bright'),
).toBeFalsy();
expect(
container.querySelector('.ant-color-picker-hex-input input')?.getAttribute('value'),
).toEqual('000000');
@@ -226,7 +228,9 @@ describe('ColorPicker', () => {
);
fireEvent.click(presetsColors[9]);
expect(presetsColors[9]).toHaveClass('ant-color-picker-presets-color-bright');
expect(
presetsColors[9].classList.contains('ant-color-picker-presets-color-bright'),
).toBeTruthy();
expect(
container.querySelector('.ant-color-picker-hex-input input')?.getAttribute('value'),
).toEqual('000000');

View File

@@ -79,7 +79,7 @@ const ColorTrigger = forwardRef<HTMLDivElement, ColorTriggerProps>((props, ref)
default:
return alpha < 100 ? `${hexString.slice(0, 7)},${alpha}%` : hexString;
}
}, [color, format, showText, activeIndex, locale.transparent, colorTextCellPrefixCls]);
}, [color, format, showText, activeIndex]);
// ============================= Render =============================
const containerNode = useMemo<React.ReactNode>(

View File

@@ -4,7 +4,6 @@ import RcColorPicker from '@rc-component/color-picker';
import type { Color } from '@rc-component/color-picker';
import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect';
import { useForceUpdate } from '../../../_util/hooks';
import Segmented from '../../../segmented';
import { AggregationColor } from '../../color';
import { PanelPickerContext } from '../../context';
@@ -71,7 +70,7 @@ const PanelPicker: FC = () => {
if (!isSingle) {
setLockedColor(colors[activeIndex]?.color);
}
}, [isSingle, colors, gradientDragging, activeIndex]);
}, [gradientDragging, activeIndex]);
const activeColor = React.useMemo(() => {
if (isSingle) {
@@ -84,12 +83,11 @@ const PanelPicker: FC = () => {
}
return colors[activeIndex]?.color;
}, [colors, value, activeIndex, isSingle, lockedColor, gradientDragging]);
}, [value, activeIndex, isSingle, lockedColor, gradientDragging]);
// ========================= Picker Color =========================
const [pickerColor, setPickerColor] = React.useState<AggregationColor | null>(activeColor);
const [forceSync, setForceSync] = useForceUpdate();
const [forceSync, setForceSync] = React.useState(0);
const mergedPickerColor = pickerColor?.equals(activeColor) ? activeColor : pickerColor;
@@ -150,7 +148,7 @@ const PanelPicker: FC = () => {
// Back of origin color in case in controlled
// This will set after `onChangeComplete` to avoid `setState` trigger rerender
// which will make `fillColor` get wrong `color.cleared` state
setForceSync();
setForceSync((ori) => ori + 1);
};
const onInputChange = (colorValue: AggregationColor) => {

View File

@@ -57,7 +57,7 @@ export default function useModeColor(
pushOption('gradient', locale.gradientColor);
return [optionList, modes];
}, [mode, locale.singleColor, locale.gradientColor]);
}, [mode]);
// ======================== Post ========================
// We need align `mode` with `color` state

View File

@@ -24801,7 +24801,7 @@ exports[`ConfigProvider components Select configProvider 1`] = `
class="config-select-selection-search"
>
<input
aria-activedescendant="rc_select_TEST_OR_SSR_list_0"
aria-activedescendant="rc_select_TEST_OR_SSR_list_1"
aria-autocomplete="list"
aria-controls="rc_select_TEST_OR_SSR_list"
aria-expanded="true"
@@ -24819,11 +24819,8 @@ exports[`ConfigProvider components Select configProvider 1`] = `
/>
</span>
<span
class="config-select-selection-item"
title="Light"
>
Light
</span>
class="config-select-selection-placeholder"
/>
</span>
</div>
<div
@@ -24837,12 +24834,17 @@ exports[`ConfigProvider components Select configProvider 1`] = `
style="height: 0px; width: 0px; overflow: hidden;"
>
<div
aria-label="Light"
aria-selected="true"
aria-selected="false"
id="rc_select_TEST_OR_SSR_list_0"
role="presentation"
/>
<div
aria-label="Light"
aria-selected="false"
id="rc_select_TEST_OR_SSR_list_1"
role="option"
>
light
Bamboo
</div>
</div>
<div
@@ -24859,8 +24861,14 @@ exports[`ConfigProvider components Select configProvider 1`] = `
style="display: flex; flex-direction: column;"
>
<div
aria-selected="true"
class="config-select-item config-select-item-option config-select-item-option-active config-select-item-option-selected"
class="config-select-item config-select-item-group"
title="grp"
>
grp
</div>
<div
aria-selected="false"
class="config-select-item config-select-item-option config-select-item-option-grouped config-select-item-option-active"
title="Light"
>
<div
@@ -24942,11 +24950,8 @@ exports[`ConfigProvider components Select configProvider componentDisabled 1`] =
/>
</span>
<span
class="config-select-selection-item"
title="Light"
>
Light
</span>
class="config-select-selection-placeholder"
/>
</span>
</div>
<span
@@ -24992,7 +24997,7 @@ exports[`ConfigProvider components Select configProvider componentSize large 1`]
class="config-select-selection-search"
>
<input
aria-activedescendant="rc_select_TEST_OR_SSR_list_0"
aria-activedescendant="rc_select_TEST_OR_SSR_list_1"
aria-autocomplete="list"
aria-controls="rc_select_TEST_OR_SSR_list"
aria-expanded="true"
@@ -25010,11 +25015,8 @@ exports[`ConfigProvider components Select configProvider componentSize large 1`]
/>
</span>
<span
class="config-select-selection-item"
title="Light"
>
Light
</span>
class="config-select-selection-placeholder"
/>
</span>
</div>
<div
@@ -25028,12 +25030,17 @@ exports[`ConfigProvider components Select configProvider componentSize large 1`]
style="height: 0px; width: 0px; overflow: hidden;"
>
<div
aria-label="Light"
aria-selected="true"
aria-selected="false"
id="rc_select_TEST_OR_SSR_list_0"
role="presentation"
/>
<div
aria-label="Light"
aria-selected="false"
id="rc_select_TEST_OR_SSR_list_1"
role="option"
>
light
Bamboo
</div>
</div>
<div
@@ -25050,8 +25057,14 @@ exports[`ConfigProvider components Select configProvider componentSize large 1`]
style="display: flex; flex-direction: column;"
>
<div
aria-selected="true"
class="config-select-item config-select-item-option config-select-item-option-active config-select-item-option-selected"
class="config-select-item config-select-item-group"
title="grp"
>
grp
</div>
<div
aria-selected="false"
class="config-select-item config-select-item-option config-select-item-option-grouped config-select-item-option-active"
title="Light"
>
<div
@@ -25115,7 +25128,7 @@ exports[`ConfigProvider components Select configProvider componentSize middle 1`
class="config-select-selection-search"
>
<input
aria-activedescendant="rc_select_TEST_OR_SSR_list_0"
aria-activedescendant="rc_select_TEST_OR_SSR_list_1"
aria-autocomplete="list"
aria-controls="rc_select_TEST_OR_SSR_list"
aria-expanded="true"
@@ -25133,11 +25146,8 @@ exports[`ConfigProvider components Select configProvider componentSize middle 1`
/>
</span>
<span
class="config-select-selection-item"
title="Light"
>
Light
</span>
class="config-select-selection-placeholder"
/>
</span>
</div>
<div
@@ -25151,12 +25161,17 @@ exports[`ConfigProvider components Select configProvider componentSize middle 1`
style="height: 0px; width: 0px; overflow: hidden;"
>
<div
aria-label="Light"
aria-selected="true"
aria-selected="false"
id="rc_select_TEST_OR_SSR_list_0"
role="presentation"
/>
<div
aria-label="Light"
aria-selected="false"
id="rc_select_TEST_OR_SSR_list_1"
role="option"
>
light
Bamboo
</div>
</div>
<div
@@ -25173,8 +25188,14 @@ exports[`ConfigProvider components Select configProvider componentSize middle 1`
style="display: flex; flex-direction: column;"
>
<div
aria-selected="true"
class="config-select-item config-select-item-option config-select-item-option-active config-select-item-option-selected"
class="config-select-item config-select-item-group"
title="grp"
>
grp
</div>
<div
aria-selected="false"
class="config-select-item config-select-item-option config-select-item-option-grouped config-select-item-option-active"
title="Light"
>
<div
@@ -25238,7 +25259,7 @@ exports[`ConfigProvider components Select configProvider componentSize small 1`]
class="config-select-selection-search"
>
<input
aria-activedescendant="rc_select_TEST_OR_SSR_list_0"
aria-activedescendant="rc_select_TEST_OR_SSR_list_1"
aria-autocomplete="list"
aria-controls="rc_select_TEST_OR_SSR_list"
aria-expanded="true"
@@ -25256,11 +25277,8 @@ exports[`ConfigProvider components Select configProvider componentSize small 1`]
/>
</span>
<span
class="config-select-selection-item"
title="Light"
>
Light
</span>
class="config-select-selection-placeholder"
/>
</span>
</div>
<div
@@ -25274,12 +25292,17 @@ exports[`ConfigProvider components Select configProvider componentSize small 1`]
style="height: 0px; width: 0px; overflow: hidden;"
>
<div
aria-label="Light"
aria-selected="true"
aria-selected="false"
id="rc_select_TEST_OR_SSR_list_0"
role="presentation"
/>
<div
aria-label="Light"
aria-selected="false"
id="rc_select_TEST_OR_SSR_list_1"
role="option"
>
light
Bamboo
</div>
</div>
<div
@@ -25296,8 +25319,14 @@ exports[`ConfigProvider components Select configProvider componentSize small 1`]
style="display: flex; flex-direction: column;"
>
<div
aria-selected="true"
class="config-select-item config-select-item-option config-select-item-option-active config-select-item-option-selected"
class="config-select-item config-select-item-group"
title="grp"
>
grp
</div>
<div
aria-selected="false"
class="config-select-item config-select-item-option config-select-item-option-grouped config-select-item-option-active"
title="Light"
>
<div
@@ -25361,7 +25390,7 @@ exports[`ConfigProvider components Select normal 1`] = `
class="ant-select-selection-search"
>
<input
aria-activedescendant="rc_select_TEST_OR_SSR_list_0"
aria-activedescendant="rc_select_TEST_OR_SSR_list_1"
aria-autocomplete="list"
aria-controls="rc_select_TEST_OR_SSR_list"
aria-expanded="true"
@@ -25379,11 +25408,8 @@ exports[`ConfigProvider components Select normal 1`] = `
/>
</span>
<span
class="ant-select-selection-item"
title="Light"
>
Light
</span>
class="ant-select-selection-placeholder"
/>
</span>
</div>
<div
@@ -25397,12 +25423,17 @@ exports[`ConfigProvider components Select normal 1`] = `
style="height: 0px; width: 0px; overflow: hidden;"
>
<div
aria-label="Light"
aria-selected="true"
aria-selected="false"
id="rc_select_TEST_OR_SSR_list_0"
role="presentation"
/>
<div
aria-label="Light"
aria-selected="false"
id="rc_select_TEST_OR_SSR_list_1"
role="option"
>
light
Bamboo
</div>
</div>
<div
@@ -25419,8 +25450,14 @@ exports[`ConfigProvider components Select normal 1`] = `
style="display: flex; flex-direction: column;"
>
<div
aria-selected="true"
class="ant-select-item ant-select-item-option ant-select-item-option-active ant-select-item-option-selected"
class="ant-select-item ant-select-item-group"
title="grp"
>
grp
</div>
<div
aria-selected="false"
class="ant-select-item ant-select-item-option ant-select-item-option-grouped ant-select-item-option-active"
title="Light"
>
<div
@@ -25484,7 +25521,7 @@ exports[`ConfigProvider components Select prefixCls 1`] = `
class="prefix-Select-selection-search"
>
<input
aria-activedescendant="rc_select_TEST_OR_SSR_list_0"
aria-activedescendant="rc_select_TEST_OR_SSR_list_1"
aria-autocomplete="list"
aria-controls="rc_select_TEST_OR_SSR_list"
aria-expanded="true"
@@ -25502,11 +25539,8 @@ exports[`ConfigProvider components Select prefixCls 1`] = `
/>
</span>
<span
class="prefix-Select-selection-item"
title="Light"
>
Light
</span>
class="prefix-Select-selection-placeholder"
/>
</span>
</div>
<div
@@ -25520,12 +25554,17 @@ exports[`ConfigProvider components Select prefixCls 1`] = `
style="height: 0px; width: 0px; overflow: hidden;"
>
<div
aria-label="Light"
aria-selected="true"
aria-selected="false"
id="rc_select_TEST_OR_SSR_list_0"
role="presentation"
/>
<div
aria-label="Light"
aria-selected="false"
id="rc_select_TEST_OR_SSR_list_1"
role="option"
>
light
Bamboo
</div>
</div>
<div
@@ -25542,8 +25581,14 @@ exports[`ConfigProvider components Select prefixCls 1`] = `
style="display: flex; flex-direction: column;"
>
<div
aria-selected="true"
class="prefix-Select-item prefix-Select-item-option prefix-Select-item-option-active prefix-Select-item-option-selected"
class="prefix-Select-item prefix-Select-item-group"
title="grp"
>
grp
</div>
<div
aria-selected="false"
class="prefix-Select-item prefix-Select-item-option prefix-Select-item-option-grouped prefix-Select-item-option-active"
title="Light"
>
<div
@@ -39894,7 +39939,7 @@ exports[`ConfigProvider components Transfer configProvider 1`] = `
exports[`ConfigProvider components Transfer configProvider componentDisabled 1`] = `
<div
class="config-transfer config-transfer-disabled"
class="config-transfer"
>
<div
class="config-transfer-list"
@@ -39921,7 +39966,6 @@ exports[`ConfigProvider components Transfer configProvider componentDisabled 1`]
<span
aria-label="down"
class="anticon anticon-down config-dropdown-trigger config-transfer-list-header-dropdown"
disabled=""
role="img"
>
<svg
@@ -40092,7 +40136,6 @@ exports[`ConfigProvider components Transfer configProvider componentDisabled 1`]
<span
aria-label="down"
class="anticon anticon-down config-dropdown-trigger config-transfer-list-header-dropdown"
disabled=""
role="img"
>
<svg
@@ -41767,12 +41810,11 @@ exports[`ConfigProvider components Tree configProvider 1`] = `
exports[`ConfigProvider components Tree configProvider componentDisabled 1`] = `
<div>
<div
class="config-tree config-tree-icon-hide config-tree-disabled"
class="config-tree config-tree-icon-hide"
>
<div>
<input
aria-label="for screen reader"
disabled=""
style="width: 0px; height: 0px; display: flex; overflow: hidden; opacity: 0; border: 0px; padding: 0px; margin: 0px;"
tabindex="0"
value=""
@@ -41806,7 +41848,7 @@ exports[`ConfigProvider components Tree configProvider componentDisabled 1`] = `
>
<div
aria-expanded="false"
class="config-tree-treenode config-tree-treenode-disabled config-tree-treenode-switcher-close config-tree-treenode-leaf-last config-tree-treenode-leaf"
class="config-tree-treenode config-tree-treenode-switcher-close config-tree-treenode-leaf-last config-tree-treenode-leaf"
draggable="false"
role="treeitem"
>
@@ -41834,12 +41876,11 @@ exports[`ConfigProvider components Tree configProvider componentDisabled 1`] = `
</div>
</div>
<div
class="config-tree config-tree-block-node config-tree-disabled config-tree-directory"
class="config-tree config-tree-block-node config-tree-directory"
>
<div>
<input
aria-label="for screen reader"
disabled=""
style="width: 0px; height: 0px; display: flex; overflow: hidden; opacity: 0; border: 0px; padding: 0px; margin: 0px;"
tabindex="0"
value=""
@@ -41873,7 +41914,7 @@ exports[`ConfigProvider components Tree configProvider componentDisabled 1`] = `
>
<div
aria-expanded="false"
class="config-tree-treenode config-tree-treenode-disabled config-tree-treenode-switcher-close config-tree-treenode-leaf-last config-tree-treenode-leaf"
class="config-tree-treenode config-tree-treenode-switcher-close config-tree-treenode-leaf-last config-tree-treenode-leaf"
draggable="false"
role="treeitem"
>

View File

@@ -59,12 +59,11 @@ import TreeSelect from '../../tree-select';
import Upload from '../../upload';
dayjs.extend(customParseFormat);
jest.mock('rc-util/lib/Portal');
describe('ConfigProvider', () => {
describe('components', () => {
const testPair = (name: string, renderComponent: (props?: any) => React.ReactElement<any>) => {
function testPair(name: string, renderComponent: (props?: any) => React.ReactElement): void {
const isArray = ['Menu', 'TimePicker', 'Tooltip'].includes(name);
describe(`${name}`, () => {
// normal
@@ -123,7 +122,7 @@ describe('ConfigProvider', () => {
expect(isArray ? container.children : container.firstChild).toMatchSnapshot();
});
});
};
}
// Alert
testPair('Alert', (props) => (
@@ -431,12 +430,11 @@ describe('ConfigProvider', () => {
// Select
testPair('Select', (props) => (
<Select
open
defaultValue={'light'}
options={[{ label: 'Light', value: 'light' }]}
{...props}
/>
<Select {...props} open>
<Select.OptGroup key="grp">
<Select.Option key="Bamboo">Light</Select.Option>
</Select.OptGroup>
</Select>
));
// Skeleton

View File

@@ -82,8 +82,7 @@ describe('ConfigProvider.Locale', () => {
const datepicke = wrapper.container.querySelector<HTMLInputElement>('.ant-picker-input input');
expect(datepicke?.value).toBe('');
expect(datepicke?.placeholder).toBe('请选择日期');
expect(wrapper.container.querySelector<HTMLElement>('.ant-pagination-item-1')).toHaveClass(
expect(wrapper.container.querySelector('.ant-pagination-item-1')?.className).toContain(
'ant-pagination-item-active',
);
@@ -94,7 +93,6 @@ describe('ConfigProvider.Locale', () => {
expect(
wrapper.container.querySelector<HTMLInputElement>('.ant-picker-input input')?.value,
).not.toBe('');
wrapper.rerender(
<ConfigProvider locale={{} as Locale}>
<DatePicker />
@@ -110,7 +108,7 @@ describe('ConfigProvider.Locale', () => {
expect(datepicker?.value).not.toBe('');
expect(datepicker?.value).toContain('-10');
expect(wrapper.container.querySelector('.ant-pagination-item-3')).toHaveClass(
expect(wrapper.container.querySelector('.ant-pagination-item-3')?.className).toContain(
'ant-pagination-item-active',
);
});

View File

@@ -132,7 +132,6 @@ export interface TableConfig extends ComponentStyleConfig {
export interface ImageConfig extends ComponentStyleConfig {
preview?: Partial<Record<'closeIcon', React.ReactNode>>;
fallback?: string;
}
export type CollapseConfig = ComponentStyleConfig & Pick<CollapseProps, 'expandIcon'>;
@@ -215,7 +214,7 @@ export type CascaderConfig = ComponentStyleConfig &
Pick<CascaderProps, 'variant' | 'styles' | 'classNames'>;
export type TreeSelectConfig = ComponentStyleConfig &
Pick<TreeSelectProps, 'variant' | 'styles' | 'classNames' | 'switcherIcon'>;
Pick<TreeSelectProps, 'variant' | 'styles' | 'classNames'>;
export type DatePickerConfig = ComponentStyleConfig &
Pick<DatePickerProps, 'variant' | 'styles' | 'classNames'>;

View File

@@ -36,6 +36,7 @@ type DirectionType = ConfigProviderProps['direction'];
const InputGroup = Input.Group;
const ButtonGroup = Button.Group;
const { Option } = Select;
const { TreeNode } = Tree;
const { Search } = Input;
@@ -99,27 +100,19 @@ const Page: React.FC<{ placement: Placement }> = ({ placement }) => {
const [showBadge, setShowBadge] = useState(true);
const selectBefore = (
<Select
defaultValue="Http://"
style={{ width: 90 }}
options={[
{ label: 'Http://', value: 'Http://' },
{ label: 'Https://', value: 'Https://' },
]}
/>
<Select defaultValue="Http://" style={{ width: 90 }}>
<Option value="Http://">Http://</Option>
<Option value="Https://">Https://</Option>
</Select>
);
const selectAfter = (
<Select
defaultValue=".com"
style={{ width: 80 }}
options={[
{ label: '.com', value: '.com' },
{ label: '.jp', value: '.jp' },
{ label: '.cn', value: '.cn' },
{ label: '.org', value: '.org' },
]}
/>
<Select defaultValue=".com" style={{ width: 80 }}>
<Option value=".com">.com</Option>
<Option value=".jp">.jp</Option>
<Option value=".cn">.cn</Option>
<Option value=".org">.org</Option>
</Select>
);
// ==== Cascader ====
@@ -287,13 +280,10 @@ const Page: React.FC<{ placement: Placement }> = ({ placement }) => {
</InputGroup>
<br />
<InputGroup compact>
<Select
defaultValue="Option1"
options={[
{ label: 'Option1', value: 'Option1' },
{ label: 'Option2', value: 'Option2' },
]}
/>
<Select defaultValue="Option1">
<Option value="Option1">Option1</Option>
<Option value="Option2">Option2</Option>
</Select>
<Input style={{ width: '50%' }} defaultValue="input content" />
<InputNumber />
</InputGroup>
@@ -302,50 +292,32 @@ const Page: React.FC<{ placement: Placement }> = ({ placement }) => {
<br />
<br />
<div style={{ marginBottom: 16 }}>
<Space.Compact>
{selectBefore}
<Input defaultValue="mysite" />
{selectAfter}
</Space.Compact>
<Input addonBefore={selectBefore} addonAfter={selectAfter} defaultValue="mysite" />
</div>
<br />
<Row>
<Col span={12}>
<Divider orientation="start">Select example</Divider>
<Divider orientation="left">Select example</Divider>
<Space wrap>
<Select
mode="multiple"
defaultValue="مورچه"
style={{ width: 120 }}
options={[
{ label: 'jack', value: 'jack' },
{ label: 'مورچه', value: 'مورچه' },
{ label: 'disabled', value: 'disabled', disabled: true },
{ label: 'yiminghe', value: 'Yiminghe' },
]}
/>
<Select
disabled
defaultValue="مورچه"
style={{ width: 120 }}
options={[{ label: 'مورچه', value: 'مورچه' }]}
/>
<Select
loading
defaultValue="مورچه"
style={{ width: 120 }}
options={[{ label: 'مورچه', value: 'مورچه' }]}
/>
<Select
showSearch
style={{ width: 200 }}
placeholder="Select a person"
options={[
{ label: 'jack', value: 'jack' },
{ label: 'سعید', value: 'سعید' },
{ label: 'Tom', value: 'tom' },
]}
/>
<Select mode="multiple" defaultValue="مورچه" style={{ width: 120 }}>
<Option value="jack">Jack</Option>
<Option value="مورچه">مورچه</Option>
<Option value="disabled" disabled>
Disabled
</Option>
<Option value="Yiminghe">yiminghe</Option>
</Select>
<Select defaultValue="مورچه" style={{ width: 120 }} disabled>
<Option value="مورچه">مورچه</Option>
</Select>
<Select defaultValue="مورچه" style={{ width: 120 }} loading>
<Option value="مورچه">مورچه</Option>
</Select>
<Select showSearch style={{ width: 200 }} placeholder="Select a person">
<Option value="jack">Jack</Option>
<Option value="سعید">سعید</Option>
<Option value="tom">Tom</Option>
</Select>
</Space>
</Col>
<Col span={12}>

View File

@@ -41,6 +41,7 @@ type Locale = ConfigProviderProps['locale'];
dayjs.locale('en');
const { Option } = Select;
const { RangePicker } = DatePicker;
const columns: TableProps['columns'] = [
@@ -131,14 +132,10 @@ const Page: React.FC = () => {
>
<Pagination defaultCurrent={1} total={50} showSizeChanger />
<Space wrap>
<Select
showSearch
style={{ width: 200 }}
options={[
{ label: 'jack', value: 'jack' },
{ label: 'lucy', value: 'lucy' },
]}
/>
<Select showSearch style={{ width: 200 }}>
<Option value="jack">jack</Option>
<Option value="lucy">lucy</Option>
</Select>
<DatePicker />
<TimePicker />
<RangePicker />

View File

@@ -132,7 +132,7 @@ const {
| floatButton | Set FloatButton common props | { backTopIcon?: React.ReactNode } | - | 5.27.0 |
| floatButtonGroup | Set FloatButton.Group common props | { closeIcon?: React.ReactNode } | - | 5.16.0 |
| form | Set Form common props | { className?: string, style?: React.CSSProperties, validateMessages?: [ValidateMessages](/components/form/#validatemessages), requiredMark?: boolean \| `optional`, scrollToFirstError?: boolean \| [Options](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options) } | - | `requiredMark`: 4.8.0; `colon`: 4.18.0; `scrollToFirstError`: 5.2.0; `className` and `style`: 5.7.0 |
| image | Set Image common props | { className?: string, style?: React.CSSProperties, preview?: { closeIcon?: React.ReactNode }, fallback?: string } | - | 5.7.0, `closeIcon`: 5.14.0, `fallback`: 5.28.0 |
| image | Set Image common props | { className?: string, style?: React.CSSProperties, preview?: { closeIcon?: React.ReactNode } } | - | 5.7.0, `closeIcon`: 5.14.0 |
| input | Set Input common props | { autoComplete?: string, className?: string, style?: React.CSSProperties, allowClear?: boolean \| { clearIcon?: ReactNode } } | - | 4.2.0, `allowClear`: 5.15.0 |
| textArea | Set TextArea common props | { autoComplete?: string, className?: string, style?: React.CSSProperties, allowClear?: boolean \| { clearIcon?: ReactNode } } | - | 5.15.0 |
| layout | Set Layout common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
@@ -168,7 +168,7 @@ const {
| popconfirm | Set Popconfirm common props | { className?: string, style?: React.CSSProperties, classNames?:[Popconfirm\["classNames"\]](/components/popconfirm#api), styles?: [Popconfirm\["styles"\]](/components/popconfirm#api) } | - | 5.23.0 |
| transfer | Set Transfer common props | { className?: string, style?: React.CSSProperties, selectionsIcon?: React.ReactNode } | - | 5.7.0, `selectionsIcon`: 5.14.0 |
| tree | Set Tree common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| treeSelect | Set TreeSelect common props | { classNames?:[TreeSelect\["classNames"\]](/components/tree-select#api), styles?: [TreeSelect\["styles"\]](/components/tree-select#api), switcherIcon?: [TreeSelect\["switcherIcon"\]](/components/tree-select#api) } | - | 5.25.0, `switcherIcon`: 5.28.0 |
| treeSelect | Set TreeSelect common props | { classNames?:[TreeSelect\["classNames"\]](/components/tree-select#api), styles?: [TreeSelect\["styles"\]](/components/tree-select#api) } | - | 5.25.0 |
| typography | Set Typography common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| upload | Set Upload common props | { className?: string, style?: React.CSSProperties, customRequest?: [Upload\["customRequest"\]](/components/upload#api) } | - | 5.7.0, `customRequest`: 5.27.0 |
| wave | Config wave effect | { disabled?: boolean, showEffect?: (node: HTMLElement, info: { className, token, component }) => void } | - | 5.8.0 |

View File

@@ -134,7 +134,7 @@ const {
| floatButton | 设置 FloatButton 组件的通用属性 | { backTopIcon?: React.ReactNode } | - | 5.27.0 |
| floatButtonGroup | 设置 FloatButton.Group 组件的通用属性 | { closeIcon?: React.ReactNode } | - | 5.16.0 |
| form | 设置 Form 组件的通用属性 | { className?: string, style?: React.CSSProperties, validateMessages?: [ValidateMessages](/components/form-cn#validatemessages), `requiredMark`?: boolean \| `optional`, colon?: boolean, scrollToFirstError?: boolean \| [Options](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options)} | - | `requiredMark`: 4.8.0; `colon`: 4.18.0; `scrollToFirstError`: 5.2.0; `className``style`: 5.7.0 |
| image | 设置 Image 组件的通用属性 | { className?: string, style?: React.CSSProperties, preview?: { closeIcon?: React.ReactNode }, fallback?: string } | - | 5.7.0, `closeIcon`: 5.14.0, `fallback`: 5.28.0 |
| image | 设置 Image 组件的通用属性 | { className?: string, style?: React.CSSProperties, preview?: { closeIcon?: React.ReactNode } } | - | 5.7.0, `closeIcon`: 5.14.0 |
| input | 设置 Input 组件的通用属性 | { autoComplete?: string, className?: string, style?: React.CSSProperties, allowClear?: boolean \| { clearIcon?: ReactNode } } | - | 5.7.0, `allowClear`: 5.15.0 |
| textArea | 设置 TextArea 组件的通用属性 | { autoComplete?: string, className?: string, style?: React.CSSProperties, allowClear?: boolean \| { clearIcon?: ReactNode } } | - | 5.15.0 |
| layout | 设置 Layout 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
@@ -170,7 +170,7 @@ const {
| popconfirm | 设置 Popconfirm 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?:[Popconfirm\["classNames"\]](/components/popconfirm-cn#api), styles?: [Popconfirm\["styles"\]](/components/popconfirm-cn#api) } | - | 5.23.0 |
| transfer | 设置 Transfer 组件的通用属性 | { className?: string, style?: React.CSSProperties, selectionsIcon?: React.ReactNode } | - | 5.7.0, `selectionsIcon`: 5.14.0 |
| tree | 设置 Tree 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| treeSelect | 设置 TreeSelect 组件的通用属性 | { classNames?:[TreeSelect\["classNames"\]](/components/tree-select-cn#api), styles?: [TreeSelect\["styles"\]](/components/tree-select-cn#api), switcherIcon?: [TreeSelect\["switcherIcon"\]](/components/tree-select-cn#api) } | - | 5.25.0, `switcherIcon`: 5.28.0 |
| treeSelect | 设置 TreeSelect 组件的通用属性 | { classNames?:[TreeSelect\["classNames"\]](/components/tree-select-cn#api), styles?: [TreeSelect\["styles"\]](/components/tree-select-cn#api) } | - | 5.25.0 |
| typography | 设置 Typography 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| upload | 设置 Upload 组件的通用属性 | { className?: string, style?: React.CSSProperties, customRequest?: [Upload\["customRequest"\]](/components/upload-cn#api) } | - | 5.7.0, `customRequest`: 5.27.0 |
| wave | 设置水波纹特效 | { disabled?: boolean, showEffect?: (node: HTMLElement, info: { className, token, component }) => void } | - | 5.8.0 |

View File

@@ -480,22 +480,4 @@ describe('DatePicker', () => {
rerender(<DatePicker value={somePoint} allowClear={{}} />);
expect(getClearButton()).toBeTruthy();
});
it('suffixIcon', () => {
const { rerender, container } = render(<DatePicker />);
expect(container.querySelector('.ant-picker-suffix')!.children.length).toBeTruthy();
rerender(<DatePicker suffixIcon />);
expect(container.querySelector('.ant-picker-suffix')!.children.length).toBeTruthy();
rerender(<DatePicker suffixIcon={false} />);
expect(container.querySelector('.ant-picker-suffix')!.children.length).toBeFalsy();
rerender(<DatePicker suffixIcon={null} />);
expect(container.querySelector('.ant-picker-suffix')!.children.length).toBeFalsy();
rerender(<DatePicker suffixIcon={'123'} />);
expect(container.querySelector('.ant-picker-suffix')?.textContent).toBe('123');
expect(container.children).toMatchSnapshot();
});
});

View File

@@ -643,31 +643,6 @@ Array [
]
`;
exports[`DatePicker suffixIcon 1`] = `
Array [
<div
class="ant-picker ant-picker-outlined"
>
<div
class="ant-picker-input"
>
<input
aria-invalid="false"
autocomplete="off"
placeholder="Select date"
size="12"
value=""
/>
<span
class="ant-picker-suffix"
>
123
</span>
</div>
</div>,
]
`;
exports[`DatePicker support DatePicker.generatePicker 1`] = `
<div
class="ant-picker ant-picker-outlined"

View File

@@ -7414,165 +7414,6 @@ exports[`renders components/date-picker/demo/suffix.tsx correctly 1`] = `
</div>
`;
exports[`renders components/date-picker/demo/suffixIcon-debug.tsx correctly 1`] = `
<div
class="ant-space ant-space-vertical ant-space-gap-row-small ant-space-gap-col-small"
>
<div
class="ant-space-item"
>
<div
class="ant-picker ant-picker-outlined"
>
<div
class="ant-picker-input"
>
<input
aria-invalid="false"
autocomplete="off"
placeholder="Select date"
size="12"
value=""
/>
<span
class="ant-picker-suffix"
>
<span
aria-label="calendar"
class="anticon anticon-calendar"
role="img"
>
<svg
aria-hidden="true"
data-icon="calendar"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"
/>
</svg>
</span>
</span>
</div>
</div>
</div>
<div
class="ant-space-item"
>
<div
class="ant-picker ant-picker-outlined"
>
<div
class="ant-picker-input"
>
<input
aria-invalid="false"
autocomplete="off"
placeholder="Select date"
size="12"
value=""
/>
<span
class="ant-picker-suffix"
/>
</div>
</div>
</div>
<div
class="ant-space-item"
>
<div
class="ant-picker ant-picker-outlined"
>
<div
class="ant-picker-input"
>
<input
aria-invalid="false"
autocomplete="off"
placeholder="Select date"
size="12"
value=""
/>
<span
class="ant-picker-suffix"
>
<span
aria-label="calendar"
class="anticon anticon-calendar"
role="img"
>
<svg
aria-hidden="true"
data-icon="calendar"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"
/>
</svg>
</span>
</span>
</div>
</div>
</div>
<div
class="ant-space-item"
>
<div
class="ant-picker ant-picker-outlined"
>
<div
class="ant-picker-input"
>
<input
aria-invalid="false"
autocomplete="off"
placeholder="Select date"
size="12"
value=""
/>
<span
class="ant-picker-suffix"
/>
</div>
</div>
</div>
<div
class="ant-space-item"
>
<div
class="ant-picker ant-picker-outlined"
>
<div
class="ant-picker-input"
>
<input
aria-invalid="false"
autocomplete="off"
placeholder="Select date"
size="12"
value=""
/>
<span
class="ant-picker-suffix"
>
123
</span>
</div>
</div>
</div>
</div>
`;
exports[`renders components/date-picker/demo/switchable.tsx correctly 1`] = `
<div
class="ant-space ant-space-horizontal ant-space-align-center ant-space-gap-row-small ant-space-gap-col-small"

View File

@@ -2,14 +2,14 @@ import type { render } from '../../../tests/utils';
import { fireEvent } from '../../../tests/utils';
export function openPicker(wrapper: ReturnType<typeof render>, index = 0) {
const inputEle = wrapper.container?.querySelectorAll<HTMLInputElement>('input')?.[index];
const inputEle = wrapper.container?.querySelectorAll<HTMLInputElement>('input')?.[index]!;
fireEvent.mouseDown(inputEle);
fireEvent.focus(inputEle);
fireEvent.click(inputEle);
}
export function closePicker(wrapper: ReturnType<typeof render>, index = 0) {
fireEvent.blur(wrapper.container?.querySelectorAll('input')[index]);
fireEvent.blur(wrapper.container?.querySelectorAll('input')[index]!);
}
export function selectCell(wrapper: ReturnType<typeof render>, text: string | number, index = 0) {

View File

@@ -1,7 +0,0 @@
## zh-CN
suffixIcon 测试。
## en-US
suffixIcon test.

View File

@@ -1,14 +0,0 @@
import React from 'react';
import { DatePicker, Space } from 'antd';
const App: React.FC = () => (
<Space direction="vertical">
<DatePicker suffixIcon />
<DatePicker suffixIcon={false} />
<DatePicker />
<DatePicker suffixIcon={null} />
<DatePicker suffixIcon={'123'} />
</Space>
);
export default App;

View File

@@ -2,20 +2,19 @@ import React, { useState } from 'react';
import type { DatePickerProps, TimePickerProps } from 'antd';
import { DatePicker, Select, Space, TimePicker } from 'antd';
const { Option } = Select;
type PickerType = 'time' | 'date';
interface PickerWithTypeProps {
const PickerWithType = ({
type,
onChange,
}: {
type: PickerType;
onChange: TimePickerProps['onChange'] | DatePickerProps['onChange'];
}
const PickerWithType: React.FC<PickerWithTypeProps> = ({ type, onChange }) => {
if (type === 'time') {
return <TimePicker onChange={onChange} />;
}
if (type === 'date') {
return <DatePicker onChange={onChange} />;
}
}) => {
if (type === 'time') return <TimePicker onChange={onChange} />;
if (type === 'date') return <DatePicker onChange={onChange} />;
return <DatePicker picker={type} onChange={onChange} />;
};
@@ -24,19 +23,14 @@ const App: React.FC = () => {
return (
<Space>
<Select
aria-label="Picker Type"
value={type}
onChange={setType}
options={[
{ label: 'Time', value: 'time' },
{ label: 'Date', value: 'date' },
{ label: 'Week', value: 'week' },
{ label: 'Month', value: 'month' },
{ label: 'Quarter', value: 'quarter' },
{ label: 'Year', value: 'year' },
]}
/>
<Select aria-label="Picker Type" value={type} onChange={setType}>
<Option value="time">Time</Option>
<Option value="date">Date</Option>
<Option value="week">Week</Option>
<Option value="month">Month</Option>
<Option value="quarter">Quarter</Option>
<Option value="year">Year</Option>
</Select>
<PickerWithType type={type} onChange={(value) => console.log(value)} />
</Space>
);

View File

@@ -1,36 +0,0 @@
import React from 'react';
import CalendarOutlined from '@ant-design/icons/CalendarOutlined';
import ClockCircleOutlined from '@ant-design/icons/ClockCircleOutlined';
import type { PickerMode } from 'rc-picker/lib/interface';
import { TIME } from '../generatePicker/constant';
interface SuffixIconProps {
picker?: PickerMode;
hasFeedback?: boolean;
feedbackIcon?: React.ReactNode;
suffixIcon?: React.ReactNode;
}
const SuffixIcon: React.FC<SuffixIconProps> = ({
picker,
hasFeedback,
feedbackIcon,
suffixIcon,
}) => {
if (suffixIcon === null || suffixIcon === false) {
return null;
}
if (suffixIcon === true || suffixIcon === undefined) {
return (
<>
{picker === TIME ? <ClockCircleOutlined /> : <CalendarOutlined />}
{hasFeedback && feedbackIcon}
</>
);
}
return suffixIcon;
};
export default SuffixIcon;

View File

@@ -1,5 +1,7 @@
import * as React from 'react';
import { forwardRef, useContext, useImperativeHandle } from 'react';
import CalendarOutlined from '@ant-design/icons/CalendarOutlined';
import ClockCircleOutlined from '@ant-design/icons/ClockCircleOutlined';
import SwapRightOutlined from '@ant-design/icons/SwapRightOutlined';
import cls from 'classnames';
import { RangePicker as RCRangePicker } from 'rc-picker';
@@ -7,7 +9,7 @@ import type { PickerRef } from 'rc-picker';
import type { GenerateConfig } from 'rc-picker/lib/generate/index';
import ContextIsolator from '../../_util/ContextIsolator';
import { useZIndex } from '../../_util/hooks';
import { useZIndex } from '../../_util/hooks/useZIndex';
import { getMergedStatus, getStatusClassNames } from '../../_util/statusUtils';
import type { AnyObject } from '../../_util/type';
import { devUseWarning } from '../../_util/warning';
@@ -19,14 +21,13 @@ import { FormItemInputContext } from '../../form/context';
import useVariant from '../../form/hooks/useVariants';
import { useLocale } from '../../locale';
import { useCompactItemContext } from '../../space/Compact';
import useMergedPickerSemantic from '../hooks/useMergedPickerSemantic';
import enUS from '../locale/en_US';
import useStyle from '../style';
import { getRangePlaceholder, useIcons } from '../util';
import { TIME } from './constant';
import type { RangePickerProps } from './interface';
import SuffixIcon from './SuffixIcon';
import useComponents from './useComponents';
import useMergedPickerSemantic from '../hooks/useMergedPickerSemantic';
const generateRangePicker = <DateType extends AnyObject = AnyObject>(
generateConfig: GenerateConfig<DateType>,
@@ -54,7 +55,6 @@ const generateRangePicker = <DateType extends AnyObject = AnyObject>(
picker,
styles,
classNames,
suffixIcon,
...restProps
} = props;
@@ -112,7 +112,14 @@ const generateRangePicker = <DateType extends AnyObject = AnyObject>(
// ===================== FormItemInput =====================
const formItemContext = useContext(FormItemInputContext);
const { hasFeedback, status: contextStatus, feedbackIcon } = formItemContext;
const mergedSuffixIcon = <SuffixIcon {...{ picker, hasFeedback, feedbackIcon, suffixIcon }} />;
const suffixNode = (
<>
{picker === TIME ? <ClockCircleOutlined /> : <CalendarOutlined />}
{hasFeedback && feedbackIcon}
</>
);
useImperativeHandle(ref, () => innerRef.current!);
const [contextLocale] = useLocale('Calendar', enUS);
@@ -134,7 +141,7 @@ const generateRangePicker = <DateType extends AnyObject = AnyObject>(
ref={innerRef as any} // Need to modify PickerRef
placement={placement}
placeholder={getRangePlaceholder(locale, picker, placeholder)}
suffixIcon={mergedSuffixIcon}
suffixIcon={suffixNode}
prevIcon={<span className={`${prefixCls}-prev-icon`} />}
nextIcon={<span className={`${prefixCls}-next-icon`} />}
superPrevIcon={<span className={`${prefixCls}-super-prev-icon`} />}

View File

@@ -1,5 +1,7 @@
import * as React from 'react';
import { forwardRef, useContext, useImperativeHandle } from 'react';
import CalendarOutlined from '@ant-design/icons/CalendarOutlined';
import ClockCircleOutlined from '@ant-design/icons/ClockCircleOutlined';
import cls from 'classnames';
import RCPicker from 'rc-picker';
import type { PickerRef } from 'rc-picker';
@@ -7,7 +9,7 @@ import type { GenerateConfig } from 'rc-picker/lib/generate/index';
import type { PickerMode } from 'rc-picker/lib/interface';
import ContextIsolator from '../../_util/ContextIsolator';
import { useZIndex } from '../../_util/hooks';
import { useZIndex } from '../../_util/hooks/useZIndex';
import { getMergedStatus, getStatusClassNames } from '../../_util/statusUtils';
import type { AnyObject } from '../../_util/type';
import { devUseWarning } from '../../_util/warning';
@@ -19,7 +21,6 @@ import { FormItemInputContext } from '../../form/context';
import useVariant from '../../form/hooks/useVariants';
import { useLocale } from '../../locale';
import { useCompactItemContext } from '../../space/Compact';
import useMergedPickerSemantic from '../hooks/useMergedPickerSemantic';
import enUS from '../locale/en_US';
import useStyle from '../style';
import { getPlaceholder, useIcons } from '../util';
@@ -36,8 +37,8 @@ import {
YEARPICKER,
} from './constant';
import type { GenericTimePickerProps, PickerProps, PickerPropsWithMultiple } from './interface';
import SuffixIcon from './SuffixIcon';
import useComponents from './useComponents';
import useMergedPickerSemantic from '../hooks/useMergedPickerSemantic';
const generatePicker = <DateType extends AnyObject = AnyObject>(
generateConfig: GenerateConfig<DateType>,
@@ -68,7 +69,6 @@ const generatePicker = <DateType extends AnyObject = AnyObject>(
onCalendarChange,
styles,
classNames,
suffixIcon,
...restProps
} = props;
@@ -159,9 +159,13 @@ const generatePicker = <DateType extends AnyObject = AnyObject>(
const formItemContext = useContext(FormItemInputContext);
const { hasFeedback, status: contextStatus, feedbackIcon } = formItemContext;
const mergedSuffixIcon = (
<SuffixIcon {...{ picker: mergedPicker, hasFeedback, feedbackIcon, suffixIcon }} />
const suffixNode = (
<>
{mergedPicker === 'time' ? <ClockCircleOutlined /> : <CalendarOutlined />}
{hasFeedback && feedbackIcon}
</>
);
const [contextLocale] = useLocale('DatePicker', enUS);
const locale = { ...contextLocale, ...props.locale! };
@@ -173,7 +177,7 @@ const generatePicker = <DateType extends AnyObject = AnyObject>(
<RCPicker<DateType>
ref={innerRef}
placeholder={getPlaceholder(locale, mergedPicker, placeholder)}
suffixIcon={mergedSuffixIcon}
suffixIcon={suffixNode}
placement={placement}
prevIcon={<span className={`${prefixCls}-prev-icon`} />}
nextIcon={<span className={`${prefixCls}-next-icon`} />}

View File

@@ -1,7 +1,8 @@
import * as React from 'react';
import cls from 'classnames';
import { useMergeSemantic } from '../../_util/hooks';
import useMergeSemantic from '../../_util/hooks/useMergeSemantic';
import { useComponentConfig } from '../../config-provider/context';
import type {
PickerClassNames,

Some files were not shown because too many files have changed in this diff Show More