Skip to content

Commit

Permalink
feat: rework and simplify Checkbox component
Browse files Browse the repository at this point in the history
BREAKING CHANGE:
Removed several APIs from checkbox component
  • Loading branch information
matt-cratebind committed Nov 12, 2020
1 parent 08c3969 commit 2bc7bd2
Show file tree
Hide file tree
Showing 5 changed files with 207 additions and 155 deletions.
2 changes: 1 addition & 1 deletion src/Checkbox/Checkbox.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const meta: Meta = {
export default meta;

const Template: Story<CheckboxProps> = args => {
const [checked, setChecked] = React.useState(false);
const [checked, setChecked] = React.useState(true);
return (
<Checkbox
checked={checked}
Expand Down
149 changes: 57 additions & 92 deletions src/Checkbox/index.tsx
Original file line number Diff line number Diff line change
@@ -1,113 +1,78 @@
import React, { forwardRef } from 'react';
import styled from 'styled-components';
import { CustomCheckboxContainer, CustomCheckboxInput } from '@reach/checkbox';
import { MinervaProps, Box, systemProps } from '../layout';
import VisuallyHidden from '../VisuallyHidden';
// import Icon from '../Icon';
import { MinervaProps, Box } from '../layout';
import PseudoBox from '../PseudoBox';
import { Check } from 'react-feather';

/**
* TODO:
* - Add sizes
*/

const CheckboxContainer = styled(Box)(
props => ({
display: 'flex',
alignItems: 'center',
cursor: 'pointer',
fontSize: '14px',
...props.theme.Checkbox,
}),
systemProps
);

export type ControlBoxProps = {
checked?: boolean;
theme?: any;
};

const ControlBox = ({ checked, ...props }) => (
<PseudoBox
display="flex"
alignItems="center"
justifyContent="center"
borderStyle="solid"
borderWidth="1px"
borderRadius="4px"
width="18px"
height="18px"
marginRight="8px"
padding="2px"
transition="background-color 120ms ease 0s, box-shadow 250ms ease 0s"
borderColor={checked ? '#fff' : '#ecebed'}
color={checked ? '#fff' : 'transparent'}
backgroundColor={checked ? '#5850ec' : '#fff'}
_checked={{
backgroundColor: '#000',
// borderColor: '#a4cafe',
// boxShadow: '0 0 0 3px rgba(118,169,250,.45)',
// outline: 0,
}}
_focus={{
borderColor: '#a4cafe',
boxShadow: '0 0 0 3px rgba(118,169,250,.45)',
outline: 0,
}}
{...props}
/>
);
import { useComponentStyles } from '../theme';

type BaseProps = MinervaProps & React.InputHTMLAttributes<HTMLInputElement>;

export interface CustomCheckboxProps {
// define the custom props we're going to be using
children?: React.ReactNode;
// add a question mark to make it an optional prop
disabled?: boolean;
checked?: boolean;
onChange?: () => any;
style?: any;
checkColor?: string;
}

export type CheckboxProps = CustomCheckboxProps & BaseProps;

export const Checkbox = forwardRef(function Checkbox(
props: CheckboxProps,
{
children,
onChange,
disabled,
checked,
defaultChecked,
checkColor = '#fff',
...props
}: CheckboxProps,
ref: any
) {
const { children, checked = false, onChange, onKeyDown, ...rest } = props;

// Allow checkbox to be keyboard accessible
const handleKeyDown: CheckboxProps['onKeyDown'] = e => {
if (e.key === ' ' && !onKeyDown) {
e.preventDefault();
if (onChange) {
onChange();
}
}

if (onKeyDown) {
onKeyDown(e);
}
};

const componentStyles = useComponentStyles('Checkbox');
return (
<CheckboxContainer as="label" ref={ref} {...rest}>
<CustomCheckboxContainer
data-testid="checkbox-container"
checked={checked}
<label>
<Box
as={CustomCheckboxContainer}
display="flex"
alignItems="center"
onChange={onChange}
onKeyDown={handleKeyDown}
checked={checked}
disabled={disabled}
defaultChecked={defaultChecked}
>
<VisuallyHidden as={CustomCheckboxInput} />
<ControlBox data-testid="control-box" tabIndex={0} checked={checked}>
<Check stroke="currentColor" color="currentColor" />
</ControlBox>
</CustomCheckboxContainer>
{children}
</CheckboxContainer>
<PseudoBox
as={CustomCheckboxInput}
backgroundImage={`url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='192' height='192' fill='%23000000' viewBox='0 0 256 256'%3E%3Crect width='256' height='256' fill='none'%3E%3C/rect%3E%3Cpolyline points='216 72.005 104 184 48 128.005' fill='none' stroke='${checkColor.replace(
'#',
'%23'
)}' stroke-linecap='round' stroke-linejoin='round' stroke-width='16'%3E%3C/polyline%3E%3C/svg%3E");`}
backgroundSize="100%"
backgroundPosition="center"
backgroundRepeat="no-repeat"
type="checkbox"
ref={ref}
appearance="none"
width="18px"
height="18px"
marginRight="8px"
borderRadius="4px"
borderWidth="1px"
borderStyle="solid"
padding="2px"
backgroundColor="#fff"
transition="background-color, border-color 150ms cubic-bezier(0.4, 0, 0.2, 1)"
_checked={{
backgroundColor: '#5850ec',
borderColor: '#5850ec',
}}
_focus={{
borderColor: '#a4cafe',
boxShadow: '0 0 0 3px rgba(118,169,250,.45)',
outline: 0,
}}
{...componentStyles}
{...props}
/>
{children}
</Box>
</label>
);
});

Expand Down
2 changes: 1 addition & 1 deletion src/PseudoBox/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ const even = '&:nth-of-type(even)';
const odd = '&:nth-of-type(odd)';
const disabled =
'&:disabled, &:disabled:focus, &:disabled:hover, &[aria-disabled=true], &[aria-disabled=true]:focus, &[aria-disabled=true]:hover';
const checked = '&[aria-checked=true]';
const checked = '&[aria-checked=true],&[data-state="checked"]';
const mixed = '&[aria-checked=mixed]';
const selected =
'&[aria-selected=true], &[data-selected=true], &[data-selected]';
Expand Down
106 changes: 106 additions & 0 deletions test/__snapshots__/checkbox.test.tsx.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Checkbox can have checked state changed by theme: Checked Checkbox 1`] = `
<input
aria-checked="true"
class="sc-AxjAm sc-AxhUy kZhFvc"
data-reach-custom-checkbox-input=""
type="checkbox"
/>
`;

exports[`Checkbox can have checked state changed by theme: Unchecked Checkbox 1`] = `
.c0 {
box-sizing: border-box;
min-width: 0;
color: #374151;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='192' height='192' fill='%23000000' viewBox='0 0 256 256'%3E%3Crect width='256' height='256' fill='none'%3E%3C/rect%3E%3Cpolyline points='216 72.005 104 184 48 128.005' fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='16'%3E%3C/polyline%3E%3C/svg%3E");
background-size: 100%;
background-position: center;
background-repeat: no-repeat;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
width: 18px;
height: 18px;
margin-right: 8px;
border-radius: 4px;
border-width: 1px;
border-style: solid;
padding: 2px;
background-color: #333;
-webkit-transition: background-color,border-color 150ms cubic-bezier(0.4,0,0.2,1);
transition: background-color,border-color 150ms cubic-bezier(0.4,0,0.2,1);
}
.c0:focus {
border-color: #a4cafe;
box-shadow: 0 0 0 3px rgba(118,169,250,.45);
outline: 0;
}
.c0[aria-checked=true],
.c0[data-state="checked"] {
background-color: #c33333;
}
<input
aria-checked="false"
class="c0"
data-reach-custom-checkbox-input=""
type="checkbox"
/>
`;

exports[`Checkbox shows checkbox when checked: Checked Checkbox 1`] = `
<input
aria-checked="true"
class="sc-AxjAm sc-AxhUy gFWAqT"
data-reach-custom-checkbox-input=""
type="checkbox"
/>
`;

exports[`Checkbox shows checkbox when checked: Unchecked Checkbox 1`] = `
.c0 {
box-sizing: border-box;
min-width: 0;
color: #374151;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='192' height='192' fill='%23000000' viewBox='0 0 256 256'%3E%3Crect width='256' height='256' fill='none'%3E%3C/rect%3E%3Cpolyline points='216 72.005 104 184 48 128.005' fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='16'%3E%3C/polyline%3E%3C/svg%3E");
background-size: 100%;
background-position: center;
background-repeat: no-repeat;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
width: 18px;
height: 18px;
margin-right: 8px;
border-radius: 4px;
border-width: 1px;
border-style: solid;
padding: 2px;
background-color: #fff;
-webkit-transition: background-color,border-color 150ms cubic-bezier(0.4,0,0.2,1);
transition: background-color,border-color 150ms cubic-bezier(0.4,0,0.2,1);
}
.c0:focus {
border-color: #a4cafe;
box-shadow: 0 0 0 3px rgba(118,169,250,.45);
outline: 0;
}
.c0[aria-checked=true],
.c0[data-state="checked"] {
background-color: #5850ec;
border-color: #5850ec;
}
<input
aria-checked="false"
class="c0"
data-reach-custom-checkbox-input=""
type="checkbox"
/>
`;
Loading

0 comments on commit 2bc7bd2

Please sign in to comment.