From 211073d1490fa5d42e0f8ce57d898da608cc4279 Mon Sep 17 00:00:00 2001 From: eps1lon Date: Sat, 23 Jan 2021 13:00:00 +0100 Subject: [PATCH 01/12] [ClickAwayListener] Fix `children` and `onClickAway` types --- ...kAwayListener.js => ClickAwayListener.tsx} | 68 ++++++++++++++++--- .../src/ClickAwayListener/index.js | 1 - .../{index.d.ts => index.ts} | 0 3 files changed, 57 insertions(+), 12 deletions(-) rename packages/material-ui/src/ClickAwayListener/{ClickAwayListener.js => ClickAwayListener.tsx} (70%) delete mode 100644 packages/material-ui/src/ClickAwayListener/index.js rename packages/material-ui/src/ClickAwayListener/{index.d.ts => index.ts} (100%) diff --git a/packages/material-ui/src/ClickAwayListener/ClickAwayListener.js b/packages/material-ui/src/ClickAwayListener/ClickAwayListener.tsx similarity index 70% rename from packages/material-ui/src/ClickAwayListener/ClickAwayListener.js rename to packages/material-ui/src/ClickAwayListener/ClickAwayListener.tsx index 735c990f09d823..f8f1d7c24988ff 100644 --- a/packages/material-ui/src/ClickAwayListener/ClickAwayListener.js +++ b/packages/material-ui/src/ClickAwayListener/ClickAwayListener.tsx @@ -5,22 +5,54 @@ import ownerDocument from '../utils/ownerDocument'; import useForkRef from '../utils/useForkRef'; import useEventCallback from '../utils/useEventCallback'; -function mapEventPropToEvent(eventProp) { - return eventProp.substring(2).toLowerCase(); +function mapEventPropToEvent( + eventProp: EventHandlerName, +): EventHandlerName extends `on${infer EventName}` ? Lowercase : never { + return eventProp.substring(2).toLowerCase() as any; } -function clickedRootScrollbar(event, doc) { +function clickedRootScrollbar(event: MouseEvent, doc: Document) { return ( doc.documentElement.clientWidth < event.clientX || doc.documentElement.clientHeight < event.clientY ); } +type ClickAwayMouseEventHandler = 'onClick' | 'onMouseDown' | 'onMouseUp'; +type ClickAwayTouchEventHandler = 'onTouchStart' | 'onTouchEnd'; + +export interface ClickAwayListenerProps { + /** + * The wrapped element. + */ + children: React.ReactElement; + /** + * If `true`, the React tree is ignored and only the DOM tree is considered. + * This prop changes how portaled elements are handled. + * @default false + */ + disableReactTree?: boolean; + /** + * The mouse event to listen to. You can disable the listener by providing `false`. + * @default 'onClick' + */ + mouseEvent?: ClickAwayMouseEventHandler | false; + /** + * Callback fired when a "click away" event is detected. + */ + onClickAway: (event: MouseEvent | TouchEvent) => void; + /** + * The touch event to listen to. You can disable the listener by providing `false`. + * @default 'onTouchEnd' + */ + touchEvent?: ClickAwayTouchEventHandler | false; +} + /** * Listen for click events that occur somewhere in the document, outside of the element itself. * For instance, if you need to hide a menu when people click anywhere else on your page. */ -function ClickAwayListener(props) { +function ClickAwayListener(props: ClickAwayListenerProps): JSX.Element { const { children, disableReactTree = false, @@ -29,7 +61,7 @@ function ClickAwayListener(props) { touchEvent = 'onTouchEnd', } = props; const movedRef = React.useRef(false); - const nodeRef = React.useRef(null); + const nodeRef = React.useRef(null); const activatedRef = React.useRef(false); const syntheticEventRef = React.useRef(false); @@ -44,7 +76,11 @@ function ClickAwayListener(props) { }; }, []); - const handleRef = useForkRef(children.ref, nodeRef); + const handleRef = useForkRef( + // @ts-expect-error TODO upstream fix + children.ref, + nodeRef, + ); // The handler doesn't take event.defaultPrevented into account: // @@ -52,7 +88,7 @@ function ClickAwayListener(props) { // clicking a checkbox to check it, hitting a button to submit a form, // and hitting left arrow to move the cursor in a text input etc. // Only special HTML elements have these default behaviors. - const handleClickAway = useEventCallback((event) => { + const handleClickAway = useEventCallback((event: MouseEvent | TouchEvent) => { // Given developers can stop the propagation of the synthetic event, // we can only be confident with a positive value. const insideReactTree = syntheticEventRef.current; @@ -80,7 +116,14 @@ function ClickAwayListener(props) { insideDOM = event.composedPath().indexOf(nodeRef.current) > -1; } else { insideDOM = - !doc.documentElement.contains(event.target) || nodeRef.current.contains(event.target); + !doc.documentElement.contains( + // @ts-expect-error returns `false` as intended when not dispatched from a Node + event.target, + ) || + nodeRef.current.contains( + // @ts-expect-error returns `false` as intended when not dispatched from a Node + event.target, + ); } if (!insideDOM && (disableReactTree || !insideReactTree)) { @@ -89,7 +132,7 @@ function ClickAwayListener(props) { }); // Keep track of mouse/touch events that bubbled up through the portal. - const createHandleSynthetic = (handlerName) => (event) => { + const createHandleSynthetic = (handlerName: string) => (event: React.SyntheticEvent) => { syntheticEventRef.current = true; const childrenPropsHandler = children.props[handlerName]; @@ -98,7 +141,10 @@ function ClickAwayListener(props) { } }; - const childrenProps = { ref: handleRef }; + const childrenProps: { ref: React.Ref } & Pick< + React.DOMAttributes, + ClickAwayMouseEventHandler | ClickAwayTouchEventHandler + > = { ref: handleRef }; if (touchEvent !== false) { childrenProps[touchEvent] = createHandleSynthetic(touchEvent); @@ -180,7 +226,7 @@ ClickAwayListener.propTypes = { if (process.env.NODE_ENV !== 'production') { // eslint-disable-next-line - ClickAwayListener['propTypes' + ''] = exactProp(ClickAwayListener.propTypes); + (ClickAwayListener as any)['propTypes' + ''] = exactProp(ClickAwayListener.propTypes); } export default ClickAwayListener; diff --git a/packages/material-ui/src/ClickAwayListener/index.js b/packages/material-ui/src/ClickAwayListener/index.js deleted file mode 100644 index e207d41b085555..00000000000000 --- a/packages/material-ui/src/ClickAwayListener/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './ClickAwayListener'; diff --git a/packages/material-ui/src/ClickAwayListener/index.d.ts b/packages/material-ui/src/ClickAwayListener/index.ts similarity index 100% rename from packages/material-ui/src/ClickAwayListener/index.d.ts rename to packages/material-ui/src/ClickAwayListener/index.ts From de37a9a16768e23f95e113dae1442cdff5ef0a8c Mon Sep 17 00:00:00 2001 From: eps1lon Date: Sat, 23 Jan 2021 18:28:56 +0100 Subject: [PATCH 02/12] Fix types --- packages/material-ui-utils/src/ownerDocument.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/material-ui-utils/src/ownerDocument.ts b/packages/material-ui-utils/src/ownerDocument.ts index eed9b51474ccbf..39b1b60ff95a7c 100644 --- a/packages/material-ui-utils/src/ownerDocument.ts +++ b/packages/material-ui-utils/src/ownerDocument.ts @@ -1,3 +1,3 @@ -export default function ownerDocument(node: Node | undefined): Document { +export default function ownerDocument(node: Node | null | undefined): Document { return (node && node.ownerDocument) || document; } From 4af02b77993afa7ff1b6cf2fbca85700dc517014 Mon Sep 17 00:00:00 2001 From: eps1lon Date: Sat, 23 Jan 2021 18:37:00 +0100 Subject: [PATCH 03/12] Bail early when handling TouchEvent --- .../material-ui/src/ClickAwayListener/ClickAwayListener.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/material-ui/src/ClickAwayListener/ClickAwayListener.tsx b/packages/material-ui/src/ClickAwayListener/ClickAwayListener.tsx index f8f1d7c24988ff..32e58faa3652f9 100644 --- a/packages/material-ui/src/ClickAwayListener/ClickAwayListener.tsx +++ b/packages/material-ui/src/ClickAwayListener/ClickAwayListener.tsx @@ -99,7 +99,11 @@ function ClickAwayListener(props: ClickAwayListenerProps): JSX.Element { // 1. IE11 support, which trigger the handleClickAway even after the unbind // 2. The child might render null. // 3. Behave like a blur listener. - if (!activatedRef.current || !nodeRef.current || clickedRootScrollbar(event, doc)) { + if ( + !activatedRef.current || + !nodeRef.current || + ('clientX' in event && clickedRootScrollbar(event, doc)) + ) { return; } From c0e7931fb16eaa069ed1dc747a8ac58bab349b8c Mon Sep 17 00:00:00 2001 From: eps1lon Date: Sat, 23 Jan 2021 19:34:46 +0100 Subject: [PATCH 04/12] Exclude all test files --- packages/material-ui/tsconfig.build.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/material-ui/tsconfig.build.json b/packages/material-ui/tsconfig.build.json index 32766f15f19783..3a28b86f0de3f7 100644 --- a/packages/material-ui/tsconfig.build.json +++ b/packages/material-ui/tsconfig.build.json @@ -10,7 +10,7 @@ "outDir": "build", "rootDir": "./src" }, - "include": ["src/**/*.ts"], - "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"], + "include": ["./src/**/*.ts*"], + "exclude": ["src/**/*.spec.ts*", "src/**/*.test.ts*"], "references": [{ "path": "../material-ui-unstyled/tsconfig.build.json" }] } From f7280d13d29ee84c501bee54581f3f1020d6e072 Mon Sep 17 00:00:00 2001 From: eps1lon Date: Sat, 23 Jan 2021 19:34:58 +0100 Subject: [PATCH 05/12] remove unnecessary filke --- .../ClickAwayListener/ClickAwayListener.d.ts | 43 ------------------- 1 file changed, 43 deletions(-) delete mode 100644 packages/material-ui/src/ClickAwayListener/ClickAwayListener.d.ts diff --git a/packages/material-ui/src/ClickAwayListener/ClickAwayListener.d.ts b/packages/material-ui/src/ClickAwayListener/ClickAwayListener.d.ts deleted file mode 100644 index 4259dff9c598a9..00000000000000 --- a/packages/material-ui/src/ClickAwayListener/ClickAwayListener.d.ts +++ /dev/null @@ -1,43 +0,0 @@ -import * as React from 'react'; - -export interface ClickAwayListenerProps { - /** - * The wrapped element. - */ - children?: React.ReactNode; - /** - * If `true`, the React tree is ignored and only the DOM tree is considered. - * This prop changes how portaled elements are handled. - * @default false - */ - disableReactTree?: boolean; - /** - * The mouse event to listen to. You can disable the listener by providing `false`. - * @default 'onClick' - */ - mouseEvent?: 'onClick' | 'onMouseDown' | 'onMouseUp' | false; - /** - * Callback fired when a "click away" event is detected. - */ - onClickAway: (event: React.MouseEvent) => void; - /** - * The touch event to listen to. You can disable the listener by providing `false`. - * @default 'onTouchEnd' - */ - touchEvent?: 'onTouchStart' | 'onTouchEnd' | false; -} - -/** - * Listen for click events that occur somewhere in the document, outside of the element itself. - * For instance, if you need to hide a menu when people click anywhere else on your page. - * - * Demos: - * - * - [Click Away Listener](https://material-ui.com/components/click-away-listener/) - * - [Menus](https://material-ui.com/components/menus/) - * - * API: - * - * - [ClickAwayListener API](https://material-ui.com/api/click-away-listener/) - */ -export default function ClickAwayListener(props: ClickAwayListenerProps): JSX.Element; From 370bf993a451907aed9939956da3e8343ae8b535 Mon Sep 17 00:00:00 2001 From: eps1lon Date: Mon, 25 Jan 2021 09:05:28 +0100 Subject: [PATCH 06/12] Fix downstream type issues --- docs/src/pages/components/button-group/SplitButton.tsx | 2 +- docs/src/pages/components/menus/MenuListComposition.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/pages/components/button-group/SplitButton.tsx b/docs/src/pages/components/button-group/SplitButton.tsx index fdeadef0293ae0..4a08b07caf0ed2 100644 --- a/docs/src/pages/components/button-group/SplitButton.tsx +++ b/docs/src/pages/components/button-group/SplitButton.tsx @@ -33,7 +33,7 @@ export default function SplitButton() { setOpen((prevOpen) => !prevOpen); }; - const handleClose = (event: React.MouseEvent) => { + const handleClose = (event: Event) => { if ( anchorRef.current && anchorRef.current.contains(event.target as HTMLElement) diff --git a/docs/src/pages/components/menus/MenuListComposition.tsx b/docs/src/pages/components/menus/MenuListComposition.tsx index e820d14a32263c..9070c91154b883 100644 --- a/docs/src/pages/components/menus/MenuListComposition.tsx +++ b/docs/src/pages/components/menus/MenuListComposition.tsx @@ -28,7 +28,7 @@ export default function MenuListComposition() { setOpen((prevOpen) => !prevOpen); }; - const handleClose = (event: React.MouseEvent) => { + const handleClose = (event: Event | React.SyntheticEvent) => { if ( anchorRef.current && anchorRef.current.contains(event.target as HTMLElement) From bf908e977eeabfa2671a61362baa8baf0d0a29dd Mon Sep 17 00:00:00 2001 From: eps1lon Date: Mon, 25 Jan 2021 10:07:28 +0100 Subject: [PATCH 07/12] Fix proptypes generation --- .../src/ClickAwayListener/ClickAwayListener.tsx | 7 ++++--- scripts/generateProptypes.ts | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/material-ui/src/ClickAwayListener/ClickAwayListener.tsx b/packages/material-ui/src/ClickAwayListener/ClickAwayListener.tsx index 32e58faa3652f9..97d992d30212ad 100644 --- a/packages/material-ui/src/ClickAwayListener/ClickAwayListener.tsx +++ b/packages/material-ui/src/ClickAwayListener/ClickAwayListener.tsx @@ -5,9 +5,10 @@ import ownerDocument from '../utils/ownerDocument'; import useForkRef from '../utils/useForkRef'; import useEventCallback from '../utils/useEventCallback'; -function mapEventPropToEvent( - eventProp: EventHandlerName, -): EventHandlerName extends `on${infer EventName}` ? Lowercase : never { +// TODO: return `EventHandlerName extends `on${infer EventName}` ? Lowercase : never` once generatePropTypes runs with TS 4.1 +function mapEventPropToEvent( + eventProp: ClickAwayMouseEventHandler | ClickAwayTouchEventHandler, +): 'click' | 'mousedown' | 'mouseup' | 'touchstart' | 'touchend' { return eventProp.substring(2).toLowerCase() as any; } diff --git a/scripts/generateProptypes.ts b/scripts/generateProptypes.ts index e0d4bdb6d79a4e..418bb7c6f15bf6 100644 --- a/scripts/generateProptypes.ts +++ b/scripts/generateProptypes.ts @@ -350,6 +350,8 @@ async function run(argv: HandlerArgv) { .filter((filePath) => { return filePattern.test(filePath); }); + // May not be able to understand all files due to mismatch in TS versions. + // Check `programm.getSyntacticDiagnostics()` if referenced files could not be compiled. const program = ttp.createTSProgram(files, tsconfig); const promises = files.map>(async (tsFile) => { From 885d4d7eaa9fed9e8d8ff3daf10efb9d974c376b Mon Sep 17 00:00:00 2001 From: eps1lon Date: Mon, 25 Jan 2021 10:12:14 +0100 Subject: [PATCH 08/12] yarn proptypes --- .../material-ui/src/ClickAwayListener/ClickAwayListener.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/material-ui/src/ClickAwayListener/ClickAwayListener.tsx b/packages/material-ui/src/ClickAwayListener/ClickAwayListener.tsx index 97d992d30212ad..40c1f6b7fe1f8d 100644 --- a/packages/material-ui/src/ClickAwayListener/ClickAwayListener.tsx +++ b/packages/material-ui/src/ClickAwayListener/ClickAwayListener.tsx @@ -201,7 +201,7 @@ function ClickAwayListener(props: ClickAwayListenerProps): JSX.Element { ClickAwayListener.propTypes = { // ----------------------------- Warning -------------------------------- // | These PropTypes are generated from the TypeScript type definitions | - // | To update them edit the d.ts file and run "yarn proptypes" | + // | To update them edit TypeScript types and run "yarn proptypes" | // ---------------------------------------------------------------------- /** * The wrapped element. From 71a97f157ebd2272785ab1a9e72334a8bb564d0c Mon Sep 17 00:00:00 2001 From: eps1lon Date: Mon, 25 Jan 2021 10:50:34 +0100 Subject: [PATCH 09/12] Fix component annotation for function declarations --- docs/pages/api-docs/click-away-listener.json | 2 +- docs/scripts/buildApi.ts | 17 ++++++++++++++++- .../src/ClickAwayListener/ClickAwayListener.tsx | 9 +++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/docs/pages/api-docs/click-away-listener.json b/docs/pages/api-docs/click-away-listener.json index 635def8deaccae..7867d5969660d1 100644 --- a/docs/pages/api-docs/click-away-listener.json +++ b/docs/pages/api-docs/click-away-listener.json @@ -21,7 +21,7 @@ "name": "ClickAwayListener", "styles": { "classes": [], "globalClasses": {}, "name": null }, "spread": false, - "filename": "/packages/material-ui/src/ClickAwayListener/ClickAwayListener.js", + "filename": "/packages/material-ui/src/ClickAwayListener/ClickAwayListener.tsx", "inheritance": null, "demos": "", "styledComponent": true, diff --git a/docs/scripts/buildApi.ts b/docs/scripts/buildApi.ts index fee6dc982812cb..6fa4e93cc16c0e 100644 --- a/docs/scripts/buildApi.ts +++ b/docs/scripts/buildApi.ts @@ -33,6 +33,7 @@ import createGenerateClassName from '@material-ui/styles/createGenerateClassName import getStylesCreator from '@material-ui/styles/getStylesCreator'; import { createMuiTheme } from '@material-ui/core/styles'; import { getLineFeed, getUnstyledFilename } from './helpers'; +import { bind } from 'lodash'; const DEMO_IGNORE = LANGUAGES_IN_PROGRESS.map((language) => `-${language}.md`); @@ -505,7 +506,21 @@ async function annotateComponentDefinition(context: { if (babel.types.isIdentifier(babelPath.node.declaration)) { const bindingId = babelPath.node.declaration.name; const binding = babelPath.scope.bindings[bindingId]; - node = binding.path.parentPath.node; + + // The JSDOC MUST be located at the declaration + if (babel.types.isFunctionDeclaration(binding.path.node)) { + // For function declarations the binding is equal to the declaration + // /** + // */ + // function Component() {} + node = binding.path.node; + } else { + // For variable declarations the binding points to the declarator. + // /** + // */ + // const Component = () => {} + node = binding.path.parentPath.node; + } } } diff --git a/packages/material-ui/src/ClickAwayListener/ClickAwayListener.tsx b/packages/material-ui/src/ClickAwayListener/ClickAwayListener.tsx index 40c1f6b7fe1f8d..3839c2de2ea41c 100644 --- a/packages/material-ui/src/ClickAwayListener/ClickAwayListener.tsx +++ b/packages/material-ui/src/ClickAwayListener/ClickAwayListener.tsx @@ -52,6 +52,15 @@ export interface ClickAwayListenerProps { /** * Listen for click events that occur somewhere in the document, outside of the element itself. * For instance, if you need to hide a menu when people click anywhere else on your page. + * + * Demos: + * + * - [Click Away Listener](https://material-ui.com/components/click-away-listener/) + * - [Menus](https://material-ui.com/components/menus/) + * + * API: + * + * - [ClickAwayListener API](https://material-ui.com/api/click-away-listener/) */ function ClickAwayListener(props: ClickAwayListenerProps): JSX.Element { const { From 4ee27b336152b00cc51867d9f24002a3fb87ad90 Mon Sep 17 00:00:00 2001 From: eps1lon Date: Mon, 25 Jan 2021 10:53:29 +0100 Subject: [PATCH 10/12] f --- docs/scripts/buildApi.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/scripts/buildApi.ts b/docs/scripts/buildApi.ts index 6fa4e93cc16c0e..3dd668317f3c97 100644 --- a/docs/scripts/buildApi.ts +++ b/docs/scripts/buildApi.ts @@ -921,7 +921,7 @@ async function parseComponentSource( // Ignore what we might have generated in `annotateComponentDefinition` const annotatedDescriptionMatch = fullDescription.match(/(Demos|API):\r?\n\r?\n/); if (annotatedDescriptionMatch !== null) { - reactAPI.description = fullDescription.slice(0, annotatedDescriptionMatch.index); + reactAPI.description = fullDescription.slice(0, annotatedDescriptionMatch.index).trim(); } return reactAPI; From bc2b3688aed43e6f3150b0ed971fd1d5da52edef Mon Sep 17 00:00:00 2001 From: eps1lon Date: Tue, 26 Jan 2021 11:32:20 +0100 Subject: [PATCH 11/12] making sure merge commits dont break any workflow From 569dcf4427eff1ee5f9476e53da2a31ad20e1537 Mon Sep 17 00:00:00 2001 From: eps1lon Date: Tue, 26 Jan 2021 11:39:46 +0100 Subject: [PATCH 12/12] Remove rogue auto-import --- docs/scripts/buildApi.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/scripts/buildApi.ts b/docs/scripts/buildApi.ts index 3dd668317f3c97..bca149010acd47 100644 --- a/docs/scripts/buildApi.ts +++ b/docs/scripts/buildApi.ts @@ -33,7 +33,6 @@ import createGenerateClassName from '@material-ui/styles/createGenerateClassName import getStylesCreator from '@material-ui/styles/getStylesCreator'; import { createMuiTheme } from '@material-ui/core/styles'; import { getLineFeed, getUnstyledFilename } from './helpers'; -import { bind } from 'lodash'; const DEMO_IGNORE = LANGUAGES_IN_PROGRESS.map((language) => `-${language}.md`);