Skip to content

Commit

Permalink
Merge branch 'main' into focus-on-first-item-in-MH-dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
staxly[bot] authored Sep 17, 2024
2 parents 82f2d37 + bb00e9b commit 345cbbc
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 6 deletions.
61 changes: 61 additions & 0 deletions src/app/content/highlights/components/EditCard.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import ColorPicker from './ColorPicker';
import EditCard, { EditCardProps } from './EditCard';
import Note from './Note';
import * as onClickOutsideModule from './utils/onClickOutside';
import { MAIN_CONTENT_ID } from '../../../context/constants';
import { renderToDom } from '../../../../test/reactutils';

jest.mock('./ColorPicker', () => (props: any) => <div mock-color-picker {...props} />);
jest.mock('./Note', () => (props: any) => <div mock-note {...props} ref={props.textareaRef} />);
Expand Down Expand Up @@ -576,6 +578,65 @@ describe('EditCard', () => {
mockSpyUser.mockClear();
});

it('blurs and removes selections when navigating to different elements', () => {
const mockSpyUser = jest.spyOn(selectAuth, 'user')
.mockReturnValue(formatUser(testAccountsUser));
const onHeightChange = jest.fn();

renderToDom(
<div id={MAIN_CONTENT_ID} tabIndex={-1}>
<TestContainer services={services} store={store}>
<a href='#foo'>text</a>
<EditCard
{...{...editCardProps, hasUnsavedHighlight: false}}
onHeightChange={onHeightChange}
isActive={true}
/>
</TestContainer>
</div>
);

document?.getElementById(MAIN_CONTENT_ID)?.focus();
document?.querySelector('a')?.focus();
document?.getElementById(MAIN_CONTENT_ID)?.focus();
expect(editCardProps.onBlur).toHaveBeenCalledTimes(1);
mockSpyUser.mockClear();
jest.resetAllMocks();
});

it('doesn\'t blur when there is data (existing highlight)', () => {
const mockSpyUser = jest.spyOn(selectAuth, 'user')
.mockReturnValue(formatUser(testAccountsUser));
const onHeightChange = jest.fn();
const data = {
color: highlightStyles[0].label,
...highlightData,
};

renderToDom(
<div id={MAIN_CONTENT_ID} tabIndex={-1}>
<TestContainer services={services} store={store}>
<a href='#foo'>text</a>
<EditCard
{...{
...editCardProps,
hasUnsavedHighlight: false,
}}
data={data}
onHeightChange={onHeightChange}
isActive={true}
/>
</TestContainer>
</div>
);

document?.getElementById(MAIN_CONTENT_ID)?.focus();
document?.querySelector('a')?.focus();
document?.getElementById(MAIN_CONTENT_ID)?.focus();
expect(editCardProps.onBlur).not.toHaveBeenCalled();
mockSpyUser.mockClear();
});

it('doesn\'t blur when clicking outside and editing', () => {
highlight.getStyle.mockReturnValue('red');

Expand Down
34 changes: 28 additions & 6 deletions src/app/content/highlights/components/EditCard.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Highlight } from '@openstax/highlighter';
import { HighlightColorEnum } from '@openstax/highlighter/dist/api';
import { HTMLElement, HTMLTextAreaElement } from '@openstax/types/lib.dom';
import { HTMLElement, HTMLTextAreaElement, FocusEvent } from '@openstax/types/lib.dom';
import React from 'react';
import { FormattedMessage, useIntl } from 'react-intl';
import { useDispatch, useSelector } from 'react-redux';
Expand Down Expand Up @@ -29,6 +29,7 @@ import {
useOnClickOutside
} from './utils/onClickOutside';
import scrollHighlightIntoView from './utils/scrollHighlightIntoView';
import { MAIN_CONTENT_ID } from '../../../context/constants';

export interface EditCardProps {
isActive: boolean;
Expand Down Expand Up @@ -134,24 +135,45 @@ function ActiveEditCard({
const resetAnnotation = React.useCallback(() => {
setPendingAnnotation(defaultAnnotation);
}, [defaultAnnotation]);
const [editingAnnotation, setEditing] = React.useState<boolean>(
!!props.data && !!props.data.annotation
const [editingAnnotation, setEditing] = React.useState(
Boolean(props?.data?.annotation)
);
const [confirmingDelete, setConfirmingDelete] = React.useState<boolean>(
false
);

const onBlur = props.onBlur;
const {onBlur, hasUnsavedHighlight} = props;
const blurIfNotEditing = React.useCallback(() => {
if (!props.hasUnsavedHighlight && !editingAnnotation) {
if (!hasUnsavedHighlight && !editingAnnotation) {
onBlur();
}
}, [props.hasUnsavedHighlight, editingAnnotation, onBlur]);
}, [onBlur, hasUnsavedHighlight, editingAnnotation]);

const deselectRange = React.useCallback(
({target}: FocusEvent) => {
const targetAsNode = target as HTMLElement;
const mainEl = document?.getElementById(MAIN_CONTENT_ID);

if (!props.data?.color && targetAsNode !== mainEl && mainEl?.contains(targetAsNode)) {
blurIfNotEditing();
document?.getSelection()?.removeAllRanges();
}
},
[blurIfNotEditing, props.data?.color]
);

const elements = [element, ...props.highlight.elements].filter(
isElementForOnClickOutside
);

React.useEffect(
() => {
document?.addEventListener('focusin', deselectRange);
return () => document?.removeEventListener('focusin', deselectRange);
},
[deselectRange]
);

useOnClickOutside(elements, props.isActive, blurIfNotEditing, {
capture: true,
});
Expand Down

0 comments on commit 345cbbc

Please sign in to comment.