chore: auto merge branches (#41586)

chore: merge master into feature
This commit is contained in:
github-actions[bot] 2023-04-01 13:49:02 +00:00 committed by GitHub
commit 4d2f648181
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 399 additions and 280 deletions

View File

@ -61,3 +61,18 @@ Describe changes from the user side, and list all potential break changes or oth
- [ ] Demo is updated/provided or not needed
- [ ] TypeScript definition is updated/provided or not needed
- [ ] Changelog is provided or not needed
---
<!--
Below are template for copilot to generate CR message.
Please DO NOT modify it.
-->
### 🚀 Summary
copilot:summary
### 🔍 Walkthrough
copilot:walkthrough

View File

@ -61,3 +61,17 @@
- [ ] 代码演示已提供或无须提供
- [ ] TypeScript 定义已补充或无须补充
- [ ] Changelog 已提供或无须提供
---
<!--
以下为 copilot 自动生成的 CR 结果,请勿修改
-->
### 🚀 概述
copilot:summary
### 🔍 实现细节
copilot:walkthrough

View File

@ -40,7 +40,7 @@ return <Breadcrumb routes={[{ breadcrumbName: 'sample' }]} />;
<code src="./demo/react-router.tsx" iframe="200">react-router V6</code>
<code src="./demo/separator.tsx">Configuring the Separator</code>
<code src="./demo/overlay.tsx">Bread crumbs with drop down menu</code>
<code src="./demo/separator-component.tsx">Configuring the Separator</code>
<code src="./demo/separator-component.tsx">Configuring the Separator Independently</code>
<code src="./demo/debug-routes.tsx">Debug Routes</code>
## API

View File

@ -41,7 +41,7 @@ return <Breadcrumb routes={[{ breadcrumbName: 'sample' }]} />;
<code src="./demo/react-router.tsx" iframe="200">react-router V6</code>
<code src="./demo/separator.tsx">分隔符</code>
<code src="./demo/overlay.tsx">带下拉菜单的面包屑</code>
<code src="./demo/separator-component.tsx">分隔符</code>
<code src="./demo/separator-component.tsx">独立的分隔符</code>
<code src="./demo/debug-routes.tsx">Debug Routes</code>
## API

View File

@ -7,30 +7,31 @@ import type {
DefaultOptionType,
FieldNames,
MultipleCascaderProps as RcMultipleCascaderProps,
ShowSearchType,
SingleCascaderProps as RcSingleCascaderProps,
ShowSearchType,
} from 'rc-cascader';
import RcCascader from 'rc-cascader';
import type { Placement } from 'rc-select/lib/BaseSelect';
import omit from 'rc-util/lib/omit';
import * as React from 'react';
import { ConfigContext } from '../config-provider';
import DefaultRenderEmpty from '../config-provider/defaultRenderEmpty';
import DisabledContext from '../config-provider/DisabledContext';
import type { SizeType } from '../config-provider/SizeContext';
import SizeContext from '../config-provider/SizeContext';
import DefaultRenderEmpty from '../config-provider/defaultRenderEmpty';
import { useCompactItemContext } from '../space/Compact';
import { FormItemInputContext } from '../form/context';
import getIcons from '../select/utils/iconUtil';
import type { SelectCommonPlacement } from '../_util/motion';
import { getTransitionDirection, getTransitionName } from '../_util/motion';
import type { InputStatus } from '../_util/statusUtils';
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
import warning from '../_util/warning';
import { FormItemInputContext } from '../form/context';
import getIcons from '../select/utils/iconUtil';
import genPurePanel from '../_util/PurePanel';
import useSelectStyle from '../select/style';
import useShowArrow from '../select/useShowArrow';
import genPurePanel from '../_util/PurePanel';
import useStyle from './style';
// Align the design since we use `rc-select` in root. This help:
@ -85,7 +86,7 @@ const defaultSearchRender: ShowSearchType['render'] = (inputValue, path, prefixC
optionList.push(' / ');
}
let label = (node as any)[fieldNames.label!];
let label = node[fieldNames.label!];
const type = typeof label;
if (type === 'string' || type === 'number') {
label = highlightKeyword(String(label), lower, prefixCls);
@ -266,12 +267,12 @@ const Cascader = React.forwardRef((props: CascaderProps<any>, ref: React.Ref<Cas
});
// ===================== Placement =====================
const getPlacement = () => {
const memoPlacement = React.useMemo<Placement>(() => {
if (placement !== undefined) {
return placement;
}
return isRtl ? 'bottomRight' : 'bottomLeft';
};
}, [placement, isRtl]);
// ==================== Render =====================
const renderNode = (
@ -295,7 +296,7 @@ const Cascader = React.forwardRef((props: CascaderProps<any>, ref: React.Ref<Cas
disabled={mergedDisabled}
{...(restProps as any)}
direction={mergedDirection}
placement={getPlacement()}
placement={memoPlacement}
notFoundContent={mergedNotFoundContent}
allowClear={allowClear}
showSearch={mergedShowSearch}

View File

@ -406,6 +406,24 @@ exports[`renders components/checkbox/demo/debug-line.tsx extend context correctl
</span>
</label>
</div>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
<span>
Aligned
</span>
</label>
</div>
`;

View File

@ -383,6 +383,24 @@ exports[`renders components/checkbox/demo/debug-line.tsx correctly 1`] = `
</span>
</label>
</div>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
<span>
Aligned
</span>
</label>
</div>
`;

View File

@ -1,5 +1,5 @@
import { Checkbox, ConfigProvider, Radio, Space } from 'antd';
import React from 'react';
import { Checkbox, Radio, Space } from 'antd';
const sharedStyle: React.CSSProperties = {
border: '1px solid red',
@ -43,6 +43,16 @@ const App: React.FC = () => (
<div>Bamboo</div>
<Radio value="little">Little</Radio>
</div>
<ConfigProvider
theme={{
token: {
controlHeight: 48,
},
}}
>
<Checkbox>Aligned</Checkbox>
</ConfigProvider>
</div>
);

View File

@ -71,19 +71,26 @@ export const genCheckboxStyle: GenerateStyle<CheckboxToken> = (token) => {
[checkboxCls]: {
...resetComponent(token),
top: '0.2em',
position: 'relative',
whiteSpace: 'nowrap',
lineHeight: 1,
cursor: 'pointer',
alignSelf: 'start',
// https://github.com/ant-design/ant-design/issues/41564
// Since `checkboxSize` is dynamic which should align with the text box,
// We need do calculation here for offset.
transform: `translate(0, ${
(token.lineHeight * token.fontSize) / 2 - token.checkboxSize / 2
}px)`,
// Wrapper > Checkbox > input
[`${checkboxCls}-input`]: {
position: 'absolute',
// Since baseline align will get additional space offset,
// we need to move input to top to make it align with text.
// Ref: https://github.com/ant-design/ant-design/issues/38926#issuecomment-1486137799
inset: `-0.2em 0`,
inset: 0,
zIndex: 1,
cursor: 'pointer',
opacity: 0,

View File

@ -6,13 +6,13 @@ import * as React from 'react';
import toArray from 'rc-util/lib/Children/toArray';
import omit from 'rc-util/lib/omit';
import { ConfigContext } from '../config-provider';
import initCollapseMotion from '../_util/motion';
import { cloneElement } from '../_util/reactNode';
import warning from '../_util/warning';
import type { CollapsibleType } from './CollapsePanel';
import { ConfigContext } from '../config-provider';
import type { SizeType } from '../config-provider/SizeContext';
import SizeContext from '../config-provider/SizeContext';
import type { CollapsibleType } from './CollapsePanel';
import CollapsePanel from './CollapsePanel';
import useStyle from './style';
@ -66,6 +66,8 @@ const Collapse = React.forwardRef<HTMLDivElement, CollapseProps>((props, ref) =>
ghost,
size: customizeSize,
expandIconPosition = 'start',
children,
expandIcon,
} = props;
const mergedSize = customizeSize || size || 'middle';
@ -89,7 +91,6 @@ const Collapse = React.forwardRef<HTMLDivElement, CollapseProps>((props, ref) =>
}, [expandIconPosition]);
const renderExpandIcon = (panelProps: PanelProps = {}) => {
const { expandIcon } = props;
const icon = (
expandIcon ? (
expandIcon(panelProps)
@ -121,22 +122,23 @@ const Collapse = React.forwardRef<HTMLDivElement, CollapseProps>((props, ref) =>
leavedClassName: `${prefixCls}-content-hidden`,
};
const getItems = () => {
const { children } = props;
return toArray(children).map((child: React.ReactElement, index: number) => {
if (child.props?.disabled) {
const key = child.key || String(index);
const { disabled, collapsible } = child.props;
const childProps: CollapseProps & { key: React.Key } = {
...omit(child.props, ['disabled']),
key,
collapsible: collapsible ?? (disabled ? 'disabled' : undefined),
};
return cloneElement(child, childProps);
}
return child;
});
};
const items = React.useMemo<React.ReactNode[]>(
() =>
toArray(children).map<React.ReactNode>((child, index) => {
if (child.props?.disabled) {
const key = child.key ?? String(index);
const { disabled, collapsible } = child.props;
const childProps: CollapseProps & { key: React.Key } = {
...omit(child.props, ['disabled']),
key,
collapsible: collapsible ?? (disabled ? 'disabled' : undefined),
};
return cloneElement(child, childProps);
}
return child;
}),
[children],
);
return wrapSSR(
<RcCollapse
@ -147,7 +149,7 @@ const Collapse = React.forwardRef<HTMLDivElement, CollapseProps>((props, ref) =>
prefixCls={prefixCls}
className={collapseClassName}
>
{getItems()}
{items}
</RcCollapse>,
);
});

View File

@ -1,7 +1,7 @@
import * as React from 'react';
import CloseOutlined from '@ant-design/icons/CloseOutlined';
import type { DrawerProps as RCDrawerProps } from 'rc-drawer';
import classNames from 'classnames';
import type { DrawerProps as RCDrawerProps } from 'rc-drawer';
import * as React from 'react';
export interface DrawerPanelProps {
prefixCls: string;
@ -22,18 +22,15 @@ export interface DrawerPanelProps {
children?: React.ReactNode;
}
export default function DrawerPanel(props: DrawerPanelProps) {
const DrawerPanel: React.FC<DrawerPanelProps> = (props) => {
const {
prefixCls,
title,
footer,
extra,
closable = true,
closeIcon = <CloseOutlined />,
onClose,
headerStyle,
drawerStyle,
bodyStyle,
@ -47,17 +44,16 @@ export default function DrawerPanel(props: DrawerPanelProps) {
</button>
);
function renderHeader() {
const headerNode = React.useMemo<React.ReactNode>(() => {
if (!title && !closable) {
return null;
}
return (
<div
style={headerStyle}
className={classNames(`${prefixCls}-header`, {
[`${prefixCls}-header-close-only`]: closable && !title && !extra,
})}
style={headerStyle}
>
<div className={`${prefixCls}-header-title`}>
{closeIconNode}
@ -66,28 +62,29 @@ export default function DrawerPanel(props: DrawerPanelProps) {
{extra && <div className={`${prefixCls}-extra`}>{extra}</div>}
</div>
);
}
}, [closable, closeIconNode, extra, headerStyle, prefixCls, title]);
function renderFooter() {
const footerNode = React.useMemo<React.ReactNode>(() => {
if (!footer) {
return null;
}
const footerClassName = `${prefixCls}-footer`;
return (
<div className={footerClassName} style={footerStyle}>
{footer}
</div>
);
}
}, [footer, footerStyle, prefixCls]);
return (
<div className={`${prefixCls}-wrapper-body`} style={{ ...drawerStyle }}>
{renderHeader()}
<div className={`${prefixCls}-wrapper-body`} style={drawerStyle}>
{headerNode}
<div className={`${prefixCls}-body`} style={bodyStyle}>
{children}
</div>
{renderFooter()}
{footerNode}
</div>
);
}
};
export default DrawerPanel;

View File

@ -5,19 +5,19 @@ import useEvent from 'rc-util/lib/hooks/useEvent';
import useMergedState from 'rc-util/lib/hooks/useMergedState';
import omit from 'rc-util/lib/omit';
import * as React from 'react';
import genPurePanel from '../_util/PurePanel';
import type { AdjustOverflow } from '../_util/placements';
import getPlacements from '../_util/placements';
import { cloneElement } from '../_util/reactNode';
import warning from '../_util/warning';
import { ConfigContext } from '../config-provider';
import type { MenuProps } from '../menu';
import Menu from '../menu';
import { OverrideProvider } from '../menu/OverrideContext';
import { NoCompactStyle } from '../space/Compact';
import type { AdjustOverflow } from '../_util/placements';
import getPlacements from '../_util/placements';
import genPurePanel from '../_util/PurePanel';
import { cloneElement } from '../_util/reactNode';
import warning from '../_util/warning';
import theme from '../theme';
import DropdownButton from './dropdown-button';
import useStyle from './style';
import theme from '../theme';
const Placements = [
'topLeft',
@ -92,6 +92,29 @@ type CompoundedComponent = React.FC<DropdownProps> & {
};
const Dropdown: CompoundedComponent = (props) => {
const {
menu,
arrow,
prefixCls: customizePrefixCls,
children,
trigger,
disabled,
dropdownRender,
getPopupContainer,
overlayClassName,
rootClassName,
open,
onOpenChange,
// Deprecated
visible,
onVisibleChange,
mouseEnterDelay = 0.15,
mouseLeaveDelay = 0.1,
autoAdjustOverflow = true,
placement = '',
overlay,
transitionName,
} = props;
const {
getPopupContainer: getContextPopupContainer,
getPrefixCls,
@ -118,9 +141,9 @@ const Dropdown: CompoundedComponent = (props) => {
);
}
const getTransitionName = () => {
const memoTransitionName = React.useMemo<string>(() => {
const rootPrefixCls = getPrefixCls();
const { placement = '', transitionName } = props;
if (transitionName !== undefined) {
return transitionName;
}
@ -128,10 +151,9 @@ const Dropdown: CompoundedComponent = (props) => {
return `${rootPrefixCls}-slide-down`;
}
return `${rootPrefixCls}-slide-up`;
};
}, [getPrefixCls, placement, transitionName]);
const getPlacement = () => {
const { placement } = props;
const memoPlacement = React.useMemo<string>(() => {
if (!placement) {
return direction === 'rtl' ? 'bottomRight' : 'bottomLeft';
}
@ -147,29 +169,7 @@ const Dropdown: CompoundedComponent = (props) => {
}
return placement;
};
const {
menu,
arrow,
prefixCls: customizePrefixCls,
children,
trigger,
disabled,
dropdownRender,
getPopupContainer,
overlayClassName,
rootClassName,
open,
onOpenChange,
// Deprecated
visible,
onVisibleChange,
mouseEnterDelay = 0.15,
mouseLeaveDelay = 0.1,
autoAdjustOverflow = true,
} = props;
}, [placement, direction]);
if (process.env.NODE_ENV !== 'production') {
[
@ -239,7 +239,6 @@ const Dropdown: CompoundedComponent = (props) => {
const renderOverlay = () => {
// rc-dropdown already can process the function of overlay, but we have check logic here.
// So we need render the element to check and pass back to rc-dropdown.
const { overlay } = props;
let overlayNode: React.ReactNode;
if (menu?.items) {
@ -294,10 +293,10 @@ const Dropdown: CompoundedComponent = (props) => {
overlayClassName={overlayClassNameCustomized}
prefixCls={prefixCls}
getPopupContainer={getPopupContainer || getContextPopupContainer}
transitionName={getTransitionName()}
transitionName={memoTransitionName}
trigger={triggerActions}
overlay={renderOverlay}
placement={getPlacement()}
placement={memoPlacement}
onVisibleChange={onInnerOpenChange}
>
{dropdownTrigger}

View File

@ -5,6 +5,7 @@ import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
import classNames from 'classnames';
import type { Meta } from 'rc-field-form/lib/interface';
import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect';
import isVisible from 'rc-util/lib/Dom/isVisible';
import omit from 'rc-util/lib/omit';
import * as React from 'react';
import type { FormItemProps, ValidateStatus } from '.';
@ -65,14 +66,17 @@ export default function ItemHolder(props: ItemHolderProps) {
const debounceWarnings = useDebounce(warnings);
const hasHelp = help !== undefined && help !== null;
const hasError = !!(hasHelp || errors.length || warnings.length);
const isOnScreen = !!itemRef.current && isVisible(itemRef.current);
const [marginBottom, setMarginBottom] = React.useState<number | null>(null);
useLayoutEffect(() => {
if (hasError && itemRef.current) {
// The element must be part of the DOMTree to use getComputedStyle
// https://stackoverflow.com/questions/35360711/getcomputedstyle-returns-a-cssstyledeclaration-but-all-properties-are-empty-on-a
const itemStyle = getComputedStyle(itemRef.current);
setMarginBottom(parseInt(itemStyle.marginBottom, 10));
}
}, [hasError]);
}, [hasError, isOnScreen]);
const onErrorVisibleChanged = (nextVisible: boolean) => {
if (!nextVisible) {

View File

@ -1418,6 +1418,20 @@ describe('Form', () => {
expect(container.querySelector('.drawer-select')?.className).not.toContain('status-error');
});
it('should be set up correctly marginBottom', () => {
render(
<Modal open>
<Form>
<Form.Item help='This is a help message'>
<Input />
</Form.Item>
</Form>
</Modal>,
);
expect(document.querySelector('.ant-form-item-margin-offset')).toBeTruthy();
});
it('Form.Item.useStatus should work', async () => {
const {
Item: { useStatus },
@ -1545,6 +1559,7 @@ describe('Form', () => {
marginBottom: -24,
});
});
it('form child components should be given priority to own disabled props when it in a disabled form', () => {
const props = {
name: 'file',

View File

@ -8,14 +8,14 @@ import type {
import { composeRef } from 'rc-util/lib/ref';
// eslint-disable-next-line import/no-named-as-default
import * as React from 'react';
import { ConfigContext } from '../config-provider';
import DefaultRenderEmpty from '../config-provider/defaultRenderEmpty';
import { FormItemInputContext } from '../form/context';
import Spin from '../spin';
import genPurePanel from '../_util/PurePanel';
import type { InputStatus } from '../_util/statusUtils';
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
import warning from '../_util/warning';
import { ConfigContext } from '../config-provider';
import DefaultRenderEmpty from '../config-provider/defaultRenderEmpty';
import { FormItemInputContext } from '../form/context';
import Spin from '../spin';
import useStyle from './style';
@ -81,7 +81,7 @@ const InternalMentions: React.ForwardRefRenderFunction<MentionsRef, MentionProps
ref,
) => {
const [focused, setFocused] = React.useState(false);
const innerRef = React.useRef<MentionsRef>();
const innerRef = React.useRef<MentionsRef>(null);
const mergedRef = composeRef(ref, innerRef);
// =================== Warning =====================
@ -123,7 +123,7 @@ const InternalMentions: React.ForwardRefRenderFunction<MentionsRef, MentionProps
return renderEmpty?.('Select') || <DefaultRenderEmpty componentName="Select" />;
}, [notFoundContent, renderEmpty]);
const getOptions = () => {
const mentionOptions = React.useMemo<React.ReactNode>(() => {
if (loading) {
return (
<Option value="ANTD_SEARCHING" disabled>
@ -131,9 +131,8 @@ const InternalMentions: React.ForwardRefRenderFunction<MentionsRef, MentionProps
</Option>
);
}
return children;
};
}, [loading, children]);
const mergedOptions = loading
? [
@ -176,14 +175,12 @@ const InternalMentions: React.ForwardRefRenderFunction<MentionsRef, MentionProps
onFocus={onFocus}
onBlur={onBlur}
dropdownClassName={classNames(popupClassName, rootClassName, hashId)}
ref={mergedRef as any}
ref={mergedRef}
options={mergedOptions}
suffix={hasFeedback && feedbackIcon}
classes={{
affixWrapper: classNames(hashId, className),
}}
classes={{ affixWrapper: classNames(hashId, className) }}
>
{getOptions()}
{mentionOptions}
</RcMentions>
);

View File

@ -98,11 +98,14 @@ Array [
exports[`renders components/popconfirm/demo/basic.tsx extend context correctly 1`] = `
Array [
<a
href="#"
<button
class="ant-btn ant-btn-link"
type="button"
>
Delete
</a>,
<span>
Delete
</span>
</button>,
<div
class="ant-popover ant-zoom-big-appear ant-zoom-big-appear-prepare ant-zoom-big ant-popconfirm ant-popover-placement-top"
style="left: -1000vw; top: -1000vh; box-sizing: border-box;"
@ -191,11 +194,14 @@ Array [
exports[`renders components/popconfirm/demo/dynamic-trigger.tsx extend context correctly 1`] = `
<div>
<a
href="#"
<button
class="ant-btn ant-btn-link"
type="button"
>
Delete a task
</a>
<span>
Delete a task
</span>
</button>
<div
class="ant-popover ant-zoom-big-appear ant-zoom-big-appear-prepare ant-zoom-big ant-popconfirm ant-popover-placement-top"
style="left: -1000vw; top: -1000vh; box-sizing: border-box;"
@ -307,11 +313,14 @@ exports[`renders components/popconfirm/demo/dynamic-trigger.tsx extend context c
exports[`renders components/popconfirm/demo/icon.tsx extend context correctly 1`] = `
Array [
<a
href="#"
<button
class="ant-btn ant-btn-link"
type="button"
>
Delete
</a>,
<span>
Delete
</span>
</button>,
<div
class="ant-popover ant-zoom-big-appear ant-zoom-big-appear-prepare ant-zoom-big ant-popconfirm ant-popover-placement-top"
style="left: -1000vw; top: -1000vh; box-sizing: border-box;"
@ -404,11 +413,14 @@ Array [
exports[`renders components/popconfirm/demo/locale.tsx extend context correctly 1`] = `
Array [
<a
href="#"
<button
class="ant-btn ant-btn-link"
type="button"
>
Delete
</a>,
<span>
Delete
</span>
</button>,
<div
class="ant-popover ant-zoom-big-appear ant-zoom-big-appear-prepare ant-zoom-big ant-popconfirm ant-popover-placement-top"
style="left: -1000vw; top: -1000vh; box-sizing: border-box;"

View File

@ -12,20 +12,26 @@ exports[`renders components/popconfirm/demo/async.tsx correctly 1`] = `
`;
exports[`renders components/popconfirm/demo/basic.tsx correctly 1`] = `
<a
href="#"
<button
class="ant-btn ant-btn-link"
type="button"
>
Delete
</a>
<span>
Delete
</span>
</button>
`;
exports[`renders components/popconfirm/demo/dynamic-trigger.tsx correctly 1`] = `
<div>
<a
href="#"
<button
class="ant-btn ant-btn-link"
type="button"
>
Delete a task
</a>
<span>
Delete a task
</span>
</button>
<br />
<br />
Whether directly execute
@ -53,19 +59,25 @@ exports[`renders components/popconfirm/demo/dynamic-trigger.tsx correctly 1`] =
`;
exports[`renders components/popconfirm/demo/icon.tsx correctly 1`] = `
<a
href="#"
<button
class="ant-btn ant-btn-link"
type="button"
>
Delete
</a>
<span>
Delete
</span>
</button>
`;
exports[`renders components/popconfirm/demo/locale.tsx correctly 1`] = `
<a
href="#"
<button
class="ant-btn ant-btn-link"
type="button"
>
Delete
</a>
<span>
Delete
</span>
</button>
`;
exports[`renders components/popconfirm/demo/placement.tsx correctly 1`] = `

View File

@ -1,5 +1,5 @@
import React from 'react';
import { message, Popconfirm } from 'antd';
import { Button, message, Popconfirm } from 'antd';
const confirm = (e: React.MouseEvent<HTMLElement>) => {
console.log(e);
@ -20,7 +20,7 @@ const App: React.FC = () => (
okText="Yes"
cancelText="No"
>
<a href="#">Delete</a>
<Button type="link">Delete</Button>
</Popconfirm>
);

View File

@ -1,5 +1,5 @@
import React, { useState } from 'react';
import { message, Popconfirm, Switch } from 'antd';
import { Button, message, Popconfirm, Switch } from 'antd';
const App: React.FC = () => {
const [open, setOpen] = useState(false);
@ -45,7 +45,7 @@ const App: React.FC = () => {
okText="Yes"
cancelText="No"
>
<a href="#">Delete a task</a>
<Button type="link">Delete a task</Button>
</Popconfirm>
<br />
<br />

View File

@ -1,6 +1,6 @@
import React from 'react';
import { QuestionCircleOutlined } from '@ant-design/icons';
import { Popconfirm } from 'antd';
import { Button, Popconfirm } from 'antd';
const App: React.FC = () => (
<Popconfirm
@ -8,7 +8,7 @@ const App: React.FC = () => (
description="Are you sure to delete this task?"
icon={<QuestionCircleOutlined style={{ color: 'red' }} />}
>
<a href="#">Delete</a>
<Button type="link">Delete</Button>
</Popconfirm>
);

View File

@ -1,5 +1,5 @@
import React from 'react';
import { Popconfirm } from 'antd';
import { Button, Popconfirm } from 'antd';
const App: React.FC = () => (
<Popconfirm
@ -8,7 +8,7 @@ const App: React.FC = () => (
okText="Yes"
cancelText="No"
>
<a href="#">Delete</a>
<Button type="link">Delete</Button>
</Popconfirm>
);

View File

@ -5965,6 +5965,7 @@ exports[`renders components/select/demo/responsive.tsx extend context correctly
exports[`renders components/select/demo/search.tsx extend context correctly 1`] = `
<div
class="ant-select ant-select-single ant-select-show-arrow ant-select-show-search"
style="width: 160px;"
>
<div
class="ant-select-selector"

View File

@ -2290,6 +2290,7 @@ exports[`renders components/select/demo/responsive.tsx correctly 1`] = `
exports[`renders components/select/demo/search.tsx correctly 1`] = `
<div
class="ant-select ant-select-single ant-select-show-arrow ant-select-show-search"
style="width:160px"
>
<div
class="ant-select-selector"

View File

@ -1,5 +1,5 @@
import React from 'react';
import { Select } from 'antd';
import React from 'react';
const onChange = (value: string) => {
console.log(`selected ${value}`);
@ -12,6 +12,7 @@ const onSearch = (value: string) => {
const App: React.FC = () => (
<Select
showSearch
style={{ width: 160 }}
placeholder="Select a person"
optionFilterProp="children"
onChange={onChange}

View File

@ -6,23 +6,22 @@ import type { OptionProps } from 'rc-select/lib/Option';
import type { BaseOptionType, DefaultOptionType } from 'rc-select/lib/Select';
import omit from 'rc-util/lib/omit';
import * as React from 'react';
import { ConfigContext } from '../config-provider';
import DefaultRenderEmpty from '../config-provider/defaultRenderEmpty';
import DisabledContext from '../config-provider/DisabledContext';
import type { SizeType } from '../config-provider/SizeContext';
import SizeContext from '../config-provider/SizeContext';
import { FormItemInputContext } from '../form/context';
import genPurePanel from '../_util/PurePanel';
import type { SelectCommonPlacement } from '../_util/motion';
import { getTransitionDirection, getTransitionName } from '../_util/motion';
import type { InputStatus } from '../_util/statusUtils';
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
import getIcons from './utils/iconUtil';
import { useCompactItemContext } from '../space/Compact';
import genPurePanel from '../_util/PurePanel';
import warning from '../_util/warning';
import { ConfigContext } from '../config-provider';
import DisabledContext from '../config-provider/DisabledContext';
import type { SizeType } from '../config-provider/SizeContext';
import SizeContext from '../config-provider/SizeContext';
import DefaultRenderEmpty from '../config-provider/defaultRenderEmpty';
import { FormItemInputContext } from '../form/context';
import { useCompactItemContext } from '../space/Compact';
import useStyle from './style';
import useShowArrow from './useShowArrow';
import getIcons from './utils/iconUtil';
type RawValue = string | number;
@ -182,12 +181,12 @@ const InternalSelect = <OptionType extends BaseOptionType | DefaultOptionType =
);
// ===================== Placement =====================
const getPlacement = (): SelectCommonPlacement => {
const memoPlacement = React.useMemo<SelectCommonPlacement>(() => {
if (placement !== undefined) {
return placement;
}
return direction === 'rtl' ? 'bottomRight' : 'bottomLeft';
};
}, [placement, direction]);
// ====================== Warning ======================
if (process.env.NODE_ENV !== 'production') {
@ -201,7 +200,7 @@ const InternalSelect = <OptionType extends BaseOptionType | DefaultOptionType =
// ====================== Render =======================
return wrapSSR(
<RcSelect<any, any>
ref={ref as any}
ref={ref}
virtual={virtual}
dropdownMatchSelectWidth={dropdownMatchSelectWidth}
showSearch={select?.showSearch}
@ -213,9 +212,9 @@ const InternalSelect = <OptionType extends BaseOptionType | DefaultOptionType =
)}
listHeight={listHeight}
listItemHeight={listItemHeight}
mode={mode as any}
mode={mode}
prefixCls={prefixCls}
placement={getPlacement()}
placement={memoPlacement}
direction={direction}
inputIcon={suffixIcon}
menuItemSelectedIcon={itemIcon}

View File

@ -18,7 +18,7 @@ const SliderTooltip = React.forwardRef<unknown, TooltipProps>((props, ref) => {
function keepAlign() {
rafRef.current = raf(() => {
innerRef.current?.forcePopupAlign();
innerRef.current?.forceAlign();
rafRef.current = null;
});
}

View File

@ -133,14 +133,14 @@ describe('Slider', () => {
expect(container).toMatchSnapshot();
});
it('should keepAlign by calling forcePopupAlign', async () => {
it('should keepAlign by calling forceAlign', async () => {
const ref = React.createRef<any>();
render(<SliderTooltip title="30" open ref={ref} />);
ref.current.forcePopupAlign = jest.fn();
ref.current.forceAlign = jest.fn();
act(() => {
jest.runAllTimers();
});
expect(ref.current.forcePopupAlign).toHaveBeenCalled();
expect(ref.current.forceAlign).toHaveBeenCalled();
});
it('tipFormatter should not crash with undefined value', () => {

View File

@ -1,5 +1,6 @@
import { spyElementPrototype } from 'rc-util/lib/test/domHook';
import React from 'react';
import { act } from 'react-dom/test-utils';
import type { TooltipPlacement } from '..';
import Tooltip from '..';
import mountTest from '../../../tests/shared/mountTest';
@ -590,4 +591,16 @@ describe('Tooltip', () => {
);
expect(container).toMatchSnapshot();
});
it('use ref.current.forcePopupAlign', async () => {
const ref = React.createRef<any>();
const error = jest.spyOn(console, 'error').mockImplementation(() => {});
render(<Tooltip open ref={ref} />);
act(() => {
ref.current.forcePopupAlign();
jest.runAllTimers();
});
expect(error).toHaveBeenCalled();
error.mockRestore();
});
});

View File

@ -1,16 +1,15 @@
import type { BuildInPlacements } from '@rc-component/trigger';
import classNames from 'classnames';
import RcTooltip from 'rc-tooltip';
import type { placements as Placements } from 'rc-tooltip/lib/placements';
import type {
TooltipProps as RcTooltipProps,
TooltipRef as RcTooltipRef,
} from 'rc-tooltip/lib/Tooltip';
import type { placements as Placements } from 'rc-tooltip/lib/placements';
import type { AlignType } from 'rc-trigger/lib/interface';
import useMergedState from 'rc-util/lib/hooks/useMergedState';
import type { CSSProperties } from 'react';
import * as React from 'react';
import { ConfigContext } from '../config-provider';
import theme from '../theme';
import type { PresetColorType } from '../_util/colors';
import { getTransitionName } from '../_util/motion';
import type { AdjustOverflow, PlacementsConfig } from '../_util/placements';
@ -18,6 +17,8 @@ import getPlacements from '../_util/placements';
import { cloneElement, isFragment, isValidElement } from '../_util/reactNode';
import type { LiteralUnion } from '../_util/type';
import warning from '../_util/warning';
import { ConfigContext } from '../config-provider';
import theme from '../theme';
import PurePanel from './PurePanel';
import useStyle from './style';
import { parseColor } from './util';
@ -199,6 +200,11 @@ const Tooltip = React.forwardRef<TooltipRef, TooltipProps>((props, ref) => {
afterVisibleChange,
destroyTooltipOnHide,
arrow = true,
title,
overlay,
builtinPlacements,
arrowPointAtCenter = false,
autoAdjustOverflow = true,
} = props;
const mergedShowArrow = !!arrow;
@ -261,29 +267,22 @@ const Tooltip = React.forwardRef<TooltipRef, TooltipProps>((props, ref) => {
defaultValue: props.defaultOpen ?? props.defaultVisible,
});
const isNoTitle = () => {
const { title, overlay } = props;
return !title && !overlay && title !== 0; // overlay for old version compatibility
};
const noTitle = !title && !overlay && title !== 0; // overlay for old version compatibility
const onOpenChange = (vis: boolean) => {
setOpen(isNoTitle() ? false : vis);
if (!isNoTitle()) {
setOpen(noTitle ? false : vis);
if (!noTitle) {
props.onOpenChange?.(vis);
props.onVisibleChange?.(vis);
}
};
const getTooltipPlacements = () => {
const { builtinPlacements, arrowPointAtCenter = false, autoAdjustOverflow = true } = props;
const tooltipPlacements = React.useMemo<BuildInPlacements>(() => {
let mergedArrowPointAtCenter = arrowPointAtCenter;
if (typeof arrow === 'object') {
mergedArrowPointAtCenter =
arrow.pointAtCenter ?? arrow.arrowPointAtCenter ?? arrowPointAtCenter;
}
return (
builtinPlacements ||
getPlacements({
@ -294,16 +293,15 @@ const Tooltip = React.forwardRef<TooltipRef, TooltipProps>((props, ref) => {
offset: token.marginXXS,
})
);
};
}, [arrowPointAtCenter, arrow, builtinPlacements, token]);
// 动态设置动画点
const onPopupAlign = (domNode: HTMLElement, align: AlignType) => {
const placements = getTooltipPlacements();
// 当前返回的位置
const placement = Object.keys(placements).find(
const placement = Object.keys(tooltipPlacements).find(
(key) =>
placements[key].points![0] === align.points?.[0] &&
placements[key].points![1] === align.points?.[1],
tooltipPlacements[key].points![0] === align.points?.[0] &&
tooltipPlacements[key].points![1] === align.points?.[1],
);
if (placement) {
@ -326,13 +324,12 @@ const Tooltip = React.forwardRef<TooltipRef, TooltipProps>((props, ref) => {
}
};
const getOverlay = () => {
const { title, overlay } = props;
const memoOverlay = React.useMemo<TooltipProps['overlay']>(() => {
if (title === 0) {
return title;
}
return overlay || title || '';
};
}, [overlay, title]);
const {
getPopupContainer,
@ -351,7 +348,7 @@ const Tooltip = React.forwardRef<TooltipRef, TooltipProps>((props, ref) => {
let tempOpen = open;
// Hide tooltip when there is no title
if (!('open' in props) && !('visible' in props) && isNoTitle()) {
if (!('open' in props) && !('visible' in props) && noTitle) {
tempOpen = false;
}
@ -395,14 +392,11 @@ const Tooltip = React.forwardRef<TooltipRef, TooltipProps>((props, ref) => {
mouseLeaveDelay={mouseLeaveDelay}
prefixCls={prefixCls}
overlayClassName={customOverlayClassName}
overlayStyle={{
...arrowContentStyle,
...overlayStyle,
}}
overlayStyle={{ ...arrowContentStyle, ...overlayStyle }}
getTooltipContainer={getPopupContainer || getTooltipContainer || getContextPopupContainer}
ref={tooltipRef}
builtinPlacements={getTooltipPlacements()}
overlay={getOverlay()}
builtinPlacements={tooltipPlacements}
overlay={memoOverlay}
visible={tempOpen}
onVisibleChange={onOpenChange}
afterVisibleChange={afterOpenChange ?? afterVisibleChange}

View File

@ -83,9 +83,6 @@ Array [
<div
class="ant-tour-content"
>
<div
class="ant-tour-arrow"
/>
<div
class="ant-tour-inner"
>
@ -249,9 +246,6 @@ Array [
<div
class="ant-tour-content"
>
<div
class="ant-tour-arrow"
/>
<div
class="ant-tour-inner"
>
@ -401,9 +395,6 @@ Array [
<div
class="ant-tour-content"
>
<div
class="ant-tour-arrow"
/>
<div
class="ant-tour-inner"
>
@ -567,9 +558,6 @@ Array [
<div
class="ant-tour-primary ant-tour-content"
>
<div
class="ant-tour-arrow"
/>
<div
class="ant-tour-inner"
>

View File

@ -20,9 +20,6 @@ exports[`Tour Primary 1`] = `
<div
class="ant-tour-primary ant-tour-content"
>
<div
class="ant-tour-arrow"
/>
<div
class="ant-tour-inner"
>
@ -305,9 +302,6 @@ exports[`Tour single 1`] = `
<div
class="ant-tour-content"
>
<div
class="ant-tour-arrow"
/>
<div
class="ant-tour-inner"
>
@ -467,9 +461,6 @@ exports[`Tour step support Primary 1`] = `
<div
class="ant-tour-primary ant-tour-content"
>
<div
class="ant-tour-arrow"
/>
<div
class="ant-tour-inner"
>

View File

@ -33,7 +33,6 @@ const TourPanel: React.FC<TourPanelProps> = ({ stepProps, current, type, indicat
nextButtonProps,
prevButtonProps,
type: stepType,
arrow,
className,
} = stepProps;
@ -102,7 +101,6 @@ const TourPanel: React.FC<TourPanelProps> = ({ stepProps, current, type, indicat
`${prefixCls}-content`,
)}
>
{arrow && <div className={`${prefixCls}-arrow`} key="arrow" />}
<div className={`${prefixCls}-inner`}>
<CloseOutlined className={`${prefixCls}-close`} onClick={onClose} />
{coverNode}

View File

@ -1,32 +1,31 @@
import classNames from 'classnames';
import type { BaseSelectRef } from 'rc-select';
import type { Placement } from 'rc-select/lib/BaseSelect';
import type { TreeSelectProps as RcTreeSelectProps } from 'rc-tree-select';
import RcTreeSelect, { SHOW_ALL, SHOW_CHILD, SHOW_PARENT, TreeNode } from 'rc-tree-select';
import type { BaseOptionType, DefaultOptionType } from 'rc-tree-select/lib/TreeSelect';
import omit from 'rc-util/lib/omit';
import * as React from 'react';
import type { Placement } from 'rc-select/lib/BaseSelect';
import { ConfigContext } from '../config-provider';
import DefaultRenderEmpty from '../config-provider/defaultRenderEmpty';
import DisabledContext from '../config-provider/DisabledContext';
import type { SizeType } from '../config-provider/SizeContext';
import SizeContext from '../config-provider/SizeContext';
import { FormItemInputContext } from '../form/context';
import genPurePanel from '../_util/PurePanel';
import useSelectStyle from '../select/style';
import getIcons from '../select/utils/iconUtil';
import type { AntTreeNodeProps, TreeProps } from '../tree';
import type { SwitcherIcon } from '../tree/Tree';
import renderSwitcherIcon from '../tree/utils/iconUtil';
import type { SelectCommonPlacement } from '../_util/motion';
import { getTransitionDirection, getTransitionName } from '../_util/motion';
import type { InputStatus } from '../_util/statusUtils';
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
import { useCompactItemContext } from '../space/Compact';
import warning from '../_util/warning';
import useStyle from './style';
import { ConfigContext } from '../config-provider';
import DisabledContext from '../config-provider/DisabledContext';
import type { SizeType } from '../config-provider/SizeContext';
import SizeContext from '../config-provider/SizeContext';
import DefaultRenderEmpty from '../config-provider/defaultRenderEmpty';
import { FormItemInputContext } from '../form/context';
import useSelectStyle from '../select/style';
import useShowArrow from '../select/useShowArrow';
import getIcons from '../select/utils/iconUtil';
import { useCompactItemContext } from '../space/Compact';
import type { AntTreeNodeProps, TreeProps } from '../tree';
import type { SwitcherIcon } from '../tree/Tree';
import SwitcherIconCom from '../tree/utils/iconUtil';
import useStyle from './style';
type RawValue = string | number;
@ -209,13 +208,22 @@ const InternalTreeSelect = <
hashId,
);
const renderSwitcherIcon = (nodeProps: AntTreeNodeProps) => (
<SwitcherIconCom
prefixCls={treePrefixCls}
switcherIcon={switcherIcon}
treeNodeProps={nodeProps}
showLine={treeLine}
/>
);
const returnNode = (
<RcTreeSelect
virtual={virtual}
dropdownMatchSelectWidth={dropdownMatchSelectWidth}
disabled={mergedDisabled}
{...selectProps}
ref={ref as any}
ref={ref}
prefixCls={prefixCls}
className={mergedClassName}
listHeight={listHeight}
@ -229,9 +237,7 @@ const InternalTreeSelect = <
placement={memoizedPlacement}
removeIcon={removeIcon}
clearIcon={clearIcon}
switcherIcon={(nodeProps: AntTreeNodeProps) =>
renderSwitcherIcon(treePrefixCls, switcherIcon, nodeProps, treeLine)
}
switcherIcon={renderSwitcherIcon}
showTreeIcon={treeIcon as any}
notFoundContent={mergedNotFound}
getPopupContainer={getPopupContainer || getContextPopupContainer}

View File

@ -5,12 +5,11 @@ import RcTree from 'rc-tree';
import type { DataNode, Key } from 'rc-tree/lib/interface';
import type { Component } from 'react';
import React from 'react';
import { ConfigContext } from '../config-provider';
import initCollapseMotion from '../_util/motion';
import dropIndicatorRender from './utils/dropIndicator';
import renderSwitcherIcon from './utils/iconUtil';
import { ConfigContext } from '../config-provider';
import useStyle from './style';
import dropIndicatorRender from './utils/dropIndicator';
import SwitcherIconCom from './utils/iconUtil';
export type SwitcherIcon = React.ReactNode | ((props: AntTreeNodeProps) => React.ReactNode);
export type TreeLeafIcon = React.ReactNode | ((props: AntTreeNodeProps) => React.ReactNode);
@ -218,6 +217,15 @@ const Tree = React.forwardRef<RcTree, TreeProps>((props, ref) => {
return mergedDraggable;
}, [draggable]);
const renderSwitcherIcon = (nodeProps: AntTreeNodeProps) => (
<SwitcherIconCom
prefixCls={prefixCls}
switcherIcon={switcherIcon}
treeNodeProps={nodeProps}
showLine={showLine}
/>
);
return wrapSSR(
<RcTree
itemHeight={20}
@ -238,9 +246,7 @@ const Tree = React.forwardRef<RcTree, TreeProps>((props, ref) => {
direction={direction}
checkable={checkable ? <span className={`${prefixCls}-checkbox-inner`} /> : checkable}
selectable={selectable}
switcherIcon={(nodeProps: AntTreeNodeProps) =>
renderSwitcherIcon(prefixCls, switcherIcon, nodeProps, showLine)
}
switcherIcon={renderSwitcherIcon}
draggable={draggableConfig}
>
{children}

View File

@ -1,7 +1,7 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import React from 'react';
import { calcRangeKeys } from '../utils/dictUtil';
import renderSwitcherIcon from '../utils/iconUtil';
import SwitcherIconCom from '../utils/iconUtil';
describe('Tree util', () => {
describe('calcRangeKeys', () => {
@ -38,20 +38,16 @@ describe('Tree util', () => {
});
it('return empty array without startKey and endKey', () => {
const keys = calcRangeKeys({
treeData,
expandedKeys: ['0-0', '0-2', '0-2-0'],
});
const keys = calcRangeKeys({ treeData, expandedKeys: ['0-0', '0-2', '0-2-0'] });
expect(keys).toEqual([]);
});
});
describe('renderSwitcherIcon', () => {
describe('SwitcherIconCom', () => {
const prefixCls = 'tree';
it('returns a loading icon when loading', () => {
const { container } = render(
<>{renderSwitcherIcon(prefixCls, undefined, { loading: true }, true)}</>,
<SwitcherIconCom prefixCls={prefixCls} treeNodeProps={{ loading: true }} showLine />,
);
expect(container.getElementsByClassName(`${prefixCls}-switcher-loading-icon`)).toHaveLength(
1,
@ -60,7 +56,11 @@ describe('Tree util', () => {
it('returns nothing when node is a leaf without showLine', () => {
const { container } = render(
<>{renderSwitcherIcon(prefixCls, undefined, { loading: false, isLeaf: true }, false)}</>,
<SwitcherIconCom
prefixCls={prefixCls}
treeNodeProps={{ loading: false, isLeaf: true }}
showLine={false}
/>,
);
expect(container).toBeEmptyDOMElement();
});
@ -69,16 +69,12 @@ describe('Tree util', () => {
const testId = 'custom-icon';
const customLeafIcon = <div data-testid={testId} />;
const { container } = render(
<>
{renderSwitcherIcon(
prefixCls,
undefined,
{ loading: false, isLeaf: true },
{ showLeafIcon: customLeafIcon },
)}
</>,
<SwitcherIconCom
prefixCls={prefixCls}
treeNodeProps={{ loading: false, isLeaf: true }}
showLine={{ showLeafIcon: customLeafIcon }}
/>,
);
expect(screen.getByTestId(testId)).toBeVisible();
expect(
container.getElementsByClassName(`${prefixCls}-switcher-line-custom-icon`),
@ -90,16 +86,12 @@ describe('Tree util', () => {
[`${prefixCls}-switcher-leaf-line`, false],
])('returns %p element when showLeafIcon is %p', (expectedClassName, showLeafIcon) => {
const { container } = render(
<>
{renderSwitcherIcon(
prefixCls,
undefined,
{ loading: false, isLeaf: true },
{ showLeafIcon },
)}
</>,
<SwitcherIconCom
prefixCls={prefixCls}
treeNodeProps={{ loading: false, isLeaf: true }}
showLine={{ showLeafIcon }}
/>,
);
expect(container.getElementsByClassName(expectedClassName)).toHaveLength(1);
});
});

View File

@ -6,14 +6,18 @@ import PlusSquareOutlined from '@ant-design/icons/PlusSquareOutlined';
import classNames from 'classnames';
import * as React from 'react';
import { cloneElement, isValidElement } from '../../_util/reactNode';
import type { AntTreeNodeProps, TreeLeafIcon, SwitcherIcon } from '../Tree';
import type { AntTreeNodeProps, SwitcherIcon, TreeLeafIcon } from '../Tree';
interface SwitcherIconProps {
prefixCls: string;
treeNodeProps: AntTreeNodeProps;
switcherIcon?: SwitcherIcon;
showLine?: boolean | { showLeafIcon: boolean | TreeLeafIcon };
}
const SwitcherIconCom: React.FC<SwitcherIconProps> = (props) => {
const { prefixCls, switcherIcon, treeNodeProps, showLine } = props;
export default function renderSwitcherIcon(
prefixCls: string,
switcherIcon: SwitcherIcon,
treeNodeProps: AntTreeNodeProps,
showLine?: boolean | { showLeafIcon: boolean | TreeLeafIcon },
): React.ReactNode {
const { isLeaf, expanded, loading } = treeNodeProps;
if (loading) {
@ -40,7 +44,7 @@ export default function renderSwitcherIcon(
});
}
return leafIcon;
return leafIcon as unknown as React.ReactElement;
}
return showLeafIcon ? (
@ -61,7 +65,7 @@ export default function renderSwitcherIcon(
}
if (switcher) {
return switcher;
return switcher as unknown as React.ReactElement;
}
if (showLine) {
@ -72,4 +76,6 @@ export default function renderSwitcherIcon(
);
}
return <CaretDownFilled className={switcherCls} />;
}
};
export default SwitcherIconCom;

View File

@ -272,7 +272,7 @@
"remark-cli": "^11.0.0",
"remark-lint": "^9.0.0",
"remark-preset-lint-recommended": "^6.0.0",
"rome": "^11.0.0",
"rome": "^12.0.0",
"semver": "^7.3.5",
"simple-git": "^3.0.0",
"size-limit": "^8.1.0",

View File

@ -1,6 +1,7 @@
/* eslint-disable react/jsx-no-constructed-context-values */
import { createCache, StyleProvider } from '@ant-design/cssinjs';
import glob from 'glob';
import path from 'path';
import * as React from 'react';
import { renderToString } from 'react-dom/server';
import { render } from '../utils';
@ -20,8 +21,9 @@ export type Options = {
function baseText(doInject: boolean, component: string, options: Options = {}) {
const files = glob.globSync(`./components/${component}/demo/*.tsx`);
files.forEach((file) => {
// to compatible windows path
file = file.split(path.sep).join('/');
const testMethod =
options.skip === true ||
(Array.isArray(options.skip) && options.skip.some((c) => file.includes(c)))