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

fix(liveness): use button/aria roles for photosensitivity warning #4506

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
9 changes: 9 additions & 0 deletions .changeset/loud-dodos-crash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@aws-amplify/ui-react-liveness": patch
"@aws-amplify/ui": patch
---

fix(liveness):

Replace div with button for photosensitivity warning to it make keyboard navigable.
Add aria roles to make the warning accessible for screen readers.
6 changes: 3 additions & 3 deletions packages/react-core/src/components/FormCore/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export interface RegisterFieldParams<Values extends FormValues = FormValues> {

export interface UseFormParams<
Values extends FormValues = FormValues,
OnSubmit extends SubmitHandler<Values> = SubmitHandler<Values>
OnSubmit extends SubmitHandler<Values> = SubmitHandler<Values>,
> {
/**
* Custom error message provided to `Error` on call to `useForm` outside `FormProvider`
Expand Down Expand Up @@ -193,7 +193,7 @@ export interface UseField<Name extends string = string>
export interface UseFieldParams<
OnBlur extends FocusHandler | undefined,
OnChange extends ChangeHandler | undefined,
Values extends FormValues = FormValues
Values extends FormValues = FormValues,
> {
/**
* Controlled `disabled` state
Expand Down Expand Up @@ -252,7 +252,7 @@ export interface UseFieldParams<
*/
export interface UseControlledFieldParams<
OnBlur extends FocusHandler | undefined,
Values extends FormValues = FormValues
Values extends FormValues = FormValues,
> extends Omit<UseFieldParams<OnBlur, undefined, Values>, 'onChange'> {
/**
* Controlled text change event handler
Expand Down
2 changes: 1 addition & 1 deletion packages/react-core/src/components/FormCore/useField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const DEFAULT_ERROR_MESSAGE =
*/
export default function useField<
OnBlur extends FocusHandler,
OnChange extends ChangeHandler
OnChange extends ChangeHandler,
>(params: UseFieldParams<OnBlur, OnChange>): UseField {
const { getFieldState, registerField } = useForm({
errorMessage: DEFAULT_ERROR_MESSAGE,
Expand Down
2 changes: 1 addition & 1 deletion packages/react-core/src/components/FormCore/useForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const DEFAULT_ERROR_MESSAGE =
*/
export default function useForm<
Values extends FormValues = FormValues,
OnSubmit extends SubmitHandler = SubmitHandler
OnSubmit extends SubmitHandler = SubmitHandler,
>(options: UseFormParams<Values, OnSubmit> = {}): UseForm<Values> {
const formContext = useFormContext();
const { errorMessage, onSubmit: _onSubmit } = options;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { FormHandle, FormProviderProps } from './types';
export default function withFormProvider<
ChildComp extends AnyComponent,
ChildProps extends React.ComponentPropsWithRef<ChildComp>,
Props extends MergeProps<FormProviderProps, ChildProps>
Props extends MergeProps<FormProviderProps, ChildProps>,
>(
Child: ChildComp
): React.ForwardRefExoticComponent<
Expand Down
2 changes: 1 addition & 1 deletion packages/react-core/src/utils/createContextUtilities.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ type CreateContextUtilitiesReturn<ContextType, ContextName extends string> = {
export default function createContextUtilities<
ContextType,
ContextName extends string = string,
Message extends string | undefined = string | undefined
Message extends string | undefined = string | undefined,
>(
options: ContextOptions<ContextType, ContextName, Message>
): CreateContextUtilitiesReturn<ContextType, ContextName> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,13 +221,16 @@ export const LivenessCameraModule = (
}
}, [send, videoRef, isCameraReady, isMobileScreen]);

const photoSensitivtyWarning = React.useMemo(() => {
const photoSensitivityWarning = React.useMemo(() => {
return (
<View style={{ visibility: isStartView ? 'visible' : 'hidden' }}>
<PhotosensitiveWarning
headingText={instructionDisplayText.photosensitivyWarningHeadingText}
bodyText={instructionDisplayText.photosensitivyWarningBodyText}
infoText={instructionDisplayText.photosensitivyWarningInfoText}
bodyText={instructionDisplayText.photosensitivityWarningBodyText}
headingText={
instructionDisplayText.photosensitivityWarningHeadingText
}
infoText={instructionDisplayText.photosensitivityWarningInfoText}
labelText={instructionDisplayText.photosensitivityWarningLabelText}
/>
</View>
);
Expand Down Expand Up @@ -293,7 +296,7 @@ export const LivenessCameraModule = (

return (
<>
{photoSensitivtyWarning}
{photoSensitivityWarning}

<Flex
className={classNames(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,15 @@ export type CameraDisplayText = {
};

export type InstructionDisplayText = {
photosensitivyWarningHeadingText?: string;
photosensitivyWarningBodyText?: string;
photosensitivyWarningInfoText?: string;
goodFitCaptionText?: string;
goodFitAltText?: string;
instructionsHeaderHeadingText?: string;
instructionsHeaderBodyText?: string;
instructionsBeginCheckText?: string;
photosensitivityWarningLabelText?: string;
photosensitivityWarningHeadingText?: string;
photosensitivityWarningBodyText?: string;
photosensitivityWarningInfoText?: string;
tooFarCaptionText?: string;
tooFarAltText?: string;
startScreenBeginCheckText?: string;
Expand Down Expand Up @@ -66,10 +70,11 @@ export type ErrorDisplayText = Partial<ErrorDisplayTextFoo>;
export const defaultLivenessDisplayText: Required<LivenessDisplayText> = {
hintCenterFaceText: 'Center your face',
startScreenBeginCheckText: 'Start video check',
photosensitivyWarningHeadingText: 'Photosensitivity warning',
photosensitivyWarningBodyText:
photosensitivityWarningHeadingText: 'Photosensitivity warning',
photosensitivityWarningLabelText: 'More information about photosensitivity',
photosensitivityWarningBodyText:
'This check flashes different colors. Use caution if you are photosensitive.',
photosensitivyWarningInfoText:
photosensitivityWarningInfoText:
'Some people may experience may experience epileptic seizures when exposed to colored lights. Use caution if you, or anyone in your family, have an epileptic condition.',
goodFitCaptionText: 'Good fit',
goodFitAltText:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ export interface StartScreenComponents {
}

interface DefaultPhotosensitiveWarningProps {
labelText: string;
headingText: string;
bodyText: string;
infoText: string;
}

export const DefaultPhotosensitiveWarning = ({
labelText,
headingText,
bodyText,
infoText,
Expand All @@ -34,7 +36,9 @@ export const DefaultPhotosensitiveWarning = ({
<View className={ComponentClassName.AlertHeading}>{headingText}</View>
<View className={ComponentClassName.AlertBody}>{bodyText}</View>
</View>
<LivenessIconWithPopover>{infoText}</LivenessIconWithPopover>
<LivenessIconWithPopover labelText={labelText} headingText={headingText}>
{infoText}
</LivenessIconWithPopover>
</Flex>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@

import * as React from 'react';

import { Flex } from '@aws-amplify/ui-react';
import { Button, Flex } from '@aws-amplify/ui-react';
import { AlertIcon, useThemeBreakpoint } from '@aws-amplify/ui-react/internal';
import { LivenessClassNames } from '../types/classNames';

export interface LivenessIconWithPopoverProps {
children: string;
labelText: string;
headingText: string;
}

export const LivenessIconWithPopover: React.FC<LivenessIconWithPopoverProps> =
({ children }) => {
({ children, labelText, headingText }) => {
const breakpoint = useThemeBreakpoint();
const [shouldShowPopover, setShouldShowPopover] = React.useState(false);
const wrapperRef = React.useRef<HTMLDivElement | null>(null);
Expand All @@ -31,33 +33,44 @@ export const LivenessIconWithPopover: React.FC<LivenessIconWithPopoverProps> =
}
}
document.addEventListener('mousedown', handleClickOutside);

return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [wrapperRef, shouldShowPopover]);

return (
<Flex
className={LivenessClassNames.Popover}
onClick={() => setShouldShowPopover(!shouldShowPopover)}
ref={wrapperRef}
testId="popover-icon"
>
<AlertIcon ariaHidden variation="info" />
<div className={LivenessClassNames.Popover} ref={wrapperRef}>
<Button
aria-controls="photosensitivityDesc"
aria-expanded={shouldShowPopover}
aria-haspopup="dialog"
aria-label={labelText}
colorTheme="warning"
id="popover-button"
onClick={() => setShouldShowPopover(!shouldShowPopover)}
testId="popover-icon"
>
esauerbo marked this conversation as resolved.
Show resolved Hide resolved
<AlertIcon ariaHidden variation="info" />
</Button>
{shouldShowPopover && (
<>
<Flex className={LivenessClassNames.PopoverAnchor} />
<Flex className={LivenessClassNames.PopoverAnchorSecondary} />
<Flex
aria-hidden={!shouldShowPopover}
aria-label={headingText}
className={LivenessClassNames.PopoverContainer}
left={isMobileScreen ? -190 : -108}
data-testid="popover-text"
id="photosensitivityDesc"
left={isMobileScreen ? -190 : -108}
role="dialog"
>
{children}
</Flex>
</>
)}
</Flex>
</div>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@ describe('LivenessIconWithPopover', () => {
it('should render the component content appropriately', () => {
const infoText = 'Test info text';

render(<LivenessIconWithPopover>{infoText}</LivenessIconWithPopover>);
render(
<LivenessIconWithPopover
labelText="More information about photosensitivity"
headingText="Photosensitivity warning"
>
{infoText}
</LivenessIconWithPopover>
);

const popover = screen.queryByTestId('popover-icon');
expect(screen.queryByTestId('popover-icon')).toBeInTheDocument();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,49 +32,53 @@ export function getDisplayText(
};

const {
photosensitivyWarningHeadingText,
photosensitivyWarningBodyText,
photosensitivyWarningInfoText,
goodFitCaptionText,
goodFitAltText,
tooFarCaptionText,
tooFarAltText,
cameraMinSpecificationsHeadingText,
cameraMinSpecificationsMessageText,
cameraNotFoundHeadingText,
cameraNotFoundMessageText,
retryCameraPermissionsText,
waitingCameraPermissionText,
cancelLivenessCheckText,
recordingIndicatorText,
clientHeaderText,
clientMessageText,
faceDistanceHeaderText,
faceDistanceMessageText,
goodFitCaptionText,
goodFitAltText,
hintMoveFaceFrontOfCameraText,
hintTooManyFacesText,
hintFaceDetectedText,
hintCanNotIdentifyText,
hintCenterFaceText,
hintConnectingText,
hintTooCloseText,
hintTooFarText,
hintConnectingText,
hintVerifyingText,
hintIlluminationTooBrightText,
hintIlluminationTooDarkText,
hintIlluminationNormalText,
hintHoldFaceForFreshnessText,
timeoutHeaderText,
timeoutMessageText,
faceDistanceHeaderText,
faceDistanceMessageText,
instructionsHeaderHeadingText,
instructionsHeaderBodyText,
instructionsBeginCheckText,
landscapeHeaderText,
landscapeMessageText,
multipleFacesHeaderText,
multipleFacesMessageText,
clientHeaderText,
clientMessageText,
photosensitivityWarningLabelText,
photosensitivityWarningHeadingText,
photosensitivityWarningBodyText,
photosensitivityWarningInfoText,
portraitMessageText,
recordingIndicatorText,
retryCameraPermissionsText,
serverHeaderText,
serverMessageText,
landscapeHeaderText,
landscapeMessageText,
portraitMessageText,
tryAgainText,
startScreenBeginCheckText,
hintCenterFaceText,
tooFarCaptionText,
tooFarAltText,
tryAgainText,
timeoutHeaderText,
timeoutMessageText,
waitingCameraPermissionText,
} = displayText;

const hintDisplayText: Required<HintDisplayText> = {
Expand Down Expand Up @@ -103,11 +107,15 @@ export function getDisplayText(
};

const instructionDisplayText: Required<InstructionDisplayText> = {
photosensitivyWarningHeadingText,
photosensitivyWarningBodyText,
photosensitivyWarningInfoText,
goodFitCaptionText,
goodFitAltText,
instructionsHeaderHeadingText,
instructionsHeaderBodyText,
instructionsBeginCheckText,
photosensitivityWarningLabelText,
photosensitivityWarningHeadingText,
photosensitivityWarningBodyText,
photosensitivityWarningInfoText,
tooFarCaptionText,
tooFarAltText,
startScreenBeginCheckText,
Expand Down
6 changes: 3 additions & 3 deletions packages/ui/src/theme/css/component/liveness.scss
Original file line number Diff line number Diff line change
Expand Up @@ -343,14 +343,13 @@

.amplify-liveness-popover {
position: relative;
cursor: pointer;
}

.amplify-liveness-popover__anchor {
position: absolute;
top: 26px;
left: 3px;
z-index: 3;
left: 20px;
border-style: solid;
border-width: 0 9px 9px 9px;
border-color: transparent transparent var(--amplify-colors-background-primary)
Expand All @@ -360,8 +359,8 @@
.amplify-liveness-popover__anchor-secondary {
position: absolute;
top: 24px;
left: 2px;
z-index: 2;
left: 19px;
border-style: solid;
border-width: 0 10px 10px 10px;
border-color: transparent transparent var(--amplify-colors-border-secondary)
Expand All @@ -374,6 +373,7 @@
color: var(--amplify-colors-font-primary);
flex-direction: row;
font-size: var(--amplify-font-sizes-xs);
font-weight: var(--amplify-font-weights-normal);
padding: var(--amplify-space-small);
top: 33px;
width: 240px;
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/theme/tokens/fontSizes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type FontSize =

export type FontSizes<
Output extends OutputVariantKey = unknown,
Platform = unknown
Platform = unknown,
> = DesignTokenValues<
FontSize,
FontSizeValue<Platform, Output>,
Expand Down
Loading
Loading