-
Notifications
You must be signed in to change notification settings - Fork 0
Lukebailey/infra 386 create a a11y checkbox #53
base: main
Are you sure you want to change the base?
Changes from 25 commits
17cffa8
23e5e0d
e254aa5
1ce89d5
5c819c1
d86c95d
59efa9f
c66b5cd
ca95774
0a41a35
93013ed
177e1c9
4f84d58
3c53864
dac2cfd
969c328
ab4d3dc
7bf7cdd
fc43aa6
9d2b028
6786ee4
661b8d3
aa97e6c
8b505fa
f4a25d7
965235e
8d2a0f1
5c060e9
55ab973
e8cf1a9
dce6b65
4280b21
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,4 +37,5 @@ junit.xml | |
.idea | ||
|
||
# logs | ||
*.log | ||
*.log | ||
.vscode/ | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -45,6 +45,7 @@ | |
"@commitlint/cli": "^14.1.0", | ||
"@commitlint/config-conventional": "^14.1.0", | ||
"@skypack/package-check": "^0.2.2", | ||
"@stitches/react": "^1.2.5", | ||
"@storybook/addon-actions": "^6.3.12", | ||
"@storybook/addon-essentials": "^6.3.12", | ||
"@storybook/addon-links": "^6.3.12", | ||
|
@@ -85,6 +86,8 @@ | |
"react": "^16.14.0 || ^17.0.0" | ||
}, | ||
"dependencies": { | ||
"@stitches/react": "^1.2.5" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see we moved this from dependencies to devDependencies. If we are going to do this, then we need to add |
||
"gsap": "^3.9.1", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have some slight concerns about bringing gsap in as a dependency. gsap is large library, and I'm curious how much we need it as opposed to handling things in a more light weight manner. Would love to hear some thoughts on this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought this maybe a thing! totally fine to remove GSAP and go with CSS transitions for the UI kit. See comment #53 (comment) |
||
"react-aria": "^3.12.0", | ||
"react-stately": "^3.11.0" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import type { SVGProps } from 'react'; | ||
import { styled } from '../../stitches.config'; | ||
|
||
const Path = styled('path', { | ||
'input:checked ~ div &': { | ||
transition: 'stroke-dashoffset 1s cubic-bezier(0.16, 1, 0.3, 1)', | ||
}, | ||
transition: 'stroke-dashoffset 0.2s cubic-bezier(0.11, 0, 0.5, 0)', | ||
}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should use variants to essentially add a The reason I would do this is that the style then relies on a prop and not the structure of the DOM. Right now this styling is very conditional based on where it is used and isn't really sharable. Thoughts @Boeing787 and @lukerohanbailey? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed! See comment #53 (comment) |
||
|
||
export const CheckIcon = (props: SVGProps<SVGSVGElement>) => ( | ||
<svg | ||
fill="none" | ||
focusable="false" | ||
height="1em" | ||
role="img" | ||
viewBox="0 0 16 16" | ||
width="1em" | ||
xmlns="http://www.w3.org/2000/svg" | ||
{...props} | ||
> | ||
<Path | ||
d="M0.709339 7.44716L5.70834 12.4462L15.3004 2.8541" | ||
stroke="currentColor" | ||
strokeDasharray="21 21" | ||
strokeMiterlimit="10" | ||
strokeWidth="2" | ||
/> | ||
</svg> | ||
); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import type { CheckboxProps, ToggleProps } from '@react-types/checkbox'; | ||
lukerohanbailey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
import { gsap } from 'gsap'; | ||
import type { ChangeEvent, ComponentPropsWithRef } from 'react'; | ||
import { useEffect, useRef } from 'react'; | ||
import { useCheckbox } from 'react-aria'; | ||
import { useToggleState } from 'react-stately'; | ||
import { styled } from '../../stitches.config'; | ||
import { CheckIcon } from '../animationIcons/CheckIcon'; | ||
|
||
const Label = styled('label', { | ||
'& input:checked:disabled + div': { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What are we needing to target specifically with |
||
color: '$gray50', | ||
}, | ||
display: 'block', | ||
position: 'relative', | ||
}); | ||
|
||
const Input = styled('input', { | ||
'&:checked': { | ||
backgroundColor: '$brandYellow', | ||
borderColor: '$brandYellow', | ||
}, | ||
'&:checked&:disabled': { | ||
backgroundColor: '$yellow20', | ||
borderColor: '$yellow20', | ||
}, | ||
'&:disabled': { | ||
borderColor: '$gray30', | ||
}, | ||
'&:focus': { | ||
outline: '2px solid $brandYellow', | ||
outlineOffset: '2px', | ||
}, | ||
'&:indeterminate': { | ||
borderColor: '$brandYellow', | ||
position: 'relative', | ||
}, | ||
'&:indeterminate:after': { | ||
backgroundColor: '$brandYellow', | ||
content: '""', | ||
height: '12px', | ||
left: '50%', | ||
position: 'absolute', | ||
top: '50%', | ||
transform: 'translate(-50%, -50%)', | ||
width: '12px', | ||
}, | ||
'&:invalid': { | ||
borderColor: '$uiErrorRegular', | ||
}, | ||
appearance: 'none', | ||
backgroundColor: '$brandWhite', | ||
borderColor: '$gray50', | ||
borderRadius: '$4', | ||
borderStyle: '$solid', | ||
borderWidth: '$thin', | ||
height: '24px', | ||
margin: '0', | ||
position: 'relative', | ||
width: '24px', | ||
}); | ||
|
||
const IconContainer = styled('div', { | ||
left: '50%', | ||
position: 'absolute', | ||
top: '50%', | ||
transform: 'translate(-50%, -50%)', | ||
}); | ||
|
||
type CustomCheckboxProps = { | ||
text?: string; | ||
}; | ||
|
||
export const Checkbox = ({ | ||
validationState, | ||
text, | ||
defaultSelected, | ||
isIndeterminate, | ||
...restProps | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I usually highly advise not using rest props on component libraries like this, and to specifically list all props out so you can make sure you are never spreading props improperly to base react components (if you did you'll get those annoying react warnings about a prop not being recognized). In general it would be very easy for a consumer to pass a prop that could ultimately end up getting inappropriately passed all the way down to base component that it shouldnt. |
||
}: CheckboxProps & | ||
ComponentPropsWithRef<'input'> & | ||
CustomCheckboxProps & | ||
ToggleProps) => { | ||
lukerohanbailey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const ref = useRef<HTMLInputElement>(null); | ||
const state = useToggleState({ defaultSelected, validationState }); | ||
lukerohanbailey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const { inputProps } = useCheckbox( | ||
{ isIndeterminate, ...restProps }, | ||
state, | ||
ref | ||
); | ||
const checkedTl = useRef<GSAPTimeline>(gsap.timeline({ paused: true })); | ||
|
||
useEffect(() => { | ||
checkedTl.current | ||
.to(ref.current, { | ||
duration: 0.1, | ||
ease: 'quad.easeIn', | ||
scale: 0.9, | ||
}) | ||
.to(ref.current, { | ||
duration: 0.1, | ||
ease: 'quad.easeOut', | ||
scale: 1, | ||
}); | ||
}, []); | ||
|
||
const onChange = (event: ChangeEvent<HTMLInputElement>) => { | ||
lukerohanbailey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const checked = event.target.checked; | ||
state.setSelected(checked); | ||
|
||
checkedTl.current.play(0); | ||
}; | ||
|
||
return ( | ||
<Label> | ||
<Input {...inputProps} {...restProps} onChange={onChange} ref={ref} /> | ||
lukerohanbailey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
<IconContainer> | ||
<CheckIcon strokeDashoffset={state.isSelected ? '' : '21'} /> | ||
</IconContainer> | ||
{text} | ||
</Label> | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { styled } from '../../../stitches.config'; | ||
import { Checkbox } from '../Checkbox'; | ||
|
||
const Wrapper = styled('div', { | ||
display: 'flex', | ||
flexFlow: 'row wrap', | ||
gap: '$16', | ||
width: '100%', | ||
}); | ||
|
||
export const CheckboxStates = () => ( | ||
<Wrapper> | ||
<Checkbox aria-label="Example checkbox: disabled" disabled /> | ||
<Checkbox aria-label="Example checkbox: unchecked" /> | ||
<Checkbox aria-label="Example checkbox: checked" defaultSelected /> | ||
<Checkbox | ||
aria-label="Example checkbox: focused" | ||
autoFocus | ||
defaultSelected | ||
/> | ||
<Checkbox | ||
aria-label="Example checkbox: unchecked" | ||
defaultSelected | ||
disabled | ||
/> | ||
<Checkbox aria-label="test" isIndeterminate /> | ||
<Checkbox aria-label="test" required validationState="invalid" /> | ||
</Wrapper> | ||
); | ||
|
||
export default { | ||
component: CheckboxStates, | ||
title: 'Checkbox', | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's not add .vscode to the ignore. We do like to share some configuration in projects. This project doesn't have anything shared yet, but I'd imagine we would do some similiar sharing that we do in https://github.com/contra/contra-web-app/tree/master/.vscode
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep! This has been fixed in 55ab973