From a68f41c1566f2d4b3701b241d96e183026cd6e01 Mon Sep 17 00:00:00 2001 From: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Wed, 9 Oct 2024 16:52:07 +0200 Subject: [PATCH 1/6] [core] Fix proptypes generation when multiple components per file --- .../src/injectPropTypesInFile.ts | 10 +- packages/mui-base/src/MenuItem/MenuItem.tsx | 1 + packages/mui-base/src/Option/Option.tsx | 39 ++++ packages/mui-base/src/Popper/Popper.tsx | 182 ++++++++++++++++++ packages/mui-joy/src/MenuItem/MenuItem.tsx | 58 ++++++ packages/mui-joy/src/Option/Option.tsx | 68 +++++++ .../src/PigmentHidden/PigmentHidden.tsx | 1 + packages/mui-material/src/Slider/Slider.js | 27 ++- 8 files changed, 382 insertions(+), 4 deletions(-) diff --git a/packages-internal/scripts/typescript-to-proptypes/src/injectPropTypesInFile.ts b/packages-internal/scripts/typescript-to-proptypes/src/injectPropTypesInFile.ts index 48e7ca74f5b73e..9dec9185cc7213 100644 --- a/packages-internal/scripts/typescript-to-proptypes/src/injectPropTypesInFile.ts +++ b/packages-internal/scripts/typescript-to-proptypes/src/injectPropTypesInFile.ts @@ -173,7 +173,7 @@ function createBabelPlugin({ let importName = ''; let needImport = false; let alreadyImported = false; - let originalPropTypesPath: null | babel.NodePath = null; + const originalPropTypesPaths = new Map(); const previousPropTypesSource = new Map(); function injectPropTypes(injectOptions: { @@ -201,8 +201,10 @@ function createBabelPlugin({ mapOfPropTypes.set(placeholder, source); + const originalPropTypesPath = originalPropTypesPaths.get(nodeName); + // `Component.propTypes` already exists - if (originalPropTypesPath !== null) { + if (originalPropTypesPath) { originalPropTypesPath.replaceWith(babel.template.ast(placeholder) as babel.Node); } else if (!emptyPropTypes && babelTypes.isExportNamedDeclaration(path.parent)) { // in: @@ -256,9 +258,11 @@ function createBabelPlugin({ babelTypes.isExpressionStatement(node) && babelTypes.isAssignmentExpression(node.expression, { operator: '=' }) && babelTypes.isMemberExpression(node.expression.left) && + babelTypes.isIdentifier(node.expression.left.object) && babelTypes.isIdentifier(node.expression.left.property, { name: 'propTypes' }) ) { - originalPropTypesPath = nodePath as babel.NodePath; + const componentName = node.expression.left.object.name; + originalPropTypesPaths.set(componentName, nodePath); let maybeObjectExpression = node.expression.right; // Component.propTypes = {} as any; diff --git a/packages/mui-base/src/MenuItem/MenuItem.tsx b/packages/mui-base/src/MenuItem/MenuItem.tsx index 8417b142030bc4..90be06aa86a776 100644 --- a/packages/mui-base/src/MenuItem/MenuItem.tsx +++ b/packages/mui-base/src/MenuItem/MenuItem.tsx @@ -77,6 +77,7 @@ const InnerMenuItem = React.memo( * * - [MenuItem API](https://mui.com/base-ui/react-menu/components-api/#menu-item) */ + const MenuItem = React.forwardRef(function MenuItem( props: MenuItemProps, ref: React.ForwardedRef, diff --git a/packages/mui-base/src/Option/Option.tsx b/packages/mui-base/src/Option/Option.tsx index 5531f5fd05a316..528b500b97d014 100644 --- a/packages/mui-base/src/Option/Option.tsx +++ b/packages/mui-base/src/Option/Option.tsx @@ -86,6 +86,45 @@ const InnerOption = React.memo( * * - [Option API](https://mui.com/base-ui/react-select/components-api/#option) */ + +InnerOption.propTypes /* remove-proptypes */ = { + // ┌────────────────────────────── Warning ──────────────────────────────┐ + // │ These PropTypes are generated from the TypeScript type definitions. │ + // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ + // └─────────────────────────────────────────────────────────────────────┘ + children: PropTypes.node, + className: PropTypes.string, + /** + * If `true`, the option will be disabled. + * @default false + */ + disabled: PropTypes.bool, + /** + * A text representation of the option's content. + * Used for keyboard text navigation matching. + */ + label: PropTypes.string, + /** + * The props used for each slot inside the Option. + * @default {} + */ + slotProps: PropTypes.shape({ + root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + }), + /** + * The components used for each slot inside the Option. + * Either a string to use a HTML element or a component. + * @default {} + */ + slots: PropTypes.shape({ + root: PropTypes.elementType, + }), + /** + * The value of the option. + */ + value: PropTypes.any.isRequired, +} as any; + const Option = React.forwardRef(function Option( props: OptionProps, ref: React.ForwardedRef, diff --git a/packages/mui-base/src/Popper/Popper.tsx b/packages/mui-base/src/Popper/Popper.tsx index 0715ee764ed5cd..95436af0d8e73f 100644 --- a/packages/mui-base/src/Popper/Popper.tsx +++ b/packages/mui-base/src/Popper/Popper.tsx @@ -245,6 +245,188 @@ const PopperTooltip = React.forwardRef(funct * * - [Popper API](https://mui.com/base-ui/react-popper/components-api/#popper) */ + +PopperTooltip.propTypes /* remove-proptypes */ = { + // ┌────────────────────────────── Warning ──────────────────────────────┐ + // │ These PropTypes are generated from the TypeScript type definitions. │ + // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ + // └─────────────────────────────────────────────────────────────────────┘ + /** + * An HTML element, [virtualElement](https://popper.js.org/docs/v2/virtual-elements/), + * or a function that returns either. + * It's used to set the position of the popper. + * The return value will passed as the reference object of the Popper instance. + */ + anchorEl: chainPropTypes( + PropTypes.oneOfType([HTMLElementType, PropTypes.object, PropTypes.func]), + (props) => { + if (props.open) { + const resolvedAnchorEl = resolveAnchorEl(props.anchorEl); + + if ( + resolvedAnchorEl && + isHTMLElement(resolvedAnchorEl) && + resolvedAnchorEl.nodeType === 1 + ) { + const box = resolvedAnchorEl.getBoundingClientRect(); + + if ( + process.env.NODE_ENV !== 'test' && + box.top === 0 && + box.left === 0 && + box.right === 0 && + box.bottom === 0 + ) { + return new Error( + [ + 'MUI: The `anchorEl` prop provided to the component is invalid.', + 'The anchor element should be part of the document layout.', + "Make sure the element is present in the document or that it's not display none.", + ].join('\n'), + ); + } + } else if ( + !resolvedAnchorEl || + typeof resolvedAnchorEl.getBoundingClientRect !== 'function' || + (isVirtualElement(resolvedAnchorEl) && + resolvedAnchorEl.contextElement != null && + resolvedAnchorEl.contextElement.nodeType !== 1) + ) { + return new Error( + [ + 'MUI: The `anchorEl` prop provided to the component is invalid.', + 'It should be an HTML element instance or a virtualElement ', + '(https://popper.js.org/docs/v2/virtual-elements/).', + ].join('\n'), + ); + } + } + + return null; + }, + ), + /** + * Popper render function or node. + */ + children: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ + PropTypes.node, + PropTypes.func, + ]), + /** + * Direction of the text. + * @default 'ltr' + */ + direction: PropTypes.oneOf(['ltr', 'rtl']), + /** + * The `children` will be under the DOM hierarchy of the parent component. + * @default false + */ + disablePortal: PropTypes.bool, + /** + * Popper.js is based on a "plugin-like" architecture, + * most of its features are fully encapsulated "modifiers". + * + * A modifier is a function that is called each time Popper.js needs to + * compute the position of the popper. + * For this reason, modifiers should be very performant to avoid bottlenecks. + * To learn how to create a modifier, [read the modifiers documentation](https://popper.js.org/docs/v2/modifiers/). + */ + modifiers: PropTypes.arrayOf( + PropTypes.shape({ + data: PropTypes.object, + effect: PropTypes.func, + enabled: PropTypes.bool, + fn: PropTypes.func, + name: PropTypes.any, + options: PropTypes.object, + phase: PropTypes.oneOf([ + 'afterMain', + 'afterRead', + 'afterWrite', + 'beforeMain', + 'beforeRead', + 'beforeWrite', + 'main', + 'read', + 'write', + ]), + requires: PropTypes.arrayOf(PropTypes.string), + requiresIfExists: PropTypes.arrayOf(PropTypes.string), + }), + ), + /** + * If `true`, the component is shown. + */ + open: PropTypes.bool.isRequired, + /** + * Popper placement. + * @default 'bottom' + */ + placement: PropTypes.oneOf([ + 'auto-end', + 'auto-start', + 'auto', + 'bottom-end', + 'bottom-start', + 'bottom', + 'left-end', + 'left-start', + 'left', + 'right-end', + 'right-start', + 'right', + 'top-end', + 'top-start', + 'top', + ]), + /** + * Options provided to the [`Popper.js`](https://popper.js.org/docs/v2/constructors/#options) instance. + * @default {} + */ + popperOptions: PropTypes.shape({ + modifiers: PropTypes.array, + onFirstUpdate: PropTypes.func, + placement: PropTypes.oneOf([ + 'auto-end', + 'auto-start', + 'auto', + 'bottom-end', + 'bottom-start', + 'bottom', + 'left-end', + 'left-start', + 'left', + 'right-end', + 'right-start', + 'right', + 'top-end', + 'top-start', + 'top', + ]), + strategy: PropTypes.oneOf(['absolute', 'fixed']), + }), + /** + * A ref that points to the used popper instance. + */ + popperRef: refType, + /** + * The props used for each slot inside the Popper. + * @default {} + */ + slotProps: PropTypes.shape({ + root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + }), + /** + * The components used for each slot inside the Popper. + * Either a string to use a HTML element or a component. + * @default {} + */ + slots: PropTypes.shape({ + root: PropTypes.elementType, + }), + TransitionProps: PropTypes.object, +} as any; + const Popper = React.forwardRef(function Popper< RootComponentType extends React.ElementType, >(props: PopperProps, forwardedRef: React.ForwardedRef) { diff --git a/packages/mui-joy/src/MenuItem/MenuItem.tsx b/packages/mui-joy/src/MenuItem/MenuItem.tsx index bf0c3da192b9e4..94284e09abd9ea 100644 --- a/packages/mui-joy/src/MenuItem/MenuItem.tsx +++ b/packages/mui-joy/src/MenuItem/MenuItem.tsx @@ -119,6 +119,64 @@ const MenuItem = React.memo( * - [MenuItem API](https://mui.com/joy-ui/api/menu-item/) * - inherits [ListItemButton API](https://mui.com/joy-ui/api/list-item-button/) */ + +MenuItem.propTypes /* remove-proptypes */ = { + // ┌────────────────────────────── Warning ──────────────────────────────┐ + // │ These PropTypes are generated from the TypeScript type definitions. │ + // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ + // └─────────────────────────────────────────────────────────────────────┘ + /** + * The content of the component. + */ + children: PropTypes.node, + /** + * The color of the component. It supports those theme colors that make sense for this component. + * @default 'neutral' + */ + color: PropTypes.oneOf(['danger', 'neutral', 'primary', 'success', 'warning']), + /** + * @ignore + */ + component: PropTypes.elementType, + /** + * @ignore + */ + disabled: PropTypes.bool, + /** + * @ignore + */ + id: PropTypes.string, + /** + * The content direction flow. + * @default 'horizontal' + */ + orientation: PropTypes.oneOf(['horizontal', 'vertical']), + /** + * If `true`, the component is selected. + * @default false + */ + selected: PropTypes.bool, + /** + * The props used for each slot inside. + * @default {} + */ + slotProps: PropTypes.shape({ + root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + }), + /** + * The components used for each slot inside. + * @default {} + */ + slots: PropTypes.shape({ + root: PropTypes.elementType, + }), + /** + * The [global variant](https://mui.com/joy-ui/main-features/global-variants/) to use. + * @default 'plain' + */ + variant: PropTypes.oneOf(['outlined', 'plain', 'soft', 'solid']), +} as any; + const StableMenuItem = React.forwardRef(function StableMenuItem( props: MenuItemProps, ref: React.ForwardedRef, diff --git a/packages/mui-joy/src/Option/Option.tsx b/packages/mui-joy/src/Option/Option.tsx index 4a1a095ef20d0e..c5ce6135256d13 100644 --- a/packages/mui-joy/src/Option/Option.tsx +++ b/packages/mui-joy/src/Option/Option.tsx @@ -112,6 +112,74 @@ const Option = React.memo( * * - [Option API](https://mui.com/joy-ui/api/option/) */ + +Option.propTypes /* remove-proptypes */ = { + // ┌────────────────────────────── Warning ──────────────────────────────┐ + // │ These PropTypes are generated from the TypeScript type definitions. │ + // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ + // └─────────────────────────────────────────────────────────────────────┘ + /** + * The content of the component. + */ + children: PropTypes.node, + /** + * The color of the component. It supports those theme colors that make sense for this component. + * @default 'neutral' + */ + color: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ + PropTypes.oneOf(['danger', 'neutral', 'primary', 'success', 'warning']), + PropTypes.string, + ]), + /** + * @ignore + */ + component: PropTypes.elementType, + /** + * If `true`, the component is disabled. + * @default false + */ + disabled: PropTypes.bool, + /** + * A text representation of the option's content. + * Used for keyboard text navigation matching. + */ + label: PropTypes.oneOfType([PropTypes.element, PropTypes.string]), + /** + * The props used for each slot inside. + * @default {} + */ + slotProps: PropTypes.shape({ + root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + }), + /** + * The components used for each slot inside. + * @default {} + */ + slots: PropTypes.shape({ + root: PropTypes.elementType, + }), + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), + PropTypes.func, + PropTypes.object, + ]), + /** + * The option value. + */ + value: PropTypes.any.isRequired, + /** + * The [global variant](https://mui.com/joy-ui/main-features/global-variants/) to use. + * @default 'plain' + */ + variant: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ + PropTypes.oneOf(['outlined', 'plain', 'soft', 'solid']), + PropTypes.string, + ]), +} as any; + const StableOption = React.forwardRef(function StableOption( props: OptionProps, ref: React.ForwardedRef, diff --git a/packages/mui-material/src/PigmentHidden/PigmentHidden.tsx b/packages/mui-material/src/PigmentHidden/PigmentHidden.tsx index c4abce51b2f243..6d0ae79e15f516 100644 --- a/packages/mui-material/src/PigmentHidden/PigmentHidden.tsx +++ b/packages/mui-material/src/PigmentHidden/PigmentHidden.tsx @@ -250,6 +250,7 @@ HiddenCss.propTypes /* remove-proptypes */ = { */ xsUp: PropTypes.bool, } as any; + /** * * Demos: diff --git a/packages/mui-material/src/Slider/Slider.js b/packages/mui-material/src/Slider/Slider.js index 2a6291380a84ab..34435c00504f52 100644 --- a/packages/mui-material/src/Slider/Slider.js +++ b/packages/mui-material/src/Slider/Slider.js @@ -340,7 +340,7 @@ export const SliderThumb = styled('span', { })), ); -export const SliderValueLabel = styled(BaseSliderValueLabel, { +const SliderValueLabel = styled(BaseSliderValueLabel, { name: 'MuiSlider', slot: 'ValueLabel', overridesResolver: (props, styles) => styles.valueLabel, @@ -422,6 +422,31 @@ export const SliderValueLabel = styled(BaseSliderValueLabel, { })), ); +SliderValueLabel.propTypes /* remove-proptypes */ = { + // ┌────────────────────────────── Warning ──────────────────────────────┐ + // │ These PropTypes are generated from the TypeScript type definitions. │ + // │ To update them, edit the d.ts file and run `pnpm proptypes`. │ + // └─────────────────────────────────────────────────────────────────────┘ + /** + * @ignore + */ + children: PropTypes.element.isRequired, + /** + * @ignore + */ + index: PropTypes.number.isRequired, + /** + * @ignore + */ + open: PropTypes.bool.isRequired, + /** + * @ignore + */ + value: PropTypes.number.isRequired, +}; + +export { SliderValueLabel }; + export const SliderMark = styled('span', { name: 'MuiSlider', slot: 'Mark', From 001195135ba9920a9391c238c6df3e8c52cdfe9b Mon Sep 17 00:00:00 2001 From: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Wed, 9 Oct 2024 17:03:06 +0200 Subject: [PATCH 2/6] Update injectPropTypesInFile.ts --- .../typescript-to-proptypes/src/injectPropTypesInFile.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages-internal/scripts/typescript-to-proptypes/src/injectPropTypesInFile.ts b/packages-internal/scripts/typescript-to-proptypes/src/injectPropTypesInFile.ts index 9dec9185cc7213..edfc14a06e074d 100644 --- a/packages-internal/scripts/typescript-to-proptypes/src/injectPropTypesInFile.ts +++ b/packages-internal/scripts/typescript-to-proptypes/src/injectPropTypesInFile.ts @@ -258,9 +258,9 @@ function createBabelPlugin({ babelTypes.isExpressionStatement(node) && babelTypes.isAssignmentExpression(node.expression, { operator: '=' }) && babelTypes.isMemberExpression(node.expression.left) && - babelTypes.isIdentifier(node.expression.left.object) && babelTypes.isIdentifier(node.expression.left.property, { name: 'propTypes' }) ) { + babelTypes.assertIdentifier(node.expression.left.object); const componentName = node.expression.left.object.name; originalPropTypesPaths.set(componentName, nodePath); From ee6c7d22e978bb33341fe1265e5da57170721157 Mon Sep 17 00:00:00 2001 From: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Wed, 9 Oct 2024 18:02:22 +0200 Subject: [PATCH 3/6] Fix a few ts errors --- scripts/generateProptypes.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/scripts/generateProptypes.ts b/scripts/generateProptypes.ts index c75ec6f11e0cdd..d2c9bf83078795 100644 --- a/scripts/generateProptypes.ts +++ b/scripts/generateProptypes.ts @@ -5,7 +5,7 @@ import * as prettier from 'prettier'; import glob from 'fast-glob'; import * as _ from 'lodash'; import * as yargs from 'yargs'; -import * as ts from 'typescript'; +import { LiteralType } from '@mui/internal-scripts/typescript-to-proptypes/src/models'; import { fixBabelGeneratorIssues, fixLineEndings, @@ -131,14 +131,14 @@ const ignoreExternalDocumentation: Record = { Zoom: transitionCallbacks, }; -function sortBreakpointsLiteralByViewportAscending(a: ts.LiteralType, b: ts.LiteralType) { +function sortBreakpointsLiteralByViewportAscending(a: LiteralType, b: LiteralType) { // default breakpoints ordered by their size ascending const breakpointOrder: readonly unknown[] = ['"xs"', '"sm"', '"md"', '"lg"', '"xl"']; return breakpointOrder.indexOf(a.value) - breakpointOrder.indexOf(b.value); } -function sortSizeByScaleAscending(a: ts.LiteralType, b: ts.LiteralType) { +function sortSizeByScaleAscending(a: LiteralType, b: LiteralType) { const sizeOrder: readonly unknown[] = ['"small"', '"medium"', '"large"']; return sizeOrder.indexOf(a.value) - sizeOrder.indexOf(b.value); } @@ -363,9 +363,7 @@ async function run(argv: HandlerArgv) { const promises = files.map>(async (tsFile) => { const sourceFile = tsFile.includes('.d.ts') ? tsFile.replace('.d.ts', '.js') : tsFile; try { - const projectName = tsFile.match( - /packages\/mui-([a-zA-Z-]+)\/src/, - )![1] as CoreTypeScriptProjects; + const projectName = tsFile.match(/packages\/mui-([a-zA-Z-]+)\/src/)![1]; const project = buildProject(projectName); await generateProptypes(project, sourceFile, tsFile); } catch (error: any) { From 983ab6e023eeb3b3b2f1e7a920ab8c3304ac1e71 Mon Sep 17 00:00:00 2001 From: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Thu, 10 Oct 2024 09:38:35 +0200 Subject: [PATCH 4/6] extract var --- packages/mui-base/src/Option/Option.tsx | 102 +++++++------- packages/mui-joy/src/MenuItem/MenuItem.tsx | 156 ++++++++++----------- packages/mui-joy/src/Option/Option.tsx | 138 +++++++++--------- 3 files changed, 187 insertions(+), 209 deletions(-) diff --git a/packages/mui-base/src/Option/Option.tsx b/packages/mui-base/src/Option/Option.tsx index 528b500b97d014..32deb69c665507 100644 --- a/packages/mui-base/src/Option/Option.tsx +++ b/packages/mui-base/src/Option/Option.tsx @@ -20,60 +20,50 @@ function useUtilityClasses(ownerState: OptionOwnerState>(function Option< - OptionValue, - RootComponentType extends React.ElementType, - >(props: OptionProps, forwardedRef: React.ForwardedRef) { - const { - children, - disabled = false, - label, - slotProps = {}, - slots = {}, - value, - ...other - } = props; - - const Root = slots.root ?? 'li'; - - const optionRef = React.useRef(null); - const combinedRef = useForkRef(optionRef, forwardedRef); - - // If `label` is not explicitly provided, the `children` are used for convenience. - // This is used to populate the select's trigger with the selected option's label. - const computedLabel = - label ?? (typeof children === 'string' ? children : optionRef.current?.textContent?.trim()); - - const { getRootProps, selected, highlighted, index } = useOption({ - disabled, - label: computedLabel, - rootRef: combinedRef, - value, - }); - - const ownerState: OptionOwnerState = { - ...props, - disabled, - highlighted, - index, - selected, - }; - - const classes = useUtilityClasses(ownerState); - - const rootProps: WithOptionalOwnerState> = useSlotProps({ - getSlotProps: getRootProps, - elementType: Root, - externalSlotProps: slotProps.root, - externalForwardedProps: other, - className: classes.root, - ownerState, - }); - - return {children}; - }), -); +const InnerOption = React.forwardRef>(function InnerOption< + OptionValue, + RootComponentType extends React.ElementType, +>(props: OptionProps, forwardedRef: React.ForwardedRef) { + const { children, disabled = false, label, slotProps = {}, slots = {}, value, ...other } = props; + + const Root = slots.root ?? 'li'; + + const optionRef = React.useRef(null); + const combinedRef = useForkRef(optionRef, forwardedRef); + + // If `label` is not explicitly provided, the `children` are used for convenience. + // This is used to populate the select's trigger with the selected option's label. + const computedLabel = + label ?? (typeof children === 'string' ? children : optionRef.current?.textContent?.trim()); + + const { getRootProps, selected, highlighted, index } = useOption({ + disabled, + label: computedLabel, + rootRef: combinedRef, + value, + }); + + const ownerState: OptionOwnerState = { + ...props, + disabled, + highlighted, + index, + selected, + }; + + const classes = useUtilityClasses(ownerState); + + const rootProps: WithOptionalOwnerState> = useSlotProps({ + getSlotProps: getRootProps, + elementType: Root, + externalSlotProps: slotProps.root, + externalForwardedProps: other, + className: classes.root, + ownerState, + }); + + return {children}; +}); /** * An unstyled option to be used within a Select. @@ -125,6 +115,8 @@ InnerOption.propTypes /* remove-proptypes */ = { value: PropTypes.any.isRequired, } as any; +const InnerOptionMemo = React.memo(InnerOption); + const Option = React.forwardRef(function Option( props: OptionProps, ref: React.ForwardedRef, @@ -139,7 +131,7 @@ const Option = React.forwardRef(function Option( return ( - + ); }) as OptionType; diff --git a/packages/mui-joy/src/MenuItem/MenuItem.tsx b/packages/mui-joy/src/MenuItem/MenuItem.tsx index 94284e09abd9ea..a8e0ecfe7ef1a6 100644 --- a/packages/mui-joy/src/MenuItem/MenuItem.tsx +++ b/packages/mui-joy/src/MenuItem/MenuItem.tsx @@ -43,84 +43,73 @@ const MenuItemRoot = styled(StyledListItemButton, { overridesResolver: (props, styles) => styles.root, })<{ ownerState: MenuItemOwnerState }>({}); -const MenuItem = React.memo( - React.forwardRef(function MenuItem(inProps: MenuItemProps, ref: React.ForwardedRef) { - const props = useThemeProps({ - props: inProps, - name: 'JoyMenuItem', - }); - - const row = React.useContext(RowListContext); - - const { - children, - disabled: disabledProp = false, - component = 'li', - selected = false, - color: colorProp = 'neutral', - orientation = 'horizontal', - variant: variantProp = 'plain', - slots = {}, - slotProps = {}, - id, - ...other - } = props; - const { variant = variantProp, color = colorProp } = useVariantColor( - inProps.variant, - inProps.color, - ); - - const { getRootProps, disabled, focusVisible } = useMenuItem({ - id, - disabled: disabledProp, - rootRef: ref, - }); - - const ownerState = { - ...props, - component, - color, - disabled, - focusVisible, - orientation, - selected, - row, - variant, - }; - - const classes = useUtilityClasses(ownerState); - const externalForwardedProps = { ...other, component, slots, slotProps }; - - const [SlotRoot, rootProps] = useSlot('root', { - ref, - elementType: MenuItemRoot, - getSlotProps: getRootProps, - externalForwardedProps, - className: classes.root, - ownerState, - }); - - return ( - - {children} - - ); - }), -); - -/** - * - * Demos: - * - * - [Menu](https://mui.com/joy-ui/react-menu/) - * - * API: - * - * - [MenuItem API](https://mui.com/joy-ui/api/menu-item/) - * - inherits [ListItemButton API](https://mui.com/joy-ui/api/list-item-button/) - */ - -MenuItem.propTypes /* remove-proptypes */ = { +const InnerMenuItem = React.forwardRef(function InnerMenuItem( + inProps: MenuItemProps, + ref: React.ForwardedRef, +) { + const props = useThemeProps({ + props: inProps, + name: 'JoyMenuItem', + }); + + const row = React.useContext(RowListContext); + + const { + children, + disabled: disabledProp = false, + component = 'li', + selected = false, + color: colorProp = 'neutral', + orientation = 'horizontal', + variant: variantProp = 'plain', + slots = {}, + slotProps = {}, + id, + ...other + } = props; + const { variant = variantProp, color = colorProp } = useVariantColor( + inProps.variant, + inProps.color, + ); + + const { getRootProps, disabled, focusVisible } = useMenuItem({ + id, + disabled: disabledProp, + rootRef: ref, + }); + + const ownerState = { + ...props, + component, + color, + disabled, + focusVisible, + orientation, + selected, + row, + variant, + }; + + const classes = useUtilityClasses(ownerState); + const externalForwardedProps = { ...other, component, slots, slotProps }; + + const [SlotRoot, rootProps] = useSlot('root', { + ref, + elementType: MenuItemRoot, + getSlotProps: getRootProps, + externalForwardedProps, + className: classes.root, + ownerState, + }); + + return ( + + {children} + + ); +}); + +InnerMenuItem.propTypes /* remove-proptypes */ = { // ┌────────────────────────────── Warning ──────────────────────────────┐ // │ These PropTypes are generated from the TypeScript type definitions. │ // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ @@ -134,17 +123,12 @@ MenuItem.propTypes /* remove-proptypes */ = { * @default 'neutral' */ color: PropTypes.oneOf(['danger', 'neutral', 'primary', 'success', 'warning']), - /** - * @ignore - */ component: PropTypes.elementType, /** - * @ignore + * If `true`, the component is disabled. + * @default false */ disabled: PropTypes.bool, - /** - * @ignore - */ id: PropTypes.string, /** * The content direction flow. @@ -177,6 +161,8 @@ MenuItem.propTypes /* remove-proptypes */ = { variant: PropTypes.oneOf(['outlined', 'plain', 'soft', 'solid']), } as any; +const MenuItem = React.memo(InnerMenuItem); + const StableMenuItem = React.forwardRef(function StableMenuItem( props: MenuItemProps, ref: React.ForwardedRef, diff --git a/packages/mui-joy/src/Option/Option.tsx b/packages/mui-joy/src/Option/Option.tsx index c5ce6135256d13..cc7a3432cec8db 100644 --- a/packages/mui-joy/src/Option/Option.tsx +++ b/packages/mui-joy/src/Option/Option.tsx @@ -36,71 +36,72 @@ const OptionRoot = styled(StyledListItemButton as unknown as 'li', { }; }); -const Option = React.memo( - React.forwardRef(function Option(inProps: OptionProps, ref: React.ForwardedRef) { - const props = useThemeProps({ - props: inProps, - name: 'JoyOption', - }); - - const { - component = 'li', - children, - disabled = false, - value, - label, - variant: variantProp = 'plain', - color: colorProp = 'neutral', - slots = {}, - slotProps = {}, - ...other - } = props; - - const row = React.useContext(RowListContext); - const { variant = variantProp, color = colorProp } = useVariantColor( - inProps.variant, - inProps.color, - ); - const optionRef = React.useRef(null); - const combinedRef = useForkRef(optionRef, ref); - - const computedLabel = - label ?? (typeof children === 'string' ? children : optionRef.current?.innerText); - - const { getRootProps, selected, highlighted, index } = useOption({ - disabled, - label: computedLabel, - value, - rootRef: combinedRef, - }); - - const ownerState: OptionOwnerState = { - ...props, - disabled, - selected, - highlighted, - index, - component, - variant, - color, - row, - }; - - const classes = useUtilityClasses(ownerState); - const externalForwardedProps = { ...other, component, slots, slotProps }; - - const [SlotRoot, rootProps] = useSlot('root', { - ref, - getSlotProps: getRootProps, - elementType: OptionRoot, - externalForwardedProps, - className: classes.root, - ownerState, - }); - - return {children}; - }), -); +const InnerOption = React.forwardRef(function InnerOption( + inProps: OptionProps, + ref: React.ForwardedRef, +) { + const props = useThemeProps({ + props: inProps, + name: 'JoyOption', + }); + + const { + component = 'li', + children, + disabled = false, + value, + label, + variant: variantProp = 'plain', + color: colorProp = 'neutral', + slots = {}, + slotProps = {}, + ...other + } = props; + + const row = React.useContext(RowListContext); + const { variant = variantProp, color = colorProp } = useVariantColor( + inProps.variant, + inProps.color, + ); + const optionRef = React.useRef(null); + const combinedRef = useForkRef(optionRef, ref); + + const computedLabel = + label ?? (typeof children === 'string' ? children : optionRef.current?.innerText); + + const { getRootProps, selected, highlighted, index } = useOption({ + disabled, + label: computedLabel, + value, + rootRef: combinedRef, + }); + + const ownerState: OptionOwnerState = { + ...props, + disabled, + selected, + highlighted, + index, + component, + variant, + color, + row, + }; + + const classes = useUtilityClasses(ownerState); + const externalForwardedProps = { ...other, component, slots, slotProps }; + + const [SlotRoot, rootProps] = useSlot('root', { + ref, + getSlotProps: getRootProps, + elementType: OptionRoot, + externalForwardedProps, + className: classes.root, + ownerState, + }); + + return {children}; +}); /** * @@ -113,7 +114,7 @@ const Option = React.memo( * - [Option API](https://mui.com/joy-ui/api/option/) */ -Option.propTypes /* remove-proptypes */ = { +InnerOption.propTypes /* remove-proptypes */ = { // ┌────────────────────────────── Warning ──────────────────────────────┐ // │ These PropTypes are generated from the TypeScript type definitions. │ // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ @@ -130,9 +131,6 @@ Option.propTypes /* remove-proptypes */ = { PropTypes.oneOf(['danger', 'neutral', 'primary', 'success', 'warning']), PropTypes.string, ]), - /** - * @ignore - */ component: PropTypes.elementType, /** * If `true`, the component is disabled. @@ -180,6 +178,8 @@ Option.propTypes /* remove-proptypes */ = { ]), } as any; +const Option = React.memo(InnerOption); + const StableOption = React.forwardRef(function StableOption( props: OptionProps, ref: React.ForwardedRef, From 5df7ca1990c9a6bce47b747b93f98b90e65bf0bd Mon Sep 17 00:00:00 2001 From: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Thu, 10 Oct 2024 10:13:06 +0200 Subject: [PATCH 5/6] Fix api docs generation --- .../src/injectPropTypesInFile.ts | 8 ++++++- packages/mui-base/src/Option/Option.tsx | 24 +++++++++---------- packages/mui-base/src/Popper/Popper.tsx | 24 +++++++++---------- packages/mui-joy/src/MenuItem/MenuItem.tsx | 12 +++++++++- packages/mui-joy/src/Option/Option.tsx | 11 ++++++++- 5 files changed, 52 insertions(+), 27 deletions(-) diff --git a/packages-internal/scripts/typescript-to-proptypes/src/injectPropTypesInFile.ts b/packages-internal/scripts/typescript-to-proptypes/src/injectPropTypesInFile.ts index edfc14a06e074d..7a96d7b37381a4 100644 --- a/packages-internal/scripts/typescript-to-proptypes/src/injectPropTypesInFile.ts +++ b/packages-internal/scripts/typescript-to-proptypes/src/injectPropTypesInFile.ts @@ -174,7 +174,7 @@ function createBabelPlugin({ let needImport = false; let alreadyImported = false; const originalPropTypesPaths = new Map(); - const previousPropTypesSource = new Map(); + const previousPropTypesSources = new Map>(); function injectPropTypes(injectOptions: { path: babel.NodePath; @@ -184,6 +184,9 @@ function createBabelPlugin({ }) { const { path, props, usedProps, nodeName } = injectOptions; + const previousPropTypesSource = + previousPropTypesSources.get(nodeName) || new Map(); + const source = generatePropTypes(props, { ...otherOptions, importedName: importName, @@ -264,6 +267,9 @@ function createBabelPlugin({ const componentName = node.expression.left.object.name; originalPropTypesPaths.set(componentName, nodePath); + const previousPropTypesSource = new Map(); + previousPropTypesSources.set(componentName, previousPropTypesSource); + let maybeObjectExpression = node.expression.right; // Component.propTypes = {} as any; // ^^^^^^^^^ expression.right diff --git a/packages/mui-base/src/Option/Option.tsx b/packages/mui-base/src/Option/Option.tsx index 32deb69c665507..181435ac9aeb9a 100644 --- a/packages/mui-base/src/Option/Option.tsx +++ b/packages/mui-base/src/Option/Option.tsx @@ -65,18 +65,6 @@ const InnerOption = React.forwardRef>(function Inn return {children}; }); -/** - * An unstyled option to be used within a Select. - * - * Demos: - * - * - [Select](https://mui.com/base-ui/react-select/) - * - * API: - * - * - [Option API](https://mui.com/base-ui/react-select/components-api/#option) - */ - InnerOption.propTypes /* remove-proptypes */ = { // ┌────────────────────────────── Warning ──────────────────────────────┐ // │ These PropTypes are generated from the TypeScript type definitions. │ @@ -117,6 +105,18 @@ InnerOption.propTypes /* remove-proptypes */ = { const InnerOptionMemo = React.memo(InnerOption); +/** + * An unstyled option to be used within a Select. + * + * Demos: + * + * - [Select](https://mui.com/base-ui/react-select/) + * + * API: + * + * - [Option API](https://mui.com/base-ui/react-select/components-api/#option) + */ + const Option = React.forwardRef(function Option( props: OptionProps, ref: React.ForwardedRef, diff --git a/packages/mui-base/src/Popper/Popper.tsx b/packages/mui-base/src/Popper/Popper.tsx index 95436af0d8e73f..3c76ee83094713 100644 --- a/packages/mui-base/src/Popper/Popper.tsx +++ b/packages/mui-base/src/Popper/Popper.tsx @@ -234,18 +234,6 @@ const PopperTooltip = React.forwardRef(funct ); }) as PolymorphicComponent; -/** - * Poppers rely on the 3rd party library [Popper.js](https://popper.js.org/docs/v2/) for positioning. - * - * Demos: - * - * - [Popper](https://mui.com/base-ui/react-popper/) - * - * API: - * - * - [Popper API](https://mui.com/base-ui/react-popper/components-api/#popper) - */ - PopperTooltip.propTypes /* remove-proptypes */ = { // ┌────────────────────────────── Warning ──────────────────────────────┐ // │ These PropTypes are generated from the TypeScript type definitions. │ @@ -427,6 +415,18 @@ PopperTooltip.propTypes /* remove-proptypes */ = { TransitionProps: PropTypes.object, } as any; +/** + * Poppers rely on the 3rd party library [Popper.js](https://popper.js.org/docs/v2/) for positioning. + * + * Demos: + * + * - [Popper](https://mui.com/base-ui/react-popper/) + * + * API: + * + * - [Popper API](https://mui.com/base-ui/react-popper/components-api/#popper) + */ + const Popper = React.forwardRef(function Popper< RootComponentType extends React.ElementType, >(props: PopperProps, forwardedRef: React.ForwardedRef) { diff --git a/packages/mui-joy/src/MenuItem/MenuItem.tsx b/packages/mui-joy/src/MenuItem/MenuItem.tsx index a8e0ecfe7ef1a6..a3f1988e573985 100644 --- a/packages/mui-joy/src/MenuItem/MenuItem.tsx +++ b/packages/mui-joy/src/MenuItem/MenuItem.tsx @@ -162,7 +162,17 @@ InnerMenuItem.propTypes /* remove-proptypes */ = { } as any; const MenuItem = React.memo(InnerMenuItem); - +/** + * + * Demos: + * + * - [Menu](https://mui.com/joy-ui/react-menu/) + * + * API: + * + * - [MenuItem API](https://mui.com/joy-ui/api/menu-item/) + * - inherits [ListItemButton API](https://mui.com/joy-ui/api/list-item-button/) + */ const StableMenuItem = React.forwardRef(function StableMenuItem( props: MenuItemProps, ref: React.ForwardedRef, diff --git a/packages/mui-joy/src/Option/Option.tsx b/packages/mui-joy/src/Option/Option.tsx index cc7a3432cec8db..3cd09806aba323 100644 --- a/packages/mui-joy/src/Option/Option.tsx +++ b/packages/mui-joy/src/Option/Option.tsx @@ -179,7 +179,16 @@ InnerOption.propTypes /* remove-proptypes */ = { } as any; const Option = React.memo(InnerOption); - +/** + * + * Demos: + * + * - [Select](https://mui.com/joy-ui/react-select/) + * + * API: + * + * - [Option API](https://mui.com/joy-ui/api/option/) + */ const StableOption = React.forwardRef(function StableOption( props: OptionProps, ref: React.ForwardedRef, From 2961cb714b7ea1a9a45cd2d43ec426b298726e70 Mon Sep 17 00:00:00 2001 From: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Thu, 10 Oct 2024 10:27:41 +0200 Subject: [PATCH 6/6] Update MenuItem.tsx --- packages/mui-joy/src/MenuItem/MenuItem.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/mui-joy/src/MenuItem/MenuItem.tsx b/packages/mui-joy/src/MenuItem/MenuItem.tsx index a3f1988e573985..368f57bbd6982c 100644 --- a/packages/mui-joy/src/MenuItem/MenuItem.tsx +++ b/packages/mui-joy/src/MenuItem/MenuItem.tsx @@ -122,7 +122,10 @@ InnerMenuItem.propTypes /* remove-proptypes */ = { * The color of the component. It supports those theme colors that make sense for this component. * @default 'neutral' */ - color: PropTypes.oneOf(['danger', 'neutral', 'primary', 'success', 'warning']), + color: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ + PropTypes.oneOf(['danger', 'neutral', 'primary', 'success', 'warning']), + PropTypes.string, + ]), component: PropTypes.elementType, /** * If `true`, the component is disabled. @@ -158,7 +161,10 @@ InnerMenuItem.propTypes /* remove-proptypes */ = { * The [global variant](https://mui.com/joy-ui/main-features/global-variants/) to use. * @default 'plain' */ - variant: PropTypes.oneOf(['outlined', 'plain', 'soft', 'solid']), + variant: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ + PropTypes.oneOf(['outlined', 'plain', 'soft', 'solid']), + PropTypes.string, + ]), } as any; const MenuItem = React.memo(InnerMenuItem);