Skip to content

Commit

Permalink
chore: support internal props (#428)
Browse files Browse the repository at this point in the history
* chore: support internal props

* chore: more props
  • Loading branch information
zombieJ authored Dec 23, 2021
1 parent fbc51ff commit 3c74ad1
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 46 deletions.
114 changes: 69 additions & 45 deletions src/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import useUUID from './hooks/useUUID';
import { PathRegisterContext, PathUserContext } from './context/PathContext';
import useKeyRecords, { OVERFLOW_KEY } from './hooks/useKeyRecords';
import { IdContext } from './context/IdContext';
import PrivateContext from './context/PrivateContext';

/**
* Menu modify after refactor:
Expand Down Expand Up @@ -110,6 +111,16 @@ export interface MenuProps
// >>>>> Events
onClick?: MenuClickEventHandler;
onOpenChange?: (openKeys: string[]) => void;

// >>>>> Internal
/***
* @private Only used for `pro-layout`. Do not use in your prod directly
* and we do not promise any compatibility for this.
*/
_internalRenderMenuItem?: (
originNode: React.ReactElement,
menuItemProps: any,
) => React.ReactElement;
}

interface LegacyMenuProps extends MenuProps {
Expand Down Expand Up @@ -184,6 +195,9 @@ const Menu: React.FC<MenuProps> = props => {
openAnimation,
openTransitionName,

// Internal
_internalRenderMenuItem,

...restProps
} = props as LegacyMenuProps;

Expand Down Expand Up @@ -426,6 +440,14 @@ const Menu: React.FC<MenuProps> = props => {
setMounted(true);
}, []);

// ======================= Context ========================
const privateContext = React.useMemo(
() => ({
_internalRenderMenuItem,
}),
[_internalRenderMenuItem],
);

// ======================== Render ========================

// >>>>> Children
Expand Down Expand Up @@ -502,51 +524,53 @@ const Menu: React.FC<MenuProps> = props => {

// >>>>> Render
return (
<IdContext.Provider value={uuid}>
<MenuContextProvider
prefixCls={prefixCls}
mode={mergedMode}
openKeys={mergedOpenKeys}
rtl={isRtl}
// Disabled
disabled={disabled}
// Motion
motion={mounted ? motion : null}
defaultMotions={mounted ? defaultMotions : null}
// Active
activeKey={mergedActiveKey}
onActive={onActive}
onInactive={onInactive}
// Selection
selectedKeys={mergedSelectKeys}
// Level
inlineIndent={inlineIndent}
// Popup
subMenuOpenDelay={subMenuOpenDelay}
subMenuCloseDelay={subMenuCloseDelay}
forceSubMenuRender={forceSubMenuRender}
builtinPlacements={builtinPlacements}
triggerSubMenuAction={triggerSubMenuAction}
getPopupContainer={getInternalPopupContainer}
// Icon
itemIcon={itemIcon}
expandIcon={expandIcon}
// Events
onItemClick={onInternalClick}
onOpenChange={onInternalOpenChange}
>
<PathUserContext.Provider value={pathUserContext}>
{container}
</PathUserContext.Provider>

{/* 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}
</PathRegisterContext.Provider>
</div>
</MenuContextProvider>
</IdContext.Provider>
<PrivateContext.Provider value={privateContext}>
<IdContext.Provider value={uuid}>
<MenuContextProvider
prefixCls={prefixCls}
mode={mergedMode}
openKeys={mergedOpenKeys}
rtl={isRtl}
// Disabled
disabled={disabled}
// Motion
motion={mounted ? motion : null}
defaultMotions={mounted ? defaultMotions : null}
// Active
activeKey={mergedActiveKey}
onActive={onActive}
onInactive={onInactive}
// Selection
selectedKeys={mergedSelectKeys}
// Level
inlineIndent={inlineIndent}
// Popup
subMenuOpenDelay={subMenuOpenDelay}
subMenuCloseDelay={subMenuCloseDelay}
forceSubMenuRender={forceSubMenuRender}
builtinPlacements={builtinPlacements}
triggerSubMenuAction={triggerSubMenuAction}
getPopupContainer={getInternalPopupContainer}
// Icon
itemIcon={itemIcon}
expandIcon={expandIcon}
// Events
onItemClick={onInternalClick}
onOpenChange={onInternalOpenChange}
>
<PathUserContext.Provider value={pathUserContext}>
{container}
</PathUserContext.Provider>

{/* 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}
</PathRegisterContext.Provider>
</div>
</MenuContextProvider>
</IdContext.Provider>
</PrivateContext.Provider>
);
};

Expand Down
12 changes: 11 additions & 1 deletion src/MenuItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import Icon from './Icon';
import useDirectionStyle from './hooks/useDirectionStyle';
import { useFullPath, useMeasure } from './context/PathContext';
import { useMenuId } from './context/IdContext';
import PrivateContext from './context/PrivateContext';

export interface MenuItemProps
extends Omit<
Expand Down Expand Up @@ -116,6 +117,9 @@ const InternalMenuItem = (props: MenuItemProps) => {
// Active
onActive,
} = React.useContext(MenuContext);

const { _internalRenderMenuItem } = React.useContext(PrivateContext);

const itemCls = `${prefixCls}-item`;

const legacyMenuItemRef = React.useRef<any>();
Expand Down Expand Up @@ -199,7 +203,7 @@ const InternalMenuItem = (props: MenuItemProps) => {
optionRoleProps['aria-selected'] = selected;
}

return (
let renderNode = (
<LegacyMenuItem
ref={legacyMenuItemRef}
elementRef={elementRef}
Expand Down Expand Up @@ -238,6 +242,12 @@ const InternalMenuItem = (props: MenuItemProps) => {
/>
</LegacyMenuItem>
);

if (_internalRenderMenuItem) {
renderNode = _internalRenderMenuItem(renderNode, props);
}

return renderNode;
};

function MenuItem(props: MenuItemProps): React.ReactElement {
Expand Down
10 changes: 10 additions & 0 deletions src/context/PrivateContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import * as React from 'react';
import type { MenuProps } from '../Menu';

export interface PrivateContextProps {
_internalRenderMenuItem?: MenuProps['_internalRenderMenuItem'];
}

const PrivateContext = React.createContext<PrivateContextProps>({});

export default PrivateContext;
26 changes: 26 additions & 0 deletions tests/Private.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/* eslint-disable no-undef */
import React from 'react';
import { mount } from 'enzyme';
import classnames from 'classnames';
import Menu, { MenuItem } from '../src';

describe('Private Props', () => {
it('_internalRenderMenuItem', () => {
const wrapper = mount(
<Menu
_internalRenderMenuItem={node =>
React.cloneElement(node, {
className: classnames(node.props.className, 'inject-cls'),
})
}
>
<MenuItem key="1">1</MenuItem>
</Menu>,
);

console.log(wrapper.html());

expect(wrapper.exists('.inject-cls')).toBeTruthy();
});
});
/* eslint-enable */

0 comments on commit 3c74ad1

Please sign in to comment.