From f530069a4e078a95eba079e8797ae877547ece05 Mon Sep 17 00:00:00 2001 From: Yvonne Tang <42749717+yvonnetangsu@users.noreply.github.com> Date: Tue, 29 Aug 2023 17:35:40 -0700 Subject: [PATCH] ADAPT-5851 | Improve site search a11y (#364) * name browserslistrc properly * Add SR only label to search field; add type="button" to buttons * Add type="button" for all the buttons missing this attribute * a11y patch ups * fixups * Add aria live to number of search results * Pagination improvements * Return focus to open search button after search overlay is closed * Fix for document not available during SSR * Add underline on highlighted suggested search; use ul for search results structure * Remove redundant role=navigation on pagination --- .browserlistrc => .browserslistrc | 0 src/components/composite/accordion.js | 2 ++ src/components/composite/accordionItem.js | 1 + src/components/composite/modal.js | 1 + .../composite/oodGallerySlideshow.js | 17 +++++++--- src/components/composite/oodQuoteSlider.js | 4 +-- .../navigation/contentMenu/oodContentMenu.js | 1 + .../navigation/megaMenu/oodMegaMenu.js | 1 + .../navigation/megaMenu/oodMegaMenuSection.js | 1 + src/components/search/autocomplete.js | 27 +++++++++++++--- src/components/search/hits.js | 29 +++++++++-------- src/components/search/pagination.js | 17 +++++----- src/components/search/searchOverlay.js | 31 +++++++++++++++++-- src/components/search/searchResults.js | 3 ++ src/components/search/stats.js | 5 +-- src/scss/components/search/_input.scss | 1 + 16 files changed, 105 insertions(+), 36 deletions(-) rename .browserlistrc => .browserslistrc (100%) diff --git a/.browserlistrc b/.browserslistrc similarity index 100% rename from .browserlistrc rename to .browserslistrc diff --git a/src/components/composite/accordion.js b/src/components/composite/accordion.js index 20a0e259..78d5a607 100644 --- a/src/components/composite/accordion.js +++ b/src/components/composite/accordion.js @@ -87,12 +87,14 @@ class Accordion extends React.Component { {props.blok.accordionItems.length > 1 && ( <> Collapse all diff --git a/src/components/composite/accordionItem.js b/src/components/composite/accordionItem.js index 09eda720..e9efd244 100644 --- a/src/components/composite/accordionItem.js +++ b/src/components/composite/accordionItem.js @@ -50,6 +50,7 @@ class AccordionItem extends React.Component { {...(props.blok.id ? { id: props.blok.id } : {})} > { {blok.showExpandLink && ( { - + Previous Slide @@ -75,7 +80,11 @@ const oodGallerySlideshow = ({ blok }) => { {dots} - + Next Slide @@ -109,13 +118,13 @@ const oodGallerySlideshow = ({ blok }) => { const modalSliderSettings = { nextArrow: ( - + Next Slide ), prevArrow: ( - + Previous Slide diff --git a/src/components/composite/oodQuoteSlider.js b/src/components/composite/oodQuoteSlider.js index f6549e14..17db91c4 100644 --- a/src/components/composite/oodQuoteSlider.js +++ b/src/components/composite/oodQuoteSlider.js @@ -11,7 +11,7 @@ const OodQuoteSlider = ({ blok }) => { const sliderSettings = { nextArrow: ( - + Next Slide:{' '} {`${ @@ -22,7 +22,7 @@ const OodQuoteSlider = ({ blok }) => { ), prevArrow: ( - + Previous Slide:{' '} {`${activeSlide === 0 ? blok.quotes.length : activeSlide} of ${ diff --git a/src/components/navigation/contentMenu/oodContentMenu.js b/src/components/navigation/contentMenu/oodContentMenu.js index e691df73..5ceadb59 100644 --- a/src/components/navigation/contentMenu/oodContentMenu.js +++ b/src/components/navigation/contentMenu/oodContentMenu.js @@ -79,6 +79,7 @@ const OodContentMenu = (props) => { ref={ref} > { { { + const { inputId, listboxId, ...otherProps } = props; const [initialTerm, setInitialTerm] = useState(''); const { search } = useLocation(); @@ -48,11 +49,16 @@ const Autocomplete = React.forwardRef((props, ref) => { } }; + const containerProps = { + 'aria-owns': listboxId, + }; + const inputProps = { ref, value, type: 'text', placeholder: 'Search', + id: inputId, onChange, onKeyDown, onFocus: () => setShouldRenderSuggestions(true), @@ -94,6 +100,9 @@ const Autocomplete = React.forwardRef((props, ref) => { return ( + + Search this site + { onSuggestionSelected={handleSubmit} getSuggestionValue={(hit) => hit.query} renderSuggestion={(hit) => hit.query} + containerProps={containerProps} inputProps={inputProps} + renderSuggestionsContainer={({ containerProps, children }) => ( + + {children} + + )} /> {value && ( { setValue(''); @@ -113,10 +133,7 @@ const Autocomplete = React.forwardRef((props, ref) => { aria-label="Clear search input" > Clear - + )} @@ -128,7 +145,7 @@ const Autocomplete = React.forwardRef((props, ref) => { Submit search diff --git a/src/components/search/hits.js b/src/components/search/hits.js index c54a1fb8..e6a4e5ec 100644 --- a/src/components/search/hits.js +++ b/src/components/search/hits.js @@ -26,19 +26,22 @@ const Hits = (props) => { {hits.length > 0 && } - {hits.map((hit) => ( - - - {hit.title} - - - {hit.intro || hit.teaser || hit.description} - - - ))} + + {hits.map((hit) => ( + + + + + {hit.title} + + + + {hit.intro || hit.teaser || hit.description} + + + + ))} + {hits.length > 0 && } diff --git a/src/components/search/pagination.js b/src/components/search/pagination.js index 1626a27c..6a538475 100644 --- a/src/components/search/pagination.js +++ b/src/components/search/pagination.js @@ -31,7 +31,7 @@ const Pagination = ({ initialPage }) => { }; return ( - + {isPreviousVisible && ( @@ -48,10 +48,7 @@ const Pagination = ({ initialPage }) => { )} {isFirstPageVisible && ( <> - + handlePageChange(1, $event)} href={`/search-results?page=1`} @@ -60,7 +57,10 @@ const Pagination = ({ initialPage }) => { 1 - + ... > @@ -100,7 +100,10 @@ const Pagination = ({ initialPage }) => { })} {isLastPageVisible && ( <> - + ... diff --git a/src/components/search/searchOverlay.js b/src/components/search/searchOverlay.js index 1c163d0f..022e72b9 100644 --- a/src/components/search/searchOverlay.js +++ b/src/components/search/searchOverlay.js @@ -19,6 +19,17 @@ const SearchOverlay = () => { const [isEmptyErrorVisible, setIsEmptyErrorVisible] = useState(false); const inputRef = useRef(null); + const searchButton = + typeof window !== 'undefined' && + document.querySelector('.ood-header__search-button'); + + const focusSearchButton = () => { + if (searchButton) { + // wait 100ms, then focus on search button + setTimeout(() => searchButton.focus(), 100); + } + }; + useEffect(() => { if (isOpen && inputRef.current) { // when the search overlay opens, immediately give focus to the input @@ -43,7 +54,13 @@ const SearchOverlay = () => { setIsEmptyErrorVisible(false); }; - UseEscape(() => isOpen && closeSearchOverlay()); + const handleCloseOverlay = () => { + closeSearchOverlay(); + focusSearchButton(); + }; + + // close search overlay and focus search button + UseEscape(() => isOpen && handleCloseOverlay()); const { introduction, @@ -63,14 +80,20 @@ const SearchOverlay = () => { UseFocusTrap(firstTabbableRef, lastTabbableRef, isOpen); return ( - + @@ -95,6 +118,8 @@ const SearchOverlay = () => { > {isOpen && ( { searchClient={searchClient} indexName={process.env.GATSBY_ALGOLIA_SUGGESTIONS_INDEX_NAME} > + {/* This is the search bar on the search results page */} diff --git a/src/components/search/stats.js b/src/components/search/stats.js index 51f7d91c..21813216 100644 --- a/src/components/search/stats.js +++ b/src/components/search/stats.js @@ -2,8 +2,9 @@ import React from 'react'; import { connectStats } from 'react-instantsearch-dom'; const Stats = connectStats(({ nbHits }) => ( - - {nbHits} {nbHits !== 1 ? 'results' : 'result'} found: + + {nbHits} + {nbHits !== 1 ? ' results' : ' result'} found: )); diff --git a/src/scss/components/search/_input.scss b/src/scss/components/search/_input.scss index 7aa68079..8dfee194 100644 --- a/src/scss/components/search/_input.scss +++ b/src/scss/components/search/_input.scss @@ -83,6 +83,7 @@ &--highlighted { color: $su-color-digital-red; background-color: $su-color-black-10; + text-decoration: underline; } }
- {hit.intro || hit.teaser || hit.description} -
+ {hit.intro || hit.teaser || hit.description} +
- {nbHits} {nbHits !== 1 ? 'results' : 'result'} found: +
+ {nbHits} + {nbHits !== 1 ? ' results' : ' result'} found: