-
Notifications
You must be signed in to change notification settings - Fork 43
[APP-1741][APP-1742][APP-1743] add components, calc contrast ratio, a… #346
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
Changes from 16 commits
416e1e3
99c7c5d
8ba5a50
5fd8143
2f8615e
91347a9
1ca2488
730161b
4f93d3c
fbad614
de650e6
8a8ff05
d971f9f
b9d8235
c0a7d92
2242e75
49706e3
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 |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| import RotateIcon from '@elementor/icons/RotateIcon'; | ||
| import Box from '@elementor/ui/Box'; | ||
| import Button from '@elementor/ui/Button'; | ||
| import IconButton from '@elementor/ui/IconButton'; | ||
| import InputAdornment from '@elementor/ui/InputAdornment'; | ||
| import Slider from '@elementor/ui/Slider'; | ||
| import TextField from '@elementor/ui/TextField'; | ||
| import Typography from '@elementor/ui/Typography'; | ||
| import { styled } from '@elementor/ui/styles'; | ||
| import { UnstableColorPicker } from '@elementor/ui/unstable'; | ||
| import PropTypes from 'prop-types'; | ||
| import { SunIcon, SunOffIcon } from '@ea11y-apps/scanner/images'; | ||
| import { hexToHsl, hslToHex } from '@ea11y-apps/scanner/utils/convert-colors'; | ||
| import { useState } from '@wordpress/element'; | ||
| import { __ } from '@wordpress/i18n'; | ||
|
|
||
| export const ColorSet = ({ title, color, initialColor, setColor }) => { | ||
| const [selectedColor, setSelectedColor] = useState(initialColor); | ||
| const hslColor = hexToHsl(color); | ||
|
|
||
| const resetColor = () => setColor(initialColor); | ||
|
|
||
| const onColorChange = (changedColor) => { | ||
| setSelectedColor(changedColor); | ||
| setColor(changedColor); | ||
| }; | ||
|
|
||
| const onLightnessChange = (event, value) => { | ||
| const raw = event?.target?.value || value; | ||
| // Allow only digits | ||
| if (raw && !/^\d{1,2}$|^100$/.test(raw)) { | ||
| return; | ||
| } | ||
|
|
||
| const num = raw && !isNaN(raw) ? parseInt(raw, 10) : 0; | ||
|
|
||
| if (num >= 0 && num <= 100) { | ||
| const initialHslColor = hexToHsl(selectedColor); | ||
| const updatedColor = hslToHex({ | ||
| ...initialHslColor, | ||
| l: num, | ||
| }); | ||
| setColor(updatedColor); | ||
| } | ||
| }; | ||
|
|
||
| return ( | ||
| <Box> | ||
| <Typography variant="body2" as="p"> | ||
| {title} | ||
| </Typography> | ||
| <StyledColorSet> | ||
| <SunOffIcon /> | ||
| <Slider | ||
|
Contributor
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. It should have a label, bc a screen reader only announces a value with no other context. |
||
| aria-label={__('Lightness', 'pojo-accessibility')} | ||
| color="secondary" | ||
| value={hslColor.l} | ||
| size="small" | ||
| sx={{ width: '150px' }} | ||
| onChange={onLightnessChange} | ||
| /> | ||
| <SunIcon /> | ||
|
Contributor
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.
Contributor
Author
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. added to the icon |
||
| <TextField | ||
| variant="outlined" | ||
| size="small" | ||
| color="secondary" | ||
| value={hslColor.l} | ||
| onChange={onLightnessChange} | ||
| inputProps={{ | ||
| 'aria-label': __('Lightness percent', 'pojo-accessibility'), | ||
| }} | ||
| InputProps={{ | ||
| sx: { | ||
| width: '75px', | ||
| paddingRight: '4px', | ||
| marginRight: '8px', | ||
| }, | ||
| endAdornment: ( | ||
| <InputAdornment position="end"> | ||
| <Button size="small" disabled sx={{ minWidth: 'auto' }}> | ||
| % | ||
| </Button> | ||
| </InputAdornment> | ||
| ), | ||
| }} | ||
| /> | ||
| <UnstableColorPicker | ||
| hideInputFields | ||
| value={color} | ||
| onChange={onColorChange} | ||
| size="small" | ||
| /> | ||
| <IconButton | ||
| onClick={resetColor} | ||
| size="tiny" | ||
| aria-label={__('Reset', 'pojo-accessibility')} | ||
| > | ||
| <RotateIcon | ||
| fontSize="tiny" | ||
| color="disabled" | ||
| sx={{ transform: 'rotate(180deg)' }} | ||
| /> | ||
| </IconButton> | ||
| </StyledColorSet> | ||
| </Box> | ||
| ); | ||
| }; | ||
|
|
||
| const StyledColorSet = styled(Box)` | ||
| display: flex; | ||
| align-items: center; | ||
| gap: ${({ theme }) => theme.spacing(1)}; | ||
| `; | ||
|
|
||
| ColorSet.propTypes = { | ||
| title: PropTypes.string.isRequired, | ||
| color: PropTypes.string.isRequired, | ||
| initialColor: PropTypes.string.isRequired, | ||
| setColor: PropTypes.func.isRequired, | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| import Alert from '@elementor/ui/Alert'; | ||
| import AlertTitle from '@elementor/ui/AlertTitle'; | ||
| import Button from '@elementor/ui/Button'; | ||
| import Divider from '@elementor/ui/Divider'; | ||
| import Typography from '@elementor/ui/Typography'; | ||
| import PropTypes from 'prop-types'; | ||
| import { ColorSet } from '@ea11y-apps/scanner/components/color-contrast-form/color-set'; | ||
| import { useColorContrastForm } from '@ea11y-apps/scanner/hooks/use-color-contrast-form'; | ||
| import { StyledBox } from '@ea11y-apps/scanner/styles/app.styles'; | ||
| import { scannerItem } from '@ea11y-apps/scanner/types/scanner-item'; | ||
| import { checkContrastAA } from '@ea11y-apps/scanner/utils/calc-color-ratio'; | ||
| import { __ } from '@wordpress/i18n'; | ||
|
|
||
| export const ColorContrastForm = ({ items, current, setCurrent }) => { | ||
| const item = items[current]; | ||
| const { color, changeColor, background, changeBackground, onSubmit } = | ||
| useColorContrastForm({ | ||
| item, | ||
| current, | ||
| setCurrent, | ||
| }); | ||
|
|
||
| const colorData = checkContrastAA(color, background, item.node); | ||
|
|
||
| return ( | ||
| <StyledBox> | ||
| <Divider /> | ||
| <Typography variant="body2" as="p"> | ||
| {__( | ||
| 'Adjust the text or background lightness until the indicator shows an accessible level.', | ||
| 'pojo-accessibility', | ||
| )} | ||
| </Typography> | ||
| <ColorSet | ||
| title={__('Text', 'pojo-accessibility')} | ||
| color={color} | ||
| initialColor={item.messageArgs[3]} | ||
| setColor={changeColor} | ||
| /> | ||
| <ColorSet | ||
| title={__('Background', 'pojo-accessibility')} | ||
| color={background} | ||
| initialColor={item.messageArgs[4]} | ||
| setColor={changeBackground} | ||
| /> | ||
| <Alert severity={colorData.passesAA ? 'success' : 'error'}> | ||
| <AlertTitle sx={{ mr: 1 }}> | ||
| {__('Contrast level:', 'pojo-accessibility')} | ||
| </AlertTitle> | ||
| {colorData.ratio} | ||
| </Alert> | ||
| <Button | ||
| variant="contained" | ||
| size="small" | ||
| color="info" | ||
| disabled={!colorData.passesAA} | ||
| onClick={onSubmit} | ||
| > | ||
| {__('Apply changes', 'pojo-accessibility')} | ||
| </Button> | ||
| </StyledBox> | ||
| ); | ||
| }; | ||
|
|
||
| ColorContrastForm.propTypes = { | ||
| items: PropTypes.arrayOf(scannerItem).isRequired, | ||
| current: PropTypes.number.isRequired, | ||
| setCurrent: PropTypes.func.isRequired, | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| import PropTypes from 'prop-types'; | ||
| import { scannerItem } from '@ea11y-apps/scanner/types/scanner-item'; | ||
| import { useState } from '@wordpress/element'; | ||
|
|
||
| export const useColorContrastForm = ({ item, current, setCurrent }) => { | ||
| const [color, setColor] = useState(item.messageArgs[3]); | ||
| const [background, setBackground] = useState(item.messageArgs[4]); | ||
|
|
||
| const changeColor = (updColor) => { | ||
| setColor(updColor); | ||
| if (item.node?.style) { | ||
| item.node.style.color = updColor; | ||
| } | ||
| }; | ||
|
|
||
| const changeBackground = (updBackground) => { | ||
| setBackground(updBackground); | ||
| if (item.node?.style) { | ||
| item.node.style.background = updBackground; | ||
| } | ||
| }; | ||
|
|
||
| const onSubmit = () => { | ||
| setCurrent(current + 1); | ||
| }; | ||
|
|
||
| return { | ||
| color, | ||
| background, | ||
| changeColor, | ||
| changeBackground, | ||
| onSubmit, | ||
| }; | ||
| }; | ||
|
|
||
| useColorContrastForm.propTypes = { | ||
| item: scannerItem.isRequired, | ||
| current: PropTypes.number.isRequired, | ||
| setCurrent: PropTypes.func.isRequired, | ||
| }; |
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.
role="presentation"?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.
added to the icon