Skip to content

Commit e942ea3

Browse files
authored
Merge branch 'main' into image-preview
2 parents 88af331 + 625d41a commit e942ea3

File tree

19 files changed

+249
-144
lines changed

19 files changed

+249
-144
lines changed

src/app/content/components/Modal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ interface ModalWithScrollLockProps extends StyledComponentProps<'div', {}, {}, '
1313
const ModalWithScrollLock = React.forwardRef<HTMLDivElement, ModalWithScrollLockProps>(
1414
({ children, scrollLockProps, ...props }: ModalWithScrollLockProps, ref) => {
1515
return createPortal(
16-
<PopupWrapper role='dialog' aria-modal='true'>
16+
<PopupWrapper role='dialog' aria-modal='true' aria-labelledby='modal-title'>
1717
<ScrollLock {...scrollLockProps} />
1818
<ModalWrapper ref={ref} {...props}>
1919
{children}

src/app/content/components/popUp/ColorFilter.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ export default styled(ColorFilter)`
9999
padding: ${filters.dropdownContent.padding.topBottom}rem ${filters.dropdownContent.padding.sides}rem;
100100
outline: none;
101101
z-index: 1;
102+
overflow: auto;
102103
103104
${AllOrNone} {
104105
margin: 0.8rem 0 0.8rem 0.8rem;

src/app/content/components/popUp/__snapshots__/ColorFilter.spec.tsx.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,7 @@ input:checked + .c9 .c11 {
482482
padding: 0.8rem 1.6rem;
483483
outline: none;
484484
z-index: 1;
485+
overflow: auto;
485486
}
486487
487488
.c0 .c1 {

src/app/content/highlights/components/HighlightStyles.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,3 +197,13 @@ export const MyHighlightsImage = styled.img`
197197
height: ${myHighlightsImageHeight}rem;
198198
margin-top: ${popupBodyPadding}rem;
199199
`;
200+
201+
// tslint:disable-next-line:variable-name
202+
export const StyledHiddenLiveRegion = styled.div`
203+
position: absolute;
204+
width: 1px;
205+
height: 1px;
206+
overflow: hidden;
207+
clip: rect(1px, 1px, 1px, 1px);
208+
white-space: nowrap;
209+
`;

src/app/content/highlights/components/Highlights.spec.tsx

Lines changed: 96 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { HighlightColorEnum, HighlightUpdateColorEnum } from '@openstax/highlighter/dist/api';
22
import React from 'react';
33
import renderer from 'react-test-renderer';
4+
import { RawIntlProvider } from 'react-intl';
5+
import createIntl from '../../../messages/createIntl';
46
import createTestServices from '../../../../test/createTestServices';
57
import createTestStore from '../../../../test/createTestStore';
68
import { book as archiveBook, page, pageInChapter } from '../../../../test/mocks/archiveLoader';
@@ -25,17 +27,22 @@ import { highlightLocationFilters } from '../selectors';
2527
import { HighlightData, SummaryHighlights } from '../types';
2628
import { getHighlightLocationFilterForPage } from '../utils';
2729
import Highlights from './Highlights';
28-
import { NoHighlightsTip } from './Highlights';
2930
import ContextMenu from './SummaryPopup/ContextMenu';
3031
import HighlightAnnotation from './SummaryPopup/HighlightAnnotation';
3132
import HighlightDeleteWrapper from './SummaryPopup/HighlightDeleteWrapper';
3233
import { HighlightContentWrapper } from './SummaryPopup/HighlightListElement';
34+
import { NoHighlightsTip, VisuallyHiddenLiveRegion } from './HighlightsCards';
35+
36+
37+
jest.useFakeTimers();
3338

3439
const hlBlue = { id: 'hl1', color: HighlightColorEnum.Blue, annotation: 'hl1', sourceId: 'testbook1-testpage1-uuid' };
3540
const hlGreen = { id: 'hl2', color: HighlightColorEnum.Green, annotation: 'hl', sourceId: 'testbook1-testpage1-uuid' };
3641
const hlPink = { id: 'hl3', color: HighlightColorEnum.Pink, annotation: 'hl', sourceId: 'testbook1-testpage1-uuid' };
37-
const hlPurple = { annotation: 'hl', color: HighlightColorEnum.Purple,
38-
id: 'hl4', sourceId: 'testbook1-testpage1-uuid' };
42+
const hlPurple = {
43+
annotation: 'hl', color: HighlightColorEnum.Purple,
44+
id: 'hl4', sourceId: 'testbook1-testpage1-uuid',
45+
};
3946
const hlYellow = { id: 'hl5', color: HighlightColorEnum.Yellow, sourceId: 'testbook1-testpage1-uuid' };
4047

4148
describe('Highlights', () => {
@@ -51,7 +58,7 @@ describe('Highlights', () => {
5158
dispatch = jest.spyOn(store, 'dispatch');
5259

5360
store.dispatch(receiveBook(book));
54-
store.dispatch(receivePage({...page, references: []}));
61+
store.dispatch(receivePage({ ...page, references: [] }));
5562

5663
services = {
5764
...createTestServices(),
@@ -71,10 +78,10 @@ describe('Highlights', () => {
7178
const location = getHighlightLocationFilterForPage(locationFilters, pageInChapter);
7279
expect(location).toBeDefined();
7380

74-
store.dispatch(setSummaryFilters({locationIds: [location!.id, pageId]}));
81+
store.dispatch(setSummaryFilters({ locationIds: [location!.id, pageId] }));
7582
store.dispatch(receiveHighlightsTotalCounts({
76-
[pageId]: {[HighlightColorEnum.Green]: 5},
77-
[location!.id]: {[HighlightColorEnum.Green]: 2},
83+
[pageId]: { [HighlightColorEnum.Green]: 5 },
84+
[location!.id]: { [HighlightColorEnum.Green]: 2 },
7885
}, new Map()));
7986

8087
const summaryHighlights = {
@@ -86,10 +93,10 @@ describe('Highlights', () => {
8693
},
8794
} as SummaryHighlights;
8895

89-
store.dispatch(receiveSummaryHighlights(summaryHighlights, {pagination: null}));
96+
store.dispatch(receiveSummaryHighlights(summaryHighlights, { pagination: null }));
9097

9198
const component = renderer.create(<TestContainer services={services} store={store}>
92-
<Highlights/>
99+
<Highlights />
93100
</TestContainer>);
94101

95102
const sections = component.root.findAllByType(SectionHighlights);
@@ -122,8 +129,8 @@ describe('Highlights', () => {
122129
expect(location).toBeDefined();
123130

124131
store.dispatch(receiveHighlightsTotalCounts({
125-
[pageId]: {[HighlightColorEnum.Green]: 5},
126-
[location!.id]: {[HighlightColorEnum.Green]: 2},
132+
[pageId]: { [HighlightColorEnum.Green]: 5 },
133+
[location!.id]: { [HighlightColorEnum.Green]: 2 },
127134
}, new Map()));
128135

129136
const summaryHighlights = {
@@ -136,12 +143,12 @@ describe('Highlights', () => {
136143
} as SummaryHighlights;
137144

138145
renderer.act(() => {
139-
store.dispatch(setSummaryFilters({locationIds: [location!.id, pageId]}));
140-
store.dispatch(receiveSummaryHighlights(summaryHighlights, {pagination: null}));
146+
store.dispatch(setSummaryFilters({ locationIds: [location!.id, pageId] }));
147+
store.dispatch(receiveSummaryHighlights(summaryHighlights, { pagination: null }));
141148
});
142149

143150
const component = renderer.create(<TestContainer services={services} store={store}>
144-
<Highlights/>
151+
<Highlights />
145152
</TestContainer>);
146153

147154
const sections = component.root.findAllByType(SectionHighlights);
@@ -150,7 +157,7 @@ describe('Highlights', () => {
150157
expect(component.root.findAllByType(LoaderWrapper).length).toEqual(0);
151158

152159
renderer.act(() => {
153-
store.dispatch(setSummaryFilters({locationIds: [location!.id, pageId]}));
160+
store.dispatch(setSummaryFilters({ locationIds: [location!.id, pageId] }));
154161
});
155162

156163
const isLoading = component.root.findByType(LoaderWrapper);
@@ -159,14 +166,14 @@ describe('Highlights', () => {
159166

160167
it('show no highlights tip when there are no highlights for selected filters', () => {
161168
store.dispatch(receiveHighlightsTotalCounts({
162-
pageId: {[HighlightColorEnum.Green]: 5},
163-
pageId2: {[HighlightColorEnum.Green]: 2},
169+
pageId: { [HighlightColorEnum.Green]: 5 },
170+
pageId2: { [HighlightColorEnum.Green]: 2 },
164171
}, new Map()));
165-
store.dispatch(setSummaryFilters({locationIds: ['not-in-book']}));
166-
store.dispatch(receiveSummaryHighlights({}, {pagination: null}));
172+
store.dispatch(setSummaryFilters({ locationIds: ['not-in-book'] }));
173+
store.dispatch(receiveSummaryHighlights({}, { pagination: null }));
167174

168175
const component = renderer.create(<TestContainer services={services} store={store}>
169-
<Highlights/>
176+
<Highlights />
170177
</TestContainer>);
171178

172179
// i'm not sure why this type is wrong
@@ -176,7 +183,7 @@ describe('Highlights', () => {
176183

177184
it('show add highlight message when there are no highlights in specific book', () => {
178185
const component = renderer.create(<TestContainer services={services} store={store}>
179-
<Highlights/>
186+
<Highlights />
180187
</TestContainer>);
181188

182189
expect(component.root.findByProps({ id: 'i18n:toolbar:highlights:popup:body:add-highlight' }))
@@ -190,10 +197,10 @@ describe('Highlights', () => {
190197
const location = getHighlightLocationFilterForPage(locationFilters, pageInChapter);
191198
expect(location).toBeDefined();
192199

193-
store.dispatch(setSummaryFilters({locationIds: [location!.id, pageId]}));
200+
store.dispatch(setSummaryFilters({ locationIds: [location!.id, pageId] }));
194201
store.dispatch(receiveHighlightsTotalCounts({
195-
[pageId]: {[HighlightColorEnum.Green]: 5},
196-
[location!.id]: {[HighlightColorEnum.Green]: 2},
202+
[pageId]: { [HighlightColorEnum.Green]: 5 },
203+
[location!.id]: { [HighlightColorEnum.Green]: 2 },
197204
}, locationFilters));
198205

199206
const summaryHighlights = {
@@ -205,10 +212,10 @@ describe('Highlights', () => {
205212
},
206213
} as SummaryHighlights;
207214

208-
store.dispatch(receiveSummaryHighlights(summaryHighlights, {pagination: null}));
215+
store.dispatch(receiveSummaryHighlights(summaryHighlights, { pagination: null }));
209216

210217
const component = renderer.create(<TestContainer services={services} store={store}>
211-
<Highlights/>
218+
<Highlights />
212219
</TestContainer>);
213220

214221
const editingMenus = component.root.findAllByType(ContextMenu);
@@ -222,10 +229,10 @@ describe('Highlights', () => {
222229
const location = getHighlightLocationFilterForPage(locationFilters, pageInChapter);
223230
expect(location).toBeDefined();
224231

225-
store.dispatch(setSummaryFilters({locationIds: [location!.id, pageId]}));
232+
store.dispatch(setSummaryFilters({ locationIds: [location!.id, pageId] }));
226233
store.dispatch(receiveHighlightsTotalCounts({
227-
[pageId]: {[HighlightColorEnum.Green]: 5},
228-
[location!.id]: {[HighlightColorEnum.Green]: 2},
234+
[pageId]: { [HighlightColorEnum.Green]: 5 },
235+
[location!.id]: { [HighlightColorEnum.Green]: 2 },
229236
}, locationFilters));
230237

231238
const summaryHighlights = {
@@ -237,13 +244,13 @@ describe('Highlights', () => {
237244
},
238245
} as SummaryHighlights;
239246

240-
store.dispatch(receiveSummaryHighlights(summaryHighlights, {pagination: null}));
247+
store.dispatch(receiveSummaryHighlights(summaryHighlights, { pagination: null }));
241248
dispatch.mockClear();
242249

243250
const createNodeMock = () => assertDocument().createElement('div');
244251
const component = renderer.create(<TestContainer services={services} store={store}>
245-
<Highlights/>
246-
</TestContainer>, {createNodeMock});
252+
<Highlights />
253+
</TestContainer>, { createNodeMock });
247254

248255
let [firstAnnotation] = component.root.findAllByType(HighlightAnnotation);
249256
expect(firstAnnotation.props.isEditing).toEqual(false);
@@ -270,7 +277,7 @@ describe('Highlights', () => {
270277
locationFilterId: pageId,
271278
pageId,
272279
preUpdateData: {
273-
highlight: {annotation: hlBlue.annotation, color: hlBlue.color as string as HighlightUpdateColorEnum},
280+
highlight: { annotation: hlBlue.annotation, color: hlBlue.color as string as HighlightUpdateColorEnum },
274281
id: hlBlue.id,
275282
},
276283
}));
@@ -290,7 +297,7 @@ describe('Highlights', () => {
290297

291298
const confirmButton = component.root.findByProps({ 'data-testid': 'delete' });
292299

293-
renderer.act(() => confirmButton.props.onClick({preventDefault: () => null}));
300+
renderer.act(() => confirmButton.props.onClick({ preventDefault: () => null }));
294301
expect(dispatch).toHaveBeenCalled();
295302
expect(firstAnnotation.props.isEditing).toEqual(false);
296303

@@ -348,10 +355,10 @@ describe('Highlights', () => {
348355
const location = getHighlightLocationFilterForPage(locationFilters, pageInChapter);
349356
expect(location).toBeDefined();
350357

351-
store.dispatch(setSummaryFilters({locationIds: [location!.id, pageId]}));
358+
store.dispatch(setSummaryFilters({ locationIds: [location!.id, pageId] }));
352359
store.dispatch(receiveHighlightsTotalCounts({
353-
[pageId]: {[HighlightColorEnum.Green]: 5},
354-
[location!.id]: {[HighlightColorEnum.Green]: 2},
360+
[pageId]: { [HighlightColorEnum.Green]: 5 },
361+
[location!.id]: { [HighlightColorEnum.Green]: 2 },
355362
}, locationFilters));
356363

357364
const summaryHighlights = {
@@ -363,11 +370,11 @@ describe('Highlights', () => {
363370
},
364371
} as SummaryHighlights;
365372

366-
store.dispatch(receiveSummaryHighlights(summaryHighlights, {pagination: null}));
373+
store.dispatch(receiveSummaryHighlights(summaryHighlights, { pagination: null }));
367374
dispatch.mockClear();
368375

369376
const component = renderer.create(<TestContainer services={services} store={store}>
370-
<Highlights/>
377+
<Highlights />
371378
</TestContainer>);
372379

373380
renderer.act(() => {
@@ -384,7 +391,7 @@ describe('Highlights', () => {
384391
locationFilterId: pageId,
385392
pageId,
386393
preUpdateData: {
387-
highlight: {annotation: hlBlue.annotation, color: hlBlue.color as string as HighlightUpdateColorEnum},
394+
highlight: { annotation: hlBlue.annotation, color: hlBlue.color as string as HighlightUpdateColorEnum },
388395
id: hlBlue.id,
389396
},
390397
}));
@@ -397,10 +404,10 @@ describe('Highlights', () => {
397404
const location = getHighlightLocationFilterForPage(locationFilters, pageInChapter);
398405
expect(location).toBeDefined();
399406

400-
store.dispatch(setSummaryFilters({locationIds: [location!.id, pageId]}));
407+
store.dispatch(setSummaryFilters({ locationIds: [location!.id, pageId] }));
401408
store.dispatch(receiveHighlightsTotalCounts({
402-
[pageId]: {[HighlightColorEnum.Green]: 5},
403-
[location!.id]: {[HighlightColorEnum.Green]: 2},
409+
[pageId]: { [HighlightColorEnum.Green]: 5 },
410+
[location!.id]: { [HighlightColorEnum.Green]: 2 },
404411
}, locationFilters));
405412

406413
const summaryHighlights = {
@@ -412,11 +419,11 @@ describe('Highlights', () => {
412419
},
413420
} as SummaryHighlights;
414421

415-
store.dispatch(receiveSummaryHighlights(summaryHighlights, {pagination: null}));
422+
store.dispatch(receiveSummaryHighlights(summaryHighlights, { pagination: null }));
416423
dispatch.mockClear();
417424

418425
const component = renderer.create(<TestContainer services={services} store={store}>
419-
<Highlights/>
426+
<Highlights />
420427
</TestContainer>);
421428

422429
renderer.act(() => {
@@ -435,12 +442,12 @@ describe('Highlights', () => {
435442
dw.props.onCancel();
436443
}
437444

438-
requestDeleteHighlightHook.hookBody({...services, getState: store.getState, dispatch: store.dispatch})(
445+
requestDeleteHighlightHook.hookBody({ ...services, getState: store.getState, dispatch: store.dispatch })(
439446
requestDeleteHighlight(hlBlue as HighlightData, {
440447
locationFilterId: pageId,
441448
pageId,
442449
}
443-
));
450+
));
444451
});
445452

446453
expect(dispatch).toHaveBeenCalledWith(requestDeleteHighlight(hlBlue as HighlightData, {
@@ -451,3 +458,45 @@ describe('Highlights', () => {
451458
expect(component.root.findAllByType(HighlightDeleteWrapper).length).toEqual(0);
452459
});
453460
});
461+
462+
describe('VisuallyHiddenLiveRegion', () => {
463+
464+
const getTextContent = (node: any): string => {
465+
if (typeof node === 'string') return node;
466+
if (Array.isArray(node)) return node.map(getTextContent).join('');
467+
if (node && node.props && node.props.children) return getTextContent(node.props.children);
468+
return '';
469+
};
470+
471+
it('announces the message after a delay when id changes', async() => {
472+
const intl = await createIntl('en');
473+
const component = renderer.create(
474+
<RawIntlProvider value={intl}>
475+
<VisuallyHiddenLiveRegion id='test-id' />
476+
</RawIntlProvider>
477+
);
478+
479+
const liveRegion = component.root.find(
480+
el => el.props['aria-live'] === 'polite'
481+
);
482+
483+
expect(getTextContent(liveRegion)).toBe('');
484+
485+
renderer.act(() => {
486+
jest.advanceTimersByTime(100);
487+
});
488+
489+
expect(getTextContent(liveRegion)).toBe('test-id');
490+
});
491+
492+
it('clears the timer on unmount', async() => {
493+
// @ts-ignore
494+
const intl = await createIntl('en');
495+
const component = renderer.create(
496+
<RawIntlProvider value={intl}>
497+
<VisuallyHiddenLiveRegion id='test-id' />
498+
</RawIntlProvider>
499+
);
500+
component.unmount();
501+
});
502+
});

0 commit comments

Comments
 (0)