Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions packages/material-ui/src/MenuState/MenuState.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import renderProps from '../utils/renderProps';

export default class MenuState extends React.Component {
state = { anchorEl: null };

open = eventOrAnchorEl =>
this.setState({
anchorEl: eventOrAnchorEl instanceof Event ? eventOrAnchorEl.target : eventOrAnchorEl,
});

close = () => this.setState({ anchorEl: null });

render() {
const { open, close } = this;
const { menuId, children, render } = this.props;
const { anchorEl } = this.state;

const isOpen = Boolean(anchorEl);

return renderProps(
{ children, render },
{
open,
close,
isOpen,
bindTrigger: {
'aria-owns': isOpen ? menuId : null,
'aria-haspopup': true,
onClick: open,
},
bindMenu: {
id: menuId,
anchorEl,
open: isOpen,
onClose: close,
},
},
);
}
}

MenuState.propTypes = {
/**
* The render function. It will be called with a single object with the
* following properties:
* * `open(eventOrAnchorEl)` - calling this will open the menu
* * `close()` - calling this will close the menu
* * `isOpen` - `true`/`false` if the menu is open/closed
* * `bindTrigger` - a set of properties to pass to the menu trigger, including
* an `onClick` property that will open the menu
* * `bindMenu` - a set of properties to pass to the `Menu` component
*/
children: PropTypes.func,
/**
* The `id` property to use for the `Menu`. Will be passed to the render
* function as `bindMenu.id`, and also used for the `aria-owns` property
* passed to the trigger component via `bindTrigger`.
*/
menuId: PropTypes.string,
/**
* If you don't want to pass a `children` function, you can pass it as
* `render` instead.
*/
render: PropTypes.func,
};
29 changes: 29 additions & 0 deletions packages/material-ui/src/utils/renderProps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// this is basically copied from react-powerplug:
// https://github.com/renatorib/react-powerplug/blob/master/src/utils/renderProps.js

import warning from 'warning';

const isFn = prop => typeof prop === 'function';

/**
* renderProps
* is a render/children props interop.
* will pick up the prop that was used,
* or children if both are used
*/

const renderProps = ({ children, render }, ...props) => {
Copy link
Member

@oliviertassinari oliviertassinari Jul 29, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the libraries I have seen seems to abandon the render property. What about not supporting it? So we save people asking themselves the question, "What's better between a and b?". Answering the question only provides a marginal gain.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I sure wouldn't mind removing it, I only put it in there because some libs like react-powerplug seem to include it to appease people who think passing a child function is some kind of blasphemy

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I'm happy about being as opinionated as React.createContext API.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh that's true. I'm glad the React folks decided to do that, because there were some really dogmatic people complaining that children should always be a React.Node.

It's about the same as how in all of the Angular vs. React vs. Vue type videos I've seen, people said they didn't like React because something about putting HTML in JavaScript didn't feel right to them. They didn't have any more substantive criticism than that.

if (process.env.NODE_ENV !== 'production') {
warning(
isFn(children) && isFn(render),
'You are using the children and render props together.\n' +
'This is impossible, therefore, only the children will be used.',
);
}

const fn = isFn(children) ? children : render;

return fn ? fn(...props) : null;
};

export default renderProps;