Skip to content

Commit

Permalink
Fix navigation
Browse files Browse the repository at this point in the history
  • Loading branch information
RoyEJohnson committed Jul 29, 2024
1 parent d953bc1 commit e9e0082
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ const SearchResult = (props: {
const formatMessage = useIntl().formatMessage;
const active = props.page && props.currentPage
&& stripIdVersion(props.currentPage.id) === stripIdVersion(props.page.id);
const selectResultAndFocus = React.useCallback(
(result: SelectedResult) => {
props.selectResult(result);
setTimeout(() => document?.querySelector('main')?.focus(), 100);
},
[props]
);

return <Styled.NavItem>
<Styled.LinkWrapper {...(active ? {
Expand All @@ -81,7 +88,7 @@ const SearchResult = (props: {
hits={props.page.results}
testId='search-result'
getPage={() => props.page}
onClick={(result: SelectedResult) => props.selectResult(result)}
onClick={selectResultAndFocus}
selectedResult={props.selectedResult}
/>
</Styled.NavItem>;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { SearchResultHit } from '@openstax/open-search-client';
import { HTMLElement } from '@openstax/types/lib.dom';
import { HTMLElement, HTMLDivElement } from '@openstax/types/lib.dom';
import React, { Component } from 'react';
import { FormattedMessage, useIntl } from 'react-intl';
import searchIcon from '../.../../../../../../assets/search-icon-v2.svg';
Expand Down Expand Up @@ -59,13 +59,31 @@ const SearchResultsBar = React.forwardRef<
children: React.ReactNode,
}
>(
(props, ref) => <Styled.SearchResultsBar
aria-label={useIntl().formatMessage({id: 'i18n:search-results:bar'})}
data-testid='search-results-sidebar'
ref={ref}
tabIndex={-1}
{...props}
/>
(props, ref) => {
const forwardFocus = React.useCallback(
({target, currentTarget}: FocusEvent) => {
if (target !== currentTarget) {
return;
}
const currentResult = (ref as React.MutableRefObject<HTMLDivElement>)
.current.querySelector<HTMLElement>('[aria-current="true"]');

currentResult?.focus();
},
[ref]
);

return (
<Styled.SearchResultsBar
aria-label={useIntl().formatMessage({id: 'i18n:search-results:bar'})}
data-testid='search-results-sidebar'
ref={ref}
tabIndex={-1}
onFocus={forwardFocus}
{...props}
/>
);
}
);

export class SearchResultsBarWrapper extends Component<ResultsSidebarProps> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ exports[`SearchResultsSidebar matches snapshot for no search results 1`] = `
className="c0"
data-analytics-region="search-results"
data-testid="search-results-sidebar"
onFocus={[Function]}
tabIndex={-1}
>
<div>
Expand Down Expand Up @@ -775,6 +776,7 @@ exports[`SearchResultsSidebar matches snapshot with related key terms 1`] = `
className="c0"
data-analytics-region="search-results"
data-testid="search-results-sidebar"
onFocus={[Function]}
tabIndex={-1}
>
<div
Expand Down Expand Up @@ -1470,6 +1472,7 @@ exports[`SearchResultsSidebar matches snapshot with results 1`] = `
className="c0"
data-analytics-region="search-results"
data-testid="search-results-sidebar"
onFocus={[Function]}
tabIndex={-1}
>
<div
Expand Down Expand Up @@ -2005,6 +2008,7 @@ exports[`SearchResultsSidebar shows sidebar with loading state if there is a sea
className="c0"
data-analytics-region="search-results"
data-testid="search-results-sidebar"
onFocus={[Function]}
tabIndex={-1}
>
<div
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
import * as selectSearch from '../../selectors';
import { SearchScrollTarget } from '../../types';
import { SearchResultsBarWrapper } from './SearchResultsBarWrapper';
import { HTMLDivElement } from '@openstax/types/lib.dom';

describe('SearchResultsSidebar', () => {
let store: Store;
Expand Down Expand Up @@ -214,6 +215,7 @@ describe('SearchResultsSidebar', () => {
});

it('closes search results when one is clicked', () => {
jest.useFakeTimers();
store.dispatch(requestSearch('cool search'));
store.dispatch(
receiveSearchResults(
Expand All @@ -229,6 +231,7 @@ describe('SearchResultsSidebar', () => {
renderer.act(() => {
findById('search-result').props.onClick(makeEvent());
});
jest.runAllTimers();

expect(dispatch).toHaveBeenCalledWith(closeSearchResultsMobile());
});
Expand All @@ -253,7 +256,32 @@ describe('SearchResultsSidebar', () => {
expect(dispatch).toHaveBeenCalledWith(clearSearch());
});

it ('scrolls to search scroll target if result selected by user', () => {
// This is purely a code coverage test.
// It should have a selected result that receives focus when the bar is focused,
// and when the button is focused, it should keep it.
it('sidebar tries to forward focus to current search result', () => {
jest.spyOn(selectNavigation, 'persistentQueryParameters').mockReturnValue({query: 'cool search'});
renderToDom(render());
ReactTestUtils.act(
() => {
store.dispatch(receivePage({ ...pageInChapter, references: [] }));
store.dispatch(requestSearch('cool search'));
store.dispatch(receiveSearchResults(makeSearchResults([])));
}
);

const document = assertDocument();
const bar = document.querySelector<HTMLDivElement>('[class*="SearchResultsBar"]');
const button = document.querySelector('button');

ReactTestUtils.act(() => bar?.focus());
expect(document.activeElement).toBe(bar);

ReactTestUtils.act(() => button?.focus());
expect(document.activeElement).toBe(button);
})

it('scrolls to search scroll target if result selected by user', () => {
const searchResult = makeSearchResultHit({ book: archiveBook, page });
const searchScrollTarget: SearchScrollTarget = { type: 'search', index: 0, elementId: 'elementId' };
const scrollSidebarSectionIntoView = jest.spyOn(domUtils, 'scrollSidebarSectionIntoView');
Expand Down

0 comments on commit e9e0082

Please sign in to comment.