diff --git a/src/lib/viewers/controls/color-picker/ColorPickerControl.tsx b/src/lib/viewers/controls/color-picker/ColorPickerControl.tsx index 5c4b7eac3..702c9e4b0 100644 --- a/src/lib/viewers/controls/color-picker/ColorPickerControl.tsx +++ b/src/lib/viewers/controls/color-picker/ColorPickerControl.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React from 'react'; import classNames from 'classnames'; import { bdlBoxBlue } from 'box-ui-elements/es/styles/variables'; import ColorPickerPalette from './ColorPickerPalette'; @@ -17,7 +17,8 @@ export default function ColorPickerControl({ onColorSelect, ...rest }: Props): JSX.Element | null { - const [isColorPickerToggled, setIsColorPickerToggled] = useState(false); + const paletteRef = React.useRef(null); + const [isColorPickerToggled, setIsColorPickerToggled] = React.useState(false); const [isPaletteActive, handlers] = useAttention(); const handleSelect = (color: string): void => { @@ -25,10 +26,15 @@ export default function ColorPickerControl({ onColorSelect(color); }; - const handleBlur = (): void => { - if (isPaletteActive) { + const handleBlur = ({ relatedTarget }: React.FocusEvent): void => { + const { current: paletteEl } = paletteRef; + // IE11 does not have relatedTarget but update activeElement before blur + const nextTarget = relatedTarget || document.activeElement; + + if (isPaletteActive || (nextTarget && paletteEl && paletteEl.contains(nextTarget as Node))) { return; } + setIsColorPickerToggled(false); }; @@ -49,6 +55,7 @@ export default function ColorPickerControl({
{ const defaultColor = '#fff'; - const getWrapper = (props = {}): ShallowWrapper => - shallow(); + const getWrapper = (props = {}): ReactWrapper => + mount(); - const getColorPickerPalette = (wrapper: ShallowWrapper): ShallowWrapper => + const getColorPickerPalette = (wrapper: ReactWrapper): ReactWrapper => wrapper.find('[data-testid="bp-ColorPickerControl-palette"]'); - const getToggleButton = (wrapper: ShallowWrapper): ShallowWrapper => + const getToggleButton = (wrapper: ReactWrapper): ReactWrapper => wrapper.find('[data-testid="bp-ColorPickerControl-toggle"]'); describe('render', () => { @@ -36,7 +37,7 @@ describe('ColorPickerControl', () => { toggleButton.simulate('click'); expect(getToggleButton(wrapper).hasClass('bp-is-active')).toBe(true); - toggleButton.simulate('blur'); + toggleButton.simulate('blur', {}); expect(getToggleButton(wrapper).hasClass('bp-is-active')).toBe(false); }); @@ -47,7 +48,7 @@ describe('ColorPickerControl', () => { toggleButton.simulate('click'); expect(getColorPickerPalette(wrapper).hasClass('bp-is-open')).toBe(true); - toggleButton.simulate('blur'); + toggleButton.simulate('blur', {}); expect(getColorPickerPalette(wrapper).hasClass('bp-is-open')).toBe(false); }); @@ -60,6 +61,20 @@ describe('ColorPickerControl', () => { getColorPickerPalette(wrapper).simulate('focus'); expect(getColorPickerPalette(wrapper).hasClass('bp-is-open')).toBe(true); }); + + test('should not close the palette when next focus is inside palette', () => { + const wrapper = getWrapper(); + const toggleButton = getToggleButton(wrapper); + + toggleButton.simulate('click'); + expect(getColorPickerPalette(wrapper).hasClass('bp-is-open')).toBe(true); + + toggleButton.simulate('blur', { + relatedTarget: getColorPickerPalette(wrapper).getDOMNode(), + }); + + expect(getColorPickerPalette(wrapper).hasClass('bp-is-open')).toBe(true); + }); }); describe('handleSelect', () => { @@ -68,7 +83,11 @@ describe('ColorPickerControl', () => { const wrapper = getWrapper({ onColorSelect }); getToggleButton(wrapper).simulate('click'); - wrapper.find('[data-testid="bp-ColorPickerPalette"]').simulate('select', defaultColor); + + act(() => { + (wrapper.find('[data-testid="bp-ColorPickerPalette"]').prop('onSelect') as Function)(defaultColor); + }); + wrapper.update(); expect(getColorPickerPalette(wrapper).hasClass('bp-is-open')).toBe(false); expect(onColorSelect).toHaveBeenCalledWith(defaultColor);