Skip to content

Commit

Permalink
[stepper] Flow implementation for all components. (mui#4799)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexhayes committed Oct 8, 2017
1 parent 69e9fa2 commit 61f0875
Show file tree
Hide file tree
Showing 15 changed files with 481 additions and 349 deletions.
125 changes: 72 additions & 53 deletions src/Stepper/Step.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// @flow weak
// @flow

import React from 'react';
import PropTypes from 'prop-types';
import type { Element, Node } from 'react';
import classNames from 'classnames';
import withStyles from '../styles/withStyles';
import type { Orientation } from './Stepper';

export const styles = theme => ({
export const styles = (theme: Object) => ({
root: {
flex: '0 0 auto',
},
Expand All @@ -26,7 +27,69 @@ export const styles = theme => ({
},
});

function Step(props) {
type ProvidedProps = {
alternativeLabel: boolean,
classes: Object,
connector: Element<any>,
disabled: boolean,
index: number,
last: boolean,
optional: boolean,
orientation: Orientation,
};

export type Props = {
/**
* Sets the step as active. Is passed to child components.
*/
active?: boolean,
/**
* @ignore
* Set internally by Stepper when it's supplied with the alternativeLabel prop.
*/
alternativeLabel?: boolean,
/**
* Should be `Step` sub-components such as `StepLabel`, `StepContent`.
*/
children?: Node,
/**
* @ignore
*/
classes?: Object,
/**
* Mark the step as completed. Is passed to child components.
*/
completed?: boolean,
/**
* @ignore
* Passed down from Stepper if alternativeLabel is also set.
*/
connector?: Element<any>,
/**
* Mark the step as disabled, will also disable the button if
* `StepButton` is a child of `Step`. Is passed to child components.
*/
disabled?: boolean,
/**
* @ignore
* Used internally for numbering.
*/
index?: number,
/**
* @ignore
*/
last?: boolean,
/**
* Define this step as optional.
*/
optional?: boolean,
/**
* @ignore
*/
orientation?: Orientation,
};

function Step(props: ProvidedProps & Props) {
const {
active,
alternativeLabel,
Expand Down Expand Up @@ -69,55 +132,11 @@ function Step(props) {
);
}

Step.propTypes = {
/**
* Sets the step as active. Is passed to child components.
*/
active: PropTypes.bool,
/**
* @ignore
* Set internally by Stepper when it's supplied with the alternativeLabel prop.
*/
alternativeLabel: PropTypes.bool,
/**
* Should be `Step` sub-components such as `StepLabel`.
*/
children: PropTypes.node,
/**
* @ignore
*/
classes: PropTypes.object,
/**
* Mark the step as completed. Is passed to child components.
*/
completed: PropTypes.bool,
/**
* @ignore
* Passed down from Stepper if alternativeLabel is also set.
*/
connector: PropTypes.node,
/**
* Mark the step as disabled, will also disable the button if
* `StepButton` is a child of `Step`. Is passed to child components.
*/
disabled: PropTypes.bool,
/**
* @ignore
* Used internally for numbering.
*/
index: PropTypes.number.isRequired,
/**
* @ignore
*/
last: PropTypes.bool,
/**
* Define this step as optional.
*/
optional: PropTypes.bool,
/**
* @ignore
*/
orientation: PropTypes.oneOf(['horizontal', 'vertical']).isRequired,
Step.defaultProps = {
active: false,
completed: false,
disabled: false,
optional: false,
};

export default withStyles(styles)(Step);
142 changes: 84 additions & 58 deletions src/Stepper/StepButton.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
// @flow weak
// @flow
// @inheritedComponent ButtonBase

import React from 'react';
import PropTypes from 'prop-types';
import React, { Children } from 'react';
import type { Element } from 'react';
import classNames from 'classnames';
import withStyles from '../styles/withStyles';
import ButtonBase from '../ButtonBase';
import StepLabel from './StepLabel';
import type { Orientation } from './Stepper';

const isLabel = child => {
return child && child.type && child.type.muiName === 'StepLabel';
const isLabel = children => {
return children && Children.count(children) === 1 && children.type && children.type === StepLabel;
};

export const styles = () => ({
Expand All @@ -24,96 +26,120 @@ export const styles = () => ({
},
});

function StepButton(props) {
const {
active,
alternativeLabel,
children,
className: classNameProp,
completed,
classes,
disabled,
icon,
iconContainerClassName,
last, // eslint-disable-line no-unused-vars
optional,
orientation,
...other
} = props;
export type Icon = Element<any> | string | number;

const className = classNames(
classes.root,
([classes.alternativeLabelRoot]: alternativeLabel),
classNameProp,
);
const child = isLabel(children) ? children : <StepLabel>{children}</StepLabel>;

return (
<ButtonBase disabled={disabled} className={className} {...other}>
{React.cloneElement(child, {
active,
alternativeLabel,
completed,
disabled,
icon,
iconContainerClassName,
optional,
orientation,
})}
</ButtonBase>
);
}
type ProvidedProps = {
active: boolean,
alternativeLabel: boolean,
classes: Object,
completed: boolean,
disabled: boolean,
icon: Icon,
last: boolean,
optional: boolean,
orientation: Orientation,
};

StepButton.propTypes = {
export type Props = {
/**
* Passed from `Step` Is passed to StepLabel.
* @ignore
* Passed in via `Step` - passed through to `StepLabel`.
*/
active: PropTypes.bool,
active?: boolean,
/**
* @ignore
* Set internally by Stepper when it's supplied with the alternativeLabel prop.
*/
alternativeLabel: PropTypes.bool,
alternativeLabel?: boolean,
/**
* Can be a `StepLabel` or a node to place inside `StepLabel` as children.
*/
children: PropTypes.node,
children: Element<any>,
/**
* @ignore
*/
classes: PropTypes.object,
classes?: Object,
/**
* @ignore
*/
className: PropTypes.string,
className?: string,
/**
* @ignore
* Sets completed styling. Is passed to StepLabel.
*/
completed: PropTypes.bool,
completed?: boolean,
/**
* @ignore
* Disables the button and sets disabled styling. Is passed to StepLabel.
*/
disabled: PropTypes.bool,
disabled?: boolean,
/**
* The icon displayed by the step label.
*/
icon: PropTypes.oneOfType([PropTypes.element, PropTypes.string, PropTypes.number]),
icon?: Icon,
/**
* Override the inline-styles of the icon container element.
* Pass down to the the `StepLabel` prop `iconContainerClassName`.
*/
iconContainerClassName: PropTypes.string,
iconContainerClassName?: string,
/**
* @ignore
*/
last: PropTypes.bool,
last?: boolean,
/**
* @ignore
*/
optional: PropTypes.bool,
optional?: boolean,
/**
* @ignore
*/
orientation: PropTypes.oneOf(['horizontal', 'vertical']).isRequired,
orientation: Orientation,
};

function StepButton(props: ProvidedProps & Props) {
const {
active,
alternativeLabel,
children,
className: classNameProp,
completed,
classes,
disabled,
icon,
iconContainerClassName,
last, // eslint-disable-line no-unused-vars
optional,
orientation,
...other
} = props;

const className = classNames(
classes.root,
{
[classes.alternativeLabelRoot]: alternativeLabel,
},
classNameProp,
);
const childProps = {
active,
alternativeLabel,
completed,
disabled,
icon,
iconContainerClassName,
optional,
orientation,
};
const child = isLabel(children) ? (
React.cloneElement(children, childProps)
) : (
<StepLabel {...childProps}>{children}</StepLabel>
);

return (
<ButtonBase disabled={disabled} className={className} {...other}>
{child}
</ButtonBase>
);
}

export default withStyles(styles)(StepButton);
6 changes: 5 additions & 1 deletion src/Stepper/StepButton.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ describe('<StepButton />', () => {
});

it('merges user className into the root node', () => {
const wrapper = shallow(<StepButton className="foo" {...defaultProps} />);
const wrapper = shallow(
<StepButton className="foo" {...defaultProps}>
Hello
</StepButton>,
);

assert.include(wrapper.props().className, 'foo');
});
Expand Down
Loading

0 comments on commit 61f0875

Please sign in to comment.