Skip to content

Commit

Permalink
Fix EuiComboBox focus trap (#866)
Browse files Browse the repository at this point in the history
* Fix bug where clicking the combo box caret would focus on the button.
- Set focus on searchInput when you click the caret to open the combo box.
* Fix EuiComboBox focus trap. Make clear button keyboard-accessible.
- Redesign EuiFormControlLayout props to use icon and clear configuration objects.
* Protect against tab amounts that are not 1 or -1.
* Remove virtualized list from tab order.
* Refactor EuiFormControlLayout and add examples of it to the docs.
* Add extra space to bottom of docs site pages.
  • Loading branch information
cjcenizal authored May 25, 2018
1 parent 03a3e9e commit 08aeacd
Show file tree
Hide file tree
Showing 20 changed files with 691 additions and 274 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@

- `EuiSearchBar` no longer has an `onParse` callback, and now passes an object to `onChange` with the shape `{ query, queryText, error }` ([#863](https://github.com/elastic/eui/pull/863))
- `EuiInMemoryTable`'s `search.onChange` callback now passes an object with `{ query, queryText, error }` instead of only the query ([#863](https://github.com/elastic/eui/pull/863))
- `EuiFormControlLayout` no longer has `onClear`, `iconSide`, or `onIconClick` props. Instead of `onClear` it now accepts a `clear` object of the shape `{ onClick }`. Instead of the icon props, it now accepts a single `icon` prop which be either a string or an object of the shape `{ type, side, onClick }`. ([#866](https://github.com/elastic/eui/pull/866))

**Bug fixes**

- `EuiComboBox` is no longer a focus trap, the clear button is now keyboard-accessible, and the virtualized list no longer interferes with the tab order ([#866](https://github.com/elastic/eui/pull/866))
- `EuiButton`, `EuiButtonEmpty`, and `EuiButtonIcon` now look and behave disabled when `isDisabled={true}` ([#862](https://github.com/elastic/eui/pull/862))
- `EuiGlobalToastList` no longer triggers `Uncaught TypeError: _this.callback is not a function` ([#865](https://github.com/elastic/eui/pull/865))
- `EuiGlobalToastList` checks to see if it has dismissed a toast before re-dismissing it ([#868](https://github.com/elastic/eui/pull/868))
Expand Down
5 changes: 4 additions & 1 deletion src-docs/src/components/guide_page/guide_page.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
EuiSpacer,
EuiFlexGroup,
EuiFlexItem,
EuiButton
EuiButton,
} from '../../../../src/components';

export const GuidePage = ({ children, title, intro, componentLinkTo }) => {
Expand Down Expand Up @@ -38,6 +38,9 @@ export const GuidePage = ({ children, title, intro, componentLinkTo }) => {
</div>

{children}

{/* Give some space between the bottom of long content and the bottom of the screen */}
<EuiSpacer size="xl" />
</Fragment>
);
};
Expand Down
10 changes: 10 additions & 0 deletions src-docs/src/views/form_controls/field_password.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ export default class extends Component {
onChange={this.onChange}
compressed
/>

<EuiSpacer size="m" />

<EuiFieldPassword
placeholder="Compressed and loading"
value={this.state.value}
onChange={this.onChange}
isLoading
compressed
/>
</Fragment>
);
}
Expand Down
110 changes: 110 additions & 0 deletions src-docs/src/views/form_controls/form_control_layout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import React, {
Fragment,
} from 'react';

import {
EuiFormControlLayout,
EuiSpacer,
} from '../../../../src/components';

export default () => (
<Fragment>

<EuiFormControlLayout
icon="search"
>
<input type="text" className="euiFieldText" />
</EuiFormControlLayout>

<EuiSpacer size="m" />

<EuiFormControlLayout
isLoading
>
<input type="text" className="euiFieldText" />
</EuiFormControlLayout>

<EuiSpacer size="m" />

<EuiFormControlLayout
clear={{ onClick: () => {} }}
>
<input type="text" className="euiFieldText" />
</EuiFormControlLayout>

<EuiSpacer size="m" />

<EuiFormControlLayout
isLoading
clear={{ onClick: () => {} }}
>
<input type="text" className="euiFieldText" />
</EuiFormControlLayout>

<EuiSpacer size="m" />

<EuiFormControlLayout
isLoading
icon="search"
>
<input type="text" className="euiFieldText" />
</EuiFormControlLayout>

<EuiSpacer size="m" />

<EuiFormControlLayout
isLoading
icon={{ type: 'arrowDown', side: 'right' }}
>
<input type="text" className="euiFieldText" />
</EuiFormControlLayout>

<EuiSpacer size="m" />

<EuiFormControlLayout
clear={{ onClick: () => {} }}
icon="search"
>
<input type="text" className="euiFieldText" />
</EuiFormControlLayout>

<EuiSpacer size="m" />

<EuiFormControlLayout
clear={{ onClick: () => {} }}
icon={{ type: 'arrowDown', side: 'right' }}
>
<input type="text" className="euiFieldText" />
</EuiFormControlLayout>

<EuiSpacer size="m" />

<EuiFormControlLayout
isLoading
clear={{ onClick: () => {} }}
icon="search"
>
<input type="text" className="euiFieldText" />
</EuiFormControlLayout>

<EuiSpacer size="m" />

<EuiFormControlLayout
isLoading
clear={{ onClick: () => {} }}
icon={{ type: 'arrowDown', side: 'right' }}
>
<input type="text" className="euiFieldText" />
</EuiFormControlLayout>

<EuiSpacer size="m" />

<EuiFormControlLayout
isLoading
clear={{ onClick: () => {} }}
icon="search"
>
<input type="text" className="euiFieldText" />
</EuiFormControlLayout>
</Fragment>
);
35 changes: 34 additions & 1 deletion src-docs/src/views/form_controls/form_controls_example.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { Fragment } from 'react';

import { renderToHtml } from '../../services';

Expand All @@ -14,6 +14,7 @@ import {
EuiFieldSearch,
EuiFieldText,
EuiFilePicker,
EuiFormControlLayout,
EuiLink,
EuiRadio,
EuiRange,
Expand Down Expand Up @@ -74,6 +75,10 @@ import Switch from './switch';
const switchSource = require('!!raw-loader!./switch');
const switchHtml = renderToHtml(Switch);

import FormControlLayout from './form_control_layout';
const formControlLayoutSource = require('!!raw-loader!./form_control_layout');
const formControlLayoutHtml = renderToHtml(FormControlLayout);

export const FormControlsExample = {
title: 'Form controls',
sections: [{
Expand Down Expand Up @@ -255,6 +260,34 @@ export const FormControlsExample = {
EuiSwitch,
},
demo: <Switch />,
}, {
title: 'Form control layout',
source: [{
type: GuideSectionTypes.JS,
code: formControlLayoutSource,
}, {
type: GuideSectionTypes.HTML,
code: formControlLayoutHtml,
}],
text: (
<Fragment>
<p>
<EuiCode>EuiFormControlLayout</EuiCode> is generally used internally to consistently style
form controls, but it&rsquo;s published in case you want to create your own form control
which matches those of EUI. The examples below demonstrate its various states.
</p>

<p>
Note that the padding on the <EuiCode>input</EuiCode> itself doesn&rsquo;t take into account the presence
of the various icons supported by <EuiCode>EuiFormControlLayout</EuiCode>. Any input component
provided to <EuiCode>EuiFormControlLayout</EuiCode> is responsible for its own padding.
</p>
</Fragment>
),
props: {
EuiFormControlLayout,
},
demo: <FormControlLayout />,
}],
};

35 changes: 21 additions & 14 deletions src/components/combo_box/__snapshots__/combo_box.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ exports[`EuiComboBox is rendered 1`] = `
onChange={[Function]}
onClear={[Function]}
onClick={[Function]}
onClose={[Function]}
onCloseListClick={[Function]}
onFocus={[Function]}
onOpen={[Function]}
onOpenListClick={[Function]}
onRemoveOption={[Function]}
searchValue=""
selectedOptions={Array []}
singleSelection={false}
toggleButtonRef={[Function]}
updatePosition={[Function]}
value=""
/>
Expand All @@ -44,9 +45,9 @@ exports[`props isClearable is false with selectedOptions 1`] = `
isListOpen={false}
onChange={[Function]}
onClick={[Function]}
onClose={[Function]}
onCloseListClick={[Function]}
onFocus={[Function]}
onOpen={[Function]}
onOpenListClick={[Function]}
onRemoveOption={[Function]}
searchValue=""
selectedOptions={
Expand All @@ -60,6 +61,7 @@ exports[`props isClearable is false with selectedOptions 1`] = `
]
}
singleSelection={false}
toggleButtonRef={[Function]}
updatePosition={[Function]}
value="Mimas, Iapetus"
/>
Expand All @@ -81,13 +83,14 @@ exports[`props isClearable is false without selectedOptions 1`] = `
isListOpen={false}
onChange={[Function]}
onClick={[Function]}
onClose={[Function]}
onCloseListClick={[Function]}
onFocus={[Function]}
onOpen={[Function]}
onOpenListClick={[Function]}
onRemoveOption={[Function]}
searchValue=""
selectedOptions={Array []}
singleSelection={false}
toggleButtonRef={[Function]}
updatePosition={[Function]}
value=""
/>
Expand All @@ -110,9 +113,9 @@ exports[`props isDisabled 1`] = `
isListOpen={false}
onChange={[Function]}
onClick={[Function]}
onClose={[Function]}
onCloseListClick={[Function]}
onFocus={[Function]}
onOpen={[Function]}
onOpenListClick={[Function]}
onRemoveOption={[Function]}
searchValue=""
selectedOptions={
Expand All @@ -123,6 +126,7 @@ exports[`props isDisabled 1`] = `
]
}
singleSelection={false}
toggleButtonRef={[Function]}
updatePosition={[Function]}
value="Mimas"
/>
Expand All @@ -145,13 +149,14 @@ exports[`props options 1`] = `
onChange={[Function]}
onClear={[Function]}
onClick={[Function]}
onClose={[Function]}
onCloseListClick={[Function]}
onFocus={[Function]}
onOpen={[Function]}
onOpenListClick={[Function]}
onRemoveOption={[Function]}
searchValue=""
selectedOptions={Array []}
singleSelection={false}
toggleButtonRef={[Function]}
updatePosition={[Function]}
value=""
/>
Expand All @@ -174,9 +179,9 @@ exports[`props selectedOptions 1`] = `
onChange={[Function]}
onClear={[Function]}
onClick={[Function]}
onClose={[Function]}
onCloseListClick={[Function]}
onFocus={[Function]}
onOpen={[Function]}
onOpenListClick={[Function]}
onRemoveOption={[Function]}
searchValue=""
selectedOptions={
Expand All @@ -190,6 +195,7 @@ exports[`props selectedOptions 1`] = `
]
}
singleSelection={false}
toggleButtonRef={[Function]}
updatePosition={[Function]}
value="Mimas, Iapetus"
/>
Expand All @@ -212,9 +218,9 @@ exports[`props singleSelection 1`] = `
onChange={[Function]}
onClear={[Function]}
onClick={[Function]}
onClose={[Function]}
onCloseListClick={[Function]}
onFocus={[Function]}
onOpen={[Function]}
onOpenListClick={[Function]}
onRemoveOption={[Function]}
searchValue=""
selectedOptions={
Expand All @@ -225,6 +231,7 @@ exports[`props singleSelection 1`] = `
]
}
singleSelection={true}
toggleButtonRef={[Function]}
updatePosition={[Function]}
value="Mimas"
/>
Expand Down
Loading

0 comments on commit 08aeacd

Please sign in to comment.