diff --git a/src/app/content/highlights/components/EditCard.spec.tsx b/src/app/content/highlights/components/EditCard.spec.tsx
index bde834dfa5..4726a95479 100644
--- a/src/app/content/highlights/components/EditCard.spec.tsx
+++ b/src/app/content/highlights/components/EditCard.spec.tsx
@@ -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) =>
);
jest.mock('./Note', () => (props: any) => );
@@ -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(
+
+ );
+
+ document?.getElementById(MAIN_CONTENT_ID)?.focus();
+ document?.querySelector('a')?.focus();
+ document?.getElementById(MAIN_CONTENT_ID)?.focus();
+ expect(editCardProps.onBlur).toHaveBeenCalledTimes(3);
+ 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(
+
+ );
+
+ 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');
diff --git a/src/app/content/highlights/components/EditCard.tsx b/src/app/content/highlights/components/EditCard.tsx
index 043959d374..b78d21ed25 100644
--- a/src/app/content/highlights/components/EditCard.tsx
+++ b/src/app/content/highlights/components/EditCard.tsx
@@ -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';
@@ -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;
@@ -134,24 +135,45 @@ function ActiveEditCard({
const resetAnnotation = React.useCallback(() => {
setPendingAnnotation(defaultAnnotation);
}, [defaultAnnotation]);
- const [editingAnnotation, setEditing] = React.useState(
- !!props.data && !!props.data.annotation
+ const [editingAnnotation, setEditing] = React.useState(
+ Boolean(props?.data?.annotation)
);
const [confirmingDelete, setConfirmingDelete] = React.useState(
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 && 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,
});