mirror of
https://gitee.com/ant-design/ant-design.git
synced 2024-12-03 04:30:06 +08:00
commit
4d2f648181
15
.github/PULL_REQUEST_TEMPLATE.md
vendored
15
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -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
|
||||
|
14
.github/PULL_REQUEST_TEMPLATE/pr_cn.md
vendored
14
.github/PULL_REQUEST_TEMPLATE/pr_cn.md
vendored
@ -61,3 +61,17 @@
|
||||
- [ ] 代码演示已提供或无须提供
|
||||
- [ ] TypeScript 定义已补充或无须补充
|
||||
- [ ] Changelog 已提供或无须提供
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
以下为 copilot 自动生成的 CR 结果,请勿修改
|
||||
-->
|
||||
|
||||
### 🚀 概述
|
||||
|
||||
copilot:summary
|
||||
|
||||
### 🔍 实现细节
|
||||
|
||||
copilot:walkthrough
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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}
|
||||
|
@ -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>
|
||||
`;
|
||||
|
||||
|
@ -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>
|
||||
`;
|
||||
|
||||
|
@ -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>
|
||||
);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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>,
|
||||
);
|
||||
});
|
||||
|
@ -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;
|
||||
|
@ -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}
|
||||
|
@ -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) {
|
||||
|
@ -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',
|
||||
|
@ -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>
|
||||
);
|
||||
|
||||
|
@ -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;"
|
||||
|
@ -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`] = `
|
||||
|
@ -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>
|
||||
);
|
||||
|
||||
|
@ -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 />
|
||||
|
@ -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>
|
||||
);
|
||||
|
||||
|
@ -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>
|
||||
);
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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}
|
||||
|
@ -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}
|
||||
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
@ -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', () => {
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
@ -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}
|
||||
|
@ -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"
|
||||
>
|
||||
|
@ -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"
|
||||
>
|
||||
|
@ -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}
|
||||
|
@ -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}
|
||||
|
@ -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}
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
@ -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;
|
||||
|
@ -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",
|
||||
|
@ -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)))
|
||||
|
Loading…
Reference in New Issue
Block a user