From 3e9f9efd383810d5fcb883e33915a1238048e090 Mon Sep 17 00:00:00 2001 From: Caroline Horn <549577+cchaos@users.noreply.github.com> Date: Fri, 12 Jul 2019 12:14:55 -0400 Subject: [PATCH 01/20] [Feature branch] Updated form control border color (#2114) * Updated form control border color * Slighly more transparent * change sass var name to $euiFormBorderOpaqueColor --- src/components/form/_mixins.scss | 4 ++-- src/components/form/_variables.scss | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/form/_mixins.scss b/src/components/form/_mixins.scss index b9d85ca47e4..be0bc3ba6ce 100644 --- a/src/components/form/_mixins.scss +++ b/src/components/form/_mixins.scss @@ -122,12 +122,12 @@ background-size: 100% 100%; /* 3 */ @if ($borderOnly) { - box-shadow: inset 0 0 0 1px transparentize($euiColorFullShade, .84); + box-shadow: inset 0 0 0 1px $euiFormBorderColor; } @else { box-shadow: 0 1px 1px -1px transparentize($euiShadowColor, .8), 0 4px 4px -2px transparentize($euiShadowColor, .8), - inset 0 0 0 1px transparentize($euiColorFullShade, .84); + inset 0 0 0 1px $euiFormBorderColor; } } diff --git a/src/components/form/_variables.scss b/src/components/form/_variables.scss index eae29dabd11..e42d4f02023 100644 --- a/src/components/form/_variables.scss +++ b/src/components/form/_variables.scss @@ -16,8 +16,9 @@ $euiSwitchIconHeight: $euiSize !default; // Coloring $euiFormBackgroundColor: tintOrShade($euiColorLightestShade, 60%, 40%) !default; $euiFormBackgroundDisabledColor: darken($euiColorLightestShade, 2%) !default; -$euiFormBorderColor: transparentize($euiColorFullShade, .9) !default; -$euiFormBorderDisabledColor: transparentize($euiColorFullShade, .92) !default; +$euiFormBorderOpaqueColor: shade(desaturate(adjust-hue($euiColorPrimary, 22), 22.95), 26%) !default; +$euiFormBorderColor: transparentize($euiFormBorderOpaqueColor, .9) !default; +$euiFormBorderDisabledColor: transparentize($euiFormBorderOpaqueColor, .9) !default; $euiFormCustomControlDisabledIconColor: shadeOrTint($euiColorMediumShade, 38%, 48.5%) !default; // exact 508c foreground for $euiColorLightShade $euiFormControlDisabledColor: $euiColorMediumShade !default; $euiFormControlBoxShadow: 0 1px 1px -1px transparentize($euiShadowColor, .8), 0 3px 2px -2px transparentize($euiShadowColor, .8); From 6ba098bbd3c3021d9d64aaa47e85705dc6e02bbd Mon Sep 17 00:00:00 2001 From: Caroline Horn <549577+cchaos@users.noreply.github.com> Date: Tue, 23 Jul 2019 10:19:49 -0400 Subject: [PATCH 02/20] [Feature branch] Added EuiFormControlLayoutDelimited component (#2117) As a layout helper component to create date and number ranges * Added Sass var for `$euiFormControlLayoutGroupInputHeight` and compressed version --- .../form_control_layout_range.js | 137 ++++++++++++++++++ .../form_controls/form_controls_example.js | 45 ++++++ .../date_picker/_date_picker_range.scss | 3 +- .../super_date_picker/_mixins.scss | 6 +- src/components/form/_index.scss | 2 - src/components/form/_mixins.scss | 7 +- src/components/form/_variables.scss | 2 +- ...orm_control_layout_delimited.test.tsx.snap | 100 +++++++++++++ .../_form_control_layout.scss | 13 +- .../_form_control_layout_range.scss | 56 +++++++ .../form/form_control_layout/_index.scss | 2 + .../form/form_control_layout/_variables.scss | 8 + .../form_control_layout.tsx | 54 +------ .../form_control_layout_delimited.test.tsx | 52 +++++++ .../form_control_layout_delimited.tsx | 56 +++++++ .../form/form_control_layout/index.ts | 2 + src/components/form/index.js | 5 +- src/components/form/select/_select.scss | 4 +- src/components/index.js | 1 + 19 files changed, 494 insertions(+), 61 deletions(-) create mode 100644 src-docs/src/views/form_controls/form_control_layout_range.js create mode 100644 src/components/form/form_control_layout/__snapshots__/form_control_layout_delimited.test.tsx.snap create mode 100644 src/components/form/form_control_layout/_form_control_layout_range.scss create mode 100644 src/components/form/form_control_layout/_variables.scss create mode 100644 src/components/form/form_control_layout/form_control_layout_delimited.test.tsx create mode 100644 src/components/form/form_control_layout/form_control_layout_delimited.tsx diff --git a/src-docs/src/views/form_controls/form_control_layout_range.js b/src-docs/src/views/form_controls/form_control_layout_range.js new file mode 100644 index 00000000000..a11d465fc05 --- /dev/null +++ b/src-docs/src/views/form_controls/form_control_layout_range.js @@ -0,0 +1,137 @@ +import React, { Fragment } from 'react'; + +import { + EuiFormControlLayoutDelimited, + EuiSpacer, + EuiFormLabel, + EuiIcon, +} from '../../../../src/components'; + +export default () => ( + + + } + endControl={ + + } + /> + + + px} + startControl={ + + } + endControl={ + + } + /> + + + + } + endControl={ + + } + /> + + + {} }} + isLoading + startControl={ + + } + endControl={ + + } + /> + + + + } + endControl={ + + } + /> + + + + } + endControl={ + + } + /> + + + + } + endControl={ + + } + /> + + + + Add} + startControl={ + + } + delimiter="+" + endControl={ + + } + /> + + + + Merge} + startControl={ + + } + delimiter={} + endControl={ + + } + /> + + + + Read only} + startControl={ + + } + endControl={ + + } + /> + +); diff --git a/src-docs/src/views/form_controls/form_controls_example.js b/src-docs/src/views/form_controls/form_controls_example.js index 7fcdb3d04f0..9d186929cd1 100644 --- a/src-docs/src/views/form_controls/form_controls_example.js +++ b/src-docs/src/views/form_controls/form_controls_example.js @@ -18,6 +18,7 @@ import { EuiFieldText, EuiFilePicker, EuiFormControlLayout, + EuiFormControlLayoutDelimited, EuiLink, EuiRadio, EuiRadioGroup, @@ -78,6 +79,10 @@ import FormControlLayout from './form_control_layout'; const formControlLayoutSource = require('!!raw-loader!./form_control_layout'); const formControlLayoutHtml = renderToHtml(FormControlLayout); +import FormControlLayoutRange from './form_control_layout_range'; +const formControlLayoutRangeSource = require('!!raw-loader!./form_control_layout_range'); +const formControlLayoutRangeHtml = renderToHtml(FormControlLayoutRange); + export const FormControlsExample = { title: 'Form controls', sections: [ @@ -351,5 +356,45 @@ export const FormControlsExample = { }, demo: , }, + { + title: 'Form control layout delimited', + source: [ + { + type: GuideSectionTypes.JS, + code: formControlLayoutRangeSource, + }, + { + type: GuideSectionTypes.HTML, + code: formControlLayoutRangeHtml, + }, + ], + text: ( + +

+ Building block only +

+ +

+ Like EuiFormControlLayout,{' '} + EuiFormControlLayoutDelimited is generally used + internally to consistently style form controls. This component + specifically lays out two form controls with center text or icon. +

+

+ It takes all of the same props as{' '} + EuiFormControlLayout except for{' '} + children. Instead it requires both a{' '} + single startControl and a{' '} + single endControl. You can + optionally change the center content to a different string or node + (like an EuiIcon). +

+
+ ), + props: { + EuiFormControlLayoutDelimited, + }, + demo: , + }, ], }; diff --git a/src/components/date_picker/_date_picker_range.scss b/src/components/date_picker/_date_picker_range.scss index 583562cd5a3..cedeb5772b4 100644 --- a/src/components/date_picker/_date_picker_range.scss +++ b/src/components/date_picker/_date_picker_range.scss @@ -1,5 +1,6 @@ @import '../form/variables'; @import '../form/mixins'; +@import '../form/form_control_layout/variables'; /** * 1. Account for inner box-shadow style border @@ -34,7 +35,7 @@ padding: 0; .euiDatePicker { - height: $euiFormControlHeight - 2px; + height: $euiFormControlLayoutGroupInputHeight; } } diff --git a/src/components/date_picker/super_date_picker/_mixins.scss b/src/components/date_picker/super_date_picker/_mixins.scss index 02db2c4fd55..4f5a7a5e23a 100644 --- a/src/components/date_picker/super_date_picker/_mixins.scss +++ b/src/components/date_picker/super_date_picker/_mixins.scss @@ -1,10 +1,12 @@ +@import '../../form/form_control_layout/variables'; + @mixin euiSuperDatePickerText { @include euiFormControlText; display: block; width: 100%; padding: 0 $euiSizeS; - line-height: $euiFormControlHeight - 2px; - height: $euiFormControlHeight - 2px; + line-height: $euiFormControlLayoutGroupInputHeight; + height: $euiFormControlLayoutGroupInputHeight; word-break: break-all; transition: background $euiAnimSpeedFast ease-in; } diff --git a/src/components/form/_index.scss b/src/components/form/_index.scss index 689fdbb54a3..e8f451f6b3f 100644 --- a/src/components/form/_index.scss +++ b/src/components/form/_index.scss @@ -1,5 +1,3 @@ -@import 'form_control_layout/mixins'; - @import 'variables'; @import 'mixins'; diff --git a/src/components/form/_mixins.scss b/src/components/form/_mixins.scss index be0bc3ba6ce..2247c481e1e 100644 --- a/src/components/form/_mixins.scss +++ b/src/components/form/_mixins.scss @@ -1,3 +1,6 @@ +@import 'variables'; +@import 'form_control_layout/variables'; + @mixin euiPlaceholderPerBrowser { // sass-lint:disable-block no-vendor-prefixes // Each prefix must be its own content block @@ -54,11 +57,11 @@ } &--inGroup:not(:read-only) { - height: $euiFormControlHeight - 2px; /* 2 */ + height: $euiFormControlLayoutGroupInputHeight; /* 2 */ } &--inGroup#{&}--compressed:not(:read-only) { - height: $euiFormControlCompressedHeight - 2px; /* 2 */ + height: $euiFormControlLayoutGroupInputCompressedHeight; /* 2 */ } } } diff --git a/src/components/form/_variables.scss b/src/components/form/_variables.scss index e42d4f02023..f192c28b9e6 100644 --- a/src/components/form/_variables.scss +++ b/src/components/form/_variables.scss @@ -16,7 +16,7 @@ $euiSwitchIconHeight: $euiSize !default; // Coloring $euiFormBackgroundColor: tintOrShade($euiColorLightestShade, 60%, 40%) !default; $euiFormBackgroundDisabledColor: darken($euiColorLightestShade, 2%) !default; -$euiFormBorderOpaqueColor: shade(desaturate(adjust-hue($euiColorPrimary, 22), 22.95), 26%) !default; +$euiFormBorderOpaqueColor: shadeOrTint(desaturate(adjust-hue($euiColorPrimary, 22), 22.95), 26%, 60%) !default; $euiFormBorderColor: transparentize($euiFormBorderOpaqueColor, .9) !default; $euiFormBorderDisabledColor: transparentize($euiFormBorderOpaqueColor, .9) !default; $euiFormCustomControlDisabledIconColor: shadeOrTint($euiColorMediumShade, 38%, 48.5%) !default; // exact 508c foreground for $euiColorLightShade diff --git a/src/components/form/form_control_layout/__snapshots__/form_control_layout_delimited.test.tsx.snap b/src/components/form/form_control_layout/__snapshots__/form_control_layout_delimited.test.tsx.snap new file mode 100644 index 00000000000..e42a39191cb --- /dev/null +++ b/src/components/form/form_control_layout/__snapshots__/form_control_layout_delimited.test.tsx.snap @@ -0,0 +1,100 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`EuiFormControlLayoutDelimited is rendered 1`] = ` +
+
+ + start + +
+
+ → +
+
+ + end + +
+
+`; + +exports[`EuiFormControlLayoutDelimited props delimiter is rendered as a node 1`] = ` +
+
+ + start + +
+
+ +
+
+ + end + +
+
+`; + +exports[`EuiFormControlLayoutDelimited props delimiter is rendered as a string 1`] = ` +
+
+ + start + +
+
+ + +
+
+ + end + +
+
+`; diff --git a/src/components/form/form_control_layout/_form_control_layout.scss b/src/components/form/form_control_layout/_form_control_layout.scss index 6910a59c6c8..12cfbafe776 100644 --- a/src/components/form/form_control_layout/_form_control_layout.scss +++ b/src/components/form/form_control_layout/_form_control_layout.scss @@ -28,7 +28,7 @@ .euiFormControlLayout__prepend, .euiFormControlLayout__append { flex-shrink: 0; - height: $euiFormControlHeight - 2px; /* 1 */ + height: $euiFormControlLayoutGroupInputHeight; line-height: $euiFontSize; border: none; // remove any border in case it exists @@ -66,7 +66,7 @@ &.euiFormControlLayout--compressed { .euiFormControlLayout__prepend, .euiFormControlLayout__append { - height: $euiFormControlCompressedHeight - 2px; /* 1 */ + height: $euiFormControlLayoutGroupInputCompressedHeight; &.euiFormLabel, &.euiText { @@ -76,12 +76,19 @@ } } + > .euiFormControlLayout--compressed { + height: $euiFormControlLayoutGroupInputCompressedHeight; + } + // // ReadOnly alterations &.euiFormControlLayout--readOnly { @include euiFormControlReadOnlyStyle; padding: 0; /* 1 */ - background-color: transparent; // Ensures the input and layout don't double up on background color + + input { + background-color: transparent; // Ensures the input and layout don't double up on background color + } .euiFormControlLayout__prepend, .euiFormControlLayout__append { diff --git a/src/components/form/form_control_layout/_form_control_layout_range.scss b/src/components/form/form_control_layout/_form_control_layout_range.scss new file mode 100644 index 00000000000..81c7af082c3 --- /dev/null +++ b/src/components/form/form_control_layout/_form_control_layout_range.scss @@ -0,0 +1,56 @@ +.euiFormControlLayoutDelimited { + // Match just the regular drop shadow of inputs + @include euiFormControlDefaultShadow; + padding: 1px; /* 1 */ + + > .euiFormControlLayout__childrenWrapper { + display: flex; + align-items: center; + } + + input { + height: $euiFormControlLayoutGroupInputHeight; + } + + &[class*='--compressed'] input { + height: $euiFormControlLayoutGroupInputCompressedHeight; + padding-top: 0; // Fixes IE + padding-bottom: 0; // Fixes IE + } + + &[class*='--fullWidth'] input { + max-width: none; + } + + .euiFormControlLayoutIcons { + // Absolutely positioning the icons doesn't work because they + // overlay only one of controls making the layout unbalanced + position: static; // Overrider absolute + padding-left: $euiFormControlPadding; + padding-right: $euiFormControlPadding; + flex-shrink: 0; // Fixes IE + + &:not(.euiFormControlLayoutIcons--right) { + order: -1; + } + } +} + +.euiFormControlLayoutDelimited__child--noStyle { + // sass-lint:disable-block no-important + box-shadow: none !important; + border-radius: 0 !important; +} + +.euiFormControlLayoutDelimited__child--centered { + text-align: center; +} + +.euiFormControlLayoutDelimited__delimeter { + // sass-lint:disable-block no-important + // Override EuiText line-height + line-height: 1 !important; + flex: 0 0 auto; + padding-left: $euiFormControlPadding / 2; + padding-right: $euiFormControlPadding / 2; +} diff --git a/src/components/form/form_control_layout/_index.scss b/src/components/form/form_control_layout/_index.scss index f5a9b5d33bc..d14b350f0ef 100644 --- a/src/components/form/form_control_layout/_index.scss +++ b/src/components/form/form_control_layout/_index.scss @@ -1,5 +1,7 @@ +@import 'variables'; @import 'mixins'; @import 'form_control_layout'; +@import 'form_control_layout_range'; @import 'form_control_layout_icons'; @import 'form_control_layout_clear_button'; @import 'form_control_layout_custom_icon'; diff --git a/src/components/form/form_control_layout/_variables.scss b/src/components/form/form_control_layout/_variables.scss new file mode 100644 index 00000000000..8e04c7aeba1 --- /dev/null +++ b/src/components/form/form_control_layout/_variables.scss @@ -0,0 +1,8 @@ +@import '../variables'; + +/** + * 1. Account for inner box-shadow style border + */ + +$euiFormControlLayoutGroupInputHeight: $euiFormControlHeight - 2px; /* 1 */ +$euiFormControlLayoutGroupInputCompressedHeight: $euiFormControlCompressedHeight - 2px; /* 1 */ diff --git a/src/components/form/form_control_layout/form_control_layout.tsx b/src/components/form/form_control_layout/form_control_layout.tsx index aaf51f8fa70..b0198868c9a 100644 --- a/src/components/form/form_control_layout/form_control_layout.tsx +++ b/src/components/form/form_control_layout/form_control_layout.tsx @@ -17,28 +17,8 @@ export { ICON_SIDES } from './form_control_layout_icons'; type ReactElements = ReactElement | ReactElement[]; -// if `prepend` and/or `append` is specified then `children` must be undefined or a single ReactElement -interface AppendWithChildren { - append: ReactElements; - children?: ReactElement; -} -interface PrependWithChildren { - prepend: ReactElements; - children?: ReactElement; -} -type SiblingsWithChildren = AppendWithChildren | PrependWithChildren; - -type ChildrenOptions = - | SiblingsWithChildren - | { - append?: undefined | null; - prepend?: undefined | null; - children?: ReactNode; - }; - type EuiFormControlLayoutProps = CommonProps & - HTMLAttributes & - ChildrenOptions & { + HTMLAttributes & { /** * Creates an input group with element(s) coming before children */ @@ -47,6 +27,7 @@ type EuiFormControlLayoutProps = CommonProps & * Creates an input group with element(s) coming after children */ append?: ReactElements; + children?: ReactNode; icon?: EuiFormControlLayoutIconsProps['icon']; clear?: EuiFormControlLayoutIconsProps['clear']; fullWidth?: boolean; @@ -57,14 +38,6 @@ type EuiFormControlLayoutProps = CommonProps & readOnly?: boolean; }; -function isChildrenIsReactElement( - append: EuiFormControlLayoutProps['append'], - prepend: EuiFormControlLayoutProps['prepend'], - children: EuiFormControlLayoutProps['children'] -): children is ReactElement { - return (!!append || !!prepend) && children != null; -} - export class EuiFormControlLayout extends Component { render() { const { @@ -94,23 +67,14 @@ export class EuiFormControlLayout extends Component { className ); - const prependNodes = this.renderPrepends(); - const appendNodes = this.renderAppends(); - - let clonedChildren; - if (isChildrenIsReactElement(append, prepend, children)) { - clonedChildren = cloneElement(children, { - className: `${ - children.props.className - } euiFormControlLayout__child--noStyle`, - }); - } + const prependNodes = this.renderPrepends(prepend); + const appendNodes = this.renderAppends(append); return (
{prependNodes}
- {clonedChildren || children} + {children} { ); } - renderPrepends() { - const { prepend } = this.props; - + renderPrepends(prepend: ReactElements | undefined | null) { if (!prepend) { return; } @@ -137,9 +99,7 @@ export class EuiFormControlLayout extends Component { return prependNodes; } - renderAppends() { - const { append } = this.props; - + renderAppends(append: ReactElements | undefined | null) { if (!append) { return; } diff --git a/src/components/form/form_control_layout/form_control_layout_delimited.test.tsx b/src/components/form/form_control_layout/form_control_layout_delimited.test.tsx new file mode 100644 index 00000000000..22dce0c5498 --- /dev/null +++ b/src/components/form/form_control_layout/form_control_layout_delimited.test.tsx @@ -0,0 +1,52 @@ +import React from 'react'; +import { render } from 'enzyme'; +import { requiredProps } from '../../../test/required_props'; + +import { EuiFormControlLayoutDelimited } from './form_control_layout_delimited'; +import { EuiIcon } from '../../icon'; + +describe('EuiFormControlLayoutDelimited', () => { + test('is rendered', () => { + const component = render( + start} + endControl={end} + {...requiredProps} + /> + ); + + expect(component).toMatchSnapshot(); + }); + + describe('props', () => { + describe('delimiter', () => { + describe('is rendered', () => { + test('as a string', () => { + const component = render( + start} + endControl={end} + delimiter="+" + /> + ); + + expect(component).toMatchSnapshot(); + }); + + test('as a node', () => { + const icon = ; + + const component = render( + start} + endControl={end} + delimiter={icon} + /> + ); + + expect(component).toMatchSnapshot(); + }); + }); + }); + }); +}); diff --git a/src/components/form/form_control_layout/form_control_layout_delimited.tsx b/src/components/form/form_control_layout/form_control_layout_delimited.tsx new file mode 100644 index 00000000000..56f3b34481a --- /dev/null +++ b/src/components/form/form_control_layout/form_control_layout_delimited.tsx @@ -0,0 +1,56 @@ +import React, { + FunctionComponent, + ReactElement, + cloneElement, + ReactNode, +} from 'react'; +import classNames from 'classnames'; + +import { EuiText } from '../../text'; +import { EuiFormControlLayout } from './form_control_layout'; + +type EuiFormControlLayoutDelimitedProps = Partial & { + /** + * Left side control + */ + startControl: ReactElement; + /** + * Right side control + */ + endControl: ReactElement; + /** + * The center content. Accepts a string to be wrapped in a subdued EuiText + * or a single ReactElement + */ + delimiter?: ReactNode; + className?: string; +}; + +export const EuiFormControlLayoutDelimited: FunctionComponent< + EuiFormControlLayoutDelimitedProps +> = ({ startControl, endControl, delimiter = '→', className, ...rest }) => { + const classes = classNames('euiFormControlLayoutDelimited', className); + + return ( + + {addClassesToControl(startControl)} + + {delimiter} + + {addClassesToControl(endControl)} + + ); +}; + +function addClassesToControl(control: ReactElement) { + return cloneElement(control, { + className: classNames( + control.props.className, + 'euiFormControlLayoutDelimited__child--noStyle', + 'euiFormControlLayoutDelimited__child--centered' + ), + }); +} diff --git a/src/components/form/form_control_layout/index.ts b/src/components/form/form_control_layout/index.ts index 79e0774a1f0..0f16df04588 100644 --- a/src/components/form/form_control_layout/index.ts +++ b/src/components/form/form_control_layout/index.ts @@ -5,3 +5,5 @@ export { export { EuiFormControlLayoutCustomIcon, } from './form_control_layout_custom_icon'; + +export { EuiFormControlLayoutDelimited } from './form_control_layout_delimited'; diff --git a/src/components/form/index.js b/src/components/form/index.js index 75310f40940..0c0d51a554b 100644 --- a/src/components/form/index.js +++ b/src/components/form/index.js @@ -6,7 +6,10 @@ export { EuiFieldSearch } from './field_search'; export { EuiFieldText } from './field_text'; export { EuiFilePicker } from './file_picker'; export { EuiForm } from './form'; -export { EuiFormControlLayout } from './form_control_layout'; +export { + EuiFormControlLayout, + EuiFormControlLayoutDelimited, +} from './form_control_layout'; export { EuiFormErrorText } from './form_error_text'; export { EuiFormHelpText } from './form_help_text'; export { EuiFormLabel } from './form_label'; diff --git a/src/components/form/select/_select.scss b/src/components/form/select/_select.scss index 9dc5e60b1a8..93e2f795623 100644 --- a/src/components/form/select/_select.scss +++ b/src/components/form/select/_select.scss @@ -20,11 +20,11 @@ } &--inGroup { - line-height: $euiFormControlHeight - 2px; /* 2 */ + line-height: $euiFormControlLayoutGroupInputHeight; /* 2 */ } &--inGroup#{&}--compressed { - line-height: $euiFormControlCompressedHeight - 2px; /* 2 */ + line-height: $euiFormControlLayoutGroupInputCompressedHeight; /* 2 */ } // Turn off linter for some MS specific bits. diff --git a/src/components/index.js b/src/components/index.js index 8b66e302984..85a5dd2ac8f 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -106,6 +106,7 @@ export { EuiFilePicker, EuiForm, EuiFormControlLayout, + EuiFormControlLayoutDelimited, EuiFormErrorText, EuiFormHelpText, EuiFormLabel, From 9280bedde29cee2b3547b46cee61ecb1ea2c817d Mon Sep 17 00:00:00 2001 From: Caroline Horn <549577+cchaos@users.noreply.github.com> Date: Thu, 25 Jul 2019 10:11:28 -0400 Subject: [PATCH 03/20] [Feature branch] Compressed EuiSuperSelect dropdown (#2155) - Added truncation example - Added max-height --- src-docs/src/views/super_select/super_select.js | 7 +++++-- .../views/super_select/super_select_complex.js | 5 +---- .../context_menu/_context_menu_item.scss | 1 + .../form/super_select/_super_select.scss | 16 +++++++++++++++- src/components/form/super_select/super_select.js | 5 ++++- .../form/super_select/super_select.test.js | 13 +++++++++++++ 6 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src-docs/src/views/super_select/super_select.js b/src-docs/src/views/super_select/super_select.js index d6414d6d76f..b356b870072 100644 --- a/src-docs/src/views/super_select/super_select.js +++ b/src-docs/src/views/super_select/super_select.js @@ -19,8 +19,11 @@ export default class extends Component { }, { value: 'option_three', - inputDisplay: - 'Option three has a super long text to see if it will truncate or what', + inputDisplay: ( + + Option three has a super long text and added truncation + + ), }, ]; diff --git a/src-docs/src/views/super_select/super_select_complex.js b/src-docs/src/views/super_select/super_select_complex.js index 881d42842a5..fc44e4bef25 100644 --- a/src-docs/src/views/super_select/super_select_complex.js +++ b/src-docs/src/views/super_select/super_select_complex.js @@ -1,6 +1,6 @@ import React, { Component, Fragment } from 'react'; -import { EuiSuperSelect, EuiSpacer, EuiText } from '../../../../src/components'; +import { EuiSuperSelect, EuiText } from '../../../../src/components'; export default class extends Component { constructor(props) { @@ -13,7 +13,6 @@ export default class extends Component { dropdownDisplay: ( Option one -

Has a short description giving more detail to the option. @@ -28,7 +27,6 @@ export default class extends Component { dropdownDisplay: ( Option two -

Has a short description giving more detail to the option. @@ -43,7 +41,6 @@ export default class extends Component { dropdownDisplay: ( Option three -

Has a short description giving more detail to the option. diff --git a/src/components/context_menu/_context_menu_item.scss b/src/components/context_menu/_context_menu_item.scss index c3c2dfc9f04..8675f1429cb 100644 --- a/src/components/context_menu/_context_menu_item.scss +++ b/src/components/context_menu/_context_menu_item.scss @@ -31,6 +31,7 @@ .euiContextMenuItem__text { flex-grow: 1; + overflow: hidden; // allows for text truncation } .euiContextMenuItem__arrow { diff --git a/src/components/form/super_select/_super_select.scss b/src/components/form/super_select/_super_select.scss index cfae17820d0..19e9f4212fb 100644 --- a/src/components/form/super_select/_super_select.scss +++ b/src/components/form/super_select/_super_select.scss @@ -11,6 +11,13 @@ } } +.euiSuperSelect__listbox { + @include euiScrollBar; + max-height: 300px; + overflow: hidden; + overflow-y: auto; +} + .euiSuperSelect__popoverPanel[class*='bottom'] { /* 3 */ border-top-color: transparentize($euiBorderColor, .2); border-top-right-radius: 0; /* 2 */ @@ -26,11 +33,18 @@ } .euiSuperSelect__item { - &:hover, + @include euiFontSizeS; + padding: $euiSizeS; + + &:hover:not(:disabled), &:focus { text-decoration: none; background-color: $euiFocusBackgroundColor; } + + &:disabled { + cursor: not-allowed; + } } .euiSuperSelect__item--hasDividers:not(:last-of-type) { diff --git a/src/components/form/super_select/super_select.js b/src/components/form/super_select/super_select.js index 67c4b386cac..19ebab80986 100644 --- a/src/components/form/super_select/super_select.js +++ b/src/components/form/super_select/super_select.js @@ -165,6 +165,7 @@ export class EuiSuperSelect extends Component { itemLayoutAlign, fullWidth, popoverClassName, + compressed, ...rest } = this.props; @@ -206,6 +207,7 @@ export class EuiSuperSelect extends Component { className={buttonClasses} fullWidth={fullWidth} isInvalid={isInvalid} + compressed={compressed} {...rest} /> ); @@ -222,7 +224,6 @@ export class EuiSuperSelect extends Component { onKeyDown={this.onItemKeyDown} layoutAlign={itemLayoutAlign} buttonRef={node => this.setItemNode(node, index)} - style={{ width: this.state.menuWidth }} role="option" id={value} aria-selected={valueOfSelected === value} @@ -256,8 +257,10 @@ export class EuiSuperSelect extends Component {

{items}
diff --git a/src/components/form/super_select/super_select.test.js b/src/components/form/super_select/super_select.test.js index 7023fa6c7f6..20702df0380 100644 --- a/src/components/form/super_select/super_select.test.js +++ b/src/components/form/super_select/super_select.test.js @@ -42,6 +42,19 @@ describe('EuiSuperSelect', () => { expect(component).toMatchSnapshot(); }); + test('compressed is rendered', () => { + const component = render( + {}} + compressed + /> + ); + + expect(component).toMatchSnapshot(); + }); + test('select component is rendered', () => { const component = render( Date: Tue, 30 Jul 2019 13:38:11 -0400 Subject: [PATCH 04/20] [Feature Branch] Update compressed form control styles (#2174) * Updated compressed visual style in mixin * Compressed updates to from control groups * Fix compressed state overrides * Reduce horizontal padding for compressed * Icons and button icons in input groups * Added a compressed option for from` euiFormControlLayoutPadding` * Added compressed padding for inputs with icons * Fix readonly & compressed input groups * Fix group heights * Update file picker with new compressed styles * Fix delimited compressed and fullwidth styles * Fixed EuiComboBox * Added reduced padding for EuiColorPicker * Fixed date pickers * Variables for border-radius --- .../__snapshots__/color_picker.test.js.snap | 16 ++-- .../color_picker/_color_picker.scss | 15 ++++ src/components/color_picker/color_picker.js | 1 + src/components/combo_box/_combo_box.scss | 9 ++- .../combo_box_input/_combo_box_pill.scss | 18 ++++- src/components/date_picker/_date_picker.scss | 4 + .../quick_select_popover.test.js.snap | 2 + .../_quick_select_popover.scss | 4 + .../quick_select_popover.js | 1 + src/components/form/_mixins.scss | 50 +++++++++---- src/components/form/_variables.scss | 5 +- .../form/field_number/_field_number.scss | 4 + .../form/field_password/_field_password.scss | 4 + .../form/field_search/_field_search.scss | 4 + .../form/field_text/_field_text.scss | 4 + .../form/file_picker/_file_picker.scss | 33 ++++---- .../form/file_picker/file_picker.js | 10 +-- ...orm_control_layout_delimited.test.tsx.snap | 12 +-- .../_form_control_layout.scss | 75 +++++++++++++++---- ...ss => _form_control_layout_delimited.scss} | 48 +++++++++--- .../_form_control_layout_icons.scss | 9 +++ .../form/form_control_layout/_index.scss | 2 +- .../form/form_control_layout/_mixins.scss | 18 +++-- .../form/form_control_layout/_variables.scss | 1 + .../form_control_layout_delimited.tsx | 3 +- src/components/form/select/_select.scss | 1 + .../super_select/_super_select_control.scss | 1 + 27 files changed, 261 insertions(+), 93 deletions(-) rename src/components/form/form_control_layout/{_form_control_layout_range.scss => _form_control_layout_delimited.scss} (50%) diff --git a/src/components/color_picker/__snapshots__/color_picker.test.js.snap b/src/components/color_picker/__snapshots__/color_picker.test.js.snap index 9156adca9ac..2c1ee677c44 100644 --- a/src/components/color_picker/__snapshots__/color_picker.test.js.snap +++ b/src/components/color_picker/__snapshots__/color_picker.test.js.snap @@ -25,7 +25,7 @@ exports[`renders EuiColorPicker 1`] = ` {([openLabel, closeLabel]) => ( input { @@ -73,6 +73,10 @@ &.euiComboBox-isOpen { .euiComboBox__inputWrap { @include euiFormControlFocusStyle; + + &--compressed { + @include euiFormControlFocusStyle($borderOnly: true); + } } } @@ -90,6 +94,7 @@ &.euiComboBox--compressed { .euiComboBox__inputWrap { + // height: $euiFormControlCompressedHeight; /* 2 */ line-height: $euiFormControlCompressedHeight; /* 2 */ padding-top: 0; padding-bottom: 0; diff --git a/src/components/combo_box/combo_box_input/_combo_box_pill.scss b/src/components/combo_box/combo_box_input/_combo_box_pill.scss index b45de89e028..76885f306de 100644 --- a/src/components/combo_box/combo_box_input/_combo_box_pill.scss +++ b/src/components/combo_box/combo_box_input/_combo_box_pill.scss @@ -1,10 +1,22 @@ -// Overwrites the base styling of EuiBadge, to give it a larger size and margins -// that make sense in the input wrap. +/* + * 1. Overwrites the base styling of EuiBadge, to give it a larger size and margins + * that make sense in the input wrap. + */ .euiComboBoxPill { - margin: $euiSizeXS !important; // sass-lint:disable-line no-important + height: $euiSizeL - 2px; line-height: $euiSizeL - 2px; vertical-align: baseline; + &, + & + & /* 1 */ { + margin: $euiSizeXS; + } + + .euiComboBox--compressed &, + .euiComboBox--compressed & + & /* 1 */ { + margin: $euiSizeXS $euiSizeXS 0 0; + } + &--plainText { @include euiFont; @include euiTextTruncate; diff --git a/src/components/date_picker/_date_picker.scss b/src/components/date_picker/_date_picker.scss index e5e693ff171..b4ef09779af 100644 --- a/src/components/date_picker/_date_picker.scss +++ b/src/components/date_picker/_date_picker.scss @@ -28,6 +28,10 @@ // This is mostly here so that we can provide an inline version that doesn't have the // shadows and depth. .euiDatePicker { + .euiFormControlLayout { + height: auto; + } + &.euiDatePicker--shadow { .react-datepicker-popper { @include euiBottomShadowMedium; diff --git a/src/components/date_picker/super_date_picker/quick_select_popover/__snapshots__/quick_select_popover.test.js.snap b/src/components/date_picker/super_date_picker/quick_select_popover/__snapshots__/quick_select_popover.test.js.snap index a9a9fd442a9..aa037a3d983 100644 --- a/src/components/date_picker/super_date_picker/quick_select_popover/__snapshots__/quick_select_popover.test.js.snap +++ b/src/components/date_picker/super_date_picker/quick_select_popover/__snapshots__/quick_select_popover.test.js.snap @@ -2,6 +2,7 @@ exports[`EuiQuickSelectPopover is rendered 1`] = `
start @@ -24,7 +24,7 @@ exports[`EuiFormControlLayoutDelimited is rendered 1`] = `
end @@ -40,7 +40,7 @@ exports[`EuiFormControlLayoutDelimited props delimiter is rendered as a node 1`] class="euiFormControlLayout__childrenWrapper" > start @@ -61,7 +61,7 @@ exports[`EuiFormControlLayoutDelimited props delimiter is rendered as a node 1`]
end @@ -77,7 +77,7 @@ exports[`EuiFormControlLayoutDelimited props delimiter is rendered as a string 1 class="euiFormControlLayout__childrenWrapper" > start @@ -91,7 +91,7 @@ exports[`EuiFormControlLayoutDelimited props delimiter is rendered as a string 1 end diff --git a/src/components/form/form_control_layout/_form_control_layout.scss b/src/components/form/form_control_layout/_form_control_layout.scss index 12cfbafe776..db4b0a93d0f 100644 --- a/src/components/form/form_control_layout/_form_control_layout.scss +++ b/src/components/form/form_control_layout/_form_control_layout.scss @@ -3,7 +3,7 @@ .euiFormControlLayout { // Let the height expand as needed - @include euiFormControlSize(auto, $includeAlternates: true); + @include euiFormControlSize($includeAlternates: true); } .euiFormControlLayout__childrenWrapper { @@ -18,7 +18,7 @@ // Match just the regular drop shadow of inputs @include euiFormControlDefaultShadow; display: flex; - align-items: center; + align-items: stretch; padding: 1px; /* 1 */ .euiFormControlLayout__childrenWrapper { @@ -28,25 +28,61 @@ .euiFormControlLayout__prepend, .euiFormControlLayout__append { flex-shrink: 0; - height: $euiFormControlLayoutGroupInputHeight; - line-height: $euiFontSize; + height: 100%; + line-height: $euiFontSize - 1px; // The 1px less aligns the icons better border: none; // remove any border in case it exists + border-radius: 0; + + &:first-child { + border-top-left-radius: $euiFormControlLayoutGroupInputCompressedBorderRadius; + border-bottom-left-radius: $euiFormControlLayoutGroupInputCompressedBorderRadius; + } + + &:last-child { + border-top-right-radius: $euiFormControlLayoutGroupInputCompressedBorderRadius; + border-bottom-right-radius: $euiFormControlLayoutGroupInputCompressedBorderRadius; + } &:disabled { background-color: $euiFormBackgroundDisabledColor; color: $euiFormControlDisabledColor; // ensures established contrast } + // sass-lint:disable-block no-important // This is the only way to target specific components to override styling + &.euiFormLabel, + &.euiText, + &.euiButtonIcon, + &.euiIcon { + background-color: $euiFormInputGroupLabelBackground; + + & + .euiFormControlLayout__prepend, + & + .euiFormControlLayout__append { + padding-left: 0 !important; + + &.euiButtonIcon, + &.euiIcon { + width: $euiSizeL; + } + } + } + &.euiFormLabel, &.euiText { white-space: nowrap; margin-bottom: 0; padding: $euiFormControlPadding; border: none; - background-color: $euiFormInputGroupLabelBackground; line-height: $euiFontSize; } + + &.euiButtonIcon, + &.euiIcon { + padding: 0 $euiSizeS; + width: $euiSizeXL; + border: none; + transform: none !important; + } } // @@ -64,22 +100,28 @@ // Compressed alterations &.euiFormControlLayout--compressed { + @include euiFormControlDefaultShadow($borderOnly: true); + border-radius: $euiBorderRadius / 2; + .euiFormControlLayout__prepend, .euiFormControlLayout__append { - height: $euiFormControlLayoutGroupInputCompressedHeight; + &:first-child { + border-top-left-radius: $euiFormControlLayoutGroupInputCompressedBorderRadius; + border-bottom-left-radius: $euiFormControlLayoutGroupInputCompressedBorderRadius; + } + + &:last-child { + border-top-right-radius: $euiFormControlLayoutGroupInputCompressedBorderRadius; + border-bottom-right-radius: $euiFormControlLayoutGroupInputCompressedBorderRadius; + } &.euiFormLabel, &.euiText { - padding-top: $euiFormControlCompressedPadding; - padding-bottom: $euiFormControlCompressedPadding; + padding: $euiFormControlCompressedPadding; } } } - > .euiFormControlLayout--compressed { - height: $euiFormControlLayoutGroupInputCompressedHeight; - } - // // ReadOnly alterations &.euiFormControlLayout--readOnly { @@ -89,10 +131,17 @@ input { background-color: transparent; // Ensures the input and layout don't double up on background color } + } + + // + // ReadOnly-Compressed alterations + &.euiFormControlLayout--compressed.euiFormControlLayout--readOnly { .euiFormControlLayout__prepend, .euiFormControlLayout__append { - height: $euiFormControlHeight; // Matching input height, as euiFormControlSize() does not apply to the smaller height to readOnly states + height: $euiFormControlCompressedHeight; + border-top-left-radius: $euiFormControlCompressedBorderRadius; + border-bottom-left-radius: $euiFormControlCompressedBorderRadius; } } } diff --git a/src/components/form/form_control_layout/_form_control_layout_range.scss b/src/components/form/form_control_layout/_form_control_layout_delimited.scss similarity index 50% rename from src/components/form/form_control_layout/_form_control_layout_range.scss rename to src/components/form/form_control_layout/_form_control_layout_delimited.scss index 81c7af082c3..824785c0d1d 100644 --- a/src/components/form/form_control_layout/_form_control_layout_range.scss +++ b/src/components/form/form_control_layout/_form_control_layout_delimited.scss @@ -1,6 +1,11 @@ +@import '../variables'; +@import '../mixins'; + .euiFormControlLayoutDelimited { // Match just the regular drop shadow of inputs @include euiFormControlDefaultShadow; + display: flex; + align-items: stretch; padding: 1px; /* 1 */ > .euiFormControlLayout__childrenWrapper { @@ -8,18 +13,34 @@ align-items: center; } - input { - height: $euiFormControlLayoutGroupInputHeight; + &[class*='--compressed'] { + @include euiFormControlDefaultShadow($borderOnly: true); + border-radius: $euiBorderRadius / 2; + + .euiFormControlLayoutDelimited__input { + height: 100%; + padding-top: 0; // Fixes IE + padding-bottom: 0; // Fixes IE + padding-left: $euiFormControlCompressedPadding; + padding-right: $euiFormControlCompressedPadding; + } + + .euiFormControlLayoutIcons { + padding-left: $euiFormControlCompressedPadding; + padding-right: $euiFormControlCompressedPadding; + } } - &[class*='--compressed'] input { - height: $euiFormControlLayoutGroupInputCompressedHeight; - padding-top: 0; // Fixes IE - padding-bottom: 0; // Fixes IE + &[class*='--fullWidth'] .euiFormControlLayout__childrenWrapper { + width: 100%; } - &[class*='--fullWidth'] input { - max-width: none; + &[class*='--readOnly'] { + @include euiFormControlReadOnlyStyle; + + input { + background-color: transparent; // Ensures the input and layout don't double up on background color + } } .euiFormControlLayoutIcons { @@ -36,14 +57,17 @@ } } -.euiFormControlLayoutDelimited__child--noStyle { +.euiFormControlLayoutDelimited__input { // sass-lint:disable-block no-important box-shadow: none !important; border-radius: 0 !important; -} - -.euiFormControlLayoutDelimited__child--centered { text-align: center; + height: 100%; + min-width: 0; // Fixes FF + + .euiFormControlLayoutDelimited[class*='--compressed'] & { + max-width: none; + } } .euiFormControlLayoutDelimited__delimeter { diff --git a/src/components/form/form_control_layout/_form_control_layout_icons.scss b/src/components/form/form_control_layout/_form_control_layout_icons.scss index 5d2b4e12eb4..732b12d1dcb 100644 --- a/src/components/form/form_control_layout/_form_control_layout_icons.scss +++ b/src/components/form/form_control_layout/_form_control_layout_icons.scss @@ -10,11 +10,20 @@ > * + * { margin-left: $euiFormControlPadding / 2; } + + .euiFormControlLayout--compressed & { + left: $euiFormControlCompressedPadding; + } } .euiFormControlLayoutIcons--right { left: auto; right: $euiFormControlPadding; + + .euiFormControlLayout--compressed & { + left: auto; + right: $euiFormControlCompressedPadding; + } } // If the control is disabled, change the color of the icons diff --git a/src/components/form/form_control_layout/_index.scss b/src/components/form/form_control_layout/_index.scss index d14b350f0ef..a3a22b9f3a7 100644 --- a/src/components/form/form_control_layout/_index.scss +++ b/src/components/form/form_control_layout/_index.scss @@ -1,7 +1,7 @@ @import 'variables'; @import 'mixins'; @import 'form_control_layout'; -@import 'form_control_layout_range'; +@import 'form_control_layout_delimited'; @import 'form_control_layout_icons'; @import 'form_control_layout_clear_button'; @import 'form_control_layout_custom_icon'; diff --git a/src/components/form/form_control_layout/_mixins.scss b/src/components/form/form_control_layout/_mixins.scss index 8a174285f9a..bd4ab8c56e5 100644 --- a/src/components/form/form_control_layout/_mixins.scss +++ b/src/components/form/form_control_layout/_mixins.scss @@ -1,14 +1,22 @@ -@mixin euiFormControlLayoutPadding($numOfIcons, $side: 'right') { - $iconPaddingStart: $euiSizeXXL; +@import '../variables'; + +@mixin euiFormControlLayoutPadding($numOfIcons, $side: 'right', $compressed: false) { + $firstIconSize: $euiFormControlPadding + $euiSize + $euiFormControlPadding; + $secondIconSize: $euiFormControlPadding + $euiSize; + + @if ($compressed) { + $firstIconSize: $euiFormControlCompressedPadding + $euiSize + $euiFormControlCompressedPadding; + $secondIconSize: $euiFormControlCompressedPadding + $euiSize; + } @if variable-exists(numOfIcons) == false { @error '$numOfIcons:integer (1-3) must be provided to @mixin euiFormControlLayoutPadding().'; } @else if $numOfIcons == 1 { - padding-#{$side}: $iconPaddingStart; + padding-#{$side}: $firstIconSize; } @else if $numOfIcons == 2 { - padding-#{$side}: $iconPaddingStart + $euiSize; + padding-#{$side}: $firstIconSize + $secondIconSize; } @else if $numOfIcons == 3 { - padding-#{$side}: $iconPaddingStart + ($euiSize * 2) + $euiSizeS; + padding-#{$side}: $firstIconSize + ($secondIconSize * 2); } } diff --git a/src/components/form/form_control_layout/_variables.scss b/src/components/form/form_control_layout/_variables.scss index 8e04c7aeba1..75232099a40 100644 --- a/src/components/form/form_control_layout/_variables.scss +++ b/src/components/form/form_control_layout/_variables.scss @@ -6,3 +6,4 @@ $euiFormControlLayoutGroupInputHeight: $euiFormControlHeight - 2px; /* 1 */ $euiFormControlLayoutGroupInputCompressedHeight: $euiFormControlCompressedHeight - 2px; /* 1 */ +$euiFormControlLayoutGroupInputCompressedBorderRadius: $euiFormControlCompressedBorderRadius / 2; diff --git a/src/components/form/form_control_layout/form_control_layout_delimited.tsx b/src/components/form/form_control_layout/form_control_layout_delimited.tsx index 56f3b34481a..57a6a426dc4 100644 --- a/src/components/form/form_control_layout/form_control_layout_delimited.tsx +++ b/src/components/form/form_control_layout/form_control_layout_delimited.tsx @@ -49,8 +49,7 @@ function addClassesToControl(control: ReactElement) { return cloneElement(control, { className: classNames( control.props.className, - 'euiFormControlLayoutDelimited__child--noStyle', - 'euiFormControlLayoutDelimited__child--centered' + 'euiFormControlLayoutDelimited__input' ), }); } diff --git a/src/components/form/select/_select.scss b/src/components/form/select/_select.scss index 93e2f795623..26db7bbcebb 100644 --- a/src/components/form/select/_select.scss +++ b/src/components/form/select/_select.scss @@ -14,6 +14,7 @@ padding-bottom: 0; /* 2 */ &--compressed { + @include euiFormControlWithIcon($side: 'right', $compressed: true); /* 1 */ line-height: $euiFormControlCompressedHeight; /* 2 */ padding-top: 0; /* 2 */ padding-bottom: 0; /* 2 */ diff --git a/src/components/form/super_select/_super_select_control.scss b/src/components/form/super_select/_super_select_control.scss index 07460eba20b..2d96c6a81b0 100644 --- a/src/components/form/super_select/_super_select_control.scss +++ b/src/components/form/super_select/_super_select_control.scss @@ -24,6 +24,7 @@ } &--compressed { + @include euiFormControlWithIcon($side: 'right', $compressed: true); /* 1 */ line-height: $euiFormControlCompressedHeight; /* 2 */ padding-top: 0; /* 2 */ padding-bottom: 0; /* 2 */ From 335c806e38e22f94ef26fe295d97dcbf8a9a8c0a Mon Sep 17 00:00:00 2001 From: Caroline Horn <549577+cchaos@users.noreply.github.com> Date: Tue, 13 Aug 2019 09:29:24 -0400 Subject: [PATCH 05/20] [Feature branch] Compressed form rows (#2181) * Removed padding from compressed form row * Create mixin for `euiTextBreakWord` * Added option for horizontal compressed style Breaking: `compressed` is no longer passed to children * [Docs] Final compressed doc example changes * Fix combobox height * Fixed usages where spacers were needed * Deprecated `displayOnly` for `display: center` --- .../guide_locale_selector.js | 1 - .../guide_page/guide_page_chrome.js | 10 +- .../src/views/color_picker/custom_button.js | 2 + src-docs/src/views/combo_box/containers.js | 2 + src-docs/src/views/context/context.js | 2 + .../src/views/context_menu/context_menu.js | 2 + .../src/views/flyout/flyout_complicated.js | 1 + .../src/views/form_layouts/form_compressed.js | 44 +- .../form_layouts/form_layouts_example.js | 34 +- src-docs/src/views/form_layouts/form_rows.js | 3 + .../src/views/form_layouts/inline_popover.js | 3 + .../src/views/form_layouts/inline_sizing.js | 6 +- .../src/views/form_validation/validation.js | 3 + src-docs/src/views/popover/trap_focus.js | 3 + src/components/combo_box/_combo_box.scss | 6 +- .../date_popover/relative_tab.js | 2 +- .../__snapshots__/quick_select.test.js.snap | 20 +- .../quick_select_popover/quick_select.js | 3 + .../quick_select_popover/refresh_interval.js | 2 + src/components/expression/_expression.scss | 2 +- .../described_form_group.test.js.snap | 89 ++-- .../form/file_picker/_file_picker.scss | 3 +- .../form/form_help_text/_form_help_text.scss | 2 +- .../form/form_label/_form_label.scss | 1 - .../__snapshots__/form_row.test.js.snap | 425 ++++++++++++------ src/components/form/form_row/_form_row.scss | 53 ++- src/components/form/form_row/form_row.js | 86 ++-- src/components/form/form_row/form_row.test.js | 28 +- src/components/form/form_row/index.d.ts | 1 + src/components/table/_table.scss | 2 +- src/global_styling/mixins/_typography.scss | 13 +- src/global_styling/utility/_utility.scss | 5 +- 32 files changed, 602 insertions(+), 257 deletions(-) diff --git a/src-docs/src/components/guide_locale_selector/guide_locale_selector.js b/src-docs/src/components/guide_locale_selector/guide_locale_selector.js index e6f8c1065b6..52f545c5478 100644 --- a/src-docs/src/components/guide_locale_selector/guide_locale_selector.js +++ b/src-docs/src/components/guide_locale_selector/guide_locale_selector.js @@ -12,7 +12,6 @@ export const GuideLocaleSelector = ({ selectedLocale, onToggleLocale }) => { onChange={() => onToggleLocale(selectedLocale === 'en' ? 'en-xa' : 'en') } - compressed={true} /> ); diff --git a/src-docs/src/components/guide_page/guide_page_chrome.js b/src-docs/src/components/guide_page/guide_page_chrome.js index 9971aefb689..352ac98ab62 100644 --- a/src-docs/src/components/guide_page/guide_page_chrome.js +++ b/src-docs/src/components/guide_page/guide_page_chrome.js @@ -166,12 +166,10 @@ export class GuidePageChrome extends Component { selectedTheme={this.props.selectedTheme} /> {location.host === 'localhost:8030' ? ( // eslint-disable-line no-restricted-globals - - - + ) : null} diff --git a/src-docs/src/views/color_picker/custom_button.js b/src-docs/src/views/color_picker/custom_button.js index 31d715b6a91..57625d7af47 100644 --- a/src-docs/src/views/color_picker/custom_button.js +++ b/src-docs/src/views/color_picker/custom_button.js @@ -5,6 +5,7 @@ import { EuiFormRow, EuiColorPickerSwatch, EuiBadge, + EuiSpacer, } from '../../../../src/components'; import { isValidHex } from '../../../../src/services'; @@ -43,6 +44,7 @@ export class CustomButton extends Component { } /> + + + + + {action} )} diff --git a/src-docs/src/views/context_menu/context_menu.js b/src-docs/src/views/context_menu/context_menu.js index 6b7aa521605..1b724641ca3 100644 --- a/src-docs/src/views/context_menu/context_menu.js +++ b/src-docs/src/views/context_menu/context_menu.js @@ -7,6 +7,7 @@ import { EuiIcon, EuiPopover, EuiSwitch, + EuiSpacer, } from '../../../../src/components'; function flattenPanelTree(tree, array = []) { @@ -87,6 +88,7 @@ export default class extends Component { label="Current time range" /> + Copy iFrame code ), diff --git a/src-docs/src/views/flyout/flyout_complicated.js b/src-docs/src/views/flyout/flyout_complicated.js index 6901f5e3014..343d5412e63 100644 --- a/src-docs/src/views/flyout/flyout_complicated.js +++ b/src-docs/src/views/flyout/flyout_complicated.js @@ -195,6 +195,7 @@ export class FlyoutComplicated extends Component { + {flyoutContent} {htmlCode} diff --git a/src-docs/src/views/form_layouts/form_compressed.js b/src-docs/src/views/form_layouts/form_compressed.js index 7151cd19c48..8ab9ba27cb8 100644 --- a/src-docs/src/views/form_layouts/form_compressed.js +++ b/src-docs/src/views/form_layouts/form_compressed.js @@ -10,6 +10,7 @@ import { EuiFilePicker, EuiRange, EuiSelect, + EuiSpacer, EuiSwitch, EuiPanel, } from '../../../../src/components'; @@ -57,9 +58,16 @@ export default class extends Component { ], radioIdSelected: `${idPrefix}5`, comboBoxSelectionOptions: [], + value: '20', }; } + onRangeChange = e => { + this.setState({ + value: e.target.value, + }); + }; + onSwitchChange = () => { this.setState({ isSwitchChecked: !this.state.isSwitchChecked, @@ -92,31 +100,35 @@ export default class extends Component { - + display="columnCompressed"> + - + - - + + - + this.setState({ comboBoxSelectionOptions }) @@ -124,13 +136,22 @@ export default class extends Component { /> - - + + + display="rowCompressed"> - + + + Save form diff --git a/src-docs/src/views/form_layouts/form_layouts_example.js b/src-docs/src/views/form_layouts/form_layouts_example.js index dd71d1199b8..47bb218318a 100644 --- a/src-docs/src/views/form_layouts/form_layouts_example.js +++ b/src-docs/src/views/form_layouts/form_layouts_example.js @@ -108,7 +108,7 @@ export const FormLayoutsExample = { `, }, { - title: 'Compressed', + title: 'Compressed and horizontal', source: [ { type: GuideSectionTypes.JS, @@ -122,21 +122,33 @@ export const FormLayoutsExample = { text: (

If the particular form is in an area with a small amount of real - estate, you can add the prop compressed to the{' '} - EuiFormRows and it will pass down to the form - controls. + estate, you can pass{' '} + display="rowCompressed" to the{' '} + EuiFormRows but you will also need to pass{' '} + compressed=true to the form controls themselves. + For editor style controls, pass{' '} + display="columnCompressed" to align the + labels and inputs horizontally.

), props: { EuiFormRow, }, demo: , - snippet: ` - + `, + ` + +`, + ], }, { title: 'Described form groups', @@ -224,12 +236,16 @@ export const FormLayoutsExample = {

When supplying children to an EuiFormRow that is{' '} not a form control, and you need to the content to - vertically center with the other form controls, add the prop{' '} - displayOnly. + vertically center with the other form controls, change the{' '} + display prop to center or{' '} + centerCompressed.

), demo: , + snippet: ` + +`, }, { title: 'In a popover', diff --git a/src-docs/src/views/form_layouts/form_rows.js b/src-docs/src/views/form_layouts/form_rows.js index 00ea340f377..42b14c9ab53 100644 --- a/src-docs/src/views/form_layouts/form_rows.js +++ b/src-docs/src/views/form_layouts/form_rows.js @@ -10,6 +10,7 @@ import { EuiLink, EuiRange, EuiSelect, + EuiSpacer, EuiSwitch, EuiText, } from '../../../../src/components'; @@ -135,6 +136,8 @@ export default class extends Component { />
+ + Save form diff --git a/src-docs/src/views/form_layouts/inline_popover.js b/src-docs/src/views/form_layouts/inline_popover.js index 23a9fa41e66..c9af9fa01d7 100644 --- a/src-docs/src/views/form_layouts/inline_popover.js +++ b/src-docs/src/views/form_layouts/inline_popover.js @@ -10,6 +10,7 @@ import { EuiFlexItem, EuiFieldNumber, EuiRange, + EuiSpacer, EuiSwitch, } from '../../../../src/components'; @@ -125,6 +126,8 @@ export default class extends Component { + + Save ); diff --git a/src-docs/src/views/form_layouts/inline_sizing.js b/src-docs/src/views/form_layouts/inline_sizing.js index d8602cae275..d66e8da3c98 100644 --- a/src-docs/src/views/form_layouts/inline_sizing.js +++ b/src-docs/src/views/form_layouts/inline_sizing.js @@ -23,13 +23,13 @@ export default () => (
- + - - Save + + Save diff --git a/src-docs/src/views/form_validation/validation.js b/src-docs/src/views/form_validation/validation.js index ae1c6834413..b1667d0ccfc 100644 --- a/src-docs/src/views/form_validation/validation.js +++ b/src-docs/src/views/form_validation/validation.js @@ -7,6 +7,7 @@ import { EuiFormRow, EuiTextArea, EuiFieldText, + EuiSpacer, } from '../../../../src/components'; export default class extends Component { @@ -70,6 +71,8 @@ export default class extends Component { />
+ + {button} diff --git a/src-docs/src/views/popover/trap_focus.js b/src-docs/src/views/popover/trap_focus.js index 6b62246e343..7d69f04ceb5 100644 --- a/src-docs/src/views/popover/trap_focus.js +++ b/src-docs/src/views/popover/trap_focus.js @@ -4,6 +4,7 @@ import { EuiButton, EuiFormRow, EuiPopover, + EuiSpacer, EuiSwitch, } from '../../../../src/components'; @@ -54,6 +55,8 @@ export default class extends Component {
+ + Copy IFRAME code
); diff --git a/src/components/combo_box/_combo_box.scss b/src/components/combo_box/_combo_box.scss index e962ee457ff..4f3e2be4976 100644 --- a/src/components/combo_box/_combo_box.scss +++ b/src/components/combo_box/_combo_box.scss @@ -11,7 +11,7 @@ */ &--compressed, - .euiFormControlLayout--compressed { + .euiFormControlLayout { height: auto; } @@ -98,6 +98,10 @@ line-height: $euiFormControlCompressedHeight; /* 2 */ padding-top: 0; padding-bottom: 0; + + &.euiComboBox__inputWrap-isClearable { + @include euiFormControlLayoutPadding(2, $compressed: true); /* 2 */ + } } } } diff --git a/src/components/date_picker/super_date_picker/date_popover/relative_tab.js b/src/components/date_picker/super_date_picker/date_popover/relative_tab.js index 008913739e1..1399f260242 100644 --- a/src/components/date_picker/super_date_picker/date_popover/relative_tab.js +++ b/src/components/date_picker/super_date_picker/date_popover/relative_tab.js @@ -104,7 +104,7 @@ export class EuiRelativeTab extends Component { - + @@ -160,6 +161,7 @@ export class EuiQuickSelect extends Component { aria-label="Quick time value" value={this.state.timeValue} onChange={this.onTimeValueChange} + compressed /> @@ -170,6 +172,7 @@ export class EuiQuickSelect extends Component { value={this.state.timeUnits} options={timeUnitsOptions} onChange={this.onTimeUnitsChange} + compressed /> diff --git a/src/components/date_picker/super_date_picker/quick_select_popover/refresh_interval.js b/src/components/date_picker/super_date_picker/quick_select_popover/refresh_interval.js index fc618ef33f5..e7f5e52b1cb 100644 --- a/src/components/date_picker/super_date_picker/quick_select_popover/refresh_interval.js +++ b/src/components/date_picker/super_date_picker/quick_select_popover/refresh_interval.js @@ -128,6 +128,7 @@ export class EuiRefreshInterval extends Component { onChange={this.onValueChange} aria-label="Refresh interval value" data-test-subj="superDatePickerRefreshIntervalInput" + compressed /> @@ -139,6 +140,7 @@ export class EuiRefreshInterval extends Component { options={refreshUnitsOptions} onChange={this.onUnitsChange} data-test-subj="superDatePickerRefreshIntervalUnitsSelect" + compressed /> diff --git a/src/components/expression/_expression.scss b/src/components/expression/_expression.scss index 3ee436b8af4..3797018cc0b 100644 --- a/src/components/expression/_expression.scss +++ b/src/components/expression/_expression.scss @@ -3,7 +3,7 @@ * but then wrap long words */ .euiExpression { - @include euiTextOverflowWrap; /* 1 */ + @include euiTextBreakWord; /* 1 */ @include euiFontSizeS; @include euiCodeFont; diff --git a/src/components/form/described_form_group/__snapshots__/described_form_group.test.js.snap b/src/components/form/described_form_group/__snapshots__/described_form_group.test.js.snap index 7009392b9b5..7c5dbeffbc1 100644 --- a/src/components/form/described_form_group/__snapshots__/described_form_group.test.js.snap +++ b/src/components/form/described_form_group/__snapshots__/described_form_group.test.js.snap @@ -36,6 +36,7 @@ exports[`EuiDescribedFormGroup is rendered 1`] = ` > -
+
- - -
+ - Error one -
-
- -
+ Error one +
+
+ - Error two -
- - -
+ Error two +
+ + - Help text - - +
+ Help text +
+
+
diff --git a/src/components/form/file_picker/_file_picker.scss b/src/components/form/file_picker/_file_picker.scss index bb8aca6035a..f22ba6b295e 100644 --- a/src/components/form/file_picker/_file_picker.scss +++ b/src/components/form/file_picker/_file_picker.scss @@ -44,6 +44,7 @@ .euiFilePicker--compressed & { top: $euiSizeS; + left: $euiSizeS; } .euiFilePicker--large & { @@ -76,7 +77,7 @@ .euiFilePicker--compressed & { @include euiFormControlStyleCompressed($includeStates: false); - @include euiFormControlWithIcon; /* 2 */ + @include euiFormControlWithIcon($compressed: true); /* 2 */ height: $euiFormControlCompressedHeight; } diff --git a/src/components/form/form_help_text/_form_help_text.scss b/src/components/form/form_help_text/_form_help_text.scss index 2bce1e4caad..d449b0e2c6a 100644 --- a/src/components/form/form_help_text/_form_help_text.scss +++ b/src/components/form/form_help_text/_form_help_text.scss @@ -1,5 +1,5 @@ .euiFormHelpText { @include euiFontSizeXS; - padding-top: $euiSizeS; + padding-top: $euiSizeXS; color: $euiColorDarkShade; } diff --git a/src/components/form/form_label/_form_label.scss b/src/components/form/form_label/_form_label.scss index e321f960221..887d7de476c 100644 --- a/src/components/form/form_label/_form_label.scss +++ b/src/components/form/form_label/_form_label.scss @@ -4,7 +4,6 @@ .euiFormLabel { @include euiFontSizeXS; display: inline-block; - margin-bottom: $euiSizeXS; transition: all $euiAnimSpeedFast $euiAnimSlightResistance; color: $euiTitleColor; font-weight: $euiFontWeightSemiBold; diff --git a/src/components/form/form_row/__snapshots__/form_row.test.js.snap b/src/components/form/form_row/__snapshots__/form_row.test.js.snap index 0ef700dad94..c239a1bc3ac 100644 --- a/src/components/form/form_row/__snapshots__/form_row.test.js.snap +++ b/src/components/form/form_row/__snapshots__/form_row.test.js.snap @@ -3,6 +3,7 @@ exports[`EuiFormRow behavior onBlur is called in child 1`] = ` -
+
- +
+ +
`; @@ -44,6 +52,7 @@ exports[`EuiFormRow behavior onBlur is called in child 1`] = ` exports[`EuiFormRow behavior onBlur works in parent even if not in child 1`] = ` -
+
- +
+ +
`; @@ -85,6 +101,7 @@ exports[`EuiFormRow behavior onBlur works in parent even if not in child 1`] = ` exports[`EuiFormRow behavior onFocus is called in child 1`] = ` -
+
- +
+ +
`; @@ -126,6 +150,7 @@ exports[`EuiFormRow behavior onFocus is called in child 1`] = ` exports[`EuiFormRow behavior onFocus works in parent even if not in child 1`] = ` -
+
- +
+ +
`; @@ -171,9 +203,28 @@ exports[`EuiFormRow is rendered 1`] = ` data-test-subj="test subject string" id="generated-id-row" > - +
+ +
+ +`; + +exports[`EuiFormRow props compressed is rendered 1`] = ` +
+
+ +
`; @@ -182,12 +233,91 @@ exports[`EuiFormRow props describedByIds is rendered 1`] = ` className="euiFormRow" id="generated-id-row" > - +
+ +
+ +`; + +exports[`EuiFormRow props display type center is rendered 1`] = ` +
+
+ +
+
+`; + +exports[`EuiFormRow props display type centerCompressed is rendered 1`] = ` +
+
+ +
+
+`; + +exports[`EuiFormRow props display type columnCompressed is rendered 1`] = ` +
+
+ +
+
+`; + +exports[`EuiFormRow props display type row is rendered 1`] = ` +
+
+ +
+
+`; + +exports[`EuiFormRow props display type rowCompressed is rendered 1`] = ` +
+
+ +
`; @@ -197,7 +327,7 @@ exports[`EuiFormRow props displayOnly is rendered 1`] = ` id="generated-id-row" >
-
- Error -
-
- Error2 + +
+ Error +
+
+ Error2 +
`; @@ -239,16 +373,20 @@ exports[`EuiFormRow props error as string is rendered 1`] = ` class="euiFormRow" id="generated-id-row" > -
- Error + +
+ Error +
`; @@ -258,9 +396,13 @@ exports[`EuiFormRow props error is not rendered if isInvalid is false 1`] = ` class="euiFormRow" id="generated-id-row" > - +
+ +
`; @@ -269,9 +411,13 @@ exports[`EuiFormRow props fullWidth is rendered 1`] = ` class="euiFormRow euiFormRow--fullWidth" id="generated-id-row" > - +
+ +
`; @@ -280,9 +426,13 @@ exports[`EuiFormRow props hasEmptyLabelSpace is rendered 1`] = ` class="euiFormRow euiFormRow--hasEmptyLabelSpace" id="generated-id-row" > - +
+ +
`; @@ -291,17 +441,21 @@ exports[`EuiFormRow props helpText is rendered 1`] = ` class="euiFormRow" id="generated-id-row" > -
- - This is help text. - + +
+ + This is help text. + +
`; @@ -311,9 +465,13 @@ exports[`EuiFormRow props id is rendered 1`] = ` class="euiFormRow" id="id-row" > - +
+ +
`; @@ -322,18 +480,24 @@ exports[`EuiFormRow props isInvalid is rendered 1`] = ` class="euiFormRow" id="generated-id-row" > -
+
- +
+ +
`; @@ -342,36 +506,29 @@ exports[`EuiFormRow props label append is rendered 1`] = ` className="euiFormRow" id="generated-id-row" > - - -
- - label - -
-
- - append - -
- + label + + + append + +
+ +
`; @@ -380,8 +537,11 @@ exports[`EuiFormRow props label is rendered 1`] = ` className="euiFormRow" id="generated-id-row" > -
+
- +
+ +
`; @@ -403,8 +567,11 @@ exports[`EuiFormRow props label renders as a legend and subsquently a fieldset w className="euiFormRow" id="generated-id-row" > -
+
- +
+ +
`; diff --git a/src/components/form/form_row/_form_row.scss b/src/components/form/form_row/_form_row.scss index b4220f576ab..f0c6ff0e701 100644 --- a/src/components/form/form_row/_form_row.scss +++ b/src/components/form/form_row/_form_row.scss @@ -6,9 +6,8 @@ display: flex; /* 1 */ flex-direction: column; /* 1 */ max-width: $euiFormMaxWidth; - padding-bottom: $euiSizeS; - + * { + + .euiFormRow { margin-top: $euiSize; } } @@ -18,26 +17,60 @@ } .euiFormRow--hasEmptyLabelSpace { - margin-top: $euiFontSizeXS + $euiSizeS; /* 2 */ + margin-top: ($euiFontSizeXS * $euiLineHeight) + $euiSizeXS; /* 2 */ // the following ensure that contents that aren't inheritly the same height // as inputs will align to the vertical center - min-height: $euiSizeXXL; + min-height: $euiFormControlHeight; padding-bottom: 0; justify-content: center; } -.euiFormRow--compressed { - + * { - margin-top: $euiSizeS; +.euiFormRow__labelWrapper { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + margin-bottom: $euiSizeXS; +} + +.euiFormRow--horizontal { + flex-direction: row; + align-items: stretch; + + .euiFormRow__label { + @include euiTextBreakWord; + hyphens: auto; + max-width: 100%; // Fixes IE + } + + .euiFormRow__labelWrapper { + display: block; + line-height: $euiFormControlCompressedHeight - 1px; // The 1px less helps the alignment of the text baseline + width: calc(33% - #{$euiSizeS}); + margin-right: $euiSizeS; + margin-bottom: 0; } - .euiFormRow__text { - padding-top: $euiSizeM / 2; + .euiFormRow__fieldWrapper { + width: 67%; + } + + + .euiFormRow--horizontal { + margin-top: $euiSizeS; } } -.euiFormRow__displayOnlyWrapper { +.euiFormRow__fieldWrapperDisplayOnly { min-height: $euiFormControlHeight; display: flex; align-items: center; } + +.euiFormRow--compressed { + &.euiFormRow--hasEmptyLabelSpace { + min-height: $euiFormControlCompressedHeight; + } + + .euiFormRow__fieldWrapperDisplayOnly { + min-height: $euiFormControlCompressedHeight; + } +} diff --git a/src/components/form/form_row/form_row.js b/src/components/form/form_row/form_row.js index 79835cb54df..2eed0fdca96 100644 --- a/src/components/form/form_row/form_row.js +++ b/src/components/form/form_row/form_row.js @@ -8,10 +8,19 @@ import { withRequiredProp } from '../../../utils/prop_types/with_required_prop'; import { EuiFormHelpText } from '../form_help_text'; import { EuiFormErrorText } from '../form_error_text'; import { EuiFormLabel } from '../form_label'; -import { EuiFlexGroup, EuiFlexItem } from '../../flex'; import makeId from './make_id'; +const displayToClassNameMap = { + row: null, + rowCompressed: 'euiFormRow--compressed', + columnCompressed: 'euiFormRow--compressed euiFormRow--horizontal', + center: null, + centerCompressed: 'euiFormRow--compressed', +}; + +export const DISPLAYS = Object.keys(displayToClassNameMap); + export class EuiFormRow extends Component { constructor(props) { super(props); @@ -63,19 +72,39 @@ export class EuiFormRow extends Component { className, describedByIds, compressed, + display, displayOnly, ...rest } = this.props; const { id } = this.state; + /** + * Remove when `compressed` is deprecated + */ + let shimDisplay; + if (compressed && display === 'row') { + shimDisplay = 'rowCompressed'; + } else { + shimDisplay = display; + } + + /** + * Remove when `displayOnly` is deprecated + */ + if (compressed && displayOnly) { + shimDisplay = 'centerCompressed'; + } else if (displayOnly && display === 'row') { + shimDisplay = 'center'; + } + const classes = classNames( 'euiFormRow', { 'euiFormRow--hasEmptyLabelSpace': hasEmptyLabelSpace, 'euiFormRow--fullWidth': fullWidth, - 'euiFormRow--compressed': compressed, }, + displayToClassNameMap[shimDisplay], className ); @@ -110,11 +139,11 @@ export class EuiFormRow extends Component { const isLegend = label && labelType === 'legend' ? true : false; const labelID = isLegend ? `${id}-${labelType}` : undefined; - if (label) { + if (label || labelAppend) { optionalLabel = ( - // Outer div ensures the label is inline-block (only takes up as much room as it needs) -
+
{label} + {labelAppend && ' '} + {labelAppend}
); } - if (labelAppend) { - optionalLabel = ( - - {optionalLabel} - {labelAppend} - - ); - } - const optionalProps = {}; const describingIds = [...describedByIds]; @@ -155,17 +173,17 @@ export class EuiFormRow extends Component { optionalProps['aria-describedby'] = describingIds.join(' '); } - let field = cloneElement(Children.only(children), { + const field = cloneElement(Children.only(children), { id, onFocus: this.onFocus, onBlur: this.onBlur, - compressed: compressed, ...optionalProps, }); - if (displayOnly) { - field =
{field}
; - } + const fieldWrapperClasses = classNames('euiFormRow__fieldWrapper', { + euiFormRow__fieldWrapperDisplayOnly: + displayOnly || display.startsWith('center'), + }); const Element = labelType === 'legend' ? 'fieldset' : 'div'; @@ -177,9 +195,11 @@ export class EuiFormRow extends Component { aria-labelledby={labelID} // Only renders a string if label type is 'legend' > {optionalLabel} - {field} - {optionalErrors} - {optionalHelpText} +
+ {field} + {optionalErrors} + {optionalHelpText} +
); } @@ -219,11 +239,20 @@ EuiFormRow.propTypes = { */ describedByIds: PropTypes.array, /** - * Tightens up the spacing and sends down the - * compressed prop to the input + * **DEPRECATED: use `display: rowCompressed` instead.** + * When `true`, tightens up the spacing. */ compressed: PropTypes.bool, /** + * When `rowCompressed`, just tightens up the spacing; + * Set to `columnCompressed` if compressed + * and horizontal layout is needed. + * Set to `center` or `centerCompressed` to align non-input + * content better with inline rows. + */ + display: PropTypes.oneOf(DISPLAYS), + /** + * **DEPRECATED: use `display: center` instead.** * Vertically centers non-input style content so it aligns * better with input style content. */ @@ -231,6 +260,7 @@ EuiFormRow.propTypes = { }; EuiFormRow.defaultProps = { + display: 'row', hasEmptyLabelSpace: false, fullWidth: false, describedByIds: [], diff --git a/src/components/form/form_row/form_row.test.js b/src/components/form/form_row/form_row.test.js index c32f7d24ac2..69d7e270667 100644 --- a/src/components/form/form_row/form_row.test.js +++ b/src/components/form/form_row/form_row.test.js @@ -3,7 +3,7 @@ import { shallow, render, mount } from 'enzyme'; import { requiredProps } from '../../../test'; import sinon from 'sinon'; -import { EuiFormRow } from './form_row'; +import { EuiFormRow, DISPLAYS } from './form_row'; jest.mock('./make_id', () => () => 'generated-id'); @@ -200,6 +200,32 @@ describe('EuiFormRow', () => { expect(component).toMatchSnapshot(); }); + + describe('compressed', () => { + test('is rendered', () => { + const component = render( + + + + ); + + expect(component).toMatchSnapshot(); + }); + }); + + describe('display type', () => { + DISPLAYS.forEach(display => { + test(`${display} is rendered`, () => { + const component = render( + + + + ); + + expect(component).toMatchSnapshot(); + }); + }); + }); }); describe('behavior', () => { diff --git a/src/components/form/form_row/index.d.ts b/src/components/form/form_row/index.d.ts index 93aadba7221..76c08290f4c 100644 --- a/src/components/form/form_row/index.d.ts +++ b/src/components/form/form_row/index.d.ts @@ -21,6 +21,7 @@ declare module '@elastic/eui' { labelAppend?: ReactNode; describedByIds?: string[]; compressed?: boolean; + display?: 'row' | 'rowCompressed' | 'columnCompressed'; displayOnly?: boolean; }; diff --git a/src/components/table/_table.scss b/src/components/table/_table.scss index 468c0c96ec3..d4d1aa61d13 100644 --- a/src/components/table/_table.scss +++ b/src/components/table/_table.scss @@ -138,7 +138,7 @@ } .euiTableCellContent__text { - @include euiTextOverflowWrap; /* 4 */ + @include euiTextBreakWord; /* 4 */ min-width: 0; text-overflow: ellipsis; } diff --git a/src/global_styling/mixins/_typography.scss b/src/global_styling/mixins/_typography.scss index a677a7aa711..18d86ffb6b6 100644 --- a/src/global_styling/mixins/_typography.scss +++ b/src/global_styling/mixins/_typography.scss @@ -1,4 +1,5 @@ // sass-lint:disable no-vendor-prefixes +// sass-lint:disable no-important // Our base fonts @@ -96,13 +97,11 @@ letter-spacing: -.03em; } -// Overflow-wrap for breaking on word -// Does not work on `display: flex` items -@mixin euiTextOverflowWrap { - @include internetExplorerOnly { - word-break: break-all; - } - overflow-wrap: break-word; +@mixin euiTextBreakWord { + // https://css-tricks.com/snippets/css/prevent-long-urls-from-breaking-out-of-container/ + overflow-wrap: break-word !important; // makes sure the long string will wrap and not bust out of the container + word-wrap: break-word !important; // spec says, they are literally just alternate names for each other but some browsers support one and not the other + word-break: break-word; // IE doesn't understand but that's ok } // Text truncation diff --git a/src/global_styling/utility/_utility.scss b/src/global_styling/utility/_utility.scss index d5cc5c41d6f..55765690c41 100644 --- a/src/global_styling/utility/_utility.scss +++ b/src/global_styling/utility/_utility.scss @@ -25,10 +25,7 @@ .eui-textInheritColor {color: inherit !important;} .eui-textBreakWord { - // https://css-tricks.com/snippets/css/prevent-long-urls-from-breaking-out-of-container/ - overflow-wrap: break-word !important; // makes sure the long string will wrap and not bust out of the container - word-wrap: break-word !important; // spec says, they are literally just alternate names for each other but some browsers support one and not the other - word-break: break-word; // IE doesn't understand but that's ok + @include euiTextBreakWord; } .eui-textBreakAll { From fe0212198d45006980307f6439a3f59215a918fa Mon Sep 17 00:00:00 2001 From: cchaos Date: Thu, 29 Aug 2019 12:03:15 -0400 Subject: [PATCH 06/20] Fix snap --- .../__snapshots__/super_select.test.js.snap | 87 ++++++++++++++----- 1 file changed, 67 insertions(+), 20 deletions(-) diff --git a/src/components/form/super_select/__snapshots__/super_select.test.js.snap b/src/components/form/super_select/__snapshots__/super_select.test.js.snap index 8d1d83433eb..174b7a51fad 100644 --- a/src/components/form/super_select/__snapshots__/super_select.test.js.snap +++ b/src/components/form/super_select/__snapshots__/super_select.test.js.snap @@ -56,6 +56,62 @@ exports[`EuiSuperSelect is rendered 1`] = `
`; +exports[`EuiSuperSelect props compressed is rendered 1`] = ` +
+
+ +
+
+ + Select an option: , is selected + +
+
+
+
+`; + exports[`EuiSuperSelect props custom display is propagated to dropdown 1`] = `
@@ -345,6 +402,7 @@ exports[`EuiSuperSelect props more props are propogated to each option 1`] = `

@@ -711,6 +769,7 @@ exports[`EuiSuperSelect props more props are propogated to each option 2`] = `

@@ -799,6 +858,7 @@ exports[`EuiSuperSelect props more props are propogated to each option 2`] = `

@@ -923,7 +983,13 @@ exports[`EuiSuperSelect props more props are propogated to each option 2`] = `
+
+
+
+
`; @@ -168,24 +172,25 @@ exports[`EuiDualRange props disabled should render 1`] = `
+
+
+
-
-
-
`; @@ -197,23 +202,24 @@ exports[`EuiDualRange props fullWidth should render 1`] = `
+
+
+
-
-
-
`; @@ -244,6 +250,14 @@ exports[`EuiDualRange props inputs should render 1`] = `
+
+
+
-
-
-
+
+
+
-
-
-
`; @@ -278,14 +318,6 @@ exports[`EuiRange props range should render 1`] = `
-
@@ -294,6 +326,15 @@ exports[`EuiRange props range should render 1`] = ` style="margin-left:0%;width:8%" />
+
`; @@ -306,13 +347,6 @@ exports[`EuiRange props ticks should render 1`] = ` class="euiRangeTrack" style="margin-right:0.6em" > -
+
`; @@ -385,6 +427,7 @@ exports[`EuiRange props value should render 1`] = ` > .euiFormControlLayout { /* 1 */ width: auto; } diff --git a/src/components/form/range/_variables.scss b/src/components/form/range/_variables.scss index ce6a5cfd8e6..623e58acce8 100644 --- a/src/components/form/range/_variables.scss +++ b/src/components/form/range/_variables.scss @@ -12,3 +12,5 @@ $euiRangeTrackBorderColor: $euiRangeTrackColor !default; $euiRangeTrackRadius: $euiBorderRadius !default; $euiRangeDisabledOpacity: .25; + +$euiRangeHighlightHeight: $euiSizeXS; diff --git a/src/components/form/range/dual_range.js b/src/components/form/range/dual_range.js index 3d3a63f4dc6..8e0b3131bbb 100644 --- a/src/components/form/range/dual_range.js +++ b/src/components/form/range/dual_range.js @@ -4,6 +4,9 @@ import classNames from 'classnames'; import { keyCodes } from '../../../services'; import { isWithinRange } from '../../../services/number'; +import { EuiInputPopover } from '../../popover'; +import { EuiFormControlLayoutDelimited } from '../form_control_layout'; +import makeId from '../form_row/make_id'; import { EuiRangeHighlight } from './range_highlight'; import { EuiRangeInput } from './range_input'; @@ -15,15 +18,21 @@ import { EuiRangeWrapper } from './range_wrapper'; export class EuiDualRange extends Component { state = { + id: this.props.id || makeId(), hasFocus: false, rangeSliderRefAvailable: false, + isPopoverOpen: false, + rangeWidth: null, }; + maxNode = null; + minNode = null; rangeSliderRef = null; handleRangeSliderRefUpdate = ref => { this.rangeSliderRef = ref; this.setState({ rangeSliderRefAvailable: !!ref, + rangeWidth: !!ref ? ref.clientWidth : null, }); }; @@ -201,7 +210,7 @@ export class EuiDualRange extends Component { this._handleOnChange(this.lowerValue, upper, e); }; - calculateThumbPositionStyle = value => { + calculateThumbPositionStyle = (value, width) => { // Calculate the left position based on value const decimal = (value - this.props.min) / (this.props.max - this.props.min); @@ -210,7 +219,11 @@ export class EuiDualRange extends Component { valuePosition = valuePosition >= 0 ? valuePosition : 0; const EUI_THUMB_SIZE = 16; - const thumbToTrackRatio = EUI_THUMB_SIZE / this.rangeSliderRef.clientWidth; + const trackWidth = + this.props.showInput === 'only' && !!width + ? width + : this.rangeSliderRef.clientWidth; + const thumbToTrackRatio = EUI_THUMB_SIZE / trackWidth; const trackPositionScale = (1 - thumbToTrackRatio) * 100; return { left: `${valuePosition * trackPositionScale}%` }; }; @@ -221,13 +234,56 @@ export class EuiDualRange extends Component { }); }; + onInputFocus = () => { + this.setState({ + isPopoverOpen: true, + }); + }; + + onInputBlur = e => { + // Firefox returns `relatedTarget` as `null` for security reasons, but provides a proprietary `explicitOriginalTarget` + const relatedTarget = e.relatedTarget || e.explicitOriginalTarget; + if (!relatedTarget || relatedTarget.id !== this.state.id) { + this.closePopover(); + } + }; + + closePopover = () => { + this.setState({ + isPopoverOpen: false, + }); + }; + + onResize = width => { + this.setState({ + rangeWidth: width, + }); + }; + + inputRef = (node, ref) => { + if (!this.props.showInput !== 'inputWithPopover') return; + + // IE11 doesn't support the `relatedTarget` event property for blur events + // but does add it for focusout. React doesn't support `onFocusOut` so here we are. + if (this[ref] != null) { + this[ref].removeEventListener('focusout', this.onInputBlur); + } + + this[ref] = node; + + if (this[ref]) { + this[ref].addEventListener('focusout', this.onInputBlur); + } + }; + render() { const { className, compressed, disabled, fullWidth, - id, + readOnly, + id: propsId, max, min, name, @@ -245,34 +301,78 @@ export class EuiDualRange extends Component { ...rest } = this.props; - const classes = classNames('euiDualRange', className); + const { id } = this.state; + const digitTolerance = Math.max(String(min).length, String(max).length); + const showInputOnly = showInput === 'inputWithPopover'; + const canShowDropdown = showInputOnly && !readOnly && !disabled; - return ( - - {showInput && ( - - )} + const minInput = !!showInput ? ( + this.inputRef(node, 'minNode')} + /> + ) : ( + undefined + ); + + const maxInput = !!showInput ? ( + this.inputRef(node, 'maxNode')} + /> + ) : ( + undefined + ); + + const classes = classNames('euiDualRange', className); + const theRange = ( + + {!showInputOnly && minInput} {showLabels && ( {min} )} + {showRange && this.isValid && ( + + )} + this.toggleHasFocus(true)} onBlur={() => this.toggleHasFocus(false)} - style={this.calculateThumbPositionStyle(this.lowerValue || min)} + style={this.calculateThumbPositionStyle( + this.lowerValue || min, + this.state.rangeWidth + )} aria-describedby={this.props['aria-describedby']} aria-label={this.props['aria-label']} /> @@ -324,48 +440,50 @@ export class EuiDualRange extends Component { value={this.upperValue} disabled={disabled} showTicks={showTicks} - showInput={showInput} + showInput={!!showInput} onKeyDown={this.handleUpperKeyDown} onFocus={() => this.toggleHasFocus(true)} onBlur={() => this.toggleHasFocus(false)} - style={this.calculateThumbPositionStyle(this.upperValue || max)} + style={this.calculateThumbPositionStyle( + this.upperValue || max, + this.state.rangeWidth + )} aria-describedby={this.props['aria-describedby']} aria-label={this.props['aria-label']} /> )} - - {showRange && this.isValid && ( - - )} {showLabels && {max}} - {showInput && ( - + ); + + const thePopover = showInputOnly ? ( + - )} - + } + fullWidth={fullWidth} + isOpen={this.state.isPopoverOpen} + closePopover={this.closePopover} + disableFocusTrap={true} + onPanelResize={this.onResize}> + {theRange} + + ) : ( + undefined ); + + return thePopover || theRange; } } @@ -384,14 +502,16 @@ EuiDualRange.propTypes = { fullWidth: PropTypes.bool, compressed: PropTypes.bool, disabled: PropTypes.bool, + readOnly: PropTypes.bool, /** * Shows static min/max labels on the sides of the range slider */ showLabels: PropTypes.bool, /** - * Displays a input controls for direct manipulation + * Pass `true` to displays an extra input control for direct manipulation. + * Pass `'inputWithPopover'` to only show the input but show the range in a dropdown. */ - showInput: PropTypes.bool, + showInput: PropTypes.oneOf([true, false, 'inputWithPopover']), /** * Shows clickable tick marks and labels at the given interval (`step`/`tickInterval`) */ diff --git a/src/components/form/range/dual_range.test.js b/src/components/form/range/dual_range.test.js index cc010c8b06d..03c9a822023 100644 --- a/src/components/form/range/dual_range.test.js +++ b/src/components/form/range/dual_range.test.js @@ -4,6 +4,8 @@ import { requiredProps } from '../../../test/required_props'; import { EuiDualRange } from './dual_range'; +jest.mock('../form_row/make_id', () => () => 'generated-id'); + describe('EuiDualRange', () => { test('is rendered', () => { const component = render( @@ -86,6 +88,23 @@ describe('EuiDualRange', () => { expect(component).toMatchSnapshot(); }); + test('only input should render', () => { + const component = render( + {}} + showInput="inputWithPopover" + {...requiredProps} + /> + ); + + expect(component).toMatchSnapshot(); + }); + test('levels should render', () => { const component = render( { const isValid = isWithinRange( this.props.min, @@ -26,13 +38,50 @@ export class EuiRange extends Component { return isWithinRange(this.props.min, this.props.max, this.props.value); } + onInputFocus = () => { + this.setState({ + isPopoverOpen: true, + }); + }; + + onInputBlur = e => { + // Firefox returns `relatedTarget` as `null` for security reasons, but provides a proprietary `explicitOriginalTarget` + const relatedTarget = e.relatedTarget || e.explicitOriginalTarget; + if (!relatedTarget || relatedTarget.id !== this.state.id) { + this.closePopover(); + } + }; + + closePopover = () => { + this.setState({ + isPopoverOpen: false, + }); + }; + + inputRef = node => { + if (!this.props.showInput !== 'inputWithPopover') return; + + // IE11 and Safar don't support the `relatedTarget` event property for blur events + // but do add it for focusout. React doesn't support `onFocusOut` so here we are. + if (this.inputNode != null) { + this.inputNode.removeEventListener('focusout', this.onInputBlur); + } + + this.inputNode = node; + + if (this.inputNode) { + this.inputNode.addEventListener('focusout', this.onInputBlur); + } + }; + render() { const { className, compressed, disabled, fullWidth, - id, + readOnly, + id: propsId, max, min, name, @@ -54,11 +103,41 @@ export class EuiRange extends Component { ...rest } = this.props; - const classes = classNames('euiRange', className); + const { id } = this.state; + const digitTolerance = Math.max(String(min).length, String(max).length); + const showInputOnly = showInput === 'inputWithPopover'; + const canShowDropdown = showInputOnly && !readOnly && !disabled; + + const theInput = !!showInput ? ( + + ) : ( + undefined + ); - return ( - + const classes = classNames('euiRange', className); + + const theRange = ( + {showLabels && ( {min} @@ -66,6 +145,7 @@ export class EuiRange extends Component { )} + {showRange && this.isValid && ( + + )} + {showValue && !!String(value).length && ( )} - - {showRange && this.isValid && ( - - )} {showLabels && ( {max} )} - {showInput && ( - - )} + {!showInputOnly && theInput} ); + + const thePopover = showInputOnly ? ( + + {theRange} + + ) : ( + undefined + ); + + return thePopover ? thePopover : theRange; } } @@ -152,9 +238,10 @@ EuiRange.propTypes = { */ showLabels: PropTypes.bool, /** - * Displays an extra input control for direct manipulation + * Pass `true` to displays an extra input control for direct manipulation. + * Pass `'inputWithPopover'` to only show the input but show the range in a dropdown. */ - showInput: PropTypes.bool, + showInput: PropTypes.oneOf([true, false, 'inputWithPopover']), /** * Shows clickable tick marks and labels at the given interval (`step`/`tickInterval`) */ diff --git a/src/components/form/range/range.test.js b/src/components/form/range/range.test.js index 652a6c104be..a9c113e1b16 100644 --- a/src/components/form/range/range.test.js +++ b/src/components/form/range/range.test.js @@ -4,6 +4,8 @@ import { requiredProps } from '../../../test/required_props'; import { EuiRange } from './range'; +jest.mock('../form_row/make_id', () => () => 'generated-id'); + describe('EuiRange', () => { test('is rendered', () => { const component = render( @@ -99,6 +101,23 @@ describe('EuiRange', () => { expect(component).toMatchSnapshot(); }); + test('input only should render', () => { + const component = render( + {}} + showInput="inputWithPopover" + {...requiredProps} + /> + ); + + expect(component).toMatchSnapshot(); + }); + test('levels should render', () => { const component = render( = ({ upperValue, max, min, + compressed, }) => { // Calculate the width the range based on value // const rangeWidth = (value - min) / (max - min); @@ -29,6 +31,7 @@ export const EuiRangeHighlight: FunctionComponent = ({ const classes = classNames('euiRangeHighlight', { 'euiRangeHighlight--hasTicks': showTicks, + 'euiRangeHighlight--compressed': compressed, }); const progressClasses = classNames('euiRangeHighlight__progress', { diff --git a/src/components/form/range/range_input.js b/src/components/form/range/range_input.js index 732a28b5541..d7e240f9411 100644 --- a/src/components/form/range/range_input.js +++ b/src/components/form/range/range_input.js @@ -14,12 +14,16 @@ export const EuiRangeInput = ({ name, side, digitTolerance, + fullWidth, + autoSize, ...rest }) => { // Chrome will properly size the input based on the max value, but FF & IE do not. // Calculate the width of the input based on highest number of characters. // Add 2 to accomodate for input stepper - const widthStyle = { width: `${digitTolerance / 1.25 + 2}em` }; + const widthStyle = autoSize + ? { width: `${digitTolerance / 1.25 + 2}em` } + : undefined; return ( ); @@ -48,7 +53,11 @@ EuiRangeInput.propTypes = { name: PropTypes.string, digitTolerance: PropTypes.number.isRequired, side: PropTypes.oneOf(['min', 'max']), + fullWidth: PropTypes.bool, + autoSize: PropTypes.bool, + inputRef: PropTypes.func, }; EuiRangeInput.defaultProps = { side: 'max', + autoSize: true, }; diff --git a/src/components/form/range/range_levels.tsx b/src/components/form/range/range_levels.tsx index b59c63cf852..c607b100aa4 100644 --- a/src/components/form/range/range_levels.tsx +++ b/src/components/form/range/range_levels.tsx @@ -21,6 +21,7 @@ export interface EuiRangeLevelsProps { max: number; min: number; showTicks?: boolean; + compressed?: boolean; } export const EuiRangeLevels: FunctionComponent = ({ @@ -28,6 +29,7 @@ export const EuiRangeLevels: FunctionComponent = ({ max, min, showTicks, + compressed, }) => { const validateLevelIsInRange = (level: EuiRangeLevel) => { if (level.min < min) { @@ -44,6 +46,7 @@ export const EuiRangeLevels: FunctionComponent = ({ const classes = classNames('euiRangeLevels', { 'euiRangeLevels--hasTicks': showTicks, + 'euiRangeLevels--compressed': compressed, }); return ( diff --git a/src/components/form/range/range_slider.tsx b/src/components/form/range/range_slider.tsx index a9120f0fc74..4c16e277000 100644 --- a/src/components/form/range/range_slider.tsx +++ b/src/components/form/range/range_slider.tsx @@ -16,6 +16,7 @@ export type EuiRangeSliderProps = InputHTMLAttributes & min: number; max: number; step?: number; + compressed?: boolean; hasFocus?: boolean; showRange?: boolean; showTicks?: boolean; @@ -43,6 +44,7 @@ export const EuiRangeSlider: FunctionComponent< showTicks, showRange, hasFocus, + compressed, ...rest }, ref: Ref @@ -53,6 +55,7 @@ export const EuiRangeSlider: FunctionComponent< 'euiRangeSlider--hasTicks': showTicks, 'euiRangeSlider--hasFocus': hasFocus, 'euiRangeSlider--hasRange': showRange, + 'euiRangeSlider--compressed': compressed, }, className ); diff --git a/src/components/form/range/range_ticks.tsx b/src/components/form/range/range_ticks.tsx index 545707c5d54..d8d63ffc322 100644 --- a/src/components/form/range/range_ticks.tsx +++ b/src/components/form/range/range_ticks.tsx @@ -24,6 +24,7 @@ export type EuiRangeTicksProps = Omit< value?: number | string | Array; min: number; max: number; + compressed?: boolean; interval?: number; disabled?: boolean; onChange?: MouseEventHandler; @@ -38,6 +39,7 @@ export const EuiRangeTicks: FunctionComponent = ({ max, min, interval = 1, + compressed, }) => { // Calculate the width of each tick mark const percentageWidth = (interval / (max - min + interval)) * 100; @@ -48,8 +50,12 @@ export const EuiRangeTicks: FunctionComponent = ({ ? undefined : { margin: `0 ${percentageWidth / -2}%`, left: 0, right: 0 }; + const classes = classNames('euiRangeTicks', { + 'euiRangeTicks--compressed': compressed, + }); + return ( -
+
{tickSequence.map(tickValue => { const tickStyle: { left?: string; width?: string } = {}; let customTick; diff --git a/src/components/form/range/range_tooltip.tsx b/src/components/form/range/range_tooltip.tsx index 2795deacf3e..73b40f710a9 100644 --- a/src/components/form/range/range_tooltip.tsx +++ b/src/components/form/range/range_tooltip.tsx @@ -9,6 +9,7 @@ export interface EuiRangeTooltipProps { min: number; name?: string; showTicks?: boolean; + compressed?: boolean; } export const EuiRangeTooltip: FunctionComponent = ({ @@ -19,7 +20,12 @@ export const EuiRangeTooltip: FunctionComponent = ({ min, name, showTicks, + compressed, }) => { + const classes = classNames('euiRangeTooltip', { + 'euiRangeTooltip--compressed': compressed, + }); + // Calculate the left position based on value let val = 0; if (typeof value === 'number') { @@ -52,7 +58,7 @@ export const EuiRangeTooltip: FunctionComponent = ({ ); return ( -
+
; + compressed?: boolean; disabled?: boolean; showTicks?: boolean; tickInterval?: number; @@ -117,6 +118,7 @@ export class EuiRangeTrack extends Component { levels, onChange, value, + compressed, } = this.props; // TODO: Move these to only re-calculate if no-value props have changed @@ -147,9 +149,9 @@ export class EuiRangeTrack extends Component { return (
- {children} {levels && !!levels.length && ( { {tickSequence && ( { interval={tickInterval || step} /> )} + {children}
); } diff --git a/src/components/form/range/range_wrapper.tsx b/src/components/form/range/range_wrapper.tsx index 78cb6783f21..8179bab0239 100644 --- a/src/components/form/range/range_wrapper.tsx +++ b/src/components/form/range/range_wrapper.tsx @@ -4,17 +4,20 @@ import classNames from 'classnames'; export interface EuiRangeWrapperProps { className?: string; fullWidth?: boolean; + compressed?: boolean; } export const EuiRangeWrapper: FunctionComponent = ({ children, className, fullWidth, + compressed, }) => { const classes = classNames( 'euiRangeWrapper', { 'euiRangeWrapper--fullWidth': fullWidth, + 'euiRangeWrapper--compressed': compressed, }, className ); diff --git a/src/components/popover/input_popover.tsx b/src/components/popover/input_popover.tsx index 6659708b0aa..a9f4f2df543 100644 --- a/src/components/popover/input_popover.tsx +++ b/src/components/popover/input_popover.tsx @@ -15,9 +15,11 @@ import { cascadingMenuKeyCodes } from '../../services'; interface EuiInputPopoverProps extends Omit { + disableFocusTrap?: boolean; fullWidth?: boolean; input: EuiPopoverProps['button']; inputRef?: EuiPopoverProps['buttonRef']; + onPanelResize?: (width?: number) => void; } type Props = CommonProps & @@ -27,8 +29,10 @@ type Props = CommonProps & export const EuiInputPopover: FunctionComponent = ({ children, className, + disableFocusTrap = false, input, fullWidth = false, + onPanelResize, ...props }) => { const [inputEl, setInputEl] = useState(); @@ -42,6 +46,9 @@ export const EuiInputPopover: FunctionComponent = ({ if (panelEl && (!!inputElWidth || !!width)) { const newWidth = !!width ? width : inputElWidth; panelEl.style.width = `${newWidth}px`; + if (onPanelResize) { + onPanelResize(newWidth); + } } }; const onResize = () => { @@ -68,8 +75,9 @@ export const EuiInputPopover: FunctionComponent = ({ ); }); if ( - tabbableItems.length && - tabbableItems[tabbableItems.length - 1] === document.activeElement + disableFocusTrap || + (tabbableItems.length && + tabbableItems[tabbableItems.length - 1] === document.activeElement) ) { props.closePopover(); } @@ -96,7 +104,7 @@ export const EuiInputPopover: FunctionComponent = ({ panelRef={panelRef} className={classes} {...props}> - +
{children}
From 60a5362b89e9eade815b8e16ea56de5e5752642c Mon Sep 17 00:00:00 2001 From: cchaos Date: Tue, 10 Sep 2019 15:47:46 -0400 Subject: [PATCH 09/20] Added some display toggles for ranges and ranges with dropdowns to the complex example Has issues --- .../views/form_compressed/complex_example.js | 77 +++++++++++-------- src-docs/src/views/range/input_only.js | 62 ++++++--------- src-docs/src/views/range/states.js | 73 ++++++++---------- 3 files changed, 101 insertions(+), 111 deletions(-) diff --git a/src-docs/src/views/form_compressed/complex_example.js b/src-docs/src/views/form_compressed/complex_example.js index 5ca9da1b1f7..1a36d667a80 100644 --- a/src-docs/src/views/form_compressed/complex_example.js +++ b/src-docs/src/views/form_compressed/complex_example.js @@ -48,12 +48,24 @@ export default class extends Component { opacityValue: '20', buttonGroupIdSelected: `${idPrefix}1`, color: '#DB1374', + borderValue: 3, + popoverSliderValues: 16, }; this.selectTooltipContent = 'Otherwise use an EuiToolTip around the label of the form row.'; } + onPopoverSliderValueChange = e => { + this.setState({ + popoverSliderValues: e.target.value, + }); + }; + + onBorderChange = e => { + this.setState({ borderValue: e.target.value }); + }; + onColorChange = value => { this.setState({ color: value }); }; @@ -88,18 +100,16 @@ export default class extends Component { compressed prepend="Zoom levels" startControl={ - } endControl={ - } @@ -116,10 +126,12 @@ export default class extends Component { compressed value={this.state.opacityValue} onChange={this.onRangeChange} - // append="%" + append="%" /> + + {this.selectTooltipContent} @@ -153,31 +165,32 @@ export default class extends Component { /> - + + + + + - } - append="px" - /> - - - - , + 'kibana_sample_ecommerce_data', ]} - compressed + append="px" /> @@ -200,11 +213,12 @@ export default class extends Component { /> - - - diff --git a/src-docs/src/views/range/input_only.js b/src-docs/src/views/range/input_only.js index f6e1cd08463..65cf3a7084e 100644 --- a/src-docs/src/views/range/input_only.js +++ b/src-docs/src/views/range/input_only.js @@ -2,6 +2,8 @@ import React, { Component, Fragment } from 'react'; import { EuiRange, EuiSpacer, EuiDualRange } from '../../../../src/components'; +import { DisplayToggles } from '../form_controls/display_toggles'; + import makeId from '../../../../src/components/form/form_row/make_id'; export default class extends Component { @@ -42,48 +44,30 @@ export default class extends Component { render() { return ( - - - - - - - - - + + + - + + + ); } diff --git a/src-docs/src/views/range/states.js b/src-docs/src/views/range/states.js index 926f58bb189..fc97f01f8ab 100644 --- a/src-docs/src/views/range/states.js +++ b/src-docs/src/views/range/states.js @@ -1,11 +1,7 @@ import React, { Component, Fragment } from 'react'; -import { - EuiRange, - EuiSpacer, - EuiFormHelpText, - EuiDualRange, -} from '../../../../src/components'; +import { EuiRange, EuiSpacer, EuiDualRange } from '../../../../src/components'; +import { DisplayToggles } from '../form_controls/display_toggles'; import makeId from '../../../../src/components/form/form_row/make_id'; @@ -47,45 +43,38 @@ export default class extends Component { render() { return ( - - - Recommended levels are {this.levels[1].min} and above. - + + + - - - Recommended size is {this.levels[1].min}kb and above. - + + + ); } From cfb4a3604d222d92bfa5d0ea3f23bb556f6a1258 Mon Sep 17 00:00:00 2001 From: cchaos Date: Tue, 10 Sep 2019 17:13:48 -0400 Subject: [PATCH 10/20] Fix console errors --- src-docs/src/views/range/states.js | 4 ++-- .../form/form_control_layout/form_control_layout.tsx | 9 +++++---- src/components/form/range/dual_range.js | 6 ++++++ src/components/form/range/range.js | 4 ++++ 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src-docs/src/views/range/states.js b/src-docs/src/views/range/states.js index fc97f01f8ab..39b20e48929 100644 --- a/src-docs/src/views/range/states.js +++ b/src-docs/src/views/range/states.js @@ -43,7 +43,7 @@ export default class extends Component { render() { return ( - + - + & { /** * Creates an input group with element(s) coming before children */ - prepend?: string | ReactElements; + prepend?: PrependAppendType; /** * Creates an input group with element(s) coming after children */ - append?: string | ReactElements; + append?: PrependAppendType; children?: ReactNode; icon?: EuiFormControlLayoutIconsProps['icon']; clear?: EuiFormControlLayoutIconsProps['clear']; @@ -95,7 +96,7 @@ export class EuiFormControlLayout extends Component { renderSideNode( side: 'append' | 'prepend', - nodes?: string | ReactElements, + nodes?: PrependAppendType, inputId?: string ) { if (!nodes) { diff --git a/src/components/form/range/dual_range.js b/src/components/form/range/dual_range.js index 8e0b3131bbb..8d9905b4fcd 100644 --- a/src/components/form/range/dual_range.js +++ b/src/components/form/range/dual_range.js @@ -298,6 +298,8 @@ export class EuiDualRange extends Component { showRange, value, style, + isLoading, + isInvalid, ...rest } = this.props; @@ -326,6 +328,8 @@ export class EuiDualRange extends Component { readOnly={readOnly} autoSize={!showInputOnly} fullWidth={!!showInputOnly && fullWidth} + isLoading={!!showInputOnly && isLoading} + isInvalid={isInvalid} controlOnly={showInputOnly} inputRef={node => this.inputRef(node, 'minNode')} /> @@ -352,7 +356,9 @@ export class EuiDualRange extends Component { readOnly={readOnly} autoSize={!showInputOnly} fullWidth={!!showInputOnly && fullWidth} + isLoading={!!showInputOnly && isLoading} controlOnly={showInputOnly} + isInvalid={isInvalid} inputRef={node => this.inputRef(node, 'maxNode')} /> ) : ( diff --git a/src/components/form/range/range.js b/src/components/form/range/range.js index 2de1b30db00..7fb313b2b23 100644 --- a/src/components/form/range/range.js +++ b/src/components/form/range/range.js @@ -80,6 +80,7 @@ export class EuiRange extends Component { compressed, disabled, fullWidth, + isLoading, readOnly, id: propsId, max, @@ -100,6 +101,7 @@ export class EuiRange extends Component { value, style, tabIndex, + isInvalid, ...rest } = this.props; @@ -123,6 +125,8 @@ export class EuiRange extends Component { name={name} onFocus={canShowDropdown ? this.onInputFocus : undefined} fullWidth={showInputOnly && fullWidth} + isLoading={showInputOnly && isLoading} + isInvalid={isInvalid} autoSize={!showInputOnly} inputRef={this.inputRef} {...rest} From 21fa2c67abfe0051b8e633e572ab9fc2bbe370cb Mon Sep 17 00:00:00 2001 From: cchaos Date: Tue, 10 Sep 2019 17:44:35 -0400 Subject: [PATCH 11/20] Some fixes - Ranges use div spacers between slider and input instead of margin - Pre/Appends are restrictred to 50% width and truncated --- .../views/form_compressed/complex_example.js | 2 +- .../_form_control_layout.scss | 2 + .../__snapshots__/dual_range.test.js.snap | 72 +++++++++++++++++++ .../range/__snapshots__/range.test.js.snap | 39 ++++++++++ src/components/form/range/_index.scss | 1 + src/components/form/range/_range.scss | 3 + src/components/form/range/_range_input.scss | 8 --- src/components/form/range/dual_range.js | 14 +++- src/components/form/range/range.js | 7 +- 9 files changed, 136 insertions(+), 12 deletions(-) create mode 100644 src/components/form/range/_range.scss diff --git a/src-docs/src/views/form_compressed/complex_example.js b/src-docs/src/views/form_compressed/complex_example.js index 1a36d667a80..262613869b5 100644 --- a/src-docs/src/views/form_compressed/complex_example.js +++ b/src-docs/src/views/form_compressed/complex_example.js @@ -264,7 +264,7 @@ export default class extends Component { Border - + +
@@ -18,6 +21,9 @@ exports[`EuiDualRange allows value prop to accept empty strings 1`] = ` type="range" />
+
`; @@ -25,6 +31,9 @@ exports[`EuiDualRange allows value prop to accept numbers 1`] = `
+
@@ -47,6 +56,9 @@ exports[`EuiDualRange allows value prop to accept numbers 1`] = ` type="range" />
+
`; @@ -54,6 +66,9 @@ exports[`EuiDualRange is rendered 1`] = `
+
@@ -79,6 +94,9 @@ exports[`EuiDualRange is rendered 1`] = ` type="range" />
+
`; @@ -86,6 +104,9 @@ exports[`EuiDualRange props compressed should render 1`] = `
+
@@ -108,6 +129,9 @@ exports[`EuiDualRange props compressed should render 1`] = ` type="range" />
+
`; @@ -115,6 +139,9 @@ exports[`EuiDualRange props custom ticks should render 1`] = `
+
+
`; @@ -169,6 +199,9 @@ exports[`EuiDualRange props disabled should render 1`] = `
+
@@ -192,6 +225,9 @@ exports[`EuiDualRange props disabled should render 1`] = ` type="range" />
+
`; @@ -199,6 +235,9 @@ exports[`EuiDualRange props fullWidth should render 1`] = `
+
@@ -221,6 +260,9 @@ exports[`EuiDualRange props fullWidth should render 1`] = ` type="range" />
+
`; @@ -247,6 +289,9 @@ exports[`EuiDualRange props inputs should render 1`] = ` />
+
@@ -272,6 +317,9 @@ exports[`EuiDualRange props inputs should render 1`] = ` type="range" />
+
@@ -298,6 +346,9 @@ exports[`EuiDualRange props labels should render 1`] = `
+
+
`; @@ -337,6 +391,9 @@ exports[`EuiDualRange props levels should render 1`] = `
+
@@ -371,6 +428,9 @@ exports[`EuiDualRange props levels should render 1`] = ` type="range" />
+
`; @@ -428,6 +488,9 @@ exports[`EuiDualRange props range should render 1`] = `
+
@@ -450,6 +513,9 @@ exports[`EuiDualRange props range should render 1`] = ` type="range" />
+
`; @@ -457,6 +523,9 @@ exports[`EuiDualRange props ticks should render 1`] = `
+
+
`; diff --git a/src/components/form/range/__snapshots__/range.test.js.snap b/src/components/form/range/__snapshots__/range.test.js.snap index 6d2a528f324..f9dcab2eeb3 100644 --- a/src/components/form/range/__snapshots__/range.test.js.snap +++ b/src/components/form/range/__snapshots__/range.test.js.snap @@ -27,6 +27,9 @@ exports[`EuiRange allows value prop to accept a number 1`] = `
+
`; @@ -47,6 +50,9 @@ exports[`EuiRange allows value prop to accept empty string 1`] = ` value="" />
+
`; @@ -70,6 +76,9 @@ exports[`EuiRange is rendered 1`] = ` value="8" />
+
`; @@ -89,6 +98,9 @@ exports[`EuiRange props compressed should render 1`] = ` type="range" />
+
`; @@ -133,6 +145,9 @@ exports[`EuiRange props custom ticks should render 1`] = ` type="range" />
+
`; @@ -153,6 +168,9 @@ exports[`EuiRange props disabled should render 1`] = ` type="range" />
+
`; @@ -172,6 +190,9 @@ exports[`EuiRange props fullWidth should render 1`] = ` type="range" />
+
`; @@ -228,6 +249,9 @@ exports[`EuiRange props input should render 1`] = ` value="8" />
+
@@ -277,6 +301,9 @@ exports[`EuiRange props labels should render 1`] = ` > 100 +
`; @@ -308,6 +335,9 @@ exports[`EuiRange props levels should render 1`] = ` type="range" />
+
`; @@ -336,6 +366,9 @@ exports[`EuiRange props range should render 1`] = ` value="8" />
+
`; @@ -415,6 +448,9 @@ exports[`EuiRange props ticks should render 1`] = ` type="range" />
+
`; @@ -445,5 +481,8 @@ exports[`EuiRange props value should render 1`] = `
+
`; diff --git a/src/components/form/range/_index.scss b/src/components/form/range/_index.scss index b118aebe3d7..b8d4204e060 100644 --- a/src/components/form/range/_index.scss +++ b/src/components/form/range/_index.scss @@ -1,6 +1,7 @@ @import 'variables'; @import 'mixins'; +@import 'range'; @import 'range_highlight'; @import 'range_input'; @import 'range_label'; diff --git a/src/components/form/range/_range.scss b/src/components/form/range/_range.scss new file mode 100644 index 00000000000..3e94f34ce31 --- /dev/null +++ b/src/components/form/range/_range.scss @@ -0,0 +1,3 @@ +.euiRange__horizontalSpacer { + width: $euiSize; +} diff --git a/src/components/form/range/_range_input.scss b/src/components/form/range/_range_input.scss index e4954a325c8..0de7fda4ef9 100644 --- a/src/components/form/range/_range_input.scss +++ b/src/components/form/range/_range_input.scss @@ -2,14 +2,6 @@ width: auto; min-width: $euiSize * 4; - &--min { - margin-right: $euiSize; - } - - &--max { - margin-left: $euiSize; - } - .euiRange__popover & { // sass-lint:disable no-important margin: 0 !important; diff --git a/src/components/form/range/dual_range.js b/src/components/form/range/dual_range.js index 8d9905b4fcd..2839c88e154 100644 --- a/src/components/form/range/dual_range.js +++ b/src/components/form/range/dual_range.js @@ -371,7 +371,12 @@ export class EuiDualRange extends Component { className={classes} fullWidth={fullWidth} compressed={compressed}> - {!showInputOnly && minInput} + {!showInputOnly && ( + <> + {minInput} +
+ + )} {showLabels && ( {min} @@ -461,7 +466,12 @@ export class EuiDualRange extends Component { )} {showLabels && {max}} - {!showInputOnly && maxInput} + {!showInputOnly && ( + <> +
+ {maxInput} + + )} ); diff --git a/src/components/form/range/range.js b/src/components/form/range/range.js index 7fb313b2b23..5150201c17c 100644 --- a/src/components/form/range/range.js +++ b/src/components/form/range/range.js @@ -205,7 +205,12 @@ export class EuiRange extends Component { {max} )} - {!showInputOnly && theInput} + {!showInputOnly && ( + <> +
+ {theInput} + + )} ); From c96f483686dd559213472d12f7f973163232f1ef Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Tue, 10 Sep 2019 17:01:33 -0500 Subject: [PATCH 12/20] [feature/compressed-editor-controls] EuiRange + EuiFormRow (#2321) * focus management * add euiformrow to some examples * use prevention flag * Revert "add euiformrow to some examples" This reverts commit f93ef4e36d426fe7b5fcc02d308b3d89365baa11. * clean up --- src/components/form/range/dual_range.js | 86 +++++++++++++++---------- src/components/form/range/range.js | 55 ++++++++-------- 2 files changed, 82 insertions(+), 59 deletions(-) diff --git a/src/components/form/range/dual_range.js b/src/components/form/range/dual_range.js index 8e0b3131bbb..d2ae35537e6 100644 --- a/src/components/form/range/dual_range.js +++ b/src/components/form/range/dual_range.js @@ -25,8 +25,7 @@ export class EuiDualRange extends Component { rangeWidth: null, }; - maxNode = null; - minNode = null; + preventPopoverClose = false; rangeSliderRef = null; handleRangeSliderRefUpdate = ref => { this.rangeSliderRef = ref; @@ -234,21 +233,48 @@ export class EuiDualRange extends Component { }); }; - onInputFocus = () => { + onThumbFocus = e => { + if (this.props.onFocus) { + this.props.onFocus(e); + } + this.toggleHasFocus(true); + }; + + onThumbBlur = e => { + if (this.props.onBlur) { + this.props.onBlur(e); + } + this.toggleHasFocus(false); + }; + + onInputFocus = e => { + if (this.props.onFocus) { + this.props.onFocus(e); + } + this.preventPopoverClose = true; this.setState({ isPopoverOpen: true, }); }; - onInputBlur = e => { - // Firefox returns `relatedTarget` as `null` for security reasons, but provides a proprietary `explicitOriginalTarget` - const relatedTarget = e.relatedTarget || e.explicitOriginalTarget; - if (!relatedTarget || relatedTarget.id !== this.state.id) { + onInputBlur = e => + setTimeout(() => { + // Safari does not recognize any focus-related eventing for input[type=range] + // making it impossible to capture its state using active/focus/relatedTarget + // Instead, a prevention flag is set on mousedown, with a waiting period here. + // Mousedown is viable because in the popover case, it is inaccessable via keyboard (intentionally) + if (this.preventPopoverClose) { + this.preventPopoverClose = false; + return; + } + if (this.props.onBlur) { + this.props.onBlur(e); + } this.closePopover(); - } - }; + }, 200); closePopover = () => { + this.preventPopoverClose = false; this.setState({ isPopoverOpen: false, }); @@ -260,22 +286,6 @@ export class EuiDualRange extends Component { }); }; - inputRef = (node, ref) => { - if (!this.props.showInput !== 'inputWithPopover') return; - - // IE11 doesn't support the `relatedTarget` event property for blur events - // but does add it for focusout. React doesn't support `onFocusOut` so here we are. - if (this[ref] != null) { - this[ref].removeEventListener('focusout', this.onInputBlur); - } - - this[ref] = node; - - if (this[ref]) { - this[ref].addEventListener('focusout', this.onInputBlur); - } - }; - render() { const { className, @@ -294,7 +304,9 @@ export class EuiDualRange extends Component { tickInterval, ticks, levels, + onBlur, onChange, + onFocus, showRange, value, style, @@ -322,12 +334,15 @@ export class EuiDualRange extends Component { name={`${name}-minValue`} aria-describedby={this.props['aria-describedby']} aria-label={this.props['aria-label']} - onFocus={canShowDropdown ? this.onInputFocus : undefined} + onFocus={canShowDropdown ? this.onInputFocus : onFocus} + onBlur={canShowDropdown ? this.onInputBlur : onBlur} readOnly={readOnly} autoSize={!showInputOnly} fullWidth={!!showInputOnly && fullWidth} controlOnly={showInputOnly} - inputRef={node => this.inputRef(node, 'minNode')} + onMouseDown={ + showInputOnly ? () => (this.preventPopoverClose = true) : null + } /> ) : ( undefined @@ -348,12 +363,15 @@ export class EuiDualRange extends Component { name={`${name}-maxValue`} aria-describedby={this.props['aria-describedby']} aria-label={this.props['aria-label']} - onFocus={canShowDropdown ? this.onInputFocus : undefined} + onFocus={canShowDropdown ? this.onInputFocus : onFocus} + onBlur={canShowDropdown ? this.onInputBlur : onBlur} readOnly={readOnly} autoSize={!showInputOnly} fullWidth={!!showInputOnly && fullWidth} controlOnly={showInputOnly} - inputRef={node => this.inputRef(node, 'maxNode')} + onMouseDown={ + showInputOnly ? () => (this.preventPopoverClose = true) : null + } /> ) : ( undefined @@ -412,6 +430,8 @@ export class EuiDualRange extends Component { aria-hidden={true} tabIndex={-1} showRange={showRange} + onFocus={onFocus} + onBlur={onBlur} {...rest} /> @@ -425,8 +445,8 @@ export class EuiDualRange extends Component { showTicks={showTicks} showInput={!!showInput} onKeyDown={this.handleLowerKeyDown} - onFocus={() => this.toggleHasFocus(true)} - onBlur={() => this.toggleHasFocus(false)} + onFocus={this.onThumbFocus} + onBlur={this.onThumbBlur} style={this.calculateThumbPositionStyle( this.lowerValue || min, this.state.rangeWidth @@ -442,8 +462,8 @@ export class EuiDualRange extends Component { showTicks={showTicks} showInput={!!showInput} onKeyDown={this.handleUpperKeyDown} - onFocus={() => this.toggleHasFocus(true)} - onBlur={() => this.toggleHasFocus(false)} + onFocus={this.onThumbFocus} + onBlur={this.onThumbBlur} style={this.calculateThumbPositionStyle( this.upperValue || max, this.state.rangeWidth diff --git a/src/components/form/range/range.js b/src/components/form/range/range.js index 2de1b30db00..13c71f5827f 100644 --- a/src/components/form/range/range.js +++ b/src/components/form/range/range.js @@ -22,7 +22,7 @@ export class EuiRange extends Component { isPopoverOpen: false, }; - this.inputNode = null; + this.preventPopoverClose = false; } handleOnChange = e => { @@ -38,42 +38,38 @@ export class EuiRange extends Component { return isWithinRange(this.props.min, this.props.max, this.props.value); } - onInputFocus = () => { + onInputFocus = e => { + if (this.props.onFocus) { + this.props.onFocus(e); + } this.setState({ isPopoverOpen: true, }); }; - onInputBlur = e => { - // Firefox returns `relatedTarget` as `null` for security reasons, but provides a proprietary `explicitOriginalTarget` - const relatedTarget = e.relatedTarget || e.explicitOriginalTarget; - if (!relatedTarget || relatedTarget.id !== this.state.id) { + onInputBlur = e => + setTimeout(() => { + // Safari does not recognize any focus-related eventing for input[type=range] + // making it impossible to capture its state using active/focus/relatedTarget + // Instead, a prevention flag is set on mousedown, with a waiting period here. + // Mousedown is viable because in the popover case, it is inaccessable via keyboard (intentionally) + if (this.preventPopoverClose) { + this.preventPopoverClose = false; + return; + } + if (this.props.onBlur) { + this.props.onBlur(e); + } this.closePopover(); - } - }; + }, 200); closePopover = () => { + this.preventPopoverClose = false; this.setState({ isPopoverOpen: false, }); }; - inputRef = node => { - if (!this.props.showInput !== 'inputWithPopover') return; - - // IE11 and Safar don't support the `relatedTarget` event property for blur events - // but do add it for focusout. React doesn't support `onFocusOut` so here we are. - if (this.inputNode != null) { - this.inputNode.removeEventListener('focusout', this.onInputBlur); - } - - this.inputNode = node; - - if (this.inputNode) { - this.inputNode.addEventListener('focusout', this.onInputBlur); - } - }; - render() { const { className, @@ -96,7 +92,9 @@ export class EuiRange extends Component { showValue, valueAppend, valuePrepend, + onBlur, onChange, + onFocus, value, style, tabIndex, @@ -121,10 +119,10 @@ export class EuiRange extends Component { compressed={compressed} onChange={this.handleOnChange} name={name} - onFocus={canShowDropdown ? this.onInputFocus : undefined} + onFocus={canShowDropdown ? this.onInputFocus : onFocus} + onBlur={canShowDropdown ? this.onInputBlur : onBlur} fullWidth={showInputOnly && fullWidth} autoSize={!showInputOnly} - inputRef={this.inputRef} {...rest} /> ) : ( @@ -180,6 +178,11 @@ export class EuiRange extends Component { showTicks={showTicks} showRange={showRange} tabIndex={showInput ? -1 : tabIndex || null} + onMouseDown={ + showInputOnly ? () => (this.preventPopoverClose = true) : null + } + onFocus={showInput === true ? null : onFocus} + onBlur={showInputOnly ? this.onInputBlur : onBlur} {...rest} /> From a494d3538cec62bd30c8bce31d01a79e82be3a24 Mon Sep 17 00:00:00 2001 From: cchaos Date: Tue, 10 Sep 2019 20:15:33 -0400 Subject: [PATCH 13/20] Cleanup --- src-docs/src/views/combo_box/combo_box.js | 2 +- .../views/form_compressed/complex_example.js | 26 +++---- .../views/form_compressed/form_compressed.js | 21 ----- .../views/form_compressed/form_horizontal.js | 55 ------------- .../form_compressed/form_horizontal_help.js | 77 +++++++------------ .../views/form_controls/display_toggles.js | 9 ++- .../src/views/form_controls/field_text.js | 1 - src-docs/src/views/range/input_only.js | 2 +- src/components/combo_box/_combo_box.scss | 1 - .../combo_box_input/_combo_box_pill.scss | 2 +- .../date_popover/_absolute_tab.scss | 2 +- .../form/file_picker/_file_picker.scss | 2 +- .../_form_control_layout.scss | 2 +- .../__snapshots__/dual_range.test.js.snap | 72 ++++++++--------- .../range/__snapshots__/range.test.js.snap | 64 +++++++-------- .../form/range/_range_highlight.scss | 1 - src/components/form/range/dual_range.js | 7 +- src/components/form/range/dual_range.test.js | 2 +- src/components/form/range/range.test.js | 2 +- 19 files changed, 127 insertions(+), 223 deletions(-) diff --git a/src-docs/src/views/combo_box/combo_box.js b/src-docs/src/views/combo_box/combo_box.js index e2144f9db1b..973334206d4 100644 --- a/src-docs/src/views/combo_box/combo_box.js +++ b/src-docs/src/views/combo_box/combo_box.js @@ -84,7 +84,7 @@ export default class extends Component { const { selectedOptions } = this.state; return ( /* DisplayToggles wrapper for Docs only */ - + { - this.setState({ - radioIdSelected: optionId, - }); - }; - render() { return ( diff --git a/src-docs/src/views/form_compressed/form_horizontal.js b/src-docs/src/views/form_compressed/form_horizontal.js index 63f00966b13..559015f8654 100644 --- a/src-docs/src/views/form_compressed/form_horizontal.js +++ b/src-docs/src/views/form_compressed/form_horizontal.js @@ -11,48 +11,12 @@ import { EuiPanel, } from '../../../../src/components'; -import makeId from '../../../../src/components/form/form_row/make_id'; - export default class extends Component { constructor(props) { super(props); - const idPrefix = makeId(); - this.state = { isSwitchChecked: false, - checkboxes: [ - { - id: `${idPrefix}0`, - label: 'Option one', - }, - { - id: `${idPrefix}1`, - label: 'Option two is checked by default', - }, - { - id: `${idPrefix}2`, - label: 'Option three', - }, - ], - checkboxIdToSelectedMap: { - [`${idPrefix}1`]: true, - }, - radios: [ - { - id: `${idPrefix}4`, - label: 'Option one', - }, - { - id: `${idPrefix}5`, - label: 'Option two is selected by default', - }, - { - id: `${idPrefix}6`, - label: 'Option three', - }, - ], - radioIdSelected: `${idPrefix}5`, comboBoxSelectionOptions: [], value: '20', }; @@ -70,25 +34,6 @@ export default class extends Component { }); }; - onCheckboxChange = optionId => { - const newCheckboxIdToSelectedMap = { - ...this.state.checkboxIdToSelectedMap, - ...{ - [optionId]: !this.state.checkboxIdToSelectedMap[optionId], - }, - }; - - this.setState({ - checkboxIdToSelectedMap: newCheckboxIdToSelectedMap, - }); - }; - - onRadioChange = optionId => { - this.setState({ - radioIdSelected: optionId, - }); - }; - render() { return ( diff --git a/src-docs/src/views/form_compressed/form_horizontal_help.js b/src-docs/src/views/form_compressed/form_horizontal_help.js index 9cdc912c92d..b97b4b2ac66 100644 --- a/src-docs/src/views/form_compressed/form_horizontal_help.js +++ b/src-docs/src/views/form_compressed/form_horizontal_help.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React from 'react'; import { EuiFieldText, @@ -9,51 +9,32 @@ import { } from '../../../../src/components'; import { EuiToolTip } from '../../../../src/components/tool_tip'; -export default class extends Component { - constructor(props) { - super(props); +export default () => ( + + + + - this.state = { - isSwitchChecked: false, - value: '20', - }; - } - - onSwitchChange = () => { - this.setState({ - isSwitchChecked: !this.state.isSwitchChecked, - }); - }; - - render() { - return ( - - - - - - - - Label - - - } - display="columnCompressed"> - - - - ); - } -} + + + Label + + + } + display="columnCompressed"> + + + +); diff --git a/src-docs/src/views/form_controls/display_toggles.js b/src-docs/src/views/form_controls/display_toggles.js index aa9206c2b3f..b7cb8dea9a1 100644 --- a/src-docs/src/views/form_controls/display_toggles.js +++ b/src-docs/src/views/form_controls/display_toggles.js @@ -39,6 +39,7 @@ export class DisplayToggles extends Component { render() { const { + canIsDisabled, canDisabled, canReadOnly, canLoading, @@ -49,12 +50,11 @@ export class DisplayToggles extends Component { canInvalid, children, extras, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - onUpdate, } = this.props; const canProps = {}; if (canDisabled) canProps.disabled = this.state.disabled; + if (canIsDisabled) canProps.isDisabled = this.state.disabled; if (canReadOnly) canProps.readOnly = this.state.readOnly; if (canLoading) canProps.isLoading = this.state.loading; if (canFullWidth) canProps.fullWidth = this.state.fullWidth; @@ -84,7 +84,7 @@ export class DisplayToggles extends Component { }>
- {canDisabled && ( + {(canDisabled || canIsDisabled) && ( ); diff --git a/src-docs/src/views/range/input_only.js b/src-docs/src/views/range/input_only.js index 65cf3a7084e..dc8da0f16fd 100644 --- a/src-docs/src/views/range/input_only.js +++ b/src-docs/src/views/range/input_only.js @@ -56,7 +56,7 @@ export default class extends Component { - + `; -exports[`EuiDualRange props only input should render 1`] = ` +exports[`EuiDualRange props range should render 1`] = ` +
+
+
+
+
+
+ +
+
+
+`; + +exports[`EuiDualRange props slider should display in popover 1`] = `
@@ -484,41 +519,6 @@ exports[`EuiDualRange props only input should render 1`] = `
`; -exports[`EuiDualRange props range should render 1`] = ` -
-
-
-
-
-
- -
-
-
-`; - exports[`EuiDualRange props ticks should render 1`] = `
`; -exports[`EuiRange props input only should render 1`] = ` -
-
-
-
-
- -
-
-
-
-
-`; - exports[`EuiRange props input should render 1`] = `
`; +exports[`EuiRange props slider should display in popover 1`] = ` +
+
+
+
+
+ +
+
+
+
+
+`; + exports[`EuiRange props ticks should render 1`] = `
} fullWidth={fullWidth} diff --git a/src/components/form/range/dual_range.test.js b/src/components/form/range/dual_range.test.js index 03c9a822023..fad041a3a4f 100644 --- a/src/components/form/range/dual_range.test.js +++ b/src/components/form/range/dual_range.test.js @@ -88,7 +88,7 @@ describe('EuiDualRange', () => { expect(component).toMatchSnapshot(); }); - test('only input should render', () => { + test('slider should display in popover', () => { const component = render( { expect(component).toMatchSnapshot(); }); - test('input only should render', () => { + test('slider should display in popover', () => { const component = render( Date: Tue, 10 Sep 2019 22:42:39 -0400 Subject: [PATCH 14/20] Add `controlOnly` prop to EuiFieldText --- .../__snapshots__/field_text.test.js.snap | 9 +++++ src/components/form/field_text/field_text.js | 38 ++++++++++++------- .../form/field_text/field_text.test.js | 6 +++ 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/src/components/form/field_text/__snapshots__/field_text.test.js.snap b/src/components/form/field_text/__snapshots__/field_text.test.js.snap index 3e67debb490..f927679d823 100644 --- a/src/components/form/field_text/__snapshots__/field_text.test.js.snap +++ b/src/components/form/field_text/__snapshots__/field_text.test.js.snap @@ -23,6 +23,15 @@ exports[`EuiFieldText is rendered 1`] = ` `; +exports[`EuiFieldText props controlOnly is rendered 1`] = ` + + + +`; + exports[`EuiFieldText props fullWidth is rendered 1`] = ` { const classes = classNames('euiFieldText', className, { @@ -31,6 +32,24 @@ export const EuiFieldText = ({ 'euiFieldText-isLoading': isLoading, }); + const control = ( + + + + ); + + if (controlOnly) return control; + return ( - - - + {control} ); }; @@ -87,6 +94,11 @@ EuiFieldText.propTypes = { PropTypes.node, PropTypes.arrayOf(PropTypes.node), ]), + /** + * Completely removes form control layout wrapper and ignores + * icon, prepend, and append. Best used inside EuiFormControlLayoutDelimited. + */ + controlOnly: PropTypes.bool, }; EuiFieldText.defaultProps = { diff --git a/src/components/form/field_text/field_text.test.js b/src/components/form/field_text/field_text.test.js index 3694e82731d..1797eb54dc2 100644 --- a/src/components/form/field_text/field_text.test.js +++ b/src/components/form/field_text/field_text.test.js @@ -57,5 +57,11 @@ describe('EuiFieldText', () => { expect(component).toMatchSnapshot(); }); + + test('controlOnly is rendered', () => { + const component = render(); + + expect(component).toMatchSnapshot(); + }); }); }); From 15694a98d2252a2e33198f2e7717f6dbfed08379 Mon Sep 17 00:00:00 2001 From: cchaos Date: Tue, 10 Sep 2019 22:58:34 -0400 Subject: [PATCH 15/20] Update TS defs --- src/components/form/field_number/index.d.ts | 1 + src/components/form/field_text/index.d.ts | 1 + src/components/form/form_row/index.d.ts | 8 +++++++- src/components/form/range/index.d.ts | 3 ++- 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/components/form/field_number/index.d.ts b/src/components/form/field_number/index.d.ts index 24d0f9eb28c..a90e7123c38 100644 --- a/src/components/form/field_number/index.d.ts +++ b/src/components/form/field_number/index.d.ts @@ -18,6 +18,7 @@ declare module '@elastic/eui' { prepend?: ReactNode | ReactNode[]; append?: ReactNode | ReactNode[]; inputRef?: Ref; + controlOnly?: boolean; } export const EuiFieldNumber: FunctionComponent< diff --git a/src/components/form/field_text/index.d.ts b/src/components/form/field_text/index.d.ts index 61b118436bc..c88d2f18840 100644 --- a/src/components/form/field_text/index.d.ts +++ b/src/components/form/field_text/index.d.ts @@ -17,6 +17,7 @@ declare module '@elastic/eui' { prepend?: React.ReactNode; append?: React.ReactNode; compressed?: boolean; + controlOnly?: boolean; } export const EuiFieldText: FunctionComponent< diff --git a/src/components/form/form_row/index.d.ts b/src/components/form/form_row/index.d.ts index 76c08290f4c..564e4f58742 100644 --- a/src/components/form/form_row/index.d.ts +++ b/src/components/form/form_row/index.d.ts @@ -21,7 +21,13 @@ declare module '@elastic/eui' { labelAppend?: ReactNode; describedByIds?: string[]; compressed?: boolean; - display?: 'row' | 'rowCompressed' | 'columnCompressed'; + display?: + | 'row' + | 'rowCompressed' + | 'columnCompressed' + | 'center' + | 'centerCompressed' + | 'columnCompressedSwitch'; displayOnly?: boolean; }; diff --git a/src/components/form/range/index.d.ts b/src/components/form/range/index.d.ts index 14cacd52569..c2fed6e2ef9 100644 --- a/src/components/form/range/index.d.ts +++ b/src/components/form/range/index.d.ts @@ -33,6 +33,7 @@ declare module '@elastic/eui' { export interface EuiRangeProps { compressed?: boolean; + readOnly?: boolean; fullWidth?: boolean; id?: string; levels?: EuiRangeLevel[]; @@ -43,7 +44,7 @@ declare module '@elastic/eui' { // The spec allows string values for `step` but the component requires // a number. step?: number; - showInput?: boolean; + showInput?: boolean | 'inputWithPopover'; showLabels?: boolean; showRange?: boolean; showTicks?: boolean; From 8ba88c0b0583b33a7422854fd918daa228f2c24b Mon Sep 17 00:00:00 2001 From: cchaos Date: Wed, 11 Sep 2019 11:29:03 -0400 Subject: [PATCH 16/20] Some docs fixes --- src-docs/src/views/expression/expression.js | 8 ++++++-- src-docs/src/views/modal/modal.js | 3 +++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src-docs/src/views/expression/expression.js b/src-docs/src/views/expression/expression.js index 04b0aa4f6ed..37e167e0034 100644 --- a/src-docs/src/views/expression/expression.js +++ b/src-docs/src/views/expression/expression.js @@ -101,6 +101,7 @@ export default class extends Component {
When - + @@ -160,7 +163,7 @@ export default class extends Component { isOpen={this.state.example1.isOpen} closePopover={this.closeExample1} ownFocus - withTitle + panelPaddingSize="s" anchorPosition="downLeft"> {this.renderPopover1()} @@ -169,6 +172,7 @@ export default class extends Component { + + {'

Title

'}
From 69beef776cc9a1e3ab5d67f5bac208f93ed9adbe Mon Sep 17 00:00:00 2001 From: cchaos Date: Wed, 11 Sep 2019 11:53:35 -0400 Subject: [PATCH 17/20] Fix inherit screen reader ability of EuiRange By moving the id to the input if it exists --- src/components/form/range/__snapshots__/range.test.js.snap | 3 ++- src/components/form/range/range.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/form/range/__snapshots__/range.test.js.snap b/src/components/form/range/__snapshots__/range.test.js.snap index fb48f07460b..b231d8aadb5 100644 --- a/src/components/form/range/__snapshots__/range.test.js.snap +++ b/src/components/form/range/__snapshots__/range.test.js.snap @@ -207,7 +207,6 @@ exports[`EuiRange props input should render 1`] = ` aria-label="aria-label" class="euiRangeSlider" data-test-subj="test subject string" - id="id" max="10" min="1" name="name" @@ -230,6 +229,7 @@ exports[`EuiRange props input should render 1`] = ` aria-label="aria-label" class="euiFieldNumber euiRangeInput euiRangeInput--max" data-test-subj="test subject string" + id="id" max="10" min="1" name="name" @@ -358,6 +358,7 @@ exports[`EuiRange props slider should display in popover 1`] = ` aria-label="aria-label" class="euiFieldNumber euiRangeInput euiRangeInput--max" data-test-subj="test subject string" + id="id" max="10" min="1" name="name" diff --git a/src/components/form/range/range.js b/src/components/form/range/range.js index 577e4e117fd..8dbfcfe41d1 100644 --- a/src/components/form/range/range.js +++ b/src/components/form/range/range.js @@ -111,6 +111,7 @@ export class EuiRange extends Component { const theInput = !!showInput ? ( Date: Wed, 11 Sep 2019 12:00:29 -0400 Subject: [PATCH 18/20] Add dual range to complex example --- .../views/form_compressed/complex_example.js | 31 +++++++++---------- src-docs/src/views/range/input_only.js | 2 -- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src-docs/src/views/form_compressed/complex_example.js b/src-docs/src/views/form_compressed/complex_example.js index 96616e5d310..baf94c29ba9 100644 --- a/src-docs/src/views/form_compressed/complex_example.js +++ b/src-docs/src/views/form_compressed/complex_example.js @@ -4,11 +4,11 @@ import { EuiButtonIcon, EuiColorPicker, EuiColorPickerSwatch, + EuiDualRange, EuiFieldNumber, EuiFieldText, EuiFlexGroup, EuiFlexItem, - EuiFormControlLayoutDelimited, EuiFormLabel, EuiFormRow, EuiHorizontalRule, @@ -50,6 +50,7 @@ export default class extends Component { color: '#DB1374', borderValue: 3, popoverSliderValues: 16, + dualValue: [5, 10], }; this.selectTooltipContent = @@ -88,6 +89,12 @@ export default class extends Component { }); }; + onDualChange = value => { + this.setState({ + dualValue: value, + }); + }; + render() { return ( @@ -96,23 +103,15 @@ export default class extends Component { - - } - endControl={ - - } /> diff --git a/src-docs/src/views/range/input_only.js b/src-docs/src/views/range/input_only.js index dc8da0f16fd..dfb832dbd08 100644 --- a/src-docs/src/views/range/input_only.js +++ b/src-docs/src/views/range/input_only.js @@ -61,11 +61,9 @@ export default class extends Component { id={makeId()} value={this.state.dualValue} onChange={this.onDualChange} - compressed showInput="inputWithPopover" showLabels levels={this.levels} - readOnly /> From b3d66316cf82ce115d984ec3fa586b95d1199645 Mon Sep 17 00:00:00 2001 From: cchaos Date: Wed, 11 Sep 2019 13:39:56 -0400 Subject: [PATCH 19/20] Quick fix for IE --- src-docs/src/views/form_compressed/complex_example.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src-docs/src/views/form_compressed/complex_example.js b/src-docs/src/views/form_compressed/complex_example.js index baf94c29ba9..cdbf81ede3b 100644 --- a/src-docs/src/views/form_compressed/complex_example.js +++ b/src-docs/src/views/form_compressed/complex_example.js @@ -263,7 +263,7 @@ export default class extends Component { Border - + - + Date: Wed, 11 Sep 2019 14:05:11 -0400 Subject: [PATCH 20/20] cl --- CHANGELOG.md | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01f5eb929c0..2d76d690142 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,38 @@ ## [`master`](https://github.com/elastic/eui/tree/master) -No public interface changes since `13.8.1`. +### Feature: Compressed Form Controls ([#2167](https://github.com/elastic/eui/pull/2167)) + +- Altered the look of `compressed` form controls to look more subtle +- Created `EuiFormControlLayoutDelimited` for dual inputs indicating a range +- Added compressed and column style layouts to `EuiFormRow` via `display` prop +- Reduced overall height of `compressed` `EuiRange` and `EuiDualRange` +- Added `showInput = 'inputWithPopover'` option for `compressed` `EuiRange` and `EuiDualRange` to display the slider in a popover + +- Made all inputs in the `EuiSuperDatePicker` popover `compressed` +- Added `controlOnly` prop to `EuiFieldText` and `EuiFieldNumber` +- Allow `style` prop to be passed down in `EuiColorPickerSwatch` +- `EuiFilePicker` now has `default` and `large` display sizes that both have `compressed` alternatives +- Allow strings to be passed as `append`/`prepend` props and added a11y support +- Added a max height with overflow to `EuiSuperSelect` + +**Bug fixes** + +- Fixed `EuiColorPicker` padding on right to accomodate down caret +- Fixed sizings of `EuiComboBox` and pills +- Fixed truncation on `EuiContextMenuItem` +- Fixed style of more `append`/`prepend` options of `EuiFormControlLayout` + +**Deprecations** + +- `EuiFormRow`'s `compressed` prop deprecated in favor of `display: rowCompressed` +- `EuiFormRow`'s `displayOnly` prop deprecated in favor of `display: center` + +**Breaking changes** + +- SASS mixin `euiTextOverflowWrap()` has been removed in favor of `euiTextBreakWord()` +- `EuiFormLabel` no longer has a bottom margin +- `EuiFormRow` no longer has bottom padding, nor does it add margin to any `+ *` siblings only sibling `EuiFormRow`s + ## [`13.8.1`](https://github.com/elastic/eui/tree/v13.8.1)