diff --git a/docs/pages/api-docs/typography.json b/docs/pages/api-docs/typography.json
index 1245dd47c7e337..9e780146bd7b5a 100644
--- a/docs/pages/api-docs/typography.json
+++ b/docs/pages/api-docs/typography.json
@@ -81,5 +81,5 @@
"filename": "/packages/material-ui/src/Typography/Typography.js",
"inheritance": null,
"demos": "
",
- "styledComponent": false
+ "styledComponent": true
}
diff --git a/docs/scripts/buildApi.ts b/docs/scripts/buildApi.ts
index 6e324d49d73a54..6a63e35c809b7f 100644
--- a/docs/scripts/buildApi.ts
+++ b/docs/scripts/buildApi.ts
@@ -688,6 +688,50 @@ async function updateStylesDefinition(context: {
// Do nothing as not every components has an unstyled version
}
+ // If there is no unstyledFile we need to extract this info from the component's definition file
+ if (typesFilename !== unstyledFileName) {
+ try {
+ const typesSource = readFileSync(typesFilename, { encoding: 'utf8' });
+ const typesAST = await babel.parseAsync(typesSource, {
+ configFile: false,
+ filename: typesFilename,
+ presets: [require.resolve('@babel/preset-typescript')],
+ });
+ if (typesAST === null) {
+ throw new Error('No AST returned from babel.');
+ }
+
+ traverse(typesAST, {
+ TSPropertySignature(babelPath) {
+ const { node } = babelPath;
+ const possiblyPropName = (node.key as babel.types.Identifier).name;
+ if (possiblyPropName === 'classes' && node.typeAnnotation !== null) {
+ const members = (node.typeAnnotation.typeAnnotation as babel.types.TSTypeLiteral)
+ .members;
+
+ if (members) {
+ styles.descriptions = styles.descriptions || {};
+ members.forEach((member) => {
+ const className = ((member as babel.types.TSPropertySignature)
+ .key as babel.types.Identifier).name;
+ styles.classes.push(className);
+ if (member.leadingComments) {
+ styles.descriptions[className] = trimComment(member.leadingComments[0].value);
+ }
+ });
+ }
+ }
+ },
+ });
+
+ if (styles.classes.length > 0) {
+ styles.name = generateMuiName(path.parse(component.filename).name);
+ }
+ } catch (e) {
+ // Do nothing as not every components has an unstyled version
+ }
+ }
+
styles.classes = Array.from(new Set(styles.classes));
}
diff --git a/docs/src/modules/components/Link.js b/docs/src/modules/components/Link.js
index 97fe732e68db3f..7cb5c643b7e116 100644
--- a/docs/src/modules/components/Link.js
+++ b/docs/src/modules/components/Link.js
@@ -8,18 +8,18 @@ import MuiLink from '@material-ui/core/Link';
import { useUserLanguage } from 'docs/src/modules/utils/i18n';
const NextComposed = React.forwardRef(function NextComposed(props, ref) {
- const { as, href, ...other } = props;
+ const { linkAs, href, ...other } = props;
return (
-
+
);
});
NextComposed.propTypes = {
- as: PropTypes.string,
href: PropTypes.string,
+ linkAs: PropTypes.string,
};
// A styled version of the Next.js Link component:
@@ -64,7 +64,7 @@ function Link(props) {
if (naked) {
return (
({
}));
```
+### Core components
+
+As the core components use emotion as a styled engine, the props used by emotion are not intercepted. The prop `as` in the following codesnippet will not be propagated to the `SomeOtherComponent`.
+
+``
+
### AppBar
- [AppBar] Remove z-index when position static and relative
diff --git a/examples/nextjs-with-typescript/src/Link.tsx b/examples/nextjs-with-typescript/src/Link.tsx
index 18d0af0b620997..59a30a098a2eef 100644
--- a/examples/nextjs-with-typescript/src/Link.tsx
+++ b/examples/nextjs-with-typescript/src/Link.tsx
@@ -6,16 +6,16 @@ import NextLink, { LinkProps as NextLinkProps } from 'next/link';
import MuiLink, { LinkProps as MuiLinkProps } from '@material-ui/core/Link';
type NextComposedProps = Omit, 'href'> &
- NextLinkProps;
+ Omit & { linkAs?: NextLinkProps['as'] };
const NextComposed = React.forwardRef((props, ref) => {
- const { as, href, replace, scroll, passHref, shallow, prefetch, ...other } = props;
+ const { href, linkAs, replace, scroll, passHref, shallow, prefetch, ...other } = props;
return (
', () => {
const mount = createMount();
let classes;
- let typographyClasses;
const render = createClientRender();
before(() => {
- typographyClasses = getClasses();
classes = getClasses();
});
diff --git a/packages/material-ui/src/InputAdornment/InputAdornment.test.js b/packages/material-ui/src/InputAdornment/InputAdornment.test.js
index d83c9ce185f7e7..5d795ab470fe7a 100644
--- a/packages/material-ui/src/InputAdornment/InputAdornment.test.js
+++ b/packages/material-ui/src/InputAdornment/InputAdornment.test.js
@@ -1,7 +1,7 @@
import * as React from 'react';
import { expect } from 'chai';
import { getClasses, createMount, createClientRender, describeConformance } from 'test/utils';
-import Typography from '../Typography';
+import { typographyClasses } from '../Typography';
import InputAdornment from './InputAdornment';
import TextField from '../TextField';
import FormControl from '../FormControl';
@@ -26,7 +26,6 @@ describe('', () => {
it('should wrap text children in a Typography', () => {
const { container } = render(foo);
- const typographyClasses = getClasses();
const typography = container.querySelector(`.${typographyClasses.root}`);
expect(typography).not.to.equal(null);
@@ -161,7 +160,6 @@ describe('', () => {
foo
,
);
- const typographyClasses = getClasses();
expect(container.querySelector(`.${typographyClasses.root}`)).to.equal(null);
});
diff --git a/packages/material-ui/src/Link/Link.test.js b/packages/material-ui/src/Link/Link.test.js
index 10a757c07d8a6e..1e9b972a715900 100644
--- a/packages/material-ui/src/Link/Link.test.js
+++ b/packages/material-ui/src/Link/Link.test.js
@@ -10,7 +10,7 @@ import {
fireEvent,
} from 'test/utils';
import Link from './Link';
-import Typography from '../Typography';
+import Typography, { typographyClasses } from '../Typography';
function focusVisible(element) {
act(() => {
@@ -24,11 +24,9 @@ describe('', () => {
const mount = createMount();
const render = createClientRender();
let classes;
- let typographyClasses;
before(() => {
classes = getClasses(Home);
- typographyClasses = getClasses();
});
describeConformance(Home, () => ({
diff --git a/packages/material-ui/src/ListItemText/ListItemText.test.js b/packages/material-ui/src/ListItemText/ListItemText.test.js
index 7181636e79e247..687eeecf416ea3 100644
--- a/packages/material-ui/src/ListItemText/ListItemText.test.js
+++ b/packages/material-ui/src/ListItemText/ListItemText.test.js
@@ -1,18 +1,16 @@
import * as React from 'react';
import { expect } from 'chai';
import { getClasses, createMount, createClientRender, describeConformance } from 'test/utils';
-import Typography from '../Typography';
+import Typography, { typographyClasses } from '../Typography';
import ListItemText from './ListItemText';
describe('', () => {
const mount = createMount();
const render = createClientRender();
let classes;
- let typographyClasses;
before(() => {
classes = getClasses();
- typographyClasses = getClasses();
});
describeConformance(, () => ({
diff --git a/packages/material-ui/src/StepLabel/StepLabel.test.js b/packages/material-ui/src/StepLabel/StepLabel.test.js
index 747985cda0d3fe..2851ea3c8c101e 100644
--- a/packages/material-ui/src/StepLabel/StepLabel.test.js
+++ b/packages/material-ui/src/StepLabel/StepLabel.test.js
@@ -1,7 +1,7 @@
import * as React from 'react';
import { expect } from 'chai';
import { getClasses, createClientRender, createMount, describeConformance } from 'test/utils';
-import Typography from '../Typography';
+import Typography, { typographyClasses } from '../Typography';
import Stepper from '../Stepper';
import Step from '../Step';
import StepIcon from '../StepIcon';
@@ -10,14 +10,12 @@ import StepLabel from './StepLabel';
describe('', () => {
let classes;
let iconClasses;
- let typographyClasses;
const mount = createMount({ strict: true });
const render = createClientRender();
before(() => {
classes = getClasses();
iconClasses = getClasses();
- typographyClasses = getClasses();
});
describeConformance(, () => ({
diff --git a/packages/material-ui/src/Typography/Typography.js b/packages/material-ui/src/Typography/Typography.js
index 49033ad16b7707..05401c3c660e98 100644
--- a/packages/material-ui/src/Typography/Typography.js
+++ b/packages/material-ui/src/Typography/Typography.js
@@ -1,106 +1,69 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
-import { useThemeVariants } from '@material-ui/styles';
-import withStyles from '../styles/withStyles';
+import experimentalStyled from '../styles/experimentalStyled';
+import useThemeProps from '../styles/useThemeProps';
import capitalize from '../utils/capitalize';
+import typographyClasses, { getTypographyUtilityClass } from './typographyClasses';
-export const styles = (theme) => ({
- /* Styles applied to the root element. */
- root: {
- margin: 0,
- },
- /* Styles applied to the root element if `variant="body2"`. */
- body2: theme.typography.body2,
- /* Styles applied to the root element if `variant="body1"`. */
- body1: theme.typography.body1,
- /* Styles applied to the root element if `variant="caption"`. */
- caption: theme.typography.caption,
- /* Styles applied to the root element if `variant="button"`. */
- button: theme.typography.button,
- /* Styles applied to the root element if `variant="h1"`. */
- h1: theme.typography.h1,
- /* Styles applied to the root element if `variant="h2"`. */
- h2: theme.typography.h2,
- /* Styles applied to the root element if `variant="h3"`. */
- h3: theme.typography.h3,
- /* Styles applied to the root element if `variant="h4"`. */
- h4: theme.typography.h4,
- /* Styles applied to the root element if `variant="h5"`. */
- h5: theme.typography.h5,
- /* Styles applied to the root element if `variant="h6"`. */
- h6: theme.typography.h6,
- /* Styles applied to the root element if `variant="subtitle1"`. */
- subtitle1: theme.typography.subtitle1,
- /* Styles applied to the root element if `variant="subtitle2"`. */
- subtitle2: theme.typography.subtitle2,
- /* Styles applied to the root element if `variant="overline"`. */
- overline: theme.typography.overline,
- /* Styles applied to the root element if `variant="inherit"`. */
- inherit: {},
- /* Styles applied to the root element if `align="left"`. */
- alignLeft: {
- textAlign: 'left',
- },
- /* Styles applied to the root element if `align="center"`. */
- alignCenter: {
- textAlign: 'center',
- },
- /* Styles applied to the root element if `align="right"`. */
- alignRight: {
- textAlign: 'right',
- },
- /* Styles applied to the root element if `align="justify"`. */
- alignJustify: {
- textAlign: 'justify',
- },
- /* Styles applied to the root element if `nowrap={true}`. */
- noWrap: {
+const getTextColor = (color, palette) => {
+ if (color.indexOf('text') === 0) {
+ return palette.text[color.split('text').pop().toLowerCase()];
+ }
+
+ if (color === 'inherit' || color === 'initial') {
+ return color;
+ }
+
+ return palette[color].main;
+};
+
+const overridesResolver = (props, styles) => {
+ const { styleProps = {} } = props;
+
+ const styleOverrides = {
+ ...styles.root,
+ ...(styleProps.variant && styles[styleProps.variant]),
+ ...(styleProps.color && styles[`color${capitalize(styleProps.color)}`]),
+ ...(styleProps.align && styles[`align${capitalize(styleProps.align)}`]),
+ ...(styleProps.display && styles[`display${capitalize(styleProps.display)}`]),
+ ...(styleProps.noWrap && styles.noWrap),
+ ...(styleProps.gutterBottom && styles.gutterBottom),
+ ...(styleProps.paragraph && styles.paragraph),
+ };
+
+ return styleOverrides;
+};
+
+export const TypographyRoot = experimentalStyled(
+ 'span',
+ {},
+ { name: 'Typography', slot: 'Root', overridesResolver },
+)((props) => ({
+ margin: 0,
+ ...(props.styleProps.variant && props.theme.typography[props.styleProps.variant]),
+ ...(props.styleProps.align !== 'inherit' && {
+ textAlign: props.styleProps.align,
+ }),
+ ...(props.styleProps.noWrap && {
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
- },
- /* Styles applied to the root element if `gutterBottom={true}`. */
- gutterBottom: {
+ }),
+ ...(props.styleProps.gutterBottom && {
marginBottom: '0.35em',
- },
- /* Styles applied to the root element if `paragraph={true}`. */
- paragraph: {
+ }),
+ ...(props.styleProps.paragraph && {
marginBottom: 16,
- },
- /* Styles applied to the root element if `color="inherit"`. */
- colorInherit: {
- color: 'inherit',
- },
- /* Styles applied to the root element if `color="primary"`. */
- colorPrimary: {
- color: theme.palette.primary.main,
- },
- /* Styles applied to the root element if `color="secondary"`. */
- colorSecondary: {
- color: theme.palette.secondary.main,
- },
- /* Styles applied to the root element if `color="textPrimary"`. */
- colorTextPrimary: {
- color: theme.palette.text.primary,
- },
- /* Styles applied to the root element if `color="textSecondary"`. */
- colorTextSecondary: {
- color: theme.palette.text.secondary,
- },
- /* Styles applied to the root element if `color="error"`. */
- colorError: {
- color: theme.palette.error.main,
- },
- /* Styles applied to the root element if `display="inline"`. */
- displayInline: {
- display: 'inline',
- },
- /* Styles applied to the root element if `display="block"`. */
- displayBlock: {
- display: 'block',
- },
-});
+ }),
+ ...(props.styleProps.color &&
+ props.styleProps.color !== 'initial' && {
+ color: getTextColor(props.styleProps.color, props.theme.palette),
+ }),
+ ...(props.styleProps.display !== 'initial' && {
+ display: props.styleProps.display,
+ }),
+}));
const defaultVariantMapping = {
h1: 'h1',
@@ -116,10 +79,40 @@ const defaultVariantMapping = {
inherit: 'p',
};
-const Typography = React.forwardRef(function Typography(props, ref) {
+const useTypographyClasses = (props) => {
+ const { align, color, display, gutterBottom, noWrap, paragraph, variant, classes = {} } = props;
+
+ const utilityClasses = {
+ root: clsx(
+ typographyClasses['root'],
+ classes['root'],
+ getTypographyUtilityClass(`color${capitalize(color)}`),
+ classes[`color${capitalize(color)}`],
+ typographyClasses[`align${capitalize(align)}`],
+ classes[`align${capitalize(align)}`],
+ typographyClasses[`display${capitalize(display)}`],
+ classes[`display${capitalize(display)}`],
+ getTypographyUtilityClass(variant),
+ classes[variant],
+ {
+ [typographyClasses['gutterBottom']]: gutterBottom,
+ [classes['gutterBottom']]: gutterBottom,
+ [typographyClasses['noWrap']]: noWrap,
+ [classes['noWrap']]: noWrap,
+ [typographyClasses['paragraph']]: paragraph,
+ [classes['paragraph']]: paragraph,
+ },
+ ),
+ };
+
+ return utilityClasses;
+};
+
+const Typography = React.forwardRef(function Typography(inProps, ref) {
+ const props = useThemeProps({ props: inProps, name: 'MuiTypography' });
+
const {
align = 'inherit',
- classes,
className,
color = 'initial',
component,
@@ -132,43 +125,33 @@ const Typography = React.forwardRef(function Typography(props, ref) {
...other
} = props;
- const themeVariantsClasses = useThemeVariants(
- {
- ...props,
- align,
- color,
- display,
- gutterBottom,
- noWrap,
- paragraph,
- variant,
- variantMapping,
- },
- 'MuiTypography',
- );
+ const stateAndProps = {
+ ...props,
+ align,
+ className,
+ color,
+ component,
+ display,
+ gutterBottom,
+ noWrap,
+ paragraph,
+ variant,
+ variantMapping,
+ };
const Component =
component ||
(paragraph ? 'p' : variantMapping[variant] || defaultVariantMapping[variant]) ||
'span';
+ const classes = useTypographyClasses(stateAndProps);
+
return (
-
);
@@ -282,4 +265,4 @@ Typography.propTypes = {
variantMapping: PropTypes /* @typescript-to-proptypes-ignore */.object,
};
-export default withStyles(styles, { name: 'MuiTypography' })(Typography);
+export default Typography;
diff --git a/packages/material-ui/src/Typography/Typography.test.js b/packages/material-ui/src/Typography/Typography.test.js
index 4c62786a1b77c5..9516f56d762223 100644
--- a/packages/material-ui/src/Typography/Typography.test.js
+++ b/packages/material-ui/src/Typography/Typography.test.js
@@ -1,32 +1,26 @@
// @ts-check
import * as React from 'react';
import { expect } from 'chai';
-import { getClasses, createClientRender, createMount, describeConformance } from 'test/utils';
+import { createClientRender, createMount, describeConformanceV5 } from 'test/utils';
import Typography from './Typography';
+import classes from './typographyClasses';
describe('', () => {
/**
* @type {ReturnType}
*/
const mount = createMount();
- /**
- * // we test at runtime that this is equal to
- * Record
- * @type {Record}
- */
- let classes;
const render = createClientRender();
- before(() => {
- classes = getClasses();
- });
-
- describeConformance(, () => ({
+ describeConformanceV5(, () => ({
classes,
inheritComponent: 'p',
mount,
refInstanceof: window.HTMLParagraphElement,
+ muiName: 'MuiTypography',
+ testVariantProps: { color: 'secondary', variant: 'dot' },
+ skip: ['componentsProp'],
}));
it('should render the text', () => {
@@ -50,17 +44,30 @@ describe('', () => {
expect(container.firstChild).to.have.class(classes.alignCenter);
});
- ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'subtitle1', 'body2', 'body1', 'caption', 'button'].forEach(
- (variant) => {
- it(`should render ${variant} text`, () => {
- // @ts-ignore literal/tuple type widening
- const { container } = render(Hello);
-
- expect(classes[variant] != null).to.equal(true);
- expect(container.firstChild).to.have.class(classes[variant]);
- });
- },
- );
+ [
+ 'h1',
+ 'h2',
+ 'h3',
+ 'h4',
+ 'h5',
+ 'h6',
+ 'subtitle1',
+ 'body2',
+ 'body1',
+ 'caption',
+ 'button',
+ 'overline',
+ ].forEach((variant) => {
+ it(`should render ${variant} text`, () => {
+ // @ts-ignore literal/tuple type widening
+ const { container } = render(Hello);
+
+ expect(classes).to.have.property(variant);
+
+ // @ts-ignore
+ expect(container.firstChild).to.have.class(classes[variant]);
+ });
+ });
[
['primary', 'colorPrimary'],
@@ -73,7 +80,9 @@ describe('', () => {
// @ts-ignore literal/tuple type widening
const { container } = render(Hello);
+ // @ts-ignore
expect(classes[className] != null).to.equal(true);
+ // @ts-ignore
expect(container.firstChild).to.have.class(classes[className]);
});
});
diff --git a/packages/material-ui/src/Typography/index.js b/packages/material-ui/src/Typography/index.js
index eabc291fd82ca6..64ab72d2bf4100 100644
--- a/packages/material-ui/src/Typography/index.js
+++ b/packages/material-ui/src/Typography/index.js
@@ -1 +1,2 @@
export { default } from './Typography';
+export { default as typographyClasses, getTypographyUtilityClass } from './typographyClasses';
diff --git a/packages/material-ui/src/Typography/typographyClasses.js b/packages/material-ui/src/Typography/typographyClasses.js
new file mode 100644
index 00000000000000..c187797c7dde81
--- /dev/null
+++ b/packages/material-ui/src/Typography/typographyClasses.js
@@ -0,0 +1,38 @@
+export function getTypographyUtilityClass(name) {
+ return `MuiTypography-${name}`;
+}
+
+const typographyClasses = {
+ root: getTypographyUtilityClass('root'),
+ h1: getTypographyUtilityClass('h1'),
+ h2: getTypographyUtilityClass('h2'),
+ h3: getTypographyUtilityClass('h3'),
+ h4: getTypographyUtilityClass('h4'),
+ h5: getTypographyUtilityClass('h5'),
+ h6: getTypographyUtilityClass('h6'),
+ subtitle1: getTypographyUtilityClass('subtitle1'),
+ subtitle2: getTypographyUtilityClass('subtitle2'),
+ body1: getTypographyUtilityClass('body1'),
+ body2: getTypographyUtilityClass('body2'),
+ inherit: getTypographyUtilityClass('inherit'),
+ button: getTypographyUtilityClass('button'),
+ caption: getTypographyUtilityClass('caption'),
+ overline: getTypographyUtilityClass('overline'),
+ alignLeft: getTypographyUtilityClass('alignLeft'),
+ alignRight: getTypographyUtilityClass('alignRight'),
+ alignCenter: getTypographyUtilityClass('alignCenter'),
+ alignJustify: getTypographyUtilityClass('alignJustify'),
+ noWrap: getTypographyUtilityClass('noWrap'),
+ gutterBottom: getTypographyUtilityClass('gutterBottom'),
+ paragraph: getTypographyUtilityClass('paragraph'),
+ colorInherit: getTypographyUtilityClass('colorInherit'),
+ colorPrimary: getTypographyUtilityClass('colorPrimary'),
+ colorSecondary: getTypographyUtilityClass('colorSecondary'),
+ colorTextPrimary: getTypographyUtilityClass('colorTextPrimary'),
+ colorTextSecondary: getTypographyUtilityClass('colorTextSecondary'),
+ colorError: getTypographyUtilityClass('colorError'),
+ displayInline: getTypographyUtilityClass('displayInline'),
+ displayBlock: getTypographyUtilityClass('displayBlock'),
+};
+
+export default typographyClasses;