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.
This commit is contained in:
Sebastian Sdorra
2021-12-16 11:51:34 +01:00
committed by GitHub
parent 11673e6d07
commit a3ec5bcbeb
4 changed files with 48 additions and 12 deletions

View File

@@ -0,0 +1,2 @@
- type: changed
description: Improved quick search experience for screen readers ([#1898](https://github.com/scm-manager/scm-manager/pull/1898))

View File

@@ -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",

View File

@@ -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",

View File

@@ -117,33 +117,54 @@ const HitsList: FC<HitsProps> = ({ hits, index, clear, gotoDetailSearch }) => {
return <EmptyHits />;
}
return (
<>
<ul id="omni-search-results" aria-expanded="true" role="listbox">
{hits.map((hit, idx) => (
<div key={id(hit)} onMouseDown={e => e.preventDefault()} onClick={clear}>
<li
key={id(hit)}
onMouseDown={e => e.preventDefault()}
onClick={clear}
role="option"
aria-selected={idx === index}
id={idx === index ? "omni-search-selected-option" : undefined}
>
<Link
className={classNames("is-flex", "dropdown-item", "has-text-weight-medium", "is-ellipsis-overflow", {
"is-active": idx === index
})}
title={id(hit)}
to={`/repo/${id(hit)}`}
role="option"
data-omnisearch="true"
>
<AvatarSection hit={hit} />
{id(hit)}
</Link>
</div>
</li>
))}
</>
</ul>
);
};
const Hits: FC<HitsProps> = ({ showHelp, gotoDetailSearch, ...rest }) => {
type ScreenReaderHitSummaryProps = {
hits: Hit[];
};
const ScreenReaderHitSummary: FC<ScreenReaderHitSummaryProps> = ({ hits }) => {
const [t] = useTranslation("commons");
const key = hits.length > 0 ? "screenReaderHint" : "screenReaderHintNoResult";
return (
<span aria-live="assertive" className="is-sr-only">
{t(`search.quickSearch.${key}`, { count: hits.length })}
</span>
);
};
const Hits: FC<HitsProps> = ({ showHelp, gotoDetailSearch, hits, ...rest }) => {
const [t] = useTranslation("commons");
return (
<>
<div aria-expanded="true" role="listbox" className="dropdown-content">
<div className="dropdown-content">
<ScreenReaderHitSummary hits={hits} />
<ResultHeading
className={classNames(
"dropdown-item",
@@ -159,7 +180,7 @@ const Hits: FC<HitsProps> = ({ showHelp, gotoDetailSearch, ...rest }) => {
<span>{t("search.quickSearch.resultHeading")}</span>
<SyntaxHelp onClick={showHelp} />
</ResultHeading>
<HitsList showHelp={showHelp} gotoDetailSearch={gotoDetailSearch} {...rest} />
<HitsList showHelp={showHelp} gotoDetailSearch={gotoDetailSearch} hits={hits} {...rest} />
<MoreResults gotoDetailSearch={gotoDetailSearch} />
</div>
</>
@@ -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 : (