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

[Joy] Add Typography component #30489

Merged
merged 16 commits into from
Jan 7, 2022
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions docs/pages/experiments/joy/style-guide.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import {
styled,
ColorPaletteProp,
TypographySystem,
createGetThemeVar,
createGetCssVar,
} from '@mui/joy/styles';

const getThemeVar = createGetThemeVar();
const getCssVar = createGetCssVar();

const rgb2hex = (rgb: string) =>
`#${(rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/) || [])
Expand All @@ -26,7 +26,7 @@ const Typography = styled('p', {
({ theme, level = 'body1', color }) => [
{ margin: 0 },
theme.typography[level],
color && color !== 'context' && { color: getThemeVar(`palette-${color}-textColor`) },
color && color !== 'context' && { color: getCssVar(`palette-${color}-textColor`) },
],
);

Expand Down Expand Up @@ -85,12 +85,12 @@ const ColorToken = ({ name, value }: { name: string; value: string }) => {
<Box
ref={ref}
sx={(theme) => ({
borderRadius: `calc(${theme.getThemeVar('radius-md')} / 2)`,
borderRadius: `calc(${theme.getCssVar('radius-md')} / 2)`,
bgcolor: value,
width: 64,
height: 64,
mb: 1,
boxShadow: theme.getThemeVar('shadow-sm'),
boxShadow: theme.getCssVar('shadow-sm'),
})}
/>
<Typography level="body3">{name}</Typography>
Expand Down Expand Up @@ -118,7 +118,7 @@ const PaletteTokens = () => {
<summary
style={{
marginBottom: '0.5rem',
fontFamily: getThemeVar('fontFamily-body'),
fontFamily: getCssVar('fontFamily-body'),
cursor: 'pointer',
}}
>
Expand Down
101 changes: 101 additions & 0 deletions docs/pages/experiments/joy/typography.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import * as React from 'react';
// @ts-ignore
import { jsx as _jsx } from 'react/jsx-runtime';
import { CssVarsProvider, styled, useColorScheme, FontSize } from '@mui/joy/styles';
import Box from '@mui/joy/Box';
import Button from '@mui/joy/Button';
import Typography from '@mui/joy/Typography';

export const SvgIcon = styled('svg', {
shouldForwardProp: (prop) => prop !== 'fontSize' && prop !== 'sx',
})<{
fontSize: keyof FontSize | 'inherit';
}>(({ theme, fontSize }) => ({
userSelect: 'none',
width: '1em',
height: '1em',
display: 'inline-block',
fill: 'currentColor',
flexShrink: 0,
...(fontSize && {
fontSize: fontSize === 'inherit' ? 'inherit' : theme.vars.fontSize[fontSize],
}),
}));

function createSvgIcon(path: any, displayName: any, initialProps?: any) {
const Component = (props: any, ref: any) =>
(
<SvgIcon
data-testid={`${displayName}Icon`}
ref={ref}
viewBox="0 0 24 24"
fontSize="xl"
{...initialProps}
{...props}
sx={{ ...initialProps?.sx, ...props.sx }}
>
{path}
</SvgIcon>
) as unknown as typeof SvgIcon;

// @ts-ignore
return React.memo(React.forwardRef(Component));
}

export const Moon = createSvgIcon(
_jsx('path', {
d: 'M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9 9-4.03 9-9c0-.46-.04-.92-.1-1.36-.98 1.37-2.58 2.26-4.4 2.26-2.98 0-5.4-2.42-5.4-5.4 0-1.81.89-3.42 2.26-4.4-.44-.06-.9-.1-1.36-.1z',
}),
'DarkMode',
);

export const Sun = createSvgIcon(
_jsx('path', {
d: 'M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zM2 13h2c.55 0 1-.45 1-1s-.45-1-1-1H2c-.55 0-1 .45-1 1s.45 1 1 1zm18 0h2c.55 0 1-.45 1-1s-.45-1-1-1h-2c-.55 0-1 .45-1 1s.45 1 1 1zM11 2v2c0 .55.45 1 1 1s1-.45 1-1V2c0-.55-.45-1-1-1s-1 .45-1 1zm0 18v2c0 .55.45 1 1 1s1-.45 1-1v-2c0-.55-.45-1-1-1s-1 .45-1 1zM5.99 4.58c-.39-.39-1.03-.39-1.41 0-.39.39-.39 1.03 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0s.39-1.03 0-1.41L5.99 4.58zm12.37 12.37c-.39-.39-1.03-.39-1.41 0-.39.39-.39 1.03 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0 .39-.39.39-1.03 0-1.41l-1.06-1.06zm1.06-10.96c.39-.39.39-1.03 0-1.41-.39-.39-1.03-.39-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0l1.06-1.06zM7.05 18.36c.39-.39.39-1.03 0-1.41-.39-.39-1.03-.39-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0l1.06-1.06z',
}),
'LightMode',
);

const ColorSchemePicker = () => {
const { mode, setMode } = useColorScheme();
const [mounted, setMounted] = React.useState(false);
React.useEffect(() => {
setMounted(true);
}, []);
if (!mounted) {
return null;
}

return (
<Button
variant="outlined"
onClick={() => {
if (mode === 'light') {
setMode('dark');
} else {
setMode('light');
}
}}
sx={{ minWidth: 40, p: '0.25rem' }}
>
{mode === 'light' ? <Moon /> : <Sun />}
</Button>
);
};

export default function JoyTypography() {
return (
<CssVarsProvider>
<Box sx={{ py: 5, maxWidth: { md: 1152, xl: 1536 }, mx: 'auto' }}>
<Box sx={{ px: 3, pb: 4 }}>
<ColorSchemePicker />
</Box>
{(['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'body1', 'body2', 'body3'] as const).map((level) => (
<Typography gutterBottom level={level} key={level}>
{`${level} - typography`}
</Typography>
))}
</Box>
</CssVarsProvider>
);
}
18 changes: 18 additions & 0 deletions packages/mui-joy/src/Typography/Typography.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import * as React from 'react';
import Typography from '@mui/joy/Typography';

<Typography component="a" href="/">
Text
</Typography>;

function Link(props: JSX.IntrinsicElements['a']) {
return <a {...props} />;
}
<Typography component={Link} href="/">
Text
</Typography>;

// @ts-expect-error href is not exist in div
<Typography component="div" href="/">
Text
</Typography>;
88 changes: 88 additions & 0 deletions packages/mui-joy/src/Typography/Typography.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import * as React from 'react';
import { expect } from 'chai';
import { createRenderer, describeConformance } from 'test/utils';
import Typography, { typographyClasses as classes } from '@mui/joy/Typography';
import { ThemeProvider } from '@mui/joy/styles';

describe('<Typography />', () => {
const { render } = createRenderer();

describeConformance(<Typography />, () => ({
classes,
inheritComponent: 'p',
ThemeProvider,
render,
refInstanceof: window.HTMLParagraphElement,
muiName: 'MuiTypography',
testStateOverrides: { prop: 'level', value: 'h2', styleKey: 'h2' },
skip: ['componentsProp', 'classesRoot', 'themeVariants'],
}));

it('should render the text', () => {
const { container } = render(<Typography>Hello</Typography>);
expect(container.firstChild).to.have.text('Hello');
});

it('should render body1 root by default', () => {
const { container } = render(<Typography>Hello</Typography>);

expect(container.firstChild).to.have.class(classes.body1);
expect(container.firstChild).to.have.class(classes.root);
});

['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'body1', 'body2', 'body3'].forEach((level) => {
it(`should render ${level} text`, () => {
const { container } = render(<Typography level={level}>Hello</Typography>);

expect(classes).to.have.property(level);

expect(container.firstChild).to.have.class(classes[level]);
});
});

describe('headline', () => {
it('should render the mapped headline', () => {
const { getByText } = render(<Typography level="h6">Hello</Typography>);

expect(getByText(/hello/i).tagName).to.equal('H6');
});

it('should render a h1', () => {
const { getByText } = render(<Typography component="h1">Hello</Typography>);

expect(getByText(/hello/i).tagName).to.equal('H1');
});
});

describe('prop: levelMapping', () => {
it('should work with a single value', () => {
const { getByText } = render(
<Typography level="h6" levelMapping={{ h6: 'aside' }}>
Hello
</Typography>,
);

expect(getByText(/hello/i).tagName).to.equal('ASIDE');
});

it('should work even with an empty mapping', () => {
const { getByText } = render(
<Typography level="h6" levelMapping={{}}>
Hello
</Typography>,
);

expect(getByText(/hello/i).tagName).to.equal('H6');
});
});

it('combines system properties with the sx prop', () => {
const { container } = render(<Typography mt={2} mr={1} sx={{ marginRight: 5, mb: 2 }} />);

expect(container.firstChild).toHaveComputedStyle({
marginTop: '16px',
marginRight: '40px',
marginBottom: '16px',
});
});
});
Loading