mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
ColorPickerInput: Enable input clearing (#53587)
* ColorPickerInput: Enable removing selected color * ColorPickerInput: Add tests * ColorInput: Add isClearable prop
This commit is contained in:
parent
d4f382892d
commit
7944f3a692
@ -10,51 +10,60 @@ import { Input, Props as InputProps } from '../Input/Input';
|
||||
|
||||
import { ColorPickerProps } from './ColorPickerPopover';
|
||||
|
||||
interface ColorInputProps extends ColorPickerProps, Omit<InputProps, 'color' | 'onChange'> {}
|
||||
interface ColorInputProps extends ColorPickerProps, Omit<InputProps, 'color' | 'onChange'> {
|
||||
isClearable?: boolean;
|
||||
}
|
||||
|
||||
const ColorInput = forwardRef<HTMLInputElement, ColorInputProps>(({ color, onChange, ...inputProps }, ref) => {
|
||||
const [value, setValue] = useState(color);
|
||||
const [previousColor, setPreviousColor] = useState(color);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const updateColor = useMemo(() => debounce(onChange, 100), []);
|
||||
const ColorInput = forwardRef<HTMLInputElement, ColorInputProps>(
|
||||
({ color, onChange, isClearable = false, ...inputProps }, ref) => {
|
||||
const [value, setValue] = useState(color);
|
||||
const [previousColor, setPreviousColor] = useState(color);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const updateColor = useMemo(() => debounce(onChange, 100), []);
|
||||
|
||||
useEffect(() => {
|
||||
const newColor = tinycolor(color);
|
||||
if (newColor.isValid() && color !== previousColor) {
|
||||
setValue(newColor.toString());
|
||||
setPreviousColor(color);
|
||||
}
|
||||
}, [color, previousColor]);
|
||||
useEffect(() => {
|
||||
const newColor = tinycolor(color);
|
||||
if (newColor.isValid() && color !== previousColor) {
|
||||
setValue(newColor.toString());
|
||||
setPreviousColor(color);
|
||||
}
|
||||
}, [color, previousColor]);
|
||||
|
||||
const onChangeColor = (event: React.SyntheticEvent<HTMLInputElement>) => {
|
||||
const newColor = tinycolor(event.currentTarget.value);
|
||||
const onChangeColor = (event: React.SyntheticEvent<HTMLInputElement>) => {
|
||||
const { value: colorValue } = event.currentTarget;
|
||||
|
||||
setValue(event.currentTarget.value);
|
||||
setValue(colorValue);
|
||||
if (colorValue === '' && isClearable) {
|
||||
updateColor(colorValue);
|
||||
return;
|
||||
}
|
||||
const newColor = tinycolor(colorValue);
|
||||
|
||||
if (newColor.isValid()) {
|
||||
updateColor(newColor.toString());
|
||||
}
|
||||
};
|
||||
if (newColor.isValid()) {
|
||||
updateColor(newColor.toString());
|
||||
}
|
||||
};
|
||||
|
||||
const onBlur = () => {
|
||||
const newColor = tinycolor(value);
|
||||
const onBlur = () => {
|
||||
const newColor = tinycolor(value);
|
||||
|
||||
if (!newColor.isValid()) {
|
||||
setValue(color);
|
||||
}
|
||||
};
|
||||
if (!newColor.isValid()) {
|
||||
setValue(color);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Input
|
||||
{...inputProps}
|
||||
value={value}
|
||||
onChange={onChangeColor}
|
||||
onBlur={onBlur}
|
||||
addonBefore={<ColorPreview color={color} />}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<Input
|
||||
{...inputProps}
|
||||
value={value}
|
||||
onChange={onChangeColor}
|
||||
onBlur={onBlur}
|
||||
addonBefore={<ColorPreview color={color} />}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
ColorInput.displayName = 'ColorInput';
|
||||
|
||||
|
@ -0,0 +1,41 @@
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
|
||||
import { ColorPickerInput } from './ColorPickerInput';
|
||||
|
||||
const noop = () => {};
|
||||
describe('ColorPickerInput', () => {
|
||||
it('should show color popover on focus', async () => {
|
||||
render(<ColorPickerInput onChange={noop} />);
|
||||
expect(screen.queryByTestId('color-popover')).not.toBeInTheDocument();
|
||||
await userEvent.click(screen.getByRole('textbox'));
|
||||
expect(screen.getByTestId('color-popover')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should pass correct color to onChange callback', async () => {
|
||||
const mockOnChange = jest.fn();
|
||||
render(<ColorPickerInput onChange={mockOnChange} />);
|
||||
await userEvent.type(screen.getByRole('textbox'), 'rgb(255,255,255)');
|
||||
await waitFor(() => expect(mockOnChange).toHaveBeenCalledWith('rgb(255, 255, 255)'));
|
||||
});
|
||||
|
||||
it('should not pass invalid color value to onChange callback', async () => {
|
||||
const mockOnChange = jest.fn();
|
||||
render(<ColorPickerInput onChange={mockOnChange} />);
|
||||
await userEvent.type(screen.getByRole('textbox'), 'some text');
|
||||
screen.getByRole('textbox').blur();
|
||||
await waitFor(() => expect(mockOnChange).not.toHaveBeenCalled());
|
||||
expect(screen.getByRole('textbox')).toHaveValue('');
|
||||
});
|
||||
|
||||
it('should be able to reset selected value', async () => {
|
||||
const mockOnChange = jest.fn();
|
||||
render(<ColorPickerInput onChange={mockOnChange} value={'rgb(0,0,0)'} />);
|
||||
// Should show the value in the input
|
||||
expect(screen.getByDisplayValue('rgb(0,0,0)')).toBeInTheDocument();
|
||||
await userEvent.clear(screen.getByRole('textbox'));
|
||||
await waitFor(() => expect(mockOnChange).toHaveBeenCalledWith(''));
|
||||
expect(screen.getByRole('textbox')).toHaveValue('');
|
||||
});
|
||||
});
|
@ -53,13 +53,14 @@ export const ColorPickerInput = forwardRef<HTMLInputElement, ColorPickerInputPro
|
||||
<div className={styles.wrapper}>
|
||||
{isOpen && (
|
||||
<RgbaStringColorPicker
|
||||
data-testid={'color-popover'}
|
||||
color={currentColor}
|
||||
onChange={setColor}
|
||||
className={cx(paletteStyles.root, styles.picker)}
|
||||
/>
|
||||
)}
|
||||
<div onClick={() => setIsOpen(true)}>
|
||||
<ColorInput {...inputProps} theme={theme} color={currentColor} onChange={setColor} ref={ref} />
|
||||
<ColorInput {...inputProps} theme={theme} color={currentColor} onChange={setColor} ref={ref} isClearable />
|
||||
</div>
|
||||
</div>
|
||||
</ClickOutsideWrapper>
|
||||
|
Loading…
Reference in New Issue
Block a user