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

a11y: UILD-453: Improve WCAG 2.1 AA compliance for Search page #50

Merged
merged 1 commit into from
Dec 11, 2024
Merged
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
5 changes: 5 additions & 0 deletions src/common/constants/uiElements.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,9 @@ export enum DropdownItemType {
customComponent = 'customComponent',
}

export enum AriaModalKind {
Basic = 'Basic',
AdvancedSearch = 'Advanced search',
}

export const MAX_SEARCH_BAR_WIDTH = 30;
6 changes: 6 additions & 0 deletions src/components/AdvancedSearchModal/AdvancedSearchModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import { formatRawQuery, generateSearchParamsState } from '@common/helpers/search.helper';
import { Select } from '@components/Select';
import state from '@state';
import { AriaModalKind } from '@common/constants/uiElements.constants';
import './AdvancedSearchModal.scss';

enum AdvancedSearchInputs {
Expand Down Expand Up @@ -70,6 +71,7 @@ export const AdvancedSearchModal: FC<Props> = memo(({ clearValues }) => {
onCancel={closeModal}
shouldCloseOnEsc
submitButtonDisabled={submitButtonDisabled}
ariaModalKind={AriaModalKind.AdvancedSearch}
>
<div className="advanced-search-container">
{rawQuery.map(({ query, rowIndex, operator, qualifier, index }) => (
Expand All @@ -85,6 +87,7 @@ export const AdvancedSearchModal: FC<Props> = memo(({ clearValues }) => {
options={SELECT_OPERATORS}
className="cell-operator"
onChange={({ value }) => onChangeInput(value, AdvancedSearchInputs.Operator, rowIndex)}
ariaLabel={formatMessage({ id: 'ld.aria.advancedSearch.operator' })}
data-testid={`select-operators-${rowIndex}`}
/>
)}
Expand All @@ -93,13 +96,15 @@ export const AdvancedSearchModal: FC<Props> = memo(({ clearValues }) => {
data-testid={`text-input-${rowIndex}`}
value={query ?? ''}
onChange={({ target: { value } }) => onChangeInput(value, AdvancedSearchInputs.Query, rowIndex)}
ariaLabel={formatMessage({ id: 'ld.aria.advancedSearch.queryInput' })}
/>
<Select
withIntl
value={qualifier}
options={SELECT_QUALIFIERS}
className="cell-qualifier"
onChange={({ value }) => onChangeInput(value, AdvancedSearchInputs.Qualifier, rowIndex)}
ariaLabel={formatMessage({ id: 'ld.aria.advancedSearch.qualifier' })}
data-testid={`select-qualifiers-${rowIndex}`}
/>
in
Expand All @@ -109,6 +114,7 @@ export const AdvancedSearchModal: FC<Props> = memo(({ clearValues }) => {
options={SELECT_IDENTIFIERS}
className="cell-identifier"
onChange={({ value }) => onChangeInput(value, AdvancedSearchInputs.Index, rowIndex)}
ariaLabel={formatMessage({ id: 'ld.aria.advancedSearch.identifier' })}
data-testid={`select-identifiers-${rowIndex}`}
/>
</div>
Expand Down
10 changes: 8 additions & 2 deletions src/components/FullDisplay/PreviewContent.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useRecoilState } from 'recoil';
import { FormattedMessage } from 'react-intl';
import { FormattedMessage, useIntl } from 'react-intl';
import { RESOURCE_TEMPLATE_IDS } from '@common/constants/bibframe.constants';
import { generateEditResourceUrl } from '@common/helpers/navigation.helper';
import { useNavigateToEditPage } from '@common/hooks/useNavigateToEditPage';
Expand All @@ -11,6 +11,7 @@ import './FullDisplay.scss';

export const PreviewContent = () => {
const [previewContent, setPreviewContent] = useRecoilState(state.inputs.previewContent);
const { formatMessage } = useIntl();
const { navigateToEditPage } = useNavigateToEditPage();

return previewContent.map(({ id, base, userValues, initKey, title, entities }) => {
Expand All @@ -21,7 +22,12 @@ export const PreviewContent = () => {
return (
<div key={id}>
<div className="full-display-control-panel">
<Button className="close" data-testid="preview-remove" onClick={handleButtonClick}>
<Button
className="close"
data-testid="preview-remove"
onClick={handleButtonClick}
ariaLabel={formatMessage({ id: 'ld.aria.sections.closeResourcePreview' })}
>
<Times16 />
</Button>
<div className="info">
Expand Down
12 changes: 10 additions & 2 deletions src/components/Modal/Modal.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { FC, ReactNode, memo, useEffect } from 'react';
import { createPortal } from 'react-dom';
import classNames from 'classnames';
import { MODAL_CONTAINER_ID } from '@common/constants/uiElements.constants';
import { AriaModalKind, MODAL_CONTAINER_ID } from '@common/constants/uiElements.constants';
import Times16 from '@src/assets/times-16.svg?react';
import { Button, ButtonType } from '@components/Button';
import './Modal.scss';
import { useIntl } from 'react-intl';
// TODO: UILD-147 - Uncomment for using with Shadow DOM
// import { WEB_COMPONENT_NAME } from '@common/constants/web-component';

Expand All @@ -26,6 +27,7 @@ interface Props {
showModalControls?: boolean;
titleClassName?: string;
alignTitleCenter?: boolean;
ariaModalKind?: string;
}

const Modal: FC<Props> = ({
Expand All @@ -46,7 +48,9 @@ const Modal: FC<Props> = ({
showModalControls = true,
titleClassName,
alignTitleCenter = false,
ariaModalKind = AriaModalKind.Basic,
}) => {
const { formatMessage } = useIntl();
const portalElement = document.getElementById(MODAL_CONTAINER_ID) as Element;
// TODO: UILD-147 - uncomment for using with Shadow DOM
// || (document.querySelector(WEB_COMPONENT_NAME)?.shadowRoot?.getElementById(MODAL_CONTAINER_ID) as Element)
Expand All @@ -70,7 +74,11 @@ const Modal: FC<Props> = ({
<div className={classNames(['modal', className])} role="dialog" data-testid="modal">
<div className={classNames(['modal-header', classNameHeader])}>
{showCloseIconButton && (
<button onClick={onClose} className="close-button">
<button
onClick={onClose}
className="close-button"
aria-label={formatMessage({ id: 'ld.aria.modal.close' }, { modalKind: ariaModalKind })}
>
<Times16 />
</button>
)}
Expand Down
11 changes: 9 additions & 2 deletions src/components/SearchResultEntry/SearchResultEntry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { FC, useState } from 'react';
import { Link } from 'react-router-dom';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import classNames from 'classnames';
import { FormattedMessage } from 'react-intl';
import { FormattedMessage, useIntl } from 'react-intl';
import { WorkDetailsCard } from '@components/WorkDetailsCard';
import { Row, Table } from '@components/Table';
import { Button, ButtonType } from '@components/Button';
Expand Down Expand Up @@ -57,6 +57,7 @@ const instancesListHeader: Row = {
};

export const SearchResultEntry: FC<SearchResultEntry> = ({ instances, ...restOfWork }) => {
const { formatMessage } = useIntl();
const { navigateToEditPage } = useNavigateToEditPage();
const navigationState = useRecoilValue(state.search.navigationState);
const [isOpen, setIsOpen] = useState(true);
Expand Down Expand Up @@ -89,6 +90,7 @@ export const SearchResultEntry: FC<SearchResultEntry> = ({ instances, ...restOfW
type={ButtonType.Link}
onClick={() => handleOpenPreview(row?.__meta?.id)}
data-testid={`preview-button__${row.__meta.id}`}
ariaLabel={formatMessage({ id: 'ld.aria.sections.openResourcePreview' })}
>
{row.title.label}
</Button>
Expand All @@ -109,7 +111,12 @@ export const SearchResultEntry: FC<SearchResultEntry> = ({ instances, ...restOfW
selectCtl: {
children: (
<div className="row-select-container">
<input id={`row-select-ctl-${row.__meta?.key}`} type="checkbox" disabled={IS_DISABLED_FOR_ALPHA} />
<input
id={`row-select-ctl-${row.__meta?.key}`}
type="checkbox"
disabled={IS_DISABLED_FOR_ALPHA}
aria-label={formatMessage({ id: 'ld.aria.table.selectRow' })}
/>
</div>
),
},
Expand Down
6 changes: 3 additions & 3 deletions src/components/Select/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type Select = {
withIntl?: boolean;
className?: string;
ariaLabel?: string;
ariaLabelledby?: string;
ariaLabelledBy?: string;
[x: string]: any;
};

Expand All @@ -30,7 +30,7 @@ export const Select: FC<Select> = ({
withIntl = false,
className,
ariaLabel,
ariaLabelledby,
ariaLabelledBy,
...restProps
}) => {
const selectedValue = typeof value === 'string' ? value : value?.value;
Expand All @@ -48,7 +48,7 @@ export const Select: FC<Select> = ({
role="combobox"
aria-expanded="false"
aria-label={ariaLabel}
aria-labelledby={ariaLabelledby}
aria-labelledby={ariaLabelledBy}
{...restProps}
>
{options.map(id => {
Expand Down
10 changes: 8 additions & 2 deletions src/components/WorkDetailsCard/WorkDetailsCard.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { FC } from 'react';
import { Link } from 'react-router-dom';
import classNames from 'classnames';
import { FormattedMessage } from 'react-intl';
import { FormattedMessage, useIntl } from 'react-intl';
import { generateEditResourceUrl } from '@common/helpers/navigation.helper';
import { useNavigateToEditPage } from '@common/hooks/useNavigateToEditPage';
import { Button, ButtonType } from '@components/Button';
Expand All @@ -28,6 +28,7 @@ export const WorkDetailsCard: FC<WorkDetailsCard> = ({
handleOpenPreview,
titles,
}) => {
const { formatMessage } = useIntl();
const { navigateToEditPage } = useNavigateToEditPage();
const title = getTitle(titles);
const creatorName = contributors?.find(({ isCreator }) => isCreator)?.name;
Expand All @@ -39,7 +40,12 @@ export const WorkDetailsCard: FC<WorkDetailsCard> = ({
return (
<div className="work-details-card">
<div className="heading">
<Button type={ButtonType.Ghost} onClick={toggleIsOpen} data-testid="work-details-card-toggle">
<Button
type={ButtonType.Ghost}
onClick={toggleIsOpen}
data-testid="work-details-card-toggle"
ariaLabel={formatMessage({ id: isOpen ? 'ld.aria.listEntry.close' : 'ld.aria.listEntry.open' })}
>
<CaretDown className={classNames({ ['icon-collapsed']: !isOpen }, 'toggle-icon')} />
</Button>
<div className="title">
Expand Down
10 changes: 10 additions & 0 deletions translations/ui-linked-data/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,18 @@
"ld.selectBrowseOption": "Select a browse option",
"ld.searchQueryWouldBeHere": "{query} would be here",
"ld.continue": "Continue",
"ld.aria.modal.close": "Close {modalKind} modal",
"ld.aria.advancedSearch.queryInput": "Advanced search row query input",
"ld.aria.advancedSearch.operator": "Advanced search row operator",
"ld.aria.advancedSearch.qualifier": "Advanced search row qualifier",
"ld.aria.advancedSearch.identifier": "Advanced search row identifier",
"ld.aria.filters.select": "Select search identifiers",
"ld.aria.listEntry.open": "Open collapsible list entry",
"ld.aria.listEntry.close": "Close collapsible list entry",
"ld.aria.filters.textbox": "Search query textbox",
"ld.aria.table.selectRow": "Select table row",
"ld.aria.sections.openResourcePreview": "Open resource preview section",
"ld.aria.sections.closeResourcePreview": "Close resource preview section",
"ld.aria.filters.reset": "Reset filters button",
"ld.aria.filters.reset.announce": "Search field and filters are reset",
"ld.duplicateImportWarn": "Duplicate import warning",
Expand Down
Loading