Skip to content

Commit

Permalink
Merge pull request #711 from City-of-Helsinki/UHF-8590_Search_accessi…
Browse files Browse the repository at this point in the history
…bility_fix

UHF-8590: Search accessibility fix
  • Loading branch information
jeremysteerio authored Aug 18, 2023
2 parents 132e22f + b8866cf commit fb41500
Show file tree
Hide file tree
Showing 28 changed files with 132 additions and 88 deletions.
16 changes: 8 additions & 8 deletions dist/css/styles.min.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/js/district-and-project-search.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/js/job-search.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/js/linkedevents.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/js/news-archive.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/js/school-search.min.js

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ const FormContainer = () => {

const handleTitleChange = ({ target: { value } }: { target: { value: string } }) => setTitle(value);
const accordionInitiallyOpen = !!Object.keys(urlParams).find(param => Object.keys(ComponentMap).includes(param) && urlParams?.[param as keyof URLParams]?.length);
const residentialAreaLabel: string = Drupal.t('Select the residential area from the list', {}, { context: 'District and project search form label' });
const projectThemeLabel: string = Drupal.t('Project theme', {}, { context: 'District and project search form label' });
const projectStageLabel: string = Drupal.t('Project stage', {}, { context: 'District and project search form label' });
const projectTypeLabel: string = Drupal.t('Project type', {}, { context: 'District and project search form label' });

return (
<form onSubmit={handleSubmit}>
Expand All @@ -78,9 +82,9 @@ const FormContainer = () => {
value={districtSelection}
onChange={setDistrictFilter}
icon={<IconLocation />}
label={Drupal.t('Select the residential area from the list', {}, { context: 'District and project search form label' })}
label={residentialAreaLabel}
placeholder={Drupal.t('Select area', {}, { context: 'District and project search form label' })}
clearButtonAriaLabel={Drupal.t('Clear selection', {}, { context: 'District and project search clear button aria label' })}
clearButtonAriaLabel={Drupal.t('Clear @label selection', {'@label': residentialAreaLabel}, { context: 'React search clear selection label' })}
selectedItemRemoveButtonAriaLabel={Drupal.t('Remove item', {}, { context: 'District and project search remove item aria label' })}
toggleButtonAriaLabel={Drupal.t('Open the combobox', {}, { context: 'District and project search open dropdown aria label' })}
theme={{
Expand All @@ -102,17 +106,17 @@ const FormContainer = () => {
'--header-line-height': 'var(--lineheight-s)',
}}
>
<div className='district-project-search-form__filters'>
<div className='district-project-search-form__filters'>
<Combobox
multiselect
id={SearchComponents.THEME}
// @ts-ignore
options={themeOptions}
value={themeSelection}
onChange={setThemeFilter}
label={Drupal.t('Project theme', {}, { context: 'District and project search form label' })}
label={projectThemeLabel}
placeholder={Drupal.t('All themes', {}, { context: 'District and project search form label' })}
clearButtonAriaLabel={Drupal.t('Clear selection', {}, { context: 'District and project search clear button aria label' })}
clearButtonAriaLabel={Drupal.t('Clear @label selection', {'@label': projectThemeLabel}, { context: 'React search clear selection label' })}
selectedItemRemoveButtonAriaLabel={Drupal.t('Remove item', {}, { context: 'District and project search remove item aria label' })}
toggleButtonAriaLabel={Drupal.t('Open the combobox', {}, { context: 'District and project search open dropdown aria label' })}
theme={{
Expand All @@ -128,9 +132,9 @@ const FormContainer = () => {
options={phaseOptions}
value={phaseSelection}
onChange={setPhaseFilter}
label={Drupal.t('Project stage', {}, { context: 'District and project search form label' })}
label={projectStageLabel}
placeholder={Drupal.t('All stages', {}, { context: 'District and project search form label' })}
clearButtonAriaLabel={Drupal.t('Clear selection', {}, { context: 'District and project search clear button aria label' })}
clearButtonAriaLabel={Drupal.t('Clear @label selection', {'@label': projectStageLabel}, { context: 'React search clear selection label' })}
selectedItemRemoveButtonAriaLabel={Drupal.t('Remove item', {}, { context: 'District and project search remove item aria label' })}
toggleButtonAriaLabel={Drupal.t('Open the combobox', {}, { context: 'District and project search open dropdown aria label' })}
theme={{
Expand All @@ -146,9 +150,9 @@ const FormContainer = () => {
options={typeOptions}
value={typeSelection}
onChange={setTypeFilter}
label={Drupal.t('Project type', {}, { context: 'District and project search form label' })}
label={projectTypeLabel}
placeholder={Drupal.t('All types', {}, { context: 'District and project search form label' })}
clearButtonAriaLabel={Drupal.t('Clear selection', {}, { context: 'District and project search clear button aria label' })}
clearButtonAriaLabel={Drupal.t('Clear @label selection', {'@label': projectTypeLabel}, { context: 'React search clear selection label' })}
selectedItemRemoveButtonAriaLabel={Drupal.t('Remove item', {}, { context: 'District and project search remove item aria label' })}
toggleButtonAriaLabel={Drupal.t('Open the combobox', {}, { context: 'District and project search open dropdown aria label' })}
theme={{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { LoadingSpinner } from 'hds-react';
import { useAtomValue, useSetAtom } from 'jotai';
import useSWR from 'swr';
import { useEffect, SyntheticEvent } from 'react';
import { SyntheticEvent, createRef } from 'react';

import Pagination from '@/react/common/Pagination';
import ResultCard from '../components/results/ResultCard';
Expand All @@ -12,6 +12,7 @@ import Global from '../enum/Global';
import Settings from '../enum/Settings';
import type URLParams from '../types/URLParams';
import Result from '../types/Result';
import useScrollToResults from '@/react/common/hooks/useScrollToResults';

const ResultsContainer = (): JSX.Element => {
const { size } = Global;
Expand All @@ -20,6 +21,11 @@ const ResultsContainer = (): JSX.Element => {
const { error: initializationError } = useAtomValue(configurationsAtom);
const setPage = useSetAtom(setPageAtom);
const currentPage = useAtomValue(pageAtom);
const scrollTarget = createRef<HTMLDivElement>();

const choices = Boolean(window.location.search?.length);
useScrollToResults(scrollTarget, choices);

const fetcher = () => {
const proxyUrl = drupalSettings?.helfi_kymp_district_project_search?.elastic_proxy_url;
const url: string | undefined = proxyUrl || process.env.REACT_APP_ELASTIC_URL;
Expand All @@ -37,18 +43,6 @@ const ResultsContainer = (): JSX.Element => {
revalidateOnFocus: false,
});

useEffect(() => {
const el = document.getElementById('helfi-kymp-district-project-search');

if (el && window.location.search) {
const titleEl = el.querySelector<HTMLElement>('.district-project-search__results');
if (!titleEl) return;
titleEl.setAttribute('tabindex', '0');
titleEl.focus();
el.scrollIntoView({ behavior: 'smooth' });
titleEl.setAttribute('tabindex', '-1');
}
}, [data]);

if (!data && !error) {
return <LoadingSpinner />;
Expand All @@ -57,7 +51,7 @@ const ResultsContainer = (): JSX.Element => {
if (!data?.hits?.hits.length) {
return (
<div className="district-project-search__results">
<div className='district-project-search__listing__no-results'>
<div className='district-project-search__listing__no-results' ref={scrollTarget}>
<h2>{Drupal.t('Oh no! We did not find anything matching the search terms.', {}, { context: 'District and project search' })}</h2>
<p>{Drupal.t('Our website currently shows only some of the projects and residential areas of Helsinki. You can try again by removing some of the limiting search terms or by starting over.', {}, { context: 'District and project search' })}</p>
</div>
Expand All @@ -73,7 +67,7 @@ const ResultsContainer = (): JSX.Element => {
if (error || initializationError) {
console.warn(`Error loading data. ${ error || initializationError}`);
return (
<div className='district-project-search__results'>
<div className='district-project-search__results' ref={scrollTarget}>
{Drupal.t('The website encountered an unexpected error. Please try again later.')}
</div>
);
Expand All @@ -87,7 +81,7 @@ const ResultsContainer = (): JSX.Element => {
return (
<div className="district-project-search__results">
<div className="district-project-search__results_heading">
<div className="district-project-search__count__container">
<div className="district-project-search__count__container" ref={scrollTarget}>
<span className="district-project-search__count">
<span className="district-project-search__count-total">{total} </span>
<span className="district-project-search__count-label">{Drupal.t('search results', {}, { context: 'District and project search' })} </span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ const SelectionsContainer = () => {
)}
<li className='hdbt-search__clear-all'>
<Button
aria-hidden={showClearButton ? 'true' : 'false'}
aria-hidden={showClearButton ? 'false' : 'true'}
className='hdbt-search__clear-all-button'
iconLeft={<IconCross className='hdbt-search__clear-all-icon' />}
onClick={resetForm}
Expand Down
18 changes: 8 additions & 10 deletions src/js/react/apps/job-search/containers/FormContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ const FormContainer = () => {
const showCheckboxes = showContinuous || showInternships || showSummerJobs || showYouthSummerJobs;

const taskAreasLabel: string = Drupal.t('Task area', {}, { context: 'Task areas filter label' });
const employmentRelationshipLabel: string = Drupal.t('Type of employment relationship', {}, { context: 'Employment filter label' });
const languageLabel: string = Drupal.t('Language', {}, { context: 'Language filter label' });

return (
<form className='job-search-form' onSubmit={handleSubmit} action={formAction}>
Expand All @@ -117,7 +119,7 @@ const FormContainer = () => {
{/* @ts-ignore */}
<Select
clearable
clearButtonAriaLabel={Drupal.t('Clear selection', {}, { context: 'Job search clear button aria label' })}
clearButtonAriaLabel={Drupal.t('Clear @label selection', {'@label': taskAreasLabel}, { context: 'React search clear selection label' })}
className='job-search-form__dropdown'
selectedItemRemoveButtonAriaLabel={Drupal.t(
'Remove item',
Expand All @@ -136,7 +138,7 @@ const FormContainer = () => {
<div className='job-search-form__filter job-search-form__dropdown--upper'>
{/* @ts-ignore */}
<Select
clearButtonAriaLabel={Drupal.t('Clear selection', {}, { context: 'Job search clear button aria label' })}
clearButtonAriaLabel={Drupal.t('Clear @label selection', {'@label': employmentRelationshipLabel}, { context: 'React search clear selection label' })}
className='job-search-form__dropdown'
selectedItemRemoveButtonAriaLabel={Drupal.t(
'Remove item',
Expand All @@ -149,10 +151,10 @@ const FormContainer = () => {
{ context: 'Employment filter placeholder' }
)}
multiselect
label={Drupal.t('Type of employment relationship', {}, { context: 'Employment filter label' })}
label={employmentRelationshipLabel}
options={employmentOptions}
value={employmentSelection}
id={SearchComponents.TASK_AREAS}
id={SearchComponents.EMPLOYMENT_RELATIONSHIP}
onChange={setEmploymentFilter}
/>
</div>
Expand Down Expand Up @@ -190,11 +192,7 @@ const FormContainer = () => {
<div className='job-search-form__dropdowns__lower'>
<div className='job-search-form__filter job-search-form__dropdown--upper'>
<Select
clearButtonAriaLabel={Drupal.t(
'Clear selection',
{},
{ context: 'Job search clear button aria label' }
)}
clearButtonAriaLabel={Drupal.t('Clear @label selection', {'@label': languageLabel}, { context: 'React search clear selection label' })}
className='job-search-form__dropdown'
clearable
selectedItemRemoveButtonAriaLabel={Drupal.t(
Expand All @@ -203,7 +201,7 @@ const FormContainer = () => {
{ context: 'Job search remove item aria label' }
)}
placeholder={Drupal.t('All languages', {}, { context: 'Language placeholder' })}
label={Drupal.t('Language', {}, { context: 'Language filter label' })}
label={languageLabel}
// @ts-ignore
options={languagesOptions}
value={languageSelection}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ const SelectionsContainer = () => {
)}
<li className='job-search-form__clear-all'>
<Button
aria-hidden={showClearButton ? 'true' : 'false'}
aria-hidden={showClearButton ? 'false' : 'true'}
className='job-search-form__clear-all-button'
iconLeft={<IconCross className='job-search-form__clear-all-icon' />}
onClick={resetForm}
Expand Down
1 change: 1 addition & 0 deletions src/js/react/apps/job-search/enum/SearchComponents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const SearchComponents = {
EMPLOYMENT: 'employment',
KEYWORD: 'keyword',
TASK_AREAS: 'task_areas',
EMPLOYMENT_RELATIONSHIP: 'employment_relationship',
CONTINUOUS: 'continuous',
INTERNSHIPS: 'internship',
LANGUAGE: 'language',
Expand Down
5 changes: 3 additions & 2 deletions src/js/react/apps/linkedevents/components/LocationFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@ function LocationFilter() {
};

const locationHelper = Drupal.t('If you want to search for remote events, select only the option \'Internet (remote events)\'');
const selectVenueLabel: string = Drupal.t('Select a venue');

return (
<div className='hdbt-search__filter event-form__filter--location'>
<Select
className='hdbt-search__dropdown'
clearButtonAriaLabel={Drupal.t('Clear selections', {}, { context: 'Event search: clear button aria label' })}
clearButtonAriaLabel={Drupal.t('Clear @label selection', {'@label': selectVenueLabel}, { context: 'React search clear selection label' })}
helper={locationHelper}
label={Drupal.t('Select a venue')}
label={selectVenueLabel}
multiselect
// @ts-ignore
options={locationOptions}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ const FilterButton = ({ value, clearSelection }: FilterButtonProps) => (
{ context: 'Search: remove item aria label' }
)}
className='hdbt-search__remove-selection-button'
iconRight={<IconCross />}
iconRight={<IconCross className='hdbt-search__remove-selection-icon' />}
variant='supplementary'
onClick={clearSelection}
>
Expand All @@ -207,7 +207,7 @@ const ClearButton = ({ showClearButton, url }: ClearButtonProps) => {
return (
<li className='hdbt-search__clear-all'>
<Button
aria-hidden={showClearButton ? 'true' : 'false'}
aria-hidden={showClearButton ? 'false' : 'true'}
className='hdbt-search__clear-all-button'
iconLeft={<IconCross className='hdbt-search__clear-all-icon' />}
onClick={resetForm}
Expand Down
13 changes: 9 additions & 4 deletions src/js/react/apps/news-archive/components/Filter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type FilterProps = {
stateKey: keyof URLParams;
};

const Filter = ({stateKey, options, ...rest}: FilterProps) => {
const Filter = ({label, options, stateKey, ...rest}: FilterProps) => {
const [params, setParams] = useAtom(stagedParamsAtom);
const valueIds = params?.[stateKey] || [];

Expand Down Expand Up @@ -39,14 +39,19 @@ const Filter = ({stateKey, options, ...rest}: FilterProps) => {
};

return (
/* @ts-ignore */
<Select
className='news-form__filter'
clearable
clearButtonAriaLabel={Drupal.t('Clear selection')}
clearButtonAriaLabel={Drupal.t('Clear @label selection', {'@label': label}, { context: 'React search clear selection label' })}
label={label}
onChange={onChange}
multiselect
selectedItemRemoveButtonAriaLabel={Drupal.t('Remove item')}
/* @ts-ignore */
selectedItemRemoveButtonAriaLabel={Drupal.t(
'Remove item',
{},
{ context: 'Job search remove item aria label' }
)}
options={options}
value={getValue()}
{...rest}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
import { ForwardedRef, forwardRef } from 'react';

type ResultsHeadingProps = {
choices: boolean;
total: number;
}

const ResultsHeading = ({ choices, total }: ResultsHeadingProps) => {
const ResultsHeading = forwardRef((props: ResultsHeadingProps, ref: ForwardedRef<HTMLDivElement>) => {
const { choices, total } = props;

const heading = choices ?
`${Drupal.t('News based on your choices', {}, {context: 'News archive heading'}) } (${total})` :
Drupal.t('All news items', {}, {context: 'News archive heading'});

return (
<div className='news-archive__heading'>
<div className='news-archive__heading' ref={ref}>
<h2 className='news-archive__title'>
{heading}
</h2>
</div>
);
};
});

export default ResultsHeading;
9 changes: 6 additions & 3 deletions src/js/react/apps/news-archive/containers/FormContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ const FormContainer = () => {
};

const loading = isLoading || isValidating;
const topicLabel = Drupal.t('Topics', {}, { context: 'News archive topics label' });
const neighbourhoodLabel = Drupal.t('City disctricts', {}, { context: 'News archive neighbourhoods label' });
const groupLabel = Drupal.t('Target groups', {}, { context: 'News archive groups label' });

return (
<div className='news-form-wrapper'>
Expand All @@ -75,13 +78,13 @@ const FormContainer = () => {
<h2>{Drupal.t('Filter news items', {}, {context: 'News archive filter results heading'})}</h2>
<div className='news-form__filters-container'>
{topicOptions && <Filter
label={Drupal.t('Topics', {}, { context: 'News archive topics label' })}
label={topicLabel}
placeholder={Drupal.t('All topics', {}, { context: 'News archive topics placeholder' })}
options={topicOptions}
stateKey='topic'
/>}
{neighbourhoodOptions && <Filter
label={Drupal.t('City disctricts', {}, { context: 'News archive neighbourhoods label' })}
label={neighbourhoodLabel}
placeholder={Drupal.t(
'All city disctricts',
{},
Expand All @@ -91,7 +94,7 @@ const FormContainer = () => {
stateKey='neighbourhoods'
/>}
{groupOptions && <Filter
label={Drupal.t('Target groups', {}, { context: 'News archive groups label' })}
label={groupLabel}
placeholder={Drupal.t('All target groups', {}, { context: 'News archive groups placeholder' })}
options={groupOptions}
stateKey='groups'
Expand Down
Loading

0 comments on commit fb41500

Please sign in to comment.