Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 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
2 changes: 2 additions & 0 deletions assets/dev/css/ea11y-scanner-wizard.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@
overflow-y: scroll;
overflow-x: visible;
z-index: 999999;
pointer-events: none;
font-family: "Roboto", "Helvetica", "Arial", sans-serif;
}
3 changes: 3 additions & 0 deletions modules/scanner/assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
ManualLayout,
RemediationLayout,
} from '@ea11y-apps/scanner/layouts';
import { ColorContrastLayout } from '@ea11y-apps/scanner/layouts/color-contrast-layout';
import { StyledPaper } from '@ea11y-apps/scanner/styles/app.styles';
import { removeExistingFocus } from '@ea11y-apps/scanner/utils/focus-on-element';
import { useEffect } from '@wordpress/element';
Expand Down Expand Up @@ -81,6 +82,8 @@ const App = () => {
return <ManageMainLayout />;
case BLOCKS.altText:
return <AltTextLayout />;
case BLOCKS.colorContrast:
return <ColorContrastLayout />;
default:
return isManage ? <RemediationLayout /> : <ManualLayout />;
}
Expand Down
7 changes: 2 additions & 5 deletions modules/scanner/assets/js/components/alt-text-form/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,8 @@ import { ImagePreview } from '@ea11y-apps/scanner/components/alt-text-form/image
import { UpgradeContent } from '@ea11y-apps/scanner/components/upgrade-info-tip/upgrade-content';
import { AI_QUOTA_LIMIT, IS_AI_ENABLED } from '@ea11y-apps/scanner/constants';
import { useAltTextForm } from '@ea11y-apps/scanner/hooks/use-alt-text-form';
import {
StyledBox,
StyledLabel,
} from '@ea11y-apps/scanner/styles/alt-text-form.styles';
import { StyledAlert } from '@ea11y-apps/scanner/styles/app.styles';
import { StyledLabel } from '@ea11y-apps/scanner/styles/alt-text-form.styles';
import { StyledAlert, StyledBox } from '@ea11y-apps/scanner/styles/app.styles';
import { scannerItem } from '@ea11y-apps/scanner/types/scanner-item';
import { __ } from '@wordpress/i18n';

Expand Down
120 changes: 120 additions & 0 deletions modules/scanner/assets/js/components/color-contrast-form/color-set.js
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 />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

role="presentation"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added to the icon

<Slider
Copy link
Contributor

Choose a reason for hiding this comment

The 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 />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

role="presentation"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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,
};
69 changes: 69 additions & 0 deletions modules/scanner/assets/js/components/color-contrast-form/index.js
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
@@ -1,6 +1,7 @@
import ChevronLeftIcon from '@elementor/icons/ChevronLeftIcon';
import ChevronRightIcon from '@elementor/icons/ChevronRightIcon';
import Box from '@elementor/ui/Box';
import Divider from '@elementor/ui/Divider';
import IconButton from '@elementor/ui/IconButton';
import Typography from '@elementor/ui/Typography';
import { styled } from '@elementor/ui/styles';
Expand All @@ -9,7 +10,7 @@ import { mixpanelEvents, mixpanelService } from '@ea11y-apps/global/services';
import { isRTL } from '@ea11y-apps/scanner/constants';
import { __, sprintf } from '@wordpress/i18n';

export const AltTextNavigation = ({ total, current, setCurrent }) => {
export const FormNavigation = ({ total, current, setCurrent }) => {
const previous = current - 1;
const next = current + 1;
const navigate = (index, direction) => () => {
Expand All @@ -19,45 +20,50 @@ export const AltTextNavigation = ({ total, current, setCurrent }) => {
});
};
return (
<StyledBox>
<StyledNavigation>
<StyledIconButton
onClick={navigate(previous, 'previous')}
disabled={current === 0}
aria-label={__('Previous image', 'pojo-accessibility')}
>
<ChevronLeftIcon />
</StyledIconButton>
<Typography variant="body1">
{sprintf(
// Translators: %1$s - current, %2$s - total
__('%1$s of %2$s issues', 'pojo-accessibility'),
current + 1,
total,
)}
</Typography>
<StyledIconButton
onClick={navigate(next, 'next')}
disabled={current === total - 1}
aria-label={__('Next image', 'pojo-accessibility')}
>
<ChevronRightIcon />
</StyledIconButton>
</StyledNavigation>
</StyledBox>
<Navigation>
<Divider />
<StyledBox>
<StyledNavigation>
<StyledIconButton
onClick={navigate(previous, 'previous')}
disabled={current === 0}
aria-label={__('Previous', 'pojo-accessibility')}
>
<ChevronLeftIcon />
</StyledIconButton>
<Typography variant="body1">
{sprintf(
// Translators: %1$s - current, %2$s - total
__('%1$s of %2$s issues', 'pojo-accessibility'),
current + 1,
total,
)}
</Typography>
<StyledIconButton
onClick={navigate(next, 'next')}
disabled={current === total - 1}
aria-label={__('Next', 'pojo-accessibility')}
>
<ChevronRightIcon />
</StyledIconButton>
</StyledNavigation>
</StyledBox>
</Navigation>
);
};

const StyledBox = styled(Box)`
const Navigation = styled(Box)`
position: absolute;
bottom: 0;
left: 0;
width: 100%;
padding-block: ${({ theme }) => theme.spacing(2)};

`;
const StyledBox = styled(Box)`
display: flex;
justify-content: center;
padding-block: ${({ theme }) => theme.spacing(2)};
`;

const StyledNavigation = styled(Box)`
display: flex;
justify-content: space-between;
Expand All @@ -69,7 +75,7 @@ const StyledIconButton = styled(IconButton)`
${isRTL ? 'transform: rotate(180deg)' : ''}
`;

AltTextNavigation.propTypes = {
FormNavigation.propTypes = {
total: PropTypes.number.isRequired,
current: PropTypes.number.isRequired,
setCurrent: PropTypes.func.isRequired,
Expand Down
4 changes: 2 additions & 2 deletions modules/scanner/assets/js/constants/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export const BLOCK_TITLES = {
),
pageStructureNav: __('Page Structure & Navigation', 'pojo-accessibility'),
tables: __('Tables', 'pojo-accessibility'),
colorContrast: __('Color Contrast & Style', 'pojo-accessibility'),
colorContrast: __('Color contrast', 'pojo-accessibility'),
other: __('Other Accessibility Issues', 'pojo-accessibility'),
};

Expand Down Expand Up @@ -93,7 +93,7 @@ export const BLOCK_INFO = {
'pojo-accessibility',
),
colorContrast: __(
'Choose colors with strong contrast to ensure your text is readable for everyone.',
'Text and background lightness can hinder readability. Depending on text size, you may need to adjust the contrast level to improve accessibility.',
'pojo-accessibility',
),
other: __(
Expand Down
40 changes: 40 additions & 0 deletions modules/scanner/assets/js/hooks/use-color-contrast-form.js
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,
};
2 changes: 2 additions & 0 deletions modules/scanner/assets/js/images/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ export { Logo } from './logo';
export { ResolvedImage } from './resolved-image';
export { ErrorImage } from './error-image';
export { NotConnectedImage } from './not-connected-image';
export { SunIcon } from './sun-icon';
export { SunOffIcon } from './sun-off-icon';
Loading
Loading