Skip to content
This repository was archived by the owner on Jan 14, 2025. It is now read-only.

Commit b15c780

Browse files
Ben McKernanMatt Goo
Ben McKernan
authored and
Matt Goo
committed
fix(menu-surface): Hoist menu-surface via a portal (#500,#628) (#693)
1 parent efbd849 commit b15c780

File tree

6 files changed

+85
-60
lines changed

6 files changed

+85
-60
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"test:watch": "karma start karma.local.js --auto-watch",
1919
"test:unit": "npm run clean && cross-env NODE_ENV=test karma start karma.local.js --single-run",
2020
"test:unit-ci": "karma start karma.ci.js --single-run",
21-
"test:image-diff": "MDC_COMMIT_HASH=$(git rev-parse --short HEAD) MDC_BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD) mocha --require ts-node/register --require babel-core/register --ui tdd --timeout 30000 test/screenshot/diff-suite.tsx",
21+
"test:image-diff": "MDC_COMMIT_HASH=$(git rev-parse --short HEAD) MDC_BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD) mocha --require ts-node/register --require babel-core/register --ui tdd --timeout 60000 test/screenshot/diff-suite.tsx",
2222
"test:screenshots": "docker run -it --rm --cap-add=SYS_ADMIN -e MDC_GCLOUD_SERVICE_ACCOUNT_KEY=\"${MDC_GCLOUD_SERVICE_ACCOUNT_KEY}\" mdcreact/screenshots /bin/sh -c 'git checkout .; git checkout master; git pull; npm i; /home/pptruser/material-components-web-react/test/screenshot/start.sh; sleep 40s; npm run test:image-diff'",
2323
"upload:screenshots": "node ./test/screenshot/upload-screenshots.js"
2424
},

packages/menu-surface/index.tsx

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
// THE SOFTWARE.
2222

2323
import * as React from 'react';
24+
import * as ReactDOM from 'react-dom';
2425
import * as classnames from 'classnames';
2526
// @ts-ignore no .d.ts file
2627
import {MDCMenuSurfaceFoundation, MDCMenuSurfaceAdapter, Corner} from '@material/menu-surface/dist/mdc.menuSurface';
@@ -56,6 +57,7 @@ export interface MenuSurfaceState {
5657
styleTop?: number;
5758
styleBottom?: number;
5859
classList: Set<string>;
60+
mounted: boolean;
5961
};
6062

6163
interface Position {
@@ -83,6 +85,7 @@ class MenuSurface extends React.Component<MenuSurfaceProps, MenuSurfaceState> {
8385
styleTop: undefined,
8486
styleBottom: undefined,
8587
classList: new Set(),
88+
mounted: false,
8689
};
8790

8891
static defaultProps: Partial<MenuSurfaceProps> = {
@@ -104,7 +107,6 @@ class MenuSurface extends React.Component<MenuSurfaceProps, MenuSurfaceState> {
104107
anchorMargin,
105108
coordinates,
106109
fixed,
107-
open,
108110
quickOpen,
109111
} = this.props;
110112
this.handleWindowClick = (evt) => this.foundation.handleBodyClick(evt);
@@ -114,7 +116,10 @@ class MenuSurface extends React.Component<MenuSurfaceProps, MenuSurfaceState> {
114116
window.removeEventListener('click', this.handleWindowClick!);
115117
this.foundation = new MDCMenuSurfaceFoundation(this.adapter);
116118
this.foundation.init();
117-
this.hoistToBody();
119+
// this deviates from the mdc web version.
120+
// here we force the menu to hoist, and require either
121+
// this.props.(x,y) or this.props.anchorElement.
122+
this.foundation.setIsHoisted(true);
118123
this.foundation.setFixedPosition(fixed);
119124
if (coordinates) {
120125
this.setCoordinates();
@@ -128,13 +133,14 @@ class MenuSurface extends React.Component<MenuSurfaceProps, MenuSurfaceState> {
128133
if (quickOpen) {
129134
this.foundation.setQuickOpen(quickOpen);
130135
}
131-
if (open) {
132-
this.open_();
133-
}
136+
this.setState({mounted: true});
134137
}
135138

136-
componentDidUpdate(prevProps: MenuSurfaceProps) {
137-
if (this.props.open !== prevProps.open) {
139+
componentDidUpdate(prevProps: MenuSurfaceProps, prevState: MenuSurfaceState) {
140+
// if this.props.open was true for the initial render
141+
// then it will not be changed but the mounted state will be,
142+
// so this.open_() should also be called in that case.
143+
if (this.props.open !== prevProps.open || (this.props.open && this.state.mounted !== prevState.mounted)) {
138144
this.open_();
139145
}
140146
if (this.props.coordinates !== prevProps.coordinates) {
@@ -160,19 +166,6 @@ class MenuSurface extends React.Component<MenuSurfaceProps, MenuSurfaceState> {
160166
}
161167
}
162168

163-
private hoistToBody(): void {
164-
// this deviates from the mdc web version.
165-
// here we force the menu to hoist, and require either
166-
// this.props.(x,y) or this.props.anchorElement.
167-
const menuSurfaceElement = this.menuSurfaceElement.current;
168-
if (!menuSurfaceElement) return;
169-
if (!menuSurfaceElement.parentElement) return;
170-
document.body.appendChild(
171-
menuSurfaceElement.parentElement.removeChild(menuSurfaceElement)
172-
);
173-
this.foundation.setIsHoisted(true);
174-
}
175-
176169
private setCoordinates(): void {
177170
if (!this.props.coordinates) return;
178171
const {x, y} = this.props.coordinates;
@@ -359,7 +352,8 @@ class MenuSurface extends React.Component<MenuSurfaceProps, MenuSurfaceState> {
359352
children,
360353
...otherProps
361354
} = this.props;
362-
return (
355+
if (!this.state.mounted) return null;
356+
return ReactDOM.createPortal(
363357
<div
364358
className={this.classes}
365359
onKeyDown={this.handleKeydown}
@@ -368,7 +362,8 @@ class MenuSurface extends React.Component<MenuSurfaceProps, MenuSurfaceState> {
368362
{...otherProps}
369363
>
370364
{children}
371-
</div>
365+
</div>,
366+
document.body
372367
);
373368
}
374369
}

packages/menu-surface/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
"dependencies": {
2020
"@material/menu-surface": "^0.41.0",
2121
"classnames": "^2.2.5",
22-
"react": "^16.4.2"
22+
"react": "^16.4.2",
23+
"react-dom": "^16.4.2"
2324
},
2425
"publishConfig": {
2526
"access": "public"

packages/webpack.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ function getJavaScriptWebpackConfig() {
109109
externals: Object.assign(
110110
{
111111
'react': 'react',
112+
'react-dom': 'react-dom',
112113
'classnames': 'classnames',
113114
'prop-types': 'prop-types',
114115
'@material/textfield/constants': `@material/textfield/constants.js`,

test/screenshot/screenshot.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ export default class Screenshot {
233233
const page = await browser.newPage();
234234
await page.goto(`http://localhost:8080/#/${this.urlPath_}`, {
235235
waitUntil: ['networkidle2'],
236+
timeout: 600000,
236237
});
237238
// await page.waitForSelector('#screenshot-test-app');
238239
const imageBuffer = await page.screenshot({fullPage: true});

0 commit comments

Comments
 (0)