Skip to content

Commit

Permalink
feat: add _internalComponents for antd to fix findDOMNode warning (
Browse files Browse the repository at this point in the history
…#715)

* chore: update interface

* test: update testcase
  • Loading branch information
zombieJ authored May 17, 2024
1 parent 81eafee commit efba6a5
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 39 deletions.
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
"devDependencies": {
"@rc-component/father-plugin": "^1.0.0",
"@testing-library/jest-dom": "^6.1.5",
"@testing-library/react": "^14.0.0",
"@testing-library/react": "^15.0.7",
"@types/jest": "^29.5.2",
"@types/react": "^18.2.14",
"@types/react-dom": "^18.2.6",
Expand All @@ -68,8 +68,8 @@
"less": "^4.1.3",
"np": "^9.0.0",
"rc-test": "^7.0.14",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"regenerator-runtime": "^0.14.0",
"typescript": "^5.1.6"
},
Expand Down
38 changes: 28 additions & 10 deletions src/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import useMemoCallback from './hooks/useMemoCallback';
import useUUID from './hooks/useUUID';
import type {
BuiltinPlacements,
Components,
ItemType,
MenuClickEventHandler,
MenuInfo,
Expand Down Expand Up @@ -60,6 +61,7 @@ export interface MenuProps
prefixCls?: string;
rootClassName?: string;
items?: ItemType[];

/** @deprecated Please use `items` instead */
children?: React.ReactNode;

Expand Down Expand Up @@ -148,6 +150,14 @@ export interface MenuProps
disabled: boolean;
},
) => React.ReactElement;

/**
* @private NEVER! EVER! USE IN PRODUCTION!!!
* This is a hack API for `antd` to fix `findDOMNode` issue.
* Not use it! Not accept any PR try to make it as normal API.
* By zombieJ
*/
_internalComponents?: Components;
}

interface LegacyMenuProps extends MenuProps {
Expand Down Expand Up @@ -228,12 +238,20 @@ const Menu = React.forwardRef<MenuRef, MenuProps>((props, ref) => {
_internalRenderMenuItem,
_internalRenderSubMenuItem,

_internalComponents,

...restProps
} = props as LegacyMenuProps;

const childList: React.ReactElement[] = React.useMemo(
() => parseItems(children, items, EMPTY_LIST),
[children, items],
const [childList, measureChildList]: [
visibleChildList: React.ReactElement[],
measureChildList: React.ReactElement[],
] = React.useMemo(
() => [
parseItems(children, items, EMPTY_LIST, _internalComponents),
parseItems(children, items, EMPTY_LIST, {}),
],
[children, items, _internalComponents],
);

const [mounted, setMounted] = React.useState(false);
Expand Down Expand Up @@ -274,9 +292,8 @@ const Menu = React.forwardRef<MenuRef, MenuProps>((props, ref) => {
};

// >>>>> Cache & Reset open keys when inlineCollapsed changed
const [inlineCacheOpenKeys, setInlineCacheOpenKeys] = React.useState(
mergedOpenKeys,
);
const [inlineCacheOpenKeys, setInlineCacheOpenKeys] =
React.useState(mergedOpenKeys);

const mountRef = React.useRef(false);

Expand Down Expand Up @@ -352,9 +369,10 @@ const Menu = React.forwardRef<MenuRef, MenuProps>((props, ref) => {
[registerPath, unregisterPath],
);

const pathUserContext = React.useMemo(() => ({ isSubPathKey }), [
isSubPathKey,
]);
const pathUserContext = React.useMemo(
() => ({ isSubPathKey }),
[isSubPathKey],
);

React.useEffect(() => {
refreshOverflowKeys(
Expand Down Expand Up @@ -653,7 +671,7 @@ const Menu = React.forwardRef<MenuRef, MenuProps>((props, ref) => {
{/* Measure menu keys. Add `display: none` to avoid some developer miss use the Menu */}
<div style={{ display: 'none' }} aria-hidden>
<PathRegisterContext.Provider value={registerPathContext}>
{childList}
{measureChildList}
</PathRegisterContext.Provider>
</div>
</MenuContextProvider>
Expand Down
7 changes: 6 additions & 1 deletion src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ interface ItemSharedProps {
}

export interface SubMenuType extends ItemSharedProps {
type?: "submenu";
type?: 'submenu';

label?: React.ReactNode;

Expand Down Expand Up @@ -133,3 +133,8 @@ export type MenuRef = {
focus: (options?: FocusOptions) => void;
list: HTMLUListElement;
};

// ======================== Component ========================
export type ComponentType = 'submenu' | 'item' | 'group' | 'divider';

export type Components = Partial<Record<ComponentType, React.ComponentType<any>>>;
46 changes: 32 additions & 14 deletions src/utils/nodeUtil.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
import * as React from 'react';
import type { ItemType } from '../interface';
import MenuItemGroup from '../MenuItemGroup';
import SubMenu from '../SubMenu';
import Divider from '../Divider';
import type { Components, ItemType } from '../interface';
import MenuItem from '../MenuItem';
import MenuItemGroup from '../MenuItemGroup';
import SubMenu from '../SubMenu';
import { parseChildren } from './commonUtil';

function convertItemsToNodes(
list: ItemType[],
components: Required<Components>,
) {
const {
item: MergedMenuItem,
group: MergedMenuItemGroup,
submenu: MergedSubMenu,
divider: MergedDivider,
} = components;

function convertItemsToNodes(list: ItemType[]) {
return (list || [])
.map((opt, index) => {
if (opt && typeof opt === 'object') {
Expand All @@ -19,29 +28,29 @@ function convertItemsToNodes(list: ItemType[]) {
if (type === 'group') {
// Group
return (
<MenuItemGroup key={mergedKey} {...restProps} title={label}>
{convertItemsToNodes(children)}
</MenuItemGroup>
<MergedMenuItemGroup key={mergedKey} {...restProps} title={label}>
{convertItemsToNodes(children, components)}
</MergedMenuItemGroup>
);
}

// Sub Menu
return (
<SubMenu key={mergedKey} {...restProps} title={label}>
{convertItemsToNodes(children)}
</SubMenu>
<MergedSubMenu key={mergedKey} {...restProps} title={label}>
{convertItemsToNodes(children, components)}
</MergedSubMenu>
);
}

// MenuItem & Divider
if (type === 'divider') {
return <Divider key={mergedKey} {...restProps} />;
return <MergedDivider key={mergedKey} {...restProps} />;
}

return (
<MenuItem key={mergedKey} {...restProps}>
<MergedMenuItem key={mergedKey} {...restProps}>
{label}
</MenuItem>
</MergedMenuItem>
);
}

Expand All @@ -54,11 +63,20 @@ export function parseItems(
children: React.ReactNode | undefined,
items: ItemType[] | undefined,
keyPath: string[],
components: Components,
) {
let childNodes = children;

const mergedComponents: Required<Components> = {
divider: Divider,
item: MenuItem,
group: MenuItemGroup,
submenu: SubMenu,
...components,
};

if (items) {
childNodes = convertItemsToNodes(items);
childNodes = convertItemsToNodes(items, mergedComponents);
}

return parseChildren(childNodes, keyPath);
Expand Down
29 changes: 18 additions & 11 deletions tests/Responsive.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
/* eslint-disable no-undef, react/no-multi-comp, react/jsx-curly-brace-presence, max-classes-per-file */
import { fireEvent, render } from '@testing-library/react';
import { act, fireEvent, render } from '@testing-library/react';
import ResizeObserver from 'rc-resize-observer';
import KeyCode from 'rc-util/lib/KeyCode';
import { spyElementPrototype } from 'rc-util/lib/test/domHook';
import React from 'react';
import { act } from 'react-dom/test-utils';
import Menu, { MenuItem, SubMenu } from '../src';
import { OVERFLOW_KEY } from '../src/hooks/useKeyRecords';
import { last } from './util';
import { spyElementPrototype } from 'rc-util/lib/test/domHook';

jest.mock('rc-resize-observer', () => {
const R = require('react');
Expand All @@ -29,7 +28,6 @@ jest.mock('rc-resize-observer', () => {
});
});


describe('Menu.Responsive', () => {
beforeEach(() => {
global.resizeProps = null;
Expand Down Expand Up @@ -61,9 +59,18 @@ describe('Menu.Responsive', () => {

render(
<React.StrictMode>
<Menu mode="horizontal">
<MyItem key="1">Good</MyItem>
</Menu>
<Menu
mode="horizontal"
_internalComponents={{
item: MyItem,
}}
items={[
{
label: 'Good',
key: '1',
},
]}
/>
</React.StrictMode>,
);

Expand Down Expand Up @@ -115,8 +122,8 @@ describe('Menu.Responsive', () => {
get() {
return () => ({
width: 41,
})
}
});
},
}));
// Set container width
act(() => {
Expand All @@ -129,8 +136,8 @@ describe('Menu.Responsive', () => {
get() {
return () => ({
width: 20,
})
}
});
},
}));
// Resize every item
getResizeProps()
Expand Down

0 comments on commit efba6a5

Please sign in to comment.