Skip to content

Commit

Permalink
[EuiComboBox] Add alert icon when isInvalid (#6680)
Browse files Browse the repository at this point in the history
* Add `isInvalid` form control icon

* [misc nit] import order

* DRY out and fix padding offset to account for extra icon

+ remove classNames that were only being used for icon detection

* Add `aria-invalid` for screen readers

* changelog
  • Loading branch information
cee-chen authored Apr 4, 2023
1 parent a39d7bc commit 649083a
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 50 deletions.
19 changes: 17 additions & 2 deletions src/components/combo_box/__snapshots__/combo_box.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ exports[`EuiComboBox is rendered 1`] = `
class="euiFormControlLayout__childrenWrapper"
>
<div
class="euiComboBox__inputWrap euiComboBox__inputWrap-isClearable"
class="euiComboBox__inputWrap euiFormControlLayout--1icons"
data-test-subj="comboBoxInput"
tabindex="-1"
>
Expand All @@ -24,6 +24,7 @@ exports[`EuiComboBox is rendered 1`] = `
aria-autocomplete="list"
aria-controls=""
aria-expanded="false"
aria-invalid="false"
aria-label="aria-label"
data-test-subj="comboBoxSearchInput"
id="generated-id__eui-combobox-id"
Expand Down Expand Up @@ -72,6 +73,7 @@ exports[`props aria-label attribute is rendered 1`] = `
hasSelectedOptions={false}
id="generated-id__eui-combobox-id"
inputRef={[Function]}
isInvalid={false}
isListOpen={false}
noIcon={false}
onChange={[Function]}
Expand Down Expand Up @@ -106,6 +108,7 @@ exports[`props aria-labelledby attribute is rendered 1`] = `
hasSelectedOptions={false}
id="generated-id__eui-combobox-id"
inputRef={[Function]}
isInvalid={false}
isListOpen={false}
noIcon={false}
onChange={[Function]}
Expand Down Expand Up @@ -139,6 +142,7 @@ exports[`props autoFocus is rendered 1`] = `
hasSelectedOptions={true}
id="generated-id__eui-combobox-id"
inputRef={[Function]}
isInvalid={false}
isListOpen={false}
noIcon={false}
onChange={[Function]}
Expand Down Expand Up @@ -181,6 +185,7 @@ exports[`props custom ID is rendered 1`] = `
hasSelectedOptions={true}
id="test-id-1"
inputRef={[Function]}
isInvalid={false}
isListOpen={false}
noIcon={false}
onChange={[Function]}
Expand Down Expand Up @@ -223,6 +228,7 @@ exports[`props delimiter is rendered 1`] = `
hasSelectedOptions={true}
id="generated-id__eui-combobox-id"
inputRef={[Function]}
isInvalid={false}
isListOpen={false}
noIcon={false}
onChange={[Function]}
Expand Down Expand Up @@ -265,6 +271,7 @@ exports[`props full width is rendered 1`] = `
hasSelectedOptions={true}
id="generated-id__eui-combobox-id"
inputRef={[Function]}
isInvalid={false}
isListOpen={false}
noIcon={false}
onChange={[Function]}
Expand Down Expand Up @@ -304,6 +311,7 @@ exports[`props isClearable=false disallows user from clearing input when no opti
hasSelectedOptions={false}
id="generated-id__eui-combobox-id"
inputRef={[Function]}
isInvalid={false}
isListOpen={false}
noIcon={false}
onChange={[Function]}
Expand Down Expand Up @@ -336,6 +344,7 @@ exports[`props isClearable=false disallows user from clearing input when options
hasSelectedOptions={true}
id="generated-id__eui-combobox-id"
inputRef={[Function]}
isInvalid={false}
isListOpen={false}
noIcon={false}
onChange={[Function]}
Expand Down Expand Up @@ -378,6 +387,7 @@ exports[`props isDisabled is rendered 1`] = `
id="generated-id__eui-combobox-id"
inputRef={[Function]}
isDisabled={true}
isInvalid={false}
isListOpen={false}
noIcon={false}
onChange={[Function]}
Expand Down Expand Up @@ -415,7 +425,7 @@ exports[`props options list is rendered 1`] = `
class="euiFormControlLayout__childrenWrapper"
>
<div
class="euiComboBox__inputWrap euiComboBox__inputWrap-isClearable"
class="euiComboBox__inputWrap euiFormControlLayout--1icons"
data-test-subj="comboBoxInput"
tabindex="-1"
>
Expand All @@ -427,6 +437,7 @@ exports[`props options list is rendered 1`] = `
aria-autocomplete="list"
aria-controls="generated-id_listbox"
aria-expanded="true"
aria-invalid="false"
data-test-subj="comboBoxSearchInput"
id="generated-id__eui-combobox-id"
role="combobox"
Expand Down Expand Up @@ -741,6 +752,7 @@ exports[`props selectedOptions are rendered 1`] = `
hasSelectedOptions={true}
id="generated-id__eui-combobox-id"
inputRef={[Function]}
isInvalid={false}
isListOpen={false}
noIcon={false}
onChange={[Function]}
Expand Down Expand Up @@ -783,6 +795,7 @@ exports[`props singleSelection is rendered 1`] = `
hasSelectedOptions={true}
id="generated-id__eui-combobox-id"
inputRef={[Function]}
isInvalid={false}
isListOpen={false}
noIcon={false}
onChange={[Function]}
Expand Down Expand Up @@ -823,6 +836,7 @@ exports[`props singleSelection prepend and append is rendered 1`] = `
hasSelectedOptions={false}
id="generated-id__eui-combobox-id"
inputRef={[Function]}
isInvalid={false}
isListOpen={true}
noIcon={false}
onChange={[Function]}
Expand Down Expand Up @@ -865,6 +879,7 @@ exports[`props singleSelection selects existing option when opened 1`] = `
hasSelectedOptions={true}
id="generated-id__eui-combobox-id"
inputRef={[Function]}
isInvalid={false}
isListOpen={true}
noIcon={false}
onChange={[Function]}
Expand Down
40 changes: 2 additions & 38 deletions src/components/combo_box/_combo_box.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,13 @@

.euiComboBox__inputWrap {
@include euiFormControlStyle($includeStates: false, $includeSizes: true);
@include euiFormControlWithIcon($isIconOptional: true);
@include euiFormControlSize(auto, $includeAlternates: true);

padding: $euiSizeXS $euiSizeS;
padding-right: var(--eui-form-control-layout-icons-padding, $euiSizeS); /* 2 */
display: flex; /* 1 */
outline: none; // Fixes an intermittent focus ring in Firefox

// to override the padding added above
@include euiFormControlLayoutPadding(1); /* 2 */

.euiComboBoxPill {
$inputMinWidth: $euiSize;

Expand All @@ -47,22 +45,6 @@
cursor: text;
}
}

&.euiComboBox__inputWrap-isClearable {
@include euiFormControlLayoutPadding(2); /* 2 */
}

&.euiComboBox__inputWrap-isLoading {
@include euiFormControlLayoutPadding(2); /* 2 */

.euiComboBoxPlaceholder {
@include euiFormControlLayoutPadding(2); /* 2 */
}
}

&.euiComboBox__inputWrap-isLoading.euiComboBox__inputWrap-isClearable {
@include euiFormControlLayoutPadding(3); /* 2 */
}
}

/**
Expand Down Expand Up @@ -130,24 +112,6 @@
line-height: $euiFormControlCompressedHeight; /* 2 */
padding-top: 0;
padding-bottom: 0;

@include euiFormControlLayoutPadding(1, $compressed: true); /* 2 */

&.euiComboBox__inputWrap-isClearable {
@include euiFormControlLayoutPadding(2, $compressed: true); /* 2 */
}

&.euiComboBox__inputWrap-isLoading {
@include euiFormControlLayoutPadding(2, $compressed: true); /* 2 */

.euiComboBoxPlaceholder {
@include euiFormControlLayoutPadding(2, $compressed: true); /* 2 */
}
}

&.euiComboBox__inputWrap-isLoading.euiComboBox__inputWrap-isClearable {
@include euiFormControlLayoutPadding(3, $compressed: true); /* 2 */
}
}
}

Expand Down
6 changes: 4 additions & 2 deletions src/components/combo_box/combo_box.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -971,9 +971,10 @@ export class EuiComboBox<T> extends Component<
// Visually indicate the combobox is in an invalid state if it has lost focus but there is text entered in the input.
// When custom options are disabled and the user leaves the combo box after entering text that does not match any
// options, this tells the user that they've entered invalid input.
const markAsInvalid =
const markAsInvalid = !!(
isInvalid ||
((hasFocus === false || isListOpen === false) && searchValue);
((hasFocus === false || isListOpen === false) && searchValue)
);

const classes = classNames('euiComboBox', className, {
'euiComboBox--compressed': compressed,
Expand Down Expand Up @@ -1094,6 +1095,7 @@ export class EuiComboBox<T> extends Component<
append={singleSelection ? append : undefined}
prepend={singleSelection ? prepend : undefined}
isLoading={isLoading}
isInvalid={markAsInvalid}
autoFocus={autoFocus}
aria-label={ariaLabel}
aria-labelledby={ariaLabelledby}
Expand Down
23 changes: 17 additions & 6 deletions src/components/combo_box/combo_box_input/combo_box_input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,23 @@ import React, {
import classNames from 'classnames';
import AutosizeInput from 'react-input-autosize';

import { CommonProps } from '../../common';
import { htmlIdGenerator } from '../../../services';
import { EuiScreenReaderOnly } from '../../accessibility';
import {
EuiFormControlLayout,
EuiFormControlLayoutProps,
} from '../../form/form_control_layout';
import { EuiComboBoxPill } from './combo_box_pill';
import { htmlIdGenerator } from '../../../services';
import { EuiFormControlLayoutIconsProps } from '../../form/form_control_layout/form_control_layout_icons';
import { getFormControlClassNameForIconCount } from '../../form/form_control_layout/_num_icons';

import { EuiComboBoxPill } from './combo_box_pill';
import {
EuiComboBoxOptionOption,
EuiComboBoxSingleSelectionShape,
OptionHandler,
UpdatePositionHandler,
} from '../types';
import { CommonProps } from '../../common';

export interface EuiComboBoxInputProps<T> extends CommonProps {
autoSizeInputRef?: RefCallback<AutosizeInput & HTMLInputElement>;
Expand Down Expand Up @@ -61,6 +63,7 @@ export interface EuiComboBoxInputProps<T> extends CommonProps {
prepend?: EuiFormControlLayoutProps['prepend'];
append?: EuiFormControlLayoutProps['append'];
isLoading?: boolean;
isInvalid?: boolean;
autoFocus?: boolean;
'aria-label'?: string;
'aria-labelledby'?: string;
Expand Down Expand Up @@ -151,6 +154,7 @@ export class EuiComboBoxInput<T> extends Component<
prepend,
append,
isLoading,
isInvalid,
autoFocus,
'aria-label': ariaLabel,
'aria-labelledby': ariaLabelledby,
Expand Down Expand Up @@ -256,12 +260,17 @@ export class EuiComboBoxInput<T> extends Component<
};
}

const wrapClasses = classNames('euiComboBox__inputWrap', {
const numIconsClass = getFormControlClassNameForIconCount({
isDropdown: !noIcon,
clear: !!clickProps.clear,
isInvalid,
isLoading,
});

const wrapClasses = classNames('euiComboBox__inputWrap', numIconsClass, {
'euiComboBox__inputWrap--compressed': compressed,
'euiComboBox__inputWrap--fullWidth': fullWidth,
'euiComboBox__inputWrap--noWrap': singleSelection,
'euiComboBox__inputWrap-isLoading': isLoading,
'euiComboBox__inputWrap-isClearable': onClear,
'euiComboBox__inputWrap--inGroup': prepend || append,
});

Expand All @@ -271,6 +280,7 @@ export class EuiComboBoxInput<T> extends Component<
{...clickProps}
inputId={id}
isLoading={isLoading}
isInvalid={isInvalid}
compressed={compressed}
fullWidth={fullWidth}
prepend={prepend}
Expand All @@ -291,6 +301,7 @@ export class EuiComboBoxInput<T> extends Component<
aria-expanded={isListOpen}
aria-label={ariaLabel}
aria-labelledby={ariaLabelledby}
aria-invalid={isInvalid}
className="euiComboBox__input"
data-test-subj="comboBoxSearchInput"
disabled={isDisabled}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,18 @@
// we use a form control layout class on all form controls.
@for $i from 1 through 5 {
&--#{$i}icons {
padding-right: $iconPadding + ($iconSize + $marginBetweenIcons) * $i;
$paddingOffset: $iconPadding + ($iconSize + $marginBetweenIcons) * $i;

--eui-form-control-layout-icons-padding: #{$paddingOffset}; // Set this for flexible usage, e.g. components that need extra specificity
padding-right: $paddingOffset;

&[class*='compressed'] {
$iconSizeCompressed: map-get($euiFormControlIconSizes, 'small');
$iconPaddingCompressed: $euiFormControlCompressedPadding;
$paddingOffset: $iconPaddingCompressed + ($iconSizeCompressed + $marginBetweenIcons) * $i;

padding-right: $iconPaddingCompressed + ($iconSizeCompressed + $marginBetweenIcons) * $i;
--eui-form-control-layout-icons-padding: #{$paddingOffset};
padding-right: $paddingOffset;
}
}
}
Expand Down
1 change: 1 addition & 0 deletions upcoming_changelogs/6680.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Updated `EuiDatePicker` to display a warning icon and correctly set `aria-invalid` when `isInvalid` is passed

0 comments on commit 649083a

Please sign in to comment.