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

[Collapse] Migrate to emotion #24501

Merged
merged 13 commits into from
Jan 20, 2021
3 changes: 2 additions & 1 deletion docs/pages/api-docs/collapse.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"type": { "name": "enum", "description": "'horizontal'<br>&#124;&nbsp;'vertical'" },
"default": "'vertical'"
},
"sx": { "type": { "name": "object" } },
"timeout": {
"type": {
"name": "union",
Expand All @@ -34,5 +35,5 @@
"pathname": "https://reactcommunity.org/react-transition-group/transition#Transition-props"
},
"demos": "<ul><li><a href=\"/components/cards/\">Cards</a></li>\n<li><a href=\"/components/lists/\">Lists</a></li>\n<li><a href=\"/components/transitions/\">Transitions</a></li></ul>",
"styledComponent": false
"styledComponent": true
}
1 change: 1 addition & 0 deletions docs/translations/api-docs/collapse/collapse.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"component": "The component used for the root node. Either a string to use a HTML element or a component.<br>⚠️ <a href=\"/guides/composition/#caveat-with-refs\">Needs to be able to hold a ref</a>.",
"in": "If <code>true</code>, the component will transition in.",
"orientation": "The transition orientation.",
"sx": "The system prop that allows defining system overrides as well as additional CSS styles. See the <a href=\"/system/basics/#the-sx-prop\">`sx` page</a> for more details.",
"timeout": "The duration for the transition, in milliseconds. You may specify a single timeout for all transitions, or individually with an object.<br>Set to &#39;auto&#39; to automatically calculate transition time based on height."
},
"classDescriptions": {
Expand Down
7 changes: 6 additions & 1 deletion packages/material-ui/src/Collapse/Collapse.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as React from 'react';
import { InternalStandardProps as StandardProps } from '..';
import { SxProps } from '@material-ui/system';
import { InternalStandardProps as StandardProps, Theme } from '..';
import { TransitionProps } from '../transitions/transition';

export interface CollapseProps extends StandardProps<TransitionProps, 'timeout'> {
Expand Down Expand Up @@ -52,6 +53,10 @@ export interface CollapseProps extends StandardProps<TransitionProps, 'timeout'>
* @default duration.standard
*/
timeout?: TransitionProps['timeout'] | 'auto';
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
sx?: SxProps<Theme>;
}

export type CollapseClassKey = keyof NonNullable<CollapseProps['classes']>;
Expand Down
178 changes: 122 additions & 56 deletions packages/material-ui/src/Collapse/Collapse.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,71 +2,125 @@ import * as React from 'react';
import clsx from 'clsx';
import PropTypes from 'prop-types';
import { Transition } from 'react-transition-group';
import { elementTypeAcceptingRef } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
import { deepmerge, elementTypeAcceptingRef } from '@material-ui/utils';
import { unstable_composeClasses as composeClasses } from '@material-ui/unstyled';
import experimentalStyled from '../styles/experimentalStyled';
import useThemeProps from '../styles/useThemeProps';
import { duration } from '../styles/transitions';
import { getTransitionProps } from '../transitions/utils';
import useTheme from '../styles/useTheme';
import { useForkRef } from '../utils';
import collapseClasses, { getCollapseUtilityClass } from './collapseClasses';

export const styles = (theme) => ({
/* Styles applied to the root element. */
root: {
height: 0,
overflow: 'hidden',
transition: theme.transitions.create('height'),
'&$horizontal': {
height: 'auto',
width: 0,
transition: theme.transitions.create('width'),
},
const overridesResolver = (props, styles) => {
const { styleProps } = props;

return deepmerge(styles.root || {}, {
...styles[styleProps.orientation],
...(styleProps.state === 'entered' && styles.entered),
...(styleProps.state === 'exited' &&
!styleProps.in &&
styleProps.collapsedSize === '0px' &&
styles.hidden),
[`& .${collapseClasses.wrapper}`]: styles.wrapper,
[`& .${collapseClasses.wrapperInner}`]: styles.wrapperInner,
});
};

const useUtilityClasses = (styleProps) => {
const { orientation, classes } = styleProps;

const slots = {
root: ['root', `${orientation}`],
queengooborg marked this conversation as resolved.
Show resolved Hide resolved
entered: ['entered'],
hidden: ['hidden'],
wrapper: ['wrapper', `${orientation}`],
wrapperInner: ['wrapperInner', `${orientation}`],
};

return composeClasses(slots, getCollapseUtilityClass, classes);
};

const CollapseRoot = experimentalStyled(
'div',
{},
{
name: 'MuiCollapse',
slot: 'Root',
overridesResolver,
},
/* Pseudo-class applied to the root element if `orientation="horizontal"`. */
horizontal: {},
)(({ theme, styleProps }) => ({
/* Styles applied to the root element. */
height: 0,
overflow: 'hidden',
transition: theme.transitions.create('height'),
...(styleProps.orientation === 'horizontal' && {
height: 'auto',
width: 0,
transition: theme.transitions.create('width'),
}),
/* Styles applied to the root element when the transition has entered. */
entered: {
...(styleProps.state === 'entered' && {
height: 'auto',
overflow: 'visible',
'&$horizontal': {
...(styleProps.orientation === 'horizontal' && {
width: 'auto',
},
},
}),
}),
/* Styles applied to the root element when the transition has exited and `collapsedSize` = 0px. */
hidden: {
visibility: 'hidden',
},
/* Styles applied to the outer wrapper element. */
wrapper: {
// Hack to get children with a negative margin to not falsify the height computation.
display: 'flex',
width: '100%',
'&$horizontal': {
width: 'auto',
height: '100%',
},
...(styleProps.state === 'exited' &&
!styleProps.in &&
styleProps.collapsedSize === '0px' && {
visibility: 'hidden',
}),
}));

/* Styles applied to the outer wrapper element. */
const CollapseWrapper = experimentalStyled(
'div',
{},
{
name: 'MuiCollapse',
slot: 'Wrapper',
},
/* Styles applied to the inner wrapper element. */
wrapperInner: {
width: '100%',
'&$horizontal': {
width: 'auto',
height: '100%',
},
)(({ styleProps }) => ({
// Hack to get children with a negative margin to not falsify the height computation.
display: 'flex',
width: '100%',
...(styleProps.orientation === 'horizontal' && {
width: 'auto',
height: '100%',
}),
}));

/* Styles applied to the inner wrapper element. */
const CollapseWrapperInner = experimentalStyled(
'div',
{},
{
name: 'MuiCollapse',
slot: 'WrapperInner',
},
});
)(({ styleProps }) => ({
width: '100%',
...(styleProps.orientation === 'horizontal' && {
width: 'auto',
height: '100%',
}),
}));

/**
* The Collapse transition is used by the
* [Vertical Stepper](/components/steppers/#vertical-stepper) StepContent component.
* It uses [react-transition-group](https://github.com/reactjs/react-transition-group) internally.
*/
const Collapse = React.forwardRef(function Collapse(props, ref) {
const Collapse = React.forwardRef(function Collapse(inProps, ref) {
const props = useThemeProps({ props: inProps, name: 'MuiCollapse' });
const {
children,
classes,
className,
collapsedSize: collapsedSizeProp = '0px',
component: Component = 'div',
component,
in: inProp,
onEnter,
onEntered,
Expand All @@ -81,6 +135,15 @@ const Collapse = React.forwardRef(function Collapse(props, ref) {
TransitionComponent = Transition,
...other
} = props;

const styleProps = {
...props,
orientation,
queengooborg marked this conversation as resolved.
Show resolved Hide resolved
collapsedSize: collapsedSizeProp,
};

const classes = useUtilityClasses(styleProps);

const theme = useTheme();
const timer = React.useRef();
const wrapperRef = React.useRef(null);
Expand Down Expand Up @@ -224,11 +287,11 @@ const Collapse = React.forwardRef(function Collapse(props, ref) {
{...other}
>
{(state, childProps) => (
<Component
<CollapseRoot
as={component}
className={clsx(
queengooborg marked this conversation as resolved.
Show resolved Hide resolved
classes.root,
{
[classes.horizontal]: isHorizontal,
[classes.entered]: state === 'entered',
[classes.hidden]: state === 'exited' && !inProp && collapsedSize === '0px',
},
Expand All @@ -238,24 +301,23 @@ const Collapse = React.forwardRef(function Collapse(props, ref) {
[isHorizontal ? 'minWidth' : 'minHeight']: collapsedSize,
...style,
}}
styleProps={{ ...styleProps, state }}
ref={handleRef}
{...childProps}
>
<div
className={clsx(classes.wrapper, {
[classes.horizontal]: isHorizontal,
})}
<CollapseWrapper
styleProps={{ ...styleProps, state }}
className={classes.wrapper}
ref={wrapperRef}
>
<div
className={clsx(classes.wrapperInner, {
[classes.horizontal]: isHorizontal,
})}
<CollapseWrapperInner
styleProps={{ ...styleProps, state }}
className={classes.wrapperInner}
>
{children}
</div>
</div>
</Component>
</CollapseWrapperInner>
</CollapseWrapper>
</CollapseRoot>
)}
</TransitionComponent>
);
Expand Down Expand Up @@ -325,6 +387,10 @@ Collapse.propTypes = {
* @ignore
*/
style: PropTypes.object,
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
sx: PropTypes.object,
/**
* The duration for the transition, in milliseconds.
* You may specify a single timeout for all transitions, or individually with an object.
Expand All @@ -345,4 +411,4 @@ Collapse.propTypes = {

Collapse.muiSupportAuto = true;

export default withStyles(styles, { name: 'MuiCollapse' })(Collapse);
export default Collapse;
15 changes: 7 additions & 8 deletions packages/material-ui/src/Collapse/Collapse.test.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,29 @@
import * as React from 'react';
import { expect } from 'chai';
import { spy, stub, useFakeTimers } from 'sinon';
import { createClientRender, getClasses, createMount, describeConformance } from 'test/utils';
import { createClientRender, createMount, describeConformanceV5 } from 'test/utils';
import { ThemeProvider, createMuiTheme } from '@material-ui/core/styles';
import { Transition } from 'react-transition-group';
import Collapse from './Collapse';
import classes from './collapseClasses';

describe('<Collapse />', () => {
const mount = createMount({ strict: true });
let classes;
const defaultProps = {
in: true,
children: <div />,
};
const render = createClientRender();

before(() => {
classes = getClasses(<Collapse {...defaultProps} />);
});

describeConformance(<Collapse {...defaultProps} />, () => ({
describeConformanceV5(<Collapse {...defaultProps} />, () => ({
classes,
inheritComponent: Transition,
mount,
refInstanceof: window.HTMLDivElement,
testComponentPropWith: 'span',
muiName: 'MuiCollapse',
testVariantProps: { orientation: 'horizontal' },
queengooborg marked this conversation as resolved.
Show resolved Hide resolved
testDeepOverrides: { slotName: 'wrapper', slotClassName: classes.wrapper },
skip: ['componentsProp'],
}));

it('should render a container around the wrapper', () => {
Expand Down
15 changes: 15 additions & 0 deletions packages/material-ui/src/Collapse/collapseClasses.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export interface CollapseClasses {
root: string;
horizontal: string;
queengooborg marked this conversation as resolved.
Show resolved Hide resolved
vertical: string;
entered: string;
hidden: string;
wrapper: string;
wrapperInner: string;
}

declare const collapseClasses: CollapseClasses;

export function getCollapseUtilityClass(slot: string): string;

export default collapseClasses;
17 changes: 17 additions & 0 deletions packages/material-ui/src/Collapse/collapseClasses.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { generateUtilityClass, generateUtilityClasses } from '@material-ui/unstyled';

export function getCollapseUtilityClass(slot) {
return generateUtilityClass('MuiCollapse', slot);
}

const collapseClasses = generateUtilityClasses('MuiCollapse', [
'root',
'horizontal',
queengooborg marked this conversation as resolved.
Show resolved Hide resolved
'vertical',
'entered',
'hidden',
'wrapper',
'wrapperInner',
]);

export default collapseClasses;
3 changes: 3 additions & 0 deletions packages/material-ui/src/Collapse/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
export { default } from './Collapse';
export * from './Collapse';

export { default as collapseClasses } from './collapseClasses';
export * from './collapseClasses';
3 changes: 3 additions & 0 deletions packages/material-ui/src/Collapse/index.js
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
export { default } from './Collapse';

export { default as collapseClasses } from './collapseClasses';
export * from './collapseClasses';
4 changes: 1 addition & 3 deletions packages/material-ui/src/StepContent/StepContent.test.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
import * as React from 'react';
import { expect } from 'chai';
import { getClasses, createClientRender, createMount, describeConformance } from 'test/utils';
import Collapse from '../Collapse';
import { collapseClasses } from '../Collapse';
import Stepper from '../Stepper';
import Step from '../Step';
import StepContent from './StepContent';

describe('<StepContent />', () => {
let classes;
let collapseClasses;
const mount = createMount({ strict: true });
const render = createClientRender();

before(() => {
classes = getClasses(<StepContent />);
collapseClasses = getClasses(<Collapse />);
});

describeConformance(<StepContent />, () => ({
Expand Down