+
{steps}
);
}
-
- render() {
- const {horizontal} = this.props;
-
- if (horizontal) {
- return this.renderHorizontalStepper();
- }
-
- return this.renderVerticalStepper();
- }
}
export default Stepper;
diff --git a/src/Stepper/Stepper.spec.js b/src/Stepper/Stepper.spec.js
new file mode 100644
index 00000000000000..107cb3dd4c3ea3
--- /dev/null
+++ b/src/Stepper/Stepper.spec.js
@@ -0,0 +1,92 @@
+/* eslint-env mocha */
+import React from 'react';
+import {shallow} from 'enzyme';
+import {assert} from 'chai';
+import Stepper from './Stepper';
+import getMuiTheme from '../styles/getMuiTheme';
+
+describe('
', () => {
+ const muiTheme = getMuiTheme();
+ const shallowWithContext = (node, context = {}) => {
+ return shallow(node, {
+ context: {
+ muiTheme,
+ ...context,
+ },
+ });
+ };
+
+ it('merges user styles into the root node', () => {
+ const wrapper = shallowWithContext(
+
+ );
+
+ assert.strictEqual(wrapper.props().style.backgroundColor, 'purple', );
+ });
+
+ describe('rendering children', () => {
+ const wrapper = shallowWithContext(
+
+
+
+
+
+ );
+
+ const children = wrapper.children();
+
+ it('renders 3 children with connectors as separators', () => {
+ assert.strictEqual(children.length, 5);
+ assert.ok(wrapper.childAt(1).is('pure(StepConnector)'));
+ assert.ok(wrapper.childAt(3).is('pure(StepConnector)'));
+ });
+
+ assert.ok(true);
+ });
+
+ describe('controlling child props', () => {
+ it('controls children linearly based on the activeStep prop', () => {
+ const wrapper = shallowWithContext(
+
+
+
+
+
+ );
+ assert.ok(wrapper.find('.child-0').prop('active'));
+ assert.notOk(wrapper.find('.child-1').prop('active'));
+ assert.notOk(wrapper.find('.child-2').prop('active'));
+ assert.ok(wrapper.find('.child-1').prop('disabled'));
+ assert.ok(wrapper.find('.child-2').prop('disabled'));
+ wrapper.setProps({activeStep: 1});
+ assert.ok(wrapper.find('.child-0').prop('completed'));
+ assert.notOk(wrapper.find('.child-0').prop('active'));
+ assert.ok(wrapper.find('.child-1').prop('active'));
+ assert.notOk(wrapper.find('.child-2').prop('active'));
+ assert.ok(wrapper.find('.child-2').prop('disabled'));
+ });
+
+ it('controls children non-linearly based on the activeStep prop', () => {
+ const wrapper = shallowWithContext(
+
+
+
+
+
+ );
+ assert.ok(wrapper.find('.child-0').prop('active'));
+ assert.notOk(wrapper.find('.child-1').prop('active'));
+ assert.notOk(wrapper.find('.child-2').prop('active'));
+ wrapper.setProps({activeStep: 1});
+ assert.notOk(wrapper.find('.child-0').prop('active'));
+ assert.ok(wrapper.find('.child-1').prop('active'));
+ assert.notOk(wrapper.find('.child-2').prop('active'));
+ wrapper.setProps({activeStep: 2});
+ assert.notOk(wrapper.find('.child-0').prop('active'));
+ assert.notOk(wrapper.find('.child-1').prop('active'));
+ assert.ok(wrapper.find('.child-2').prop('active'));
+ });
+ });
+});
diff --git a/src/Stepper/VerticalStep.js b/src/Stepper/VerticalStep.js
deleted file mode 100644
index b98864a016112e..00000000000000
--- a/src/Stepper/VerticalStep.js
+++ /dev/null
@@ -1,323 +0,0 @@
-import React, {PropTypes} from 'react';
-import TouchRipple from '../internal/TouchRipple';
-import Avatar from '../Avatar';
-
-class Step extends React.Component {
- static propTypes = {
- /**
- * An array of nodes for handling moving or canceling steps.
- */
- actions: PropTypes.arrayOf(PropTypes.node),
-
- /**
- * Override the inline-style of the div which contains the actions.
- */
- actionsWrapperStyle: PropTypes.object,
-
- children: PropTypes.node,
-
- /**
- * Override the inline-style of the div which contains all the children, including control button groups.
- */
- childrenWrapperStyle: PropTypes.object,
-
- /**
- * Override the inline-style of the connector line.
- */
- connectorLineStyle: PropTypes.object,
-
- /**
- * @ignore
- * If true, the step is active.
- */
- isActive: PropTypes.bool,
-
- /**
- * @ignore
- * If true, the step is completed.
- */
- isCompleted: PropTypes.bool,
-
- /**
- * @ignore
- * If true, the step is the last one.
- */
- isLastStep: PropTypes.bool,
-
- /**
- * @ignore
- * If true, the header of step is hovered.
- */
- isStepHeaderHovered: PropTypes.bool,
-
- /**
- * @ignore
- * Callback function fired when the header of step is hovered.
- */
- onStepHeaderHover: PropTypes.func,
-
- /**
- * @ignore
- * Callback function fired when the header of step is touched.
- */
- onStepHeaderTouch: PropTypes.func,
-
- /**
- * @ignore
- * The index of the furthest optional step.
- */
- previousStepOptionalIndex: PropTypes.number,
-
- /**
- * Override the inline-style of step container, which contains connector line and children.
- */
- stepContainerStyle: PropTypes.object,
-
- /**
- * Override the inline-style of step header (not including left avatar).
- */
- stepHeaderStyle: PropTypes.object,
-
- /**
- * Override the inline-style of step header wrapper, including left avatar.
- */
- stepHeaderWrapperStyle: PropTypes.object,
-
- /**
- * @ignore
- * The index of step in array of Steps.
- */
- stepIndex: PropTypes.number,
-
- /**
- * Customize the step label.
- */
- stepLabel: PropTypes.node,
- };
-
- static contextTypes = {
- muiTheme: PropTypes.object.isRequired,
- createIcon: PropTypes.func,
- updateAvatarBackgroundColor: PropTypes.func,
- };
-
- componentDidMount() {
- const {isActive} = this.props;
-
- if (isActive) {
- const childrenWrapperNode = this.refs.childrenWrapper;
- childrenWrapperNode.style.opacity = 1;
-
- const containerWrapper = this.refs.containerWrapper;
- containerWrapper.style.height = `${childrenWrapperNode.children[0].offsetHeight}px`;
-
- setTimeout(() => {
- containerWrapper.style.height = 'auto';
- childrenWrapperNode.style.height = 'auto';
- }, 300);
- }
- }
-
- componentWillReceiveProps(nextProps) {
- const {isActive} = this.props;
-
- if (!isActive && nextProps.isActive) {
- const childrenWrapperNode = this.refs.childrenWrapper;
- childrenWrapperNode.style.opacity = 1;
-
- const containerWrapper = this.refs.containerWrapper;
- containerWrapper.style.height = `${childrenWrapperNode.children[0].offsetHeight}px`;
-
- setTimeout(() => {
- containerWrapper.style.height = 'auto';
- childrenWrapperNode.style.height = 'auto';
- }, 300);
- }
-
- if (isActive && !nextProps.isActive) {
- const childrenWrapperNode = this.refs.childrenWrapper;
- childrenWrapperNode.style.opacity = '0';
- childrenWrapperNode.style.height = '100%';
-
- const containerWrapper = this.refs.containerWrapper;
- containerWrapper.style.height = '32px';
- }
- }
-
- handleStepHeaderTouch = () => {
- this.props.onStepHeaderTouch(this.props.stepIndex, this);
- };
-
- handleStepHeaderMouseHover = () => {
- this.props.onStepHeaderHover(this.props.stepIndex);
- };
-
- handleStepHeaderMouseLeave = () => {
- this.props.onStepHeaderHover(-1);
- };
-
- getStyles() {
- const {
- isActive,
- isCompleted,
- isStepHeaderHovered,
-
- stepHeaderStyle,
- stepHeaderWrapperStyle,
- connectorLineStyle,
- stepContainerStyle,
- actionsWrapperStyle,
- childrenWrapperStyle,
- } = this.props;
-
- const theme = this.context.muiTheme.stepper;
-
- const customAvatarBackgroundColor = this.context.updateAvatarBackgroundColor(this);
-
- const avatarBackgroundColor = customAvatarBackgroundColor ||
- ((isActive || isCompleted) ?
- theme.activeAvatarColor :
- isStepHeaderHovered ?
- theme.hoveredAvatarColor :
- theme.inactiveAvatarColor);
-
- const stepHeaderWrapper = Object.assign({
- cursor: 'pointer',
- color: theme.inactiveTextColor,
- paddingLeft: 24,
- paddingTop: 24,
- paddingBottom: 24,
- marginTop: -32,
- position: 'relative',
-
- },
-
- stepHeaderWrapperStyle,
-
- isStepHeaderHovered && !isActive && {
- backgroundColor: theme.hoveredHeaderColor,
- color: theme.hoveredTextColor,
-
- }, (isActive || (isActive && isStepHeaderHovered) || isCompleted) && {
- color: theme.activeTextColor,
-
- }, this.props.stepIndex === 0 && {
- marginTop: 0,
- });
-
- const stepContainer = Object.assign({
- paddingLeft: 36,
- position: 'relative',
- height: 32,
- transition: 'height 0.2s',
-
- },
-
- stepContainerStyle,
-
- isActive && {
- paddingBottom: 36 + 24,
- marginBottom: 8,
- marginTop: -8,
- });
-
- const connectorLine = Object.assign({
- borderLeft: '1px solid',
- borderLeftColor: theme.connectorLineColor,
- height: '100%',
- position: 'absolute',
- marginTop: -16,
-
- },
-
- connectorLineStyle,
-
- isActive && {
- marginTop: -8,
- });
-
- const actionsWrapper = Object.assign({
- marginTop: 16,
- }, actionsWrapperStyle);
-
- const childrenWrapper = Object.assign({
- paddingLeft: 24,
- transition: 'height 0.05s',
- opacity: 0,
- overflow: 'hidden',
- }, childrenWrapperStyle);
-
- const stepHeader = Object.assign({
- display: 'flex',
- flexDirection: 'row',
- alignItems: 'center',
- }, stepHeaderStyle);
-
- return {
- avatar: {
- backgroundColor: avatarBackgroundColor,
- fontSize: 12,
- marginRight: 12,
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
- },
-
- stepHeaderWrapper: stepHeaderWrapper,
- stepContainer: stepContainer,
- connectorLine: connectorLine,
- actionsWrapper: actionsWrapper,
- childrenWrapper: childrenWrapper,
- stepHeader: stepHeader,
- };
- }
-
- render() {
- const {
- children,
- stepLabel,
- actions,
- isLastStep,
- } = this.props;
-
- const styles = this.getStyles();
-
- const icon = this.context.createIcon(this);
-
- const avatarView =
;
-
- return (
-
-
-
-
- {avatarView}
- {stepLabel}
-
-
-
-
- {!isLastStep &&
}
- {
-
-
- {children}
-
-
- {actions}
-
-
-
- }
-
-
- );
- }
-}
-
-export default Step;
diff --git a/src/Stepper/index.js b/src/Stepper/index.js
index ad35c4b1c056f7..f8a5ce9e2db2c0 100644
--- a/src/Stepper/index.js
+++ b/src/Stepper/index.js
@@ -1,5 +1,5 @@
+export Step from './Step';
+export StepButton from './StepButton';
+export StepContent from './StepContent';
+export StepLabel from './StepLabel';
export Stepper from './Stepper';
-export VerticalStep from './VerticalStep';
-export HorizontalStep from './HorizontalStep';
-
-export default from './Stepper';
diff --git a/src/SvgIcon/SvgIcon.js b/src/SvgIcon/SvgIcon.js
index ecb4f65ae08936..da33cd1c744187 100644
--- a/src/SvgIcon/SvgIcon.js
+++ b/src/SvgIcon/SvgIcon.js
@@ -2,6 +2,8 @@ import React from 'react';
import transitions from '../styles/transitions';
class SvgIcon extends React.Component {
+ static muiName = 'SvgIcon';
+
static propTypes = {
/**
* Elements passed into the SVG Icon.
diff --git a/src/index.js b/src/index.js
index baf7631bf963a8..97ac71054d73d6 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,7 +20,6 @@ export FloatingActionButton from './FloatingActionButton';
export FontIcon from './FontIcon';
export GridList from './GridList';
export GridTile from './GridList/GridTile';
-export HorizontalStep from './Stepper/HorizontalStep';
export IconButton from './IconButton';
export IconMenu from './IconMenu';
export LinearProgress from './LinearProgress';
@@ -39,7 +38,11 @@ export SelectField from './SelectField';
export Slider from './Slider';
export Subheader from './Subheader';
export SvgIcon from './SvgIcon';
-export Stepper from './Stepper';
+export Step from './Stepper/Step';
+export StepButton from './Stepper/StepButton';
+export StepContent from './Stepper/StepContent';
+export StepLabel from './Stepper/StepLabel';
+export Stepper from './Stepper/Stepper';
export Snackbar from './Snackbar';
export Tabs from './Tabs';
export Tab from './Tabs/Tab';
@@ -57,4 +60,3 @@ export Toolbar from './Toolbar';
export ToolbarGroup from './Toolbar/ToolbarGroup';
export ToolbarSeparator from './Toolbar/ToolbarSeparator';
export ToolbarTitle from './Toolbar/ToolbarTitle';
-export VerticalStep from './Stepper/VerticalStep';
diff --git a/src/internal/ExpandTransition.js b/src/internal/ExpandTransition.js
new file mode 100644
index 00000000000000..99a3a4185a062b
--- /dev/null
+++ b/src/internal/ExpandTransition.js
@@ -0,0 +1,72 @@
+import React, {PropTypes} from 'react';
+import ReactTransitionGroup from 'react-addons-transition-group';
+import ExpandTransitionChild from './ExpandTransitionChild';
+
+class ExpandTransition extends React.Component {
+ static propTypes = {
+ children: PropTypes.node,
+ enterDelay: PropTypes.number,
+ loading: PropTypes.bool,
+ open: PropTypes.bool,
+ style: PropTypes.object,
+ };
+
+ static defaultProps = {
+ enterDelay: 0,
+ loading: false,
+ open: false,
+ };
+
+ static contextTypes = {
+ muiTheme: React.PropTypes.object.isRequired,
+ };
+
+ renderChildren(children, loading) {
+ if (loading) {
+ return ([]);
+ }
+
+ return React.Children.map(children, (child) => {
+ return (
+
+ {child}
+
+ );
+ }, this);
+ }
+
+ render() {
+ const {
+ children,
+ loading,
+ open,
+ style,
+ ...other,
+ } = this.props;
+
+ const {prepareStyles} = this.context.muiTheme;
+
+ const mergedRootStyles = Object.assign({}, {
+ position: 'relative',
+ overflow: 'hidden',
+ height: '100%',
+ }, style);
+
+ const newChildren = this.renderChildren(children, loading);
+
+ return (
+
+ {open && newChildren}
+
+ );
+ }
+}
+
+export default ExpandTransition;
diff --git a/src/internal/ExpandTransitionChild.js b/src/internal/ExpandTransitionChild.js
new file mode 100644
index 00000000000000..4ca669cf285746
--- /dev/null
+++ b/src/internal/ExpandTransitionChild.js
@@ -0,0 +1,90 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import transitions from '../styles/transitions';
+
+class ExpandTransitionChild extends React.Component {
+ static propTypes = {
+ children: React.PropTypes.node,
+ enterDelay: React.PropTypes.number,
+ style: React.PropTypes.object,
+ };
+
+ static defaultProps = {
+ enterDelay: 0,
+ };
+
+ static contextTypes = {
+ muiTheme: React.PropTypes.object.isRequired,
+ };
+
+ componentDidUpdate() {
+ this.open();
+ }
+
+ componentWillUnmount() {
+ clearTimeout(this.enterTimer);
+ clearTimeout(this.leaveTimer);
+ }
+
+ componentWillAppear(callback) {
+ this.open();
+ callback();
+ }
+
+ componentWillEnter(callback) {
+ const {enterDelay} = this.props;
+ const {style} = ReactDOM.findDOMNode(this);
+ style.height = 0;
+
+ if (enterDelay) {
+ this.enterTimer = setTimeout(() => callback(), 450);
+ return;
+ }
+
+ callback();
+ }
+
+ componentDidEnter() {
+ this.open();
+ }
+
+ componentWillLeave(callback) {
+ const style = ReactDOM.findDOMNode(this).style;
+ style.height = this.refs.wrapper.clientHeight;
+ style.height = 0;
+ this.leaveTimer = setTimeout(() => callback(), 450);
+ }
+
+ open() {
+ const style = ReactDOM.findDOMNode(this).style;
+ style.height = `${this.refs.wrapper.clientHeight}px`;
+ }
+
+ render() {
+ const {
+ children,
+ style,
+ ...other,
+ } = this.props;
+
+ const {prepareStyles} = this.context.muiTheme;
+
+ const mergedRootStyles = Object.assign({
+ position: 'relative',
+ height: 0,
+ width: '100%',
+ top: 0,
+ left: 0,
+ overflow: 'hidden',
+ transition: transitions.easeOut(null, ['height', 'opacity']),
+ }, style);
+
+ return (
+
+ );
+ }
+}
+
+export default ExpandTransitionChild;
diff --git a/src/styles/getMuiTheme.js b/src/styles/getMuiTheme.js
index 1dfc2d6d8fe597..f017a9cca0cf31 100644
--- a/src/styles/getMuiTheme.js
+++ b/src/styles/getMuiTheme.js
@@ -127,6 +127,10 @@ export default function getMuiTheme(muiTheme, ...more) {
gridTile: {
textColor: white,
},
+ icon: {
+ color: palette.canvasColor,
+ backgroundColor: palette.primary1Color,
+ },
inkBar: {
backgroundColor: palette.accent1Color,
},
@@ -224,18 +228,14 @@ export default function getMuiTheme(muiTheme, ...more) {
fontWeight: typography.fontWeightMedium,
},
stepper: {
- activeAvatarColor: palette.primary1Color,
- hoveredAvatarColor: grey700,
- inactiveAvatarColor: grey500,
-
- inactiveTextColor: ColorManipulator.fade(black, 0.26),
- activeTextColor: ColorManipulator.fade(black, 0.87),
- hoveredTextColor: grey600,
-
- hoveredHeaderColor: ColorManipulator.fade(black, 0.06),
-
+ backgroundColor: 'transparent',
+ hoverBackgroundColor: ColorManipulator.fade(black, 0.06),
+ iconColor: palette.primary1Color,
+ hoveredIconColor: grey700,
+ inactiveIconColor: grey500,
+ textColor: ColorManipulator.fade(black, 0.87),
+ disabledTextColor: ColorManipulator.fade(black, 0.26),
connectorLineColor: grey400,
- avatarSize: 24,
},
table: {
backgroundColor: palette.canvasColor,