Skip to content

Commit

Permalink
Merge branch 'main' into fix-non-tabbable-highlights
Browse files Browse the repository at this point in the history
  • Loading branch information
staxly[bot] authored Aug 9, 2024
2 parents d3d00d7 + 7d7c586 commit ae19279
Show file tree
Hide file tree
Showing 18 changed files with 546 additions and 184 deletions.
2 changes: 0 additions & 2 deletions src/app/content/components/Page.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -811,7 +811,6 @@ describe('Page', () => {
await new Promise((resolve) => setImmediate(resolve));

expect(mockHighlight.addFocusedStyles).toHaveBeenCalled();
expect(scrollTo).toHaveBeenCalledWith(highlightElement);
});

it('doesn\'t scroll to search result when selected but unchanged', async() => {
Expand Down Expand Up @@ -906,7 +905,6 @@ describe('Page', () => {

expect(highlightResults).toHaveBeenCalledWith(expect.anything(), [hit]);
expect(mockHighlight.addFocusedStyles).toHaveBeenCalled();
expect(scrollTo).toHaveBeenCalledWith(highlightElement);
});

it('renders error modal for different search results', async() => {
Expand Down
6 changes: 2 additions & 4 deletions src/app/content/components/Page/searchHighlightManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import Highlighter, { Highlight } from '@openstax/highlighter';
import { HTMLElement } from '@openstax/types/lib.dom';
import isEqual from 'lodash/fp/isEqual';
import { IntlShape } from 'react-intl';
import { scrollTo } from '../../../domUtils';
import { AppState } from '../../../types';
import { memoizeStateToProps } from '../../../utils';
import * as selectSearch from '../../search/selectors';
Expand Down Expand Up @@ -75,10 +74,9 @@ const selectResult = (services: Services, previous: HighlightProp | null, curren
allImagesLoaded(services.container).then(
() => {
const target = selectedElements[0] as HTMLElement;
const container = target.closest('[tabindex]') as HTMLElement;
const focusTarget: HTMLElement | null = target.querySelector('[tabindex="0"]');

scrollTo(target);
container?.focus();
focusTarget?.focus();
}
);
}
Expand Down
97 changes: 91 additions & 6 deletions src/app/content/components/Topbar/index.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
import React from 'react';
import { Provider } from 'react-redux';
import renderer from 'react-test-renderer';
import renderer, { TestRendererOptions } from 'react-test-renderer';
import Topbar from '.';
import createTestServices from '../../../../test/createTestServices';
import createTestStore from '../../../../test/createTestStore';
import MessageProvider from '../../../../test/MessageProvider';
import { book as archiveBook } from '../../../../test/mocks/archiveLoader';
import { mockCmsBook } from '../../../../test/mocks/osWebLoader';
import { makeEvent, makeFindByTestId, makeFindOrNullByTestId, makeInputEvent } from '../../../../test/reactutils';
import {
makeEvent,
makeFindByTestId,
makeFindOrNullByTestId,
makeInputEvent,
dispatchKeyDownEvent,
renderToDom,
} from '../../../../test/reactutils';
import { act } from 'react-dom/test-utils';
import { makeSearchResults } from '../../../../test/searchResults';
import TestContainer from '../../../../test/TestContainer';
import * as Services from '../../../context/Services';
import { MiddlewareAPI, Store } from '../../../types';
import { assertDocument } from '../../../utils';
import { openMobileMenu, setTextSize } from '../../actions';
import { textResizerMaxValue, textResizerMinValue } from '../../constants';
import { HTMLElement } from '@openstax/types/lib.dom';
import { searchKeyCombination } from '../../highlights/constants';
import {
clearSearch,
closeSearchResultsMobile,
Expand All @@ -25,9 +35,15 @@ import {
import * as searchSelectors from '../../search/selectors';
import { formatBookData } from '../../utils';
import { CloseButtonNew, MenuButton, MobileSearchWrapper, SearchButton, TextResizerMenu } from './styled';
import { useMatchMobileQuery } from '../../../reactUtils';

const book = formatBookData(archiveBook, mockCmsBook);

jest.mock('../../../reactUtils', () => ({
...(jest as any).requireActual('../../../reactUtils'),
useMatchMobileQuery: jest.fn(),
}));

describe('search', () => {
let store: Store;
let dispatch: jest.SpyInstance;
Expand All @@ -43,13 +59,17 @@ describe('search', () => {
};
});

const render = () => renderer.create(<Provider store={store}>
const render = (options?: TestRendererOptions) => renderer.create(<Provider store={store}>
<Services.Provider value={services}>
<MessageProvider>
<Topbar />
</MessageProvider>
</Services.Provider>
</Provider>);
</Provider>, options);

const dispatchSearchShortcut = (target: HTMLElement | undefined) => {
dispatchKeyDownEvent({code: searchKeyCombination.code, altKey: searchKeyCombination.altKey, target});
};

it('opens and closes mobile interface', () => {
const component = render();
Expand All @@ -67,6 +87,67 @@ describe('search', () => {
});
expect(mobileSearch.props.mobileToolbarOpen).toBe(false);
expect(event.preventDefault).toHaveBeenCalledTimes(2);

});

it('goes between main and search input when no search results', () => {
const {node} = renderToDom(
<Provider store={store}>
<Services.Provider value={services}>
<MessageProvider>
<Topbar />
<main tabIndex={-1} />
</MessageProvider>
</Services.Provider>
</Provider>
);
const tb = node.querySelector<HTMLElement>('[class*="TopBar"]');

expect(document?.activeElement?.tagName).toBe('INPUT');
act(() => dispatchSearchShortcut(tb!));
expect(document?.activeElement?.tagName).toBe('MAIN');
});

it('goes to search results when provided', () => {
const {node} = renderToDom(
<Provider store={store}>
<Services.Provider value={services}>
<MessageProvider>
<Topbar />
<div className='SearchResultsBar' tabIndex={-1} />
<main tabIndex={-1} />
</MessageProvider>
</Services.Provider>
</Provider>
);
const tb = node.querySelector<HTMLElement>('[class*="TopBar"]');

store.dispatch(receiveSearchResults(makeSearchResults()));

act(() => dispatchSearchShortcut(tb!));
expect(document?.activeElement?.tagName).toBe('INPUT');
act(() => dispatchSearchShortcut(tb!));
expect(document?.activeElement?.classList.contains('SearchResultsBar')).toBe(true);
});

it('aborts on mobile', () => {
(useMatchMobileQuery as jest.Mock).mockReturnValue(true);
const {node} = renderToDom(
<Provider store={store}>
<Services.Provider value={services}>
<MessageProvider>
<Topbar />
<main tabIndex={-1} />
</MessageProvider>
</Services.Provider>
</Provider>
);
const tb = node.querySelector<HTMLElement>('[class*="TopBar"]');

act(() => dispatchSearchShortcut(tb!));
expect(document?.activeElement?.tagName).toBe('INPUT');
act(() => dispatchSearchShortcut(tb!));
expect(document?.activeElement?.tagName).not.toBe('MAIN');
});

it('doesn\'t dispatch search for empty string', () => {
Expand Down Expand Up @@ -127,7 +208,9 @@ describe('search', () => {
const findById = makeFindByTestId(component.root);

const inputEvent = makeInputEvent('cool search');
findById('desktop-search-input').props.onChange(inputEvent);
renderer.act(() => {
findById('desktop-search-input').props.onChange(inputEvent);
});

const event = makeEvent();
renderer.act(() => findById('desktop-search').props.onSubmit(event));
Expand Down Expand Up @@ -315,7 +398,9 @@ describe('search button', () => {
const findById = makeFindByTestId(component.root);

const inputEvent = makeInputEvent('cool search');
findById('desktop-search-input').props.onChange(inputEvent);
renderer.act(
() => findById('desktop-search-input').props.onChange(inputEvent)
);

const event = makeEvent();
renderer.act(() => findById('desktop-search').props.onSubmit(event));
Expand Down
Loading

0 comments on commit ae19279

Please sign in to comment.