Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[transition] Allow more accurate PropTypes #8858

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 5 additions & 1 deletion docs/src/pages/demos/dialogs/AlertDialogSlide.js
Expand Up @@ -10,6 +10,10 @@ import Dialog, {
} from 'material-ui/Dialog';
import Slide from 'material-ui/transitions/Slide';

function Transition(props) {
return <Slide direction="up" {...props} />;
}

export default class AlertDialogSlide extends React.Component {
state = {
open: false,
Expand All @@ -29,7 +33,7 @@ export default class AlertDialogSlide extends React.Component {
<Button onClick={this.handleClickOpen}>Slide in alert dialog</Button>
<Dialog
open={this.state.open}
transition={<Slide direction="up" />}
transition={Transition}
keepMounted
onRequestClose={this.handleRequestClose}
>
Expand Down
6 changes: 5 additions & 1 deletion docs/src/pages/demos/dialogs/FullScreenDialog.js
Expand Up @@ -23,6 +23,10 @@ const styles = {
},
};

function Transition(props) {
return <Slide direction="up" {...props} />;
}

class FullScreenDialog extends React.Component {
state = {
open: false,
Expand All @@ -45,7 +49,7 @@ class FullScreenDialog extends React.Component {
fullScreen
open={this.state.open}
onRequestClose={this.handleRequestClose}
transition={<Slide direction="up" />}
transition={Transition}
>
<AppBar className={classes.appBar}>
<Toolbar>
Expand Down
32 changes: 24 additions & 8 deletions docs/src/pages/demos/snackbars/DirectionSnackbar.js
Expand Up @@ -5,14 +5,30 @@ import Button from 'material-ui/Button';
import Snackbar from 'material-ui/Snackbar';
import Slide from 'material-ui/transitions/Slide';

function TransitionLeft(props) {
return <Slide direction="left" {...props} />;
}

function TransitionUp(props) {
return <Slide direction="up" {...props} />;
}

function TransitionRight(props) {
return <Slide direction="right" {...props} />;
}

function TransitionDown(props) {
return <Slide direction="down" {...props} />;
}

class DirectionSnackbar extends React.Component {
state = {
open: false,
direction: null,
transition: null,
};

handleClick = direction => () => {
this.setState({ open: true, direction });
handleClick = transition => () => {
this.setState({ open: true, transition });
};

handleRequestClose = () => {
Expand All @@ -22,14 +38,14 @@ class DirectionSnackbar extends React.Component {
render() {
return (
<div>
<Button onClick={this.handleClick('left')}>Right</Button>
<Button onClick={this.handleClick('up')}>Up</Button>
<Button onClick={this.handleClick('right')}>Left</Button>
<Button onClick={this.handleClick('down')}>Down</Button>
<Button onClick={this.handleClick(TransitionLeft)}>Right</Button>
<Button onClick={this.handleClick(TransitionUp)}>Up</Button>
<Button onClick={this.handleClick(TransitionRight)}>Left</Button>
<Button onClick={this.handleClick(TransitionDown)}>Down</Button>
<Snackbar
open={this.state.open}
onRequestClose={this.handleRequestClose}
transition={<Slide direction={this.state.direction} />}
transition={this.state.transition}
SnackbarContentProps={{
'aria-describedby': 'message-id',
}}
Expand Down
2 changes: 1 addition & 1 deletion pages/api/dialog.md
Expand Up @@ -29,7 +29,7 @@ Dialogs are overlaid modal paper based components with a backdrop.
| onExiting | TransitionCallback | | Callback fired when the dialog is exiting. |
| onRequestClose | Function | | Callback fired when the component requests to be closed.<br><br>**Signature:**<br>`function(event: object) => void`<br>*event:* The event source of the callback |
| open | boolean | false | If `true`, the Dialog is open. |
| transition | union:&nbsp;ComponentType<*><br>&nbsp;Element<any><br> | Fade | Transition component. |
| transition | ComponentType | Fade | Transition component. |
| transitionDuration | TransitionDuration | { enter: duration.enteringScreen, exit: duration.leavingScreen,} | The duration for the transition, in milliseconds. You may specify a single timeout for all transitions, or individually with an object. |

Any other properties supplied will be [spread to the root element](/customization/api#spread).
Expand Down
1 change: 0 additions & 1 deletion pages/api/input.md
Expand Up @@ -50,7 +50,6 @@ This property accepts the following keys:
- `inkbar`
- `error`
- `input`
- `inputAdorned`
- `inputDense`
- `disabled`
- `focused`
Expand Down
2 changes: 1 addition & 1 deletion pages/api/snackbar.md
Expand Up @@ -29,7 +29,7 @@ filename: /src/Snackbar/Snackbar.js
| onRequestClose | signature | | Callback fired when the component requests to be closed.<br>Typically `onRequestClose` is used to set state in the parent component, which is used to control the `Snackbar` `open` prop.<br>The `reason` parameter can optionally be used to control the response to `onRequestClose`, for example ignoring `clickaway`.<br><br>**Signature:**<br>`function(event: object, reason: string) => void`<br>*event:* The event source of the callback<br>*reason:* Can be:`"timeout"` (`autoHideDuration` expired) or: `"clickaway"` |
| <span style="color: #31a148">open *</span> | boolean | | If true, `Snackbar` is open. |
| resumeHideDuration | number | | The number of milliseconds to wait before dismissing after user interaction. If `autoHideDuration` property isn't specified, it does nothing. If `autoHideDuration` property is specified but `resumeHideDuration` isn't, we default to `autoHideDuration / 2` ms. |
| transition | union:&nbsp;ComponentType<*><br>&nbsp;Element<any><br> | | Object with Transition component, props & create Fn. |
| transition | ComponentType | | Transition component. |
| transitionDuration | TransitionDuration | { enter: duration.enteringScreen, exit: duration.leavingScreen,} | The duration for the transition, in milliseconds. You may specify a single timeout for all transitions, or individually with an object. |

Any other properties supplied will be [spread to the root element](/customization/api#spread).
Expand Down
4 changes: 2 additions & 2 deletions src/Dialog/Dialog.d.ts
Expand Up @@ -14,11 +14,11 @@ export interface DialogProps extends StandardProps<
transitionDuration?: TransitionDuration;
maxWidth?: 'xs' | 'sm' | 'md';
fullWidth?: boolean;
onBackdropClick?: Function;
onBackdropClick?: Function;
onEscapeKeyUp?: Function;
onRequestClose?: React.EventHandler<any>;
open?: boolean;
transition?: Function | React.ReactElement<any>;
transition?: React.ReactType;
}

export type DialogClassKey =
Expand Down
39 changes: 17 additions & 22 deletions src/Dialog/Dialog.js
@@ -1,7 +1,7 @@
// @flow

import React from 'react';
import type { ComponentType, Element, Node } from 'react';
import type { ComponentType, Node } from 'react';
import classNames from 'classnames';
import withStyles from '../styles/withStyles';
import { capitalizeFirstLetter } from '../utils/helpers';
Expand Down Expand Up @@ -51,6 +51,7 @@ export const styles = (theme: Object) => ({

type ProvidedProps = {
classes: Object,
transition: ComponentType<*>,
};

export type Props = {
Expand Down Expand Up @@ -139,7 +140,7 @@ export type Props = {
/**
* Transition component.
*/
transition?: ComponentType<*> | Element<any>,
transition?: ComponentType<*>,
};

/**
Expand All @@ -166,13 +167,10 @@ function Dialog(props: ProvidedProps & Props) {
onExiting,
onExited,
onRequestClose,
transition,
transition: TransitionProp,
...other
} = props;

const createTransitionFn =
typeof transition === 'function' ? React.createElement : React.cloneElement;

return (
<Modal
className={classNames(classes.root, className)}
Expand All @@ -185,20 +183,17 @@ function Dialog(props: ProvidedProps & Props) {
show={open}
{...other}
>
{createTransitionFn(
/* $FlowFixMe - FIXME See Snackbar for similar create vs clone example */
transition,
{
appear: true,
in: open,
timeout: transitionDuration,
onEnter,
onEntering,
onEntered,
onExit,
onExiting,
onExited,
},
<TransitionProp
appear
in={open}
timeout={transitionDuration}
onEnter={onEnter}
onEntering={onEntering}
onEntered={onEntered}
onExit={onExit}
onExiting={onExiting}
onExited={onExited}
>
<Paper
data-mui-test="Dialog"
elevation={24}
Expand All @@ -212,8 +207,8 @@ function Dialog(props: ProvidedProps & Props) {
)}
>
{children}
</Paper>,
)}
</Paper>
</TransitionProp>
</Modal>
);
}
Expand Down
5 changes: 3 additions & 2 deletions src/Dialog/Dialog.spec.js
Expand Up @@ -22,9 +22,10 @@ describe('<Dialog />', () => {
});

it('should render a Modal with transition', () => {
const wrapper = shallow(<Dialog transition={<div className="cloned-element-class" />} />);
const Transition = props => <div className="cloned-element-class" {...props} />;
const wrapper = shallow(<Dialog transition={Transition} />);
assert.strictEqual(
wrapper.find('.cloned-element-class').length,
wrapper.find(Transition).length,
1,
'should include element given in transition',
);
Expand Down
2 changes: 1 addition & 1 deletion src/Snackbar/Snackbar.d.ts
Expand Up @@ -22,7 +22,7 @@ export interface SnackbarProps extends StandardProps<
onRequestClose?: (event: React.SyntheticEvent<any>, reason: string) => void;
open: boolean;
SnackbarContentProps?: Object;
transition?: React.ReactNode;
transition?: React.ReactType;
}

export type SnackbarClassKey =
Expand Down
20 changes: 9 additions & 11 deletions src/Snackbar/Snackbar.js
Expand Up @@ -190,9 +190,9 @@ export type Props = {
*/
SnackbarContentProps?: Object,
/**
* Object with Transition component, props & create Fn.
* Transition component.
*/
transition?: ComponentType<*> | Element<any>,
transition?: ComponentType<*>,
/**
* The duration for the transition, in milliseconds.
* You may specify a single timeout for all transitions, or individually with an object.
Expand Down Expand Up @@ -332,7 +332,7 @@ class Snackbar extends React.Component<ProvidedProps & Props, State> {
onRequestClose,
open,
SnackbarContentProps,
transition: transitionProp,
transition: TransitionProp,
...other
} = this.props;

Expand All @@ -356,15 +356,13 @@ class Snackbar extends React.Component<ProvidedProps & Props, State> {
);

let transition;
if (typeof transitionProp === 'function') {
transition = React.createElement(transitionProp, transitionProps, transitionContent);
if (TransitionProp) {
transition = <TransitionProp {...transitionProps}>{transitionContent}</TransitionProp>;
} else {
// $FlowFixMe - rosskevin - figure this out later
transition = React.cloneElement(
// $FlowFixMe - flow isn't smart enough to handle this pattern
transitionProp || <Slide direction={vertical === 'top' ? 'down' : 'up'} />,
transitionProps,
transitionContent,
transition = (
<Slide direction={vertical === 'top' ? 'down' : 'up'} {...transitionProps}>
{transitionContent}
</Slide>
);
}

Expand Down
12 changes: 12 additions & 0 deletions src/Snackbar/Snackbar.spec.js
Expand Up @@ -245,4 +245,16 @@ describe('<Snackbar />', () => {
assert.strictEqual(wrapper.contains(children), true);
});
});

describe('prop: transition', () => {
it('should render a Snackbar with transition', () => {
const Transition = props => <div className="cloned-element-class" {...props} />;
const wrapper = shallow(<Snackbar open transition={Transition} />);
assert.strictEqual(
wrapper.find(Transition).length,
1,
'should include element given in transition',
);
});
});
});