From a3ec5bcbeb7106973e125300bf856454eed706dc Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 16 Dec 2021 11:51:34 +0100 Subject: [PATCH] Improved quick search experience for screen readers (#1898) Improved screen reader experience by adding textual description of search results, which are only readable for screen readers. --- gradle/changelog/screen_reader_search_ux.yaml | 2 + .../ui-webapp/public/locales/de/commons.json | 5 +- .../ui-webapp/public/locales/en/commons.json | 5 +- .../ui-webapp/src/containers/OmniSearch.tsx | 48 +++++++++++++++---- 4 files changed, 48 insertions(+), 12 deletions(-) create mode 100644 gradle/changelog/screen_reader_search_ux.yaml diff --git a/gradle/changelog/screen_reader_search_ux.yaml b/gradle/changelog/screen_reader_search_ux.yaml new file mode 100644 index 0000000000..04da1bc326 --- /dev/null +++ b/gradle/changelog/screen_reader_search_ux.yaml @@ -0,0 +1,2 @@ +- type: changed + description: Improved quick search experience for screen readers ([#1898](https://github.com/scm-manager/scm-manager/pull/1898)) diff --git a/scm-ui/ui-webapp/public/locales/de/commons.json b/scm-ui/ui-webapp/public/locales/de/commons.json index d044ec36c0..16337fcdd4 100644 --- a/scm-ui/ui-webapp/public/locales/de/commons.json +++ b/scm-ui/ui-webapp/public/locales/de/commons.json @@ -194,7 +194,10 @@ "moreResults": "Mehr Ergebnisse", "noResults": "Es konnten keine Repositories gefunden werden", "hintsIcon": "Suchtipps", - "hints": "Hinweise zu ihrer Suche" + "hints": "Hinweise zu ihrer Suche", + "screenReaderHintNoResult": "Keine Repositories gefunden. Mögliche weitere Ergebnistypen mit Enter anzeigen.", + "screenReaderHint": "Ein Repository gefunden. Mögliche weitere Ergebnistypen mit Enter anzeigen oder Pfeiltasten verwenden um zu den gefunden Repositories zu navigieren und mit Enter bestätigen.", + "screenReaderHint_plural": "Mindestens {{ count }} Repositories gefunden. Mögliche weitere Ergebnistypen mit Enter anzeigen oder Pfeiltasten verwenden um zu den gefunden Repositories zu navigieren und mit Enter bestätigen." }, "syntax": { "title": "Experten-Suche", diff --git a/scm-ui/ui-webapp/public/locales/en/commons.json b/scm-ui/ui-webapp/public/locales/en/commons.json index 60447a12b9..24cf4cd106 100644 --- a/scm-ui/ui-webapp/public/locales/en/commons.json +++ b/scm-ui/ui-webapp/public/locales/en/commons.json @@ -195,7 +195,10 @@ "noResults": "Could not find matching repository", "moreResults": "More Results", "hintsIcon": "Search Hints", - "hints": "Hints for your Search" + "hints": "Hints for your Search", + "screenReaderHintNoResult": "No repositories found. Other result types may be available, hit enter to navigate to complete search result.", + "screenReaderHint": "Found one repository. Hit enter to see search results of all types or use arrow keys to navigate to repository quick results and hit enter to select one of them.", + "screenReaderHint_plural": "Found at least {{ count }} repositories. Hit enter to see search results of all types or use arrow keys to navigate to repository quick results and hit enter to select one of them." }, "syntax": { "title": "Expert Search", diff --git a/scm-ui/ui-webapp/src/containers/OmniSearch.tsx b/scm-ui/ui-webapp/src/containers/OmniSearch.tsx index ce4fc423c8..35b8c8f607 100644 --- a/scm-ui/ui-webapp/src/containers/OmniSearch.tsx +++ b/scm-ui/ui-webapp/src/containers/OmniSearch.tsx @@ -117,33 +117,54 @@ const HitsList: FC = ({ hits, index, clear, gotoDetailSearch }) => { return ; } return ( - <> +
    {hits.map((hit, idx) => ( -
    e.preventDefault()} onClick={clear}> +
  • e.preventDefault()} + onClick={clear} + role="option" + aria-selected={idx === index} + id={idx === index ? "omni-search-selected-option" : undefined} + > {id(hit)} -
  • + ))} - +
); }; -const Hits: FC = ({ showHelp, gotoDetailSearch, ...rest }) => { +type ScreenReaderHitSummaryProps = { + hits: Hit[]; +}; + +const ScreenReaderHitSummary: FC = ({ hits }) => { + const [t] = useTranslation("commons"); + const key = hits.length > 0 ? "screenReaderHint" : "screenReaderHintNoResult"; + return ( + + {t(`search.quickSearch.${key}`, { count: hits.length })} + + ); +}; + +const Hits: FC = ({ showHelp, gotoDetailSearch, hits, ...rest }) => { const [t] = useTranslation("commons"); return ( <> -
+
+ = ({ showHelp, gotoDetailSearch, ...rest }) => { {t("search.quickSearch.resultHeading")} - +
@@ -298,7 +319,12 @@ const useSearchParams = () => { } const queryParams = queryString.parse(location.search); - initialQuery = queryParams.q || ""; + const q = queryParams.q; + if (Array.isArray(q)) { + initialQuery = q[0] || ""; + } else { + initialQuery = q || ""; + } } return { @@ -348,10 +374,12 @@ const OmniSearch: FC = () => { onKeyDown={onKeyDown} value={query} role="combobox" - aria-autocomplete="list" + aria-autocomplete="both" data-omnisearch="true" aria-expanded={query.length > 2} aria-label={t("search.ariaLabel")} + aria-owns="omni-search-results" + aria-activedescendant={index >= 0 ? "omni-search-selected-option" : undefined} {...handlers} /> {isLoading ? null : (