From 0a2b9909a24be0b52e654b092e0692bd7587d25c Mon Sep 17 00:00:00 2001 From: Jackie Chan Date: Thu, 4 Nov 2021 15:29:35 -0700 Subject: [PATCH] fix(preview): Close color palette when tabbed out of the palette focus (#1435) * fix(preview): Close color palette when tabbed out of the palette focus * fix(preview): Use the blur handler for both the toggle and palette * fix(preview): fix tests * fix(preview): fix test and remove useAttention usage --- .../color-picker/ColorPickerControl.tsx | 18 ++++++++---- .../color-picker/ColorPickerPalette.tsx | 4 ++- .../__tests__/ColorPickerControl-test.tsx | 28 +++++++++++++++++++ .../__tests__/ColorPickerPalette-test.tsx | 2 +- 4 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/lib/viewers/controls/color-picker/ColorPickerControl.tsx b/src/lib/viewers/controls/color-picker/ColorPickerControl.tsx index 80d791472..7ab5a63c1 100644 --- a/src/lib/viewers/controls/color-picker/ColorPickerControl.tsx +++ b/src/lib/viewers/controls/color-picker/ColorPickerControl.tsx @@ -2,7 +2,6 @@ import React from 'react'; import classNames from 'classnames'; import { bdlBoxBlue } from 'box-ui-elements/es/styles/variables'; import ColorPickerPalette from './ColorPickerPalette'; -import useAttention from '../hooks/useAttention'; import './ColorPickerControl.scss'; export type Props = { @@ -18,8 +17,8 @@ export default function ColorPickerControl({ ...rest }: Props): JSX.Element | null { const paletteRef = React.useRef(null); + const toggleRef = React.useRef(null); const [isColorPickerToggled, setIsColorPickerToggled] = React.useState(false); - const [isPaletteActive, handlers] = useAttention(); const handleSelect = (color: string): void => { setIsColorPickerToggled(false); @@ -28,10 +27,14 @@ export default function ColorPickerControl({ const handleBlur = ({ relatedTarget }: React.FocusEvent): void => { const { current: paletteEl } = paletteRef; + const { current: toggleEl } = toggleRef; // IE11 does not have relatedTarget but update activeElement before blur const nextTarget = relatedTarget || document.activeElement; + const nextEl = nextTarget ? (nextTarget as Node) : null; + const isNextInPalette = paletteEl && paletteEl.contains(nextEl); + const isNextToggle = toggleEl && toggleEl === nextEl; - if (isPaletteActive || (nextTarget && paletteEl && paletteEl.contains(nextTarget as Node))) { + if (isNextInPalette || isNextToggle) { return; } @@ -53,6 +56,7 @@ export default function ColorPickerControl({ return (
); diff --git a/src/lib/viewers/controls/color-picker/ColorPickerPalette.tsx b/src/lib/viewers/controls/color-picker/ColorPickerPalette.tsx index 4a71e180e..0ae98863d 100644 --- a/src/lib/viewers/controls/color-picker/ColorPickerPalette.tsx +++ b/src/lib/viewers/controls/color-picker/ColorPickerPalette.tsx @@ -3,10 +3,11 @@ import './ColorPickerPalette.scss'; export type Props = { colors: Array; + onBlur: React.FocusEventHandler; onSelect: (color: string) => void; }; -export default function ColorPickerPalette({ colors, onSelect }: Props): JSX.Element { +export default function ColorPickerPalette({ colors, onBlur, onSelect }: Props): JSX.Element { return (
{colors.map(color => { @@ -15,6 +16,7 @@ export default function ColorPickerPalette({ colors, onSelect }: Props): JSX.Ele key={color} className="bp-ColorPickerPalette-button" data-testid="bp-ColorPickerPalette-button" + onBlur={onBlur} onClick={(): void => onSelect(color)} style={{ backgroundColor: color, diff --git a/src/lib/viewers/controls/color-picker/__tests__/ColorPickerControl-test.tsx b/src/lib/viewers/controls/color-picker/__tests__/ColorPickerControl-test.tsx index 813cee76c..51aac2db9 100644 --- a/src/lib/viewers/controls/color-picker/__tests__/ColorPickerControl-test.tsx +++ b/src/lib/viewers/controls/color-picker/__tests__/ColorPickerControl-test.tsx @@ -76,6 +76,34 @@ describe('ColorPickerControl', () => { expect(getColorPickerPalette(wrapper).hasClass('bp-is-open')).toBe(true); }); + test('should close the palette when focus is outside palette and button', () => { + const wrapper = getWrapper(); + const toggleButton = getToggleButton(wrapper); + const colorPaletteChild = getColorPickerPalette(wrapper).getDOMNode().firstChild; + const divEl = document.createElement('div'); + + toggleButton.simulate('click'); + expect(getColorPickerPalette(wrapper).hasClass('bp-is-open')).toBe(true); + + toggleButton.simulate('blur', { + relatedTarget: colorPaletteChild, + }); + + expect(getColorPickerPalette(wrapper).hasClass('bp-is-open')).toBe(true); + + toggleButton.simulate('blur', { + relatedTarget: toggleButton.getDOMNode(), + }); + + expect(getColorPickerPalette(wrapper).hasClass('bp-is-open')).toBe(true); + + toggleButton.simulate('blur', { + relatedTarget: divEl, + }); + + expect(getColorPickerPalette(wrapper).hasClass('bp-is-open')).toBe(false); + }); + test('should call focus and stop propagation when mouse down', () => { const mockEvent = { currentTarget: { diff --git a/src/lib/viewers/controls/color-picker/__tests__/ColorPickerPalette-test.tsx b/src/lib/viewers/controls/color-picker/__tests__/ColorPickerPalette-test.tsx index 23ad73729..aaad226da 100644 --- a/src/lib/viewers/controls/color-picker/__tests__/ColorPickerPalette-test.tsx +++ b/src/lib/viewers/controls/color-picker/__tests__/ColorPickerPalette-test.tsx @@ -7,7 +7,7 @@ describe('ColorPickerPalette', () => { const colors = [defaultColor]; const getWrapper = (props = {}): ShallowWrapper => - shallow(); + shallow(); const getButton = (wrapper: ShallowWrapper): ShallowWrapper => wrapper.find('[data-testid="bp-ColorPickerPalette-button"]');