ColorPickerInput: Fixed color picker disappearing on input blur (#102241)

* ColorPickerInput: Fixed color picker disappearing on input blur

* Updates per feedback around targeting method

* Updated per feedback - testid vs id
This commit is contained in:
Collin Fingar
2025-03-18 11:56:32 -04:00
committed by GitHub
parent 20f9902ed5
commit 23ec5cfcbf
2 changed files with 28 additions and 2 deletions
@@ -12,6 +12,24 @@ describe('ColorPickerInput', () => {
expect(screen.getByTestId('color-popover')).toBeInTheDocument();
});
it('should hide color popover on blur', async () => {
render(<ColorPickerInput onChange={noop} />);
expect(screen.queryByTestId('color-popover')).not.toBeInTheDocument();
await userEvent.click(screen.getByRole('textbox'));
expect(screen.getByTestId('color-popover')).toBeInTheDocument();
await userEvent.click(document.body);
expect(screen.queryByTestId('color-popover')).not.toBeInTheDocument();
});
it('should not hide color popover on blur if clicked inside the color picker', async () => {
render(<ColorPickerInput onChange={noop} />);
expect(screen.queryByTestId('color-popover')).not.toBeInTheDocument();
await userEvent.click(screen.getByRole('textbox'));
expect(screen.getByTestId('color-popover')).toBeInTheDocument();
await userEvent.click(screen.getAllByRole('slider')[0]);
expect(screen.queryByTestId('color-popover')).toBeInTheDocument();
});
it('should pass correct color to onChange callback', async () => {
const mockOnChange = jest.fn();
render(<ColorPickerInput onChange={mockOnChange} />);
@@ -1,5 +1,5 @@
import { css, cx } from '@emotion/css';
import { useState, forwardRef } from 'react';
import { useState, forwardRef, FocusEvent } from 'react';
import { RgbaStringColorPicker } from 'react-colorful';
import { useThrottleFn } from 'react-use';
@@ -48,6 +48,14 @@ export const ColorPickerInput = forwardRef<HTMLInputElement, ColorPickerInputPro
[currentColor]
);
const handleBlur = (evt: FocusEvent<HTMLInputElement>) => {
// Unless the user clicked inside the color picker, close it on blur
const isClickInPopover = document.querySelector('[data-testid="color-popover"]')?.contains(evt.relatedTarget);
if (!isClickInPopover) {
setIsOpen(false);
}
};
return (
<ClickOutsideWrapper onClick={() => setIsOpen(false)}>
<div className={styles.wrapper}>
@@ -66,7 +74,7 @@ export const ColorPickerInput = forwardRef<HTMLInputElement, ColorPickerInputPro
onChange={setColor}
buttonAriaLabel="Open color picker"
onClick={() => setIsOpen(true)}
onBlur={() => setIsOpen(false)}
onBlur={(e) => handleBlur(e)}
ref={ref}
isClearable
/>