mirror of
https://github.com/ant-design/ant-design.git
synced 2025-08-15 13:38:29 +02:00
feat: add ContextIsolator component to Select (#54544)
* fix: add ContextIsolator component to Select * feat: add ContextIsolator to mergedPopupRender * feat: useMemo * test: add tests * fix: Delete the 'ContextIsolator' at the top level of the component * feat: add usePopupRender * test: change tests --------- Co-authored-by: 刘欢 <lh01217311@antgroup.com>
This commit is contained in:
parent
ff5218840f
commit
953a8c0278
7 changed files with 147 additions and 6 deletions
|
@ -1,4 +1,5 @@
|
|||
import React from 'react';
|
||||
import { Button, Input, Space } from 'antd';
|
||||
import type { SingleValueType } from 'rc-cascader/lib/Cascader';
|
||||
|
||||
import type { DefaultOptionType } from '..';
|
||||
|
@ -803,4 +804,41 @@ describe('Cascader', () => {
|
|||
|
||||
errSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('Cascader ContextIsolator', () => {
|
||||
const { container } = render(
|
||||
<Space.Compact>
|
||||
<Cascader
|
||||
open
|
||||
style={{ width: 120 }}
|
||||
popupRender={(menu) => {
|
||||
return (
|
||||
<div>
|
||||
{menu}
|
||||
<Button>123</Button>
|
||||
<Input style={{ width: 50 }} />
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
options={[
|
||||
{ value: 'jack', label: 'Jack' },
|
||||
{ value: 'lucy', label: 'Lucy' },
|
||||
]}
|
||||
/>
|
||||
<Button className="test-button">test</Button>
|
||||
</Space.Compact>,
|
||||
);
|
||||
|
||||
const compactButton = container.querySelector('.test-button');
|
||||
const popupElement = document.querySelector('.ant-select-dropdown');
|
||||
// selector should have compact
|
||||
expect(compactButton).toBeInTheDocument();
|
||||
expect(compactButton!.className.includes('compact')).toBeTruthy();
|
||||
// popupRender element haven't compact
|
||||
expect(popupElement).toBeInTheDocument();
|
||||
const button = popupElement!.querySelector('button');
|
||||
const input = popupElement!.querySelector('input');
|
||||
expect(button!.className.includes('compact')).toBeFalsy();
|
||||
expect(input!.className.includes('compact')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -31,6 +31,7 @@ import useVariant from '../form/hooks/useVariants';
|
|||
import mergedBuiltinPlacements from '../select/mergedBuiltinPlacements';
|
||||
import useSelectStyle from '../select/style';
|
||||
import useIcons from '../select/useIcons';
|
||||
import usePopupRender from '../select/usePopupRender';
|
||||
import useShowArrow from '../select/useShowArrow';
|
||||
import { useCompactItemContext } from '../space/Compact';
|
||||
import useBase from './hooks/useBase';
|
||||
|
@ -292,7 +293,8 @@ const Cascader = React.forwardRef<CascaderRef, CascaderProps<any>>((props, ref)
|
|||
cssVarCls,
|
||||
);
|
||||
|
||||
const mergedPopupRender = popupRender || dropdownRender;
|
||||
const mergedPopupRender = usePopupRender(popupRender || dropdownRender);
|
||||
|
||||
const mergedPopupMenuColumnStyle = popupMenuColumnStyle || dropdownMenuColumnStyle;
|
||||
const mergedOnOpenChange = onOpenChange || onDropdownVisibleChange;
|
||||
const mergedPopupStyle = styles?.popup?.root || contextStyles.popup?.root || dropdownStyle;
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import React from 'react';
|
||||
import { CloseOutlined } from '@ant-design/icons';
|
||||
import { Button, Input, Space } from 'antd';
|
||||
|
||||
import type { SelectProps } from '..';
|
||||
import Select from '..';
|
||||
import Form from '../../form';
|
||||
import { resetWarned } from '../../_util/warning';
|
||||
import focusTest from '../../../tests/shared/focusTest';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { act, fireEvent, render } from '../../../tests/utils';
|
||||
import Form from '../../form';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
|
@ -290,4 +291,42 @@ describe('Select', () => {
|
|||
errSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
it('Select ContextIsolator', () => {
|
||||
const { container } = render(
|
||||
<Space.Compact>
|
||||
<Select
|
||||
open
|
||||
defaultValue="lucy"
|
||||
style={{ width: 120 }}
|
||||
popupRender={(menu) => {
|
||||
return (
|
||||
<div>
|
||||
{menu}
|
||||
<Button>123</Button>
|
||||
<Input style={{ width: 50 }} />
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
options={[
|
||||
{ value: 'jack', label: 'Jack' },
|
||||
{ value: 'lucy', label: 'Lucy' },
|
||||
]}
|
||||
/>
|
||||
<Button className="test-button">test</Button>
|
||||
</Space.Compact>,
|
||||
);
|
||||
|
||||
const compactButton = container.querySelector('.test-button');
|
||||
const popupElement = document.querySelector('.ant-select-dropdown');
|
||||
// selector should have compact
|
||||
expect(compactButton).toBeInTheDocument();
|
||||
expect(compactButton!.className.includes('compact')).toBeTruthy();
|
||||
// popupRender element haven't compact
|
||||
expect(popupElement).toBeInTheDocument();
|
||||
const button = popupElement!.querySelector('button');
|
||||
const input = popupElement!.querySelector('input');
|
||||
expect(button!.className.includes('compact')).toBeFalsy();
|
||||
expect(input!.className.includes('compact')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -29,6 +29,7 @@ import { useToken } from '../theme/internal';
|
|||
import mergedBuiltinPlacements from './mergedBuiltinPlacements';
|
||||
import useStyle from './style';
|
||||
import useIcons from './useIcons';
|
||||
import usePopupRender from './usePopupRender';
|
||||
import useShowArrow from './useShowArrow';
|
||||
|
||||
type RawValue = string | number;
|
||||
|
@ -203,7 +204,9 @@ const InternalSelect = <
|
|||
popupMatchSelectWidth ?? dropdownMatchSelectWidth ?? contextPopupMatchSelectWidth;
|
||||
|
||||
const mergedPopupStyle = styles?.popup?.root || contextStyles.popup?.root || dropdownStyle;
|
||||
const mergedPopupRender = popupRender || dropdownRender;
|
||||
|
||||
const mergedPopupRender = usePopupRender(popupRender || dropdownRender);
|
||||
|
||||
const mergedOnOpenChange = onOpenChange || onDropdownVisibleChange;
|
||||
|
||||
// ===================== Form Status =====================
|
||||
|
|
18
components/select/usePopupRender.tsx
Normal file
18
components/select/usePopupRender.tsx
Normal file
|
@ -0,0 +1,18 @@
|
|||
import React from 'react';
|
||||
|
||||
import ContextIsolator from '../_util/ContextIsolator';
|
||||
|
||||
type RenderFunction<T extends any[]> = (...args: T) => React.ReactNode;
|
||||
|
||||
function usePopupRender<T extends [React.ReactElement, ...any[]]>(
|
||||
renderFn?: RenderFunction<T>,
|
||||
): ((...args: T) => React.ReactElement) | undefined {
|
||||
return React.useMemo(() => {
|
||||
if (!renderFn) {
|
||||
return undefined;
|
||||
}
|
||||
return (...args: T) => <ContextIsolator space>{renderFn(...args)}</ContextIsolator>;
|
||||
}, [renderFn]);
|
||||
}
|
||||
|
||||
export default usePopupRender;
|
|
@ -1,11 +1,12 @@
|
|||
import React from 'react';
|
||||
import { Button, Input, Space } from 'antd';
|
||||
|
||||
import TreeSelect, { TreeNode } from '..';
|
||||
import { resetWarned } from '../../_util/warning';
|
||||
import focusTest from '../../../tests/shared/focusTest';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { render, fireEvent } from '../../../tests/utils';
|
||||
import { fireEvent, render } from '../../../tests/utils';
|
||||
|
||||
describe('TreeSelect', () => {
|
||||
focusTest(TreeSelect, { refFocus: true });
|
||||
|
@ -162,4 +163,41 @@ describe('TreeSelect', () => {
|
|||
|
||||
errSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('TreeSelect ContextIsolator', () => {
|
||||
const { container } = render(
|
||||
<Space.Compact>
|
||||
<TreeSelect
|
||||
open
|
||||
defaultValue="lucy"
|
||||
style={{ width: 120 }}
|
||||
popupRender={(menu) => {
|
||||
return (
|
||||
<div>
|
||||
{menu}
|
||||
<Button>123</Button>
|
||||
<Input style={{ width: 50 }} />
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
treeData={[
|
||||
{ value: 'jack', title: 'Jack', children: [{ value: 'Emily', title: 'Emily' }] },
|
||||
{ value: 'lucy', title: 'Lucy' },
|
||||
]}
|
||||
/>
|
||||
<Button className="test-button">test</Button>
|
||||
</Space.Compact>,
|
||||
);
|
||||
const compactButton = container.querySelector('.test-button');
|
||||
const popupElement = document.querySelector('.ant-select-dropdown');
|
||||
// selector should have compact
|
||||
expect(compactButton).toBeInTheDocument();
|
||||
expect(compactButton!.className.includes('compact')).toBeTruthy();
|
||||
// popupRender element haven't compact
|
||||
expect(popupElement).toBeInTheDocument();
|
||||
const button = popupElement!.querySelector('button');
|
||||
const input = popupElement!.querySelector('input');
|
||||
expect(button!.className.includes('compact')).toBeFalsy();
|
||||
expect(input!.className.includes('compact')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -16,6 +16,7 @@ import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
|
|||
import { devUseWarning } from '../_util/warning';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import type { Variant } from '../config-provider';
|
||||
import { useComponentConfig } from '../config-provider/context';
|
||||
import DefaultRenderEmpty from '../config-provider/defaultRenderEmpty';
|
||||
import DisabledContext from '../config-provider/DisabledContext';
|
||||
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
|
||||
|
@ -26,6 +27,7 @@ import useVariant from '../form/hooks/useVariants';
|
|||
import mergedBuiltinPlacements from '../select/mergedBuiltinPlacements';
|
||||
import useSelectStyle from '../select/style';
|
||||
import useIcons from '../select/useIcons';
|
||||
import usePopupRender from '../select/usePopupRender';
|
||||
import useShowArrow from '../select/useShowArrow';
|
||||
import { useCompactItemContext } from '../space/Compact';
|
||||
import { useToken } from '../theme/internal';
|
||||
|
@ -33,7 +35,6 @@ import type { AntTreeNodeProps, TreeProps } from '../tree';
|
|||
import type { SwitcherIcon } from '../tree/Tree';
|
||||
import SwitcherIconCom from '../tree/utils/iconUtil';
|
||||
import useStyle from './style';
|
||||
import { useComponentConfig } from '../config-provider/context';
|
||||
|
||||
type RawValue = string | number;
|
||||
|
||||
|
@ -226,7 +227,9 @@ const InternalTreeSelect = <ValueType = any, OptionType extends DataNode = DataN
|
|||
);
|
||||
|
||||
const mergedPopupStyle = styles?.popup?.root || contextStyles?.popup?.root || dropdownStyle;
|
||||
const mergedPopupRender = popupRender || dropdownRender;
|
||||
|
||||
const mergedPopupRender = usePopupRender(popupRender || dropdownRender);
|
||||
|
||||
const mergedOnOpenChange = onOpenChange || onDropdownVisibleChange;
|
||||
|
||||
const isMultiple = !!(treeCheckable || multiple);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue