From 409edb9d44331d2f07c4873228e278cae215cd35 Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Tue, 21 Apr 2020 15:25:37 -0500 Subject: [PATCH] [EuiColorPicker] Add optional color value input (#3336) * inputDisplay prop for popover panel input * tests * docs * reinstate validation * clean up * use secondaryInputDispay name * Update src-docs/src/views/color_picker/color_picker_example.js Co-Authored-By: Caroline Horn <549577+cchaos@users.noreply.github.com> * reinstate readonly input * readonly and input spacing cleanup * CL Co-authored-by: Caroline Horn <549577+cchaos@users.noreply.github.com> --- CHANGELOG.md | 1 + .../color_picker/color_picker_example.js | 23 +++-- .../src/views/color_picker/custom_button.js | 2 + .../color_picker/color_picker.test.tsx | 36 ++++++- src/components/color_picker/color_picker.tsx | 82 +++++++++++++--- .../color_stops/color_stop_thumb.tsx | 97 +++++++------------ 6 files changed, 160 insertions(+), 81 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2486acf7cad..73e57aa9831 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Added `popoverPlacement` prop in `EuiDatePicker` ([#3359](https://github.com/elastic/eui/pull/3359)) - Extended `EuiDatePicker`'s `startDate` and `endDate` types to accept `null` values for better interoperability ([#3343](https://github.com/elastic/eui/pull/3343)) - Added `EuiCommentList` component ([#3344](https://github.com/elastic/eui/pull/3344)) +- Added secondary color value input element to `EuiColorPicker` ([#3336](https://github.com/elastic/eui/pull/3336)) **Bug Fixes** diff --git a/src-docs/src/views/color_picker/color_picker_example.js b/src-docs/src/views/color_picker/color_picker_example.js index 1f90a21358c..5aaf9300bd8 100644 --- a/src-docs/src/views/color_picker/color_picker_example.js +++ b/src-docs/src/views/color_picker/color_picker_example.js @@ -122,6 +122,7 @@ const customButtonHtml = renderToHtml(CustomButton); const customButtonSnippet = ` - Available only in EuiColorPicker. You can optionally - use a custom button as the trigger for selection using the{' '} - button prop. Please remember to add accessibility - to this component, using proper button markup and aria labeling. -

+ <> +

+ Available only in EuiColorPicker. You can + optionally use a custom button as the trigger for selection using + the button prop. Please remember to add + accessibility to this component, using proper button markup and aria + labeling. +

+

+ Additionally, use the secondaryInputDisplay prop + to show a secondary or alternative color value input. Options + include top and bottom{' '} + placement. +

+ ), snippet: [customButtonSnippet, customBadgeSnippet], demo: , diff --git a/src-docs/src/views/color_picker/custom_button.js b/src-docs/src/views/color_picker/custom_button.js index e8c227cd011..a18b6235f31 100644 --- a/src-docs/src/views/color_picker/custom_button.js +++ b/src-docs/src/views/color_picker/custom_button.js @@ -23,6 +23,7 @@ export const CustomButton = () => { { onChange={handleColorChange} color={color} isInvalid={!!errors} + secondaryInputDisplay="bottom" button={ { }); }); -test('default mode does redners child components', () => { +test('default mode does renders child components', () => { const colorPicker = mount( ); @@ -334,3 +334,37 @@ test('picker mode does not render swatches', () => { const swatches = colorPicker.find('button.euiColorPicker__swatchSelect'); expect(swatches.length).toBe(0); }); + +test('secondaryInputDisplay `top` has a popover panel input', () => { + const colorPicker = mount( + + ); + + findTestSubject(colorPicker, 'colorPickerAnchor').simulate('click'); + const inputTop = findTestSubject(colorPicker, 'topColorPickerInput'); + const inputBottom = findTestSubject(colorPicker, 'bottomColorPickerInput'); + expect(inputTop.length).toBe(1); + expect(inputBottom.length).toBe(0); +}); + +test('secondaryInputDisplay `bottom` has a popover panel input', () => { + const colorPicker = mount( + + ); + + findTestSubject(colorPicker, 'colorPickerAnchor').simulate('click'); + const inputTop = findTestSubject(colorPicker, 'topColorPickerInput'); + const inputBottom = findTestSubject(colorPicker, 'bottomColorPickerInput'); + expect(inputTop.length).toBe(0); + expect(inputBottom.length).toBe(1); +}); diff --git a/src/components/color_picker/color_picker.tsx b/src/components/color_picker/color_picker.tsx index 32982a47cdc..3ec4e6190d9 100644 --- a/src/components/color_picker/color_picker.tsx +++ b/src/components/color_picker/color_picker.tsx @@ -40,6 +40,7 @@ import { EuiFieldText, EuiFormControlLayout, EuiFormControlLayoutProps, + EuiFormRow, EuiRange, } from '../form'; import { EuiI18n } from '../i18n'; @@ -59,7 +60,7 @@ import { } from './utils'; type EuiColorPickerDisplay = 'default' | 'inline'; -type EuiColorPickerMode = 'default' | 'swatch' | 'picker'; +type EuiColorPickerMode = 'default' | 'swatch' | 'picker' | 'secondaryInput'; export interface EuiColorPickerOutput { rgba: ColorSpaces['rgba']; @@ -106,7 +107,7 @@ export interface EuiColorPickerProps */ isInvalid?: boolean; /** - * Choose between swatches with gradient picker (default), swatches only, or gradient picker only. + * Choose between swatches with gradient picker (default), swatches only, gradient picker only, or secondary input only. */ mode?: EuiColorPickerMode; /** @@ -140,6 +141,10 @@ export interface EuiColorPickerProps * Default is to display the last format entered by the user */ format?: 'hex' | 'rgba'; + /** + * Placement option for a secondary color value input. + */ + secondaryInputDisplay?: 'top' | 'bottom' | 'none'; } function isKeyboardEvent( @@ -201,6 +206,7 @@ export const EuiColorPicker: FunctionComponent = ({ append, showAlpha = false, format, + secondaryInputDisplay = 'none', }) => { const preferredFormat = useMemo(() => { if (format) return format; @@ -248,7 +254,8 @@ export const EuiColorPicker: FunctionComponent = ({ const classes = classNames('euiColorPicker', className); const popoverClass = 'euiColorPicker__popoverAnchor'; const panelClasses = classNames('euiColorPicker__popoverPanel', { - 'euiColorPicker__popoverPanel--pickerOnly': mode === 'picker', + 'euiColorPicker__popoverPanel--pickerOnly': + mode === 'picker' && secondaryInputDisplay !== 'bottom', 'euiColorPicker__popoverPanel--customButton': button, }); const swatchClass = 'euiColorPicker__swatchSelect'; @@ -414,9 +421,49 @@ export const EuiColorPicker: FunctionComponent = ({ } }; + const inlineInput = secondaryInputDisplay !== 'none' && ( + + {([colorLabel, colorErrorMessage, transparent]: string[]) => ( + + + + )} + + ); + + const showSecondaryInputOnly = mode === 'secondaryInput'; + const showPicker = mode !== 'swatch' && !showSecondaryInputOnly; + const showSwatches = mode !== 'picker' && !showSecondaryInputOnly; + const composite = ( - - {mode !== 'swatch' && ( + <> + {secondaryInputDisplay === 'top' && ( + <> + {inlineInput} + + + )} + {showPicker && (
= ({ />
)} - {mode !== 'picker' && ( + {showSwatches && ( {swatches.map((swatch, index) => ( @@ -456,8 +503,14 @@ export const EuiColorPicker: FunctionComponent = ({ ))} )} + {secondaryInputDisplay === 'bottom' && ( + <> + {mode !== 'picker' && } + {inlineInput} + + )} {showAlpha && ( - + <> = ({ /> )} - + )} -
+ ); let buttonOrInput; @@ -514,18 +567,23 @@ export const EuiColorPicker: FunctionComponent = ({ color: colorStyle, }}> - {([openLabel, closeLabel]: string[]) => ( + {([openLabel, closeLabel, transparent]: string[]) => ( = ({ onChange({ stop, color: value }); }; - const handleColorInputChange = (e: React.ChangeEvent) => - handleColorChange(e.target.value); - const handleStopChange = (value: ColorStop['stop']) => { const willBeInvalid = value > localMax || value < localMin; @@ -353,65 +350,41 @@ export const EuiColorStopThumb: FunctionComponent = ({ )} - - - - {(removeLabel: string) => ( - - )} - - - + {!readOnly && ( + + + + {(removeLabel: string) => ( + + )} + + + + )} - {!readOnly && ( - - - - - )} - {colorPickerMode !== 'swatch' && ( - - - - {([hexLabel, hexErrorMessage]: React.ReactChild[]) => ( - - - - )} - - - )} + {!readOnly && } + );