Skip to content

Commit

Permalink
[EuiColorPicker] Add isClearable and placeholder options (#3689)
Browse files Browse the repository at this point in the history
* add placeholder and isClearable props

* add tests for placeholder and isClearable props

* add empty state docs

* update placeholder snapshot

* add prop comments

* simplify example; fix typo

* add clearable option to secondary input

* CL
  • Loading branch information
thompsongl authored Jul 2, 2020
1 parent 64b63c5 commit c3732e6
Show file tree
Hide file tree
Showing 7 changed files with 283 additions and 12 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## [`master`](https://github.com/elastic/eui/tree/master)

- Added `paddingSize` prop to `EuiCard` ([#3638](https://github.com/elastic/eui/pull/3638))
- Added `isClearable` and `placeholder` options to `EuiColorPicker` ([#3689](https://github.com/elastic/eui/pull/3689))

## [`26.3.0`](https://github.com/elastic/eui/tree/v26.3.0)

Expand Down
37 changes: 37 additions & 0 deletions src-docs/src/views/color_picker/color_picker_example.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,17 @@ const customBadgeSnippet = `// Be sure to provide relevant accessibility to unma
/>
`;

import Empty from './empty_state';
const emptySource = require('!!raw-loader!./empty_state');
const emptyHtml = renderToHtml(CustomButton);
const emptySnippet = `<EuiColorPicker
onChange={handleChange}
color={chosenColor}
placeholder="Auto"
isClearable={true}
/>
`;

import Modes from './modes';
const modesSource = require('!!raw-loader!./modes');
const modesHtml = renderToHtml(Modes);
Expand Down Expand Up @@ -551,6 +562,32 @@ export const ColorPickerExample = {
snippet: [customButtonSnippet, customBadgeSnippet],
demo: <CustomButton />,
},
{
title: 'Empty state',
source: [
{
type: GuideSectionTypes.JS,
code: emptySource,
},
{
type: GuideSectionTypes.HTML,
code: emptyHtml,
},
],
text: (
<>
<p>
For instances where an &quot;empty&quot; color picker has meaning
other than transparent color value, use the{' '}
<EuiCode>placeholder</EuiCode> prop to provide context. Removing
color selection and returning to the default state can be made
easier by setting <EuiCode>isClearable=true</EuiCode>.
</p>
</>
),
snippet: emptySnippet,
demo: <Empty />,
},
{
title: 'Inline',
source: [
Expand Down
1 change: 1 addition & 0 deletions src-docs/src/views/color_picker/custom_button.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export default () => {
aria-label="Select a new color"
/>
}
isClearable={true}
/>
</EuiFormRow>
<EuiSpacer />
Expand Down
24 changes: 24 additions & 0 deletions src-docs/src/views/color_picker/empty_state.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React, { useMemo } from 'react';

import { EuiColorPicker, EuiFormRow } from '../../../../src/components';

import { useColorPickerState } from '../../../../src/services';

export default () => {
const [color, setColor, errors] = useColorPickerState();
const isInvalid = useMemo(() => color !== '' && !!errors, [color, errors]);

return (
<React.Fragment>
<EuiFormRow label="Pick a color" isInvalid={isInvalid} error={errors}>
<EuiColorPicker
onChange={setColor}
color={color}
isInvalid={isInvalid}
placeholder="Auto"
isClearable={true}
/>
</EuiFormRow>
</React.Fragment>
);
};
139 changes: 139 additions & 0 deletions src/components/color_picker/__snapshots__/color_picker.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,81 @@ exports[`renders EuiColorPicker 1`] = `
</div>
`;

exports[`renders EuiColorPicker with a clearable input 1`] = `
<div
class="euiPopover euiPopover--anchorDownLeft euiPopover--displayBlock euiColorPicker__popoverAnchor"
>
<div
class="euiPopover__anchor"
>
<div
class="euiFormControlLayout"
>
<div
class="euiFormControlLayout__childrenWrapper"
>
<div
style="color:rgb(255,238,221)"
>
<div
class="euiFormControlLayout"
>
<div
class="euiFormControlLayout__childrenWrapper"
>
<input
aria-label="Press the down key to open a popover containing color options"
autocomplete="off"
class="euiFieldText euiColorPicker__input euiFieldText--withIcon"
data-test-subj="colorPickerAnchor"
type="text"
value="#FFEEDD"
/>
<div
class="euiFormControlLayoutIcons"
>
<span
class="euiFormControlLayoutCustomIcon"
>
<div
aria-hidden="true"
class="euiFormControlLayoutCustomIcon__icon"
data-euiicon-type="swatchInput"
/>
</span>
</div>
</div>
</div>
</div>
<div
class="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
>
<button
aria-label="Clear input"
class="euiFormControlLayoutClearButton"
type="button"
>
<div
class="euiFormControlLayoutClearButton__icon"
data-euiicon-type="cross"
/>
</button>
<span
class="euiFormControlLayoutCustomIcon"
>
<div
aria-hidden="true"
class="euiFormControlLayoutCustomIcon__icon"
data-euiicon-type="arrowDown"
/>
</span>
</div>
</div>
</div>
</div>
</div>
`;

exports[`renders EuiColorPicker with a color swatch when color is defined 1`] = `
<div
class="euiPopover euiPopover--anchorDownLeft euiPopover--displayBlock euiColorPicker__popoverAnchor"
Expand Down Expand Up @@ -130,6 +205,70 @@ exports[`renders EuiColorPicker with a color swatch when color is defined 1`] =
</div>
`;

exports[`renders EuiColorPicker with a custom placeholder 1`] = `
<div
class="euiPopover euiPopover--anchorDownLeft euiPopover--displayBlock euiColorPicker__popoverAnchor"
>
<div
class="euiPopover__anchor"
>
<div
class="euiFormControlLayout"
>
<div
class="euiFormControlLayout__childrenWrapper"
>
<div>
<div
class="euiFormControlLayout"
>
<div
class="euiFormControlLayout__childrenWrapper"
>
<input
aria-label="Press the down key to open a popover containing color options"
autocomplete="off"
class="euiFieldText euiColorPicker__input euiFieldText--withIcon"
data-test-subj="colorPickerAnchor"
placeholder="Auto"
type="text"
value=""
/>
<div
class="euiFormControlLayoutIcons"
>
<span
class="euiFormControlLayoutCustomIcon"
>
<div
aria-hidden="true"
class="euiFormControlLayoutCustomIcon__icon"
data-euiicon-type="stopSlash"
/>
</span>
</div>
</div>
</div>
</div>
<div
class="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
>
<span
class="euiFormControlLayoutCustomIcon"
>
<div
aria-hidden="true"
class="euiFormControlLayoutCustomIcon__icon"
data-euiicon-type="arrowDown"
/>
</span>
</div>
</div>
</div>
</div>
</div>
`;

exports[`renders EuiColorPicker with an empty swatch when color is "" 1`] = `
<div
class="euiPopover euiPopover--anchorDownLeft euiPopover--displayBlock euiColorPicker__popoverAnchor"
Expand Down
38 changes: 38 additions & 0 deletions src/components/color_picker/color_picker.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,25 @@ test('renders EuiColorPicker with a color swatch when color is defined', () => {
expect(colorPicker).toMatchSnapshot();
});

test('renders EuiColorPicker with a custom placeholder', () => {
const colorPicker = render(
<EuiColorPicker onChange={onChange} placeholder="Auto" {...requiredProps} />
);
expect(colorPicker).toMatchSnapshot();
});

test('renders EuiColorPicker with a clearable input', () => {
const colorPicker = render(
<EuiColorPicker
onChange={onChange}
color={'#ffeedd'}
isClearable={true}
{...requiredProps}
/>
);
expect(colorPicker).toMatchSnapshot();
});

test('popover color selector is not shown by default', () => {
const colorPicker = mount(
<EuiColorPicker onChange={onChange} color="#ffeedd" {...requiredProps} />
Expand Down Expand Up @@ -282,6 +301,25 @@ test('Setting a new alpha value calls onChange', () => {
});
});

test('Clicking the "clear" button calls onChange', () => {
const colorPicker = mount(
<EuiColorPicker
onChange={onChange}
color="#ffeedd"
isClearable={true}
{...requiredProps}
/>
);

colorPicker.find('.euiFormControlLayoutClearButton').simulate('click');
expect(onChange).toBeCalled();
expect(onChange).toBeCalledWith('', {
hex: '',
isValid: false,
rgba: [NaN, NaN, NaN, 1],
});
});

test('default mode does renders child components', () => {
const colorPicker = mount(
<EuiColorPicker onChange={onChange} color="#ffeedd" {...requiredProps} />
Expand Down
55 changes: 43 additions & 12 deletions src/components/color_picker/color_picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,14 @@ export interface EuiColorPickerProps
* Placement option for a secondary color value input.
*/
secondaryInputDisplay?: 'top' | 'bottom' | 'none';
/**
* Add a button to the primary input to clear its value.
*/
isClearable?: boolean;
/**
* Text to replace the default 'Transparent' placeholder for unset color values.
*/
placeholder?: string;
}

function isKeyboardEvent(
Expand Down Expand Up @@ -207,6 +215,8 @@ export const EuiColorPicker: FunctionComponent<EuiColorPickerProps> = ({
showAlpha = false,
format,
secondaryInputDisplay = 'none',
isClearable = false,
placeholder,
}) => {
const preferredFormat = useMemo(() => {
if (format) return format;
Expand Down Expand Up @@ -361,6 +371,13 @@ export const EuiColorPicker: FunctionComponent<EuiColorPickerProps> = ({
}
};

const handleClearInput = () => {
handleOnChange('');
if (secondaryInputDisplay === 'none' && isColorSelectorShown) {
closeColorSelector();
}
};

const updateWithHsv = (hsv: ColorSpaces['hsv']) => {
const color = chroma.hsv(...hsv).alpha(alphaChannel);
let formatted;
Expand Down Expand Up @@ -436,18 +453,27 @@ export const EuiColorPicker: FunctionComponent<EuiColorPickerProps> = ({
display="rowCompressed"
isInvalid={isInvalid}
error={isInvalid ? colorErrorMessage : null}>
<EuiFieldText
compressed={true}
value={color ? color.toUpperCase() : HEX_FALLBACK}
placeholder={!color ? transparent : undefined}
onChange={handleColorInput}
isInvalid={isInvalid}
disabled={disabled}
<EuiFormControlLayout
clear={
isClearable && color && !readOnly && !disabled
? { onClick: handleClearInput }
: undefined
}
readOnly={readOnly}
aria-label={colorLabel}
autoComplete="off"
data-test-subj={`${secondaryInputDisplay}ColorPickerInput`}
/>
compressed={compressed}>
<EuiFieldText
compressed={true}
value={color ? color.toUpperCase() : HEX_FALLBACK}
placeholder={!color ? placeholder || transparent : undefined}
onChange={handleColorInput}
isInvalid={isInvalid}
disabled={disabled}
readOnly={readOnly}
aria-label={colorLabel}
autoComplete="off"
data-test-subj={`${secondaryInputDisplay}ColorPickerInput`}
/>
</EuiFormControlLayout>
</EuiFormRow>
)}
</EuiI18n>
Expand Down Expand Up @@ -557,6 +583,11 @@ export const EuiColorPicker: FunctionComponent<EuiColorPickerProps> = ({
}
: undefined
}
clear={
isClearable && color && !readOnly && !disabled
? { onClick: handleClearInput }
: undefined
}
readOnly={readOnly}
fullWidth={fullWidth}
compressed={compressed}
Expand Down Expand Up @@ -585,7 +616,7 @@ export const EuiColorPicker: FunctionComponent<EuiColorPickerProps> = ({
onClick={handleInputActivity}
onKeyDown={handleInputActivity}
value={color ? color.toUpperCase() : HEX_FALLBACK}
placeholder={!color ? transparent : undefined}
placeholder={!color ? placeholder || transparent : undefined}
id={id}
onChange={handleColorInput}
icon={chromaColor ? 'swatchInput' : 'stopSlash'}
Expand Down

0 comments on commit c3732e6

Please sign in to comment.