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 Checkbox component #31273

Merged
merged 15 commits into from
Mar 7, 2022
91 changes: 91 additions & 0 deletions docs/pages/experiments/joy/checkbox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import * as React from 'react';
import Box from '@mui/joy/Box';
import Button from '@mui/joy/Button';
import Checkbox from '@mui/joy/Checkbox';
import Typography from '@mui/joy/Typography';
import { CssVarsProvider, useColorScheme } from '@mui/joy/styles';
import Moon from '@mui/icons-material/DarkMode';
import Sun from '@mui/icons-material/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>
);
};

const props = {
size: ['sm', 'md', 'lg'],
color: ['primary', 'danger', 'info', 'success', 'warning'],
variant: ['outlined', 'light', 'contained'],
} as const;

export default function JoyCheckbox() {
return (
<CssVarsProvider>
<Box sx={{ py: 5, maxWidth: { md: 1152, xl: 1536 }, mx: 'auto' }}>
<Box sx={{ px: 3 }}>
<ColorSchemePicker />
</Box>
<Box
sx={{
display: 'flex',
flexWrap: 'wrap',
gap: 5,
'& > div': {
display: 'flex',
flexDirection: 'column',
gap: 5,
p: 2,
alignItems: 'center',
},
}}
>
{Object.entries(props).map(([propName, propValue]) => (
<Box key={propName}>
<Typography sx={{ textDecoration: 'underline' }}>{propName}</Typography>
{propValue.map((value) => (
<Box key={value}>
<Checkbox {...{ [propName]: value }} />
{value && (
<Typography level="body3" sx={{ textAlign: 'center', mt: '4px' }}>
{value}
</Typography>
)}
</Box>
))}
</Box>
))}
<Box>
<Box>
<Checkbox indeterminate />
<Typography level="body3" sx={{ textAlign: 'center', mt: '4px' }}>
indeterminate
</Typography>
</Box>
</Box>
</Box>
</Box>
</CssVarsProvider>
);
}
12 changes: 12 additions & 0 deletions docs/pages/experiments/joy/components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { GlobalStyles } from '@mui/system';
import { ThemeProvider, createTheme } from '@mui/material/styles';
import Box from '@mui/joy/Box';
import Button from '@mui/joy/Button';
import Checkbox from '@mui/joy/Checkbox';
import IconButton from '@mui/joy/IconButton';
import List from '@mui/joy/List';
import ListItem from '@mui/joy/ListItem';
Expand Down Expand Up @@ -333,6 +334,17 @@ const components: {
{ id: '--Input-adornment-offset', type: 'number', unit: 'px' },
],
},
{
name: 'Checkbox',
render: (props: any) => (
<React.Fragment>
<Checkbox {...props} />
<Checkbox checked {...props} />
<Checkbox indeterminate {...props} />
</React.Fragment>
),
cssVars: [{ id: '--Checkbox-size', type: 'number', unit: 'px', defaultValue: 20 }],
},
];

function Playground({ initialName }: { initialName?: string }) {
Expand Down
35 changes: 35 additions & 0 deletions packages/mui-joy/src/Checkbox/Checkbox.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import * as React from 'react';
import Checkbox from '@mui/joy/Checkbox';

<Checkbox />;

<Checkbox component="div" />;

<Checkbox data-testid="any" />;

<Checkbox defaultChecked />;

<Checkbox checked />;

<Checkbox indeterminate />;

<Checkbox
onChange={(event) => {
const checked = event.target.checked;
}}
/>;

<Checkbox color="primary" />;
<Checkbox color="danger" />;
<Checkbox color="info" />;
<Checkbox color="success" />;
<Checkbox color="warning" />;
<Checkbox color="neutral" />;

<Checkbox variant="outlined" />;
<Checkbox variant="light" />;
<Checkbox variant="contained" />;

<Checkbox size="sm" />;
<Checkbox size="md" />;
<Checkbox size="lg" />;
87 changes: 87 additions & 0 deletions packages/mui-joy/src/Checkbox/Checkbox.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import * as React from 'react';
import { expect } from 'chai';
import { describeConformance, act, createRenderer, fireEvent } from 'test/utils';
import Checkbox, { checkboxClasses as classes } from '@mui/joy/Checkbox';
import { ThemeProvider } from '@mui/joy/styles';

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

describeConformance(<Checkbox />, () => ({
classes,
render,
ThemeProvider,
muiName: 'MuiCheckbox',
testDeepOverrides: [{ slotName: 'input', slotClassName: classes.input }],
refInstanceof: window.HTMLSpanElement,
skip: ['componentProp', 'componentsProp', 'classesRoot', 'propsSpread', 'themeVariants'],
}));

it('should have the classes required for Checkbox', () => {
expect(classes).to.include.all.keys(['root', 'checked', 'disabled']);
});

it('renders a `role="checkbox"` with the Unchecked state by default', () => {
const { getByRole } = render(<Checkbox />);

expect(getByRole('checkbox')).to.have.property('checked', false);
});

it('renders a checkbox with the Checked state when checked', () => {
const { getByRole } = render(<Checkbox defaultChecked />);

expect(getByRole('checkbox')).to.have.property('checked', true);
});

it('the checkbox can be disabled', () => {
const { getByRole } = render(<Checkbox disabled />);

expect(getByRole('checkbox')).to.have.property('disabled', true);
});

it('the Checked state changes after change events', () => {
const { getByRole } = render(<Checkbox defaultChecked />);

// how a user would trigger it
act(() => {
getByRole('checkbox').click();
fireEvent.change(getByRole('checkbox'), { target: { checked: '' } });
});

expect(getByRole('checkbox')).to.have.property('checked', false);
});

it('should have configurable color', () => {
const { container, rerender } = render(<Checkbox />);

expect(container.firstChild).to.have.class(classes.colorNeutral); // default

rerender(<Checkbox color="primary" />);
expect(container.firstChild).to.have.class(classes.colorPrimary);
});

it('should have configurable variant', () => {
const { container, rerender } = render(<Checkbox />);

expect(container.firstChild).to.have.class(classes.variantOutlined); // default

rerender(<Checkbox variant="light" />);
expect(container.firstChild).to.have.class(classes.variantLight);
});

it('should have configurable size', () => {
const { container, rerender } = render(<Checkbox />);

expect(container.firstChild).to.have.class(classes.sizeMd); // default

rerender(<Checkbox size="sm" />);
expect(container.firstChild).to.have.class(classes.sizeSm);
});

describe('prop: indeterminate', () => {
siriwatknp marked this conversation as resolved.
Show resolved Hide resolved
it('should render an indeterminate icon', () => {
const { getByTestId } = render(<Checkbox indeterminate />);
expect(getByTestId('HorizontalRuleIcon')).not.to.equal(null);
});
});
});
Loading