mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Disable Binary
field type in Exception entries (#139370)
* refactor field.tsx+ introduce useField and types * handle binary disabled field * finish hooks tests + disabled types test * apply docs-team comments Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
8089c01a13
commit
ef2f33f873
9 changed files with 1477 additions and 184 deletions
765
packages/kbn-securitysolution-autocomplete/src/field/__tests__/__snapshots__/index.test.tsx.snap
generated
Normal file
765
packages/kbn-securitysolution-autocomplete/src/field/__tests__/__snapshots__/index.test.tsx.snap
generated
Normal file
|
@ -0,0 +1,765 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`FieldComponent should allow user to clear values if isClearable is true 1`] = `
|
||||
Object {
|
||||
"asFragment": [Function],
|
||||
"baseElement": <body>
|
||||
<div>
|
||||
<div
|
||||
class="euiComboBox euiComboBox--fullWidth"
|
||||
data-test-subj="fieldAutocompleteComboBox"
|
||||
>
|
||||
<div
|
||||
class="euiFormControlLayout euiFormControlLayout--fullWidth"
|
||||
>
|
||||
<div
|
||||
class="euiFormControlLayout__childrenWrapper"
|
||||
>
|
||||
<div
|
||||
class="euiComboBox__inputWrap euiComboBox__inputWrap--fullWidth euiComboBox__inputWrap--noWrap euiComboBox__inputWrap-isClearable"
|
||||
data-test-subj="comboBoxInput"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="euiComboBoxPill euiComboBoxPill--plainText"
|
||||
>
|
||||
machine.os.raw
|
||||
</span>
|
||||
<div
|
||||
class="euiComboBox__input"
|
||||
style="font-size: 14px; display: inline-block;"
|
||||
>
|
||||
<input
|
||||
aria-autocomplete="list"
|
||||
aria-controls=""
|
||||
aria-expanded="false"
|
||||
data-test-subj="comboBoxSearchInput"
|
||||
id="generated-id__eui-combobox-id"
|
||||
role="combobox"
|
||||
style="box-sizing: content-box; width: 2px;"
|
||||
value=""
|
||||
/>
|
||||
<div
|
||||
style="position: absolute; top: 0px; left: 0px; visibility: hidden; height: 0px; overflow: scroll; white-space: pre; font-family: -webkit-small-control; letter-spacing: normal; text-transform: none;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
|
||||
>
|
||||
<button
|
||||
aria-label="Clear input"
|
||||
class="euiFormControlLayoutClearButton"
|
||||
data-test-subj="comboBoxClearButton"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="euiFormControlLayoutClearButton__icon"
|
||||
data-euiicon-type="cross"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
aria-label="Open list of options"
|
||||
class="euiFormControlLayoutCustomIcon euiFormControlLayoutCustomIcon--clickable"
|
||||
data-test-subj="comboBoxToggleListButton"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="euiFormControlLayoutCustomIcon__icon"
|
||||
data-euiicon-type="arrowDown"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>,
|
||||
"container": <div>
|
||||
<div
|
||||
class="euiComboBox euiComboBox--fullWidth"
|
||||
data-test-subj="fieldAutocompleteComboBox"
|
||||
>
|
||||
<div
|
||||
class="euiFormControlLayout euiFormControlLayout--fullWidth"
|
||||
>
|
||||
<div
|
||||
class="euiFormControlLayout__childrenWrapper"
|
||||
>
|
||||
<div
|
||||
class="euiComboBox__inputWrap euiComboBox__inputWrap--fullWidth euiComboBox__inputWrap--noWrap euiComboBox__inputWrap-isClearable"
|
||||
data-test-subj="comboBoxInput"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="euiComboBoxPill euiComboBoxPill--plainText"
|
||||
>
|
||||
machine.os.raw
|
||||
</span>
|
||||
<div
|
||||
class="euiComboBox__input"
|
||||
style="font-size: 14px; display: inline-block;"
|
||||
>
|
||||
<input
|
||||
aria-autocomplete="list"
|
||||
aria-controls=""
|
||||
aria-expanded="false"
|
||||
data-test-subj="comboBoxSearchInput"
|
||||
id="generated-id__eui-combobox-id"
|
||||
role="combobox"
|
||||
style="box-sizing: content-box; width: 2px;"
|
||||
value=""
|
||||
/>
|
||||
<div
|
||||
style="position: absolute; top: 0px; left: 0px; visibility: hidden; height: 0px; overflow: scroll; white-space: pre; font-family: -webkit-small-control; letter-spacing: normal; text-transform: none;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
|
||||
>
|
||||
<button
|
||||
aria-label="Clear input"
|
||||
class="euiFormControlLayoutClearButton"
|
||||
data-test-subj="comboBoxClearButton"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="euiFormControlLayoutClearButton__icon"
|
||||
data-euiicon-type="cross"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
aria-label="Open list of options"
|
||||
class="euiFormControlLayoutCustomIcon euiFormControlLayoutCustomIcon--clickable"
|
||||
data-test-subj="comboBoxToggleListButton"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="euiFormControlLayoutCustomIcon__icon"
|
||||
data-euiicon-type="arrowDown"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
"debug": [Function],
|
||||
"findAllByAltText": [Function],
|
||||
"findAllByDisplayValue": [Function],
|
||||
"findAllByLabelText": [Function],
|
||||
"findAllByPlaceholderText": [Function],
|
||||
"findAllByRole": [Function],
|
||||
"findAllByTestId": [Function],
|
||||
"findAllByText": [Function],
|
||||
"findAllByTitle": [Function],
|
||||
"findByAltText": [Function],
|
||||
"findByDisplayValue": [Function],
|
||||
"findByLabelText": [Function],
|
||||
"findByPlaceholderText": [Function],
|
||||
"findByRole": [Function],
|
||||
"findByTestId": [Function],
|
||||
"findByText": [Function],
|
||||
"findByTitle": [Function],
|
||||
"getAllByAltText": [Function],
|
||||
"getAllByDisplayValue": [Function],
|
||||
"getAllByLabelText": [Function],
|
||||
"getAllByPlaceholderText": [Function],
|
||||
"getAllByRole": [Function],
|
||||
"getAllByTestId": [Function],
|
||||
"getAllByText": [Function],
|
||||
"getAllByTitle": [Function],
|
||||
"getByAltText": [Function],
|
||||
"getByDisplayValue": [Function],
|
||||
"getByLabelText": [Function],
|
||||
"getByPlaceholderText": [Function],
|
||||
"getByRole": [Function],
|
||||
"getByTestId": [Function],
|
||||
"getByText": [Function],
|
||||
"getByTitle": [Function],
|
||||
"queryAllByAltText": [Function],
|
||||
"queryAllByDisplayValue": [Function],
|
||||
"queryAllByLabelText": [Function],
|
||||
"queryAllByPlaceholderText": [Function],
|
||||
"queryAllByRole": [Function],
|
||||
"queryAllByTestId": [Function],
|
||||
"queryAllByText": [Function],
|
||||
"queryAllByTitle": [Function],
|
||||
"queryByAltText": [Function],
|
||||
"queryByDisplayValue": [Function],
|
||||
"queryByLabelText": [Function],
|
||||
"queryByPlaceholderText": [Function],
|
||||
"queryByRole": [Function],
|
||||
"queryByTestId": [Function],
|
||||
"queryByText": [Function],
|
||||
"queryByTitle": [Function],
|
||||
"rerender": [Function],
|
||||
"unmount": [Function],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`FieldComponent should render the component disabled if isDisabled is true 1`] = `
|
||||
Object {
|
||||
"asFragment": [Function],
|
||||
"baseElement": <body>
|
||||
<div>
|
||||
<div
|
||||
class="euiComboBox euiComboBox--fullWidth euiComboBox-isDisabled"
|
||||
data-test-subj="fieldAutocompleteComboBox"
|
||||
>
|
||||
<div
|
||||
class="euiFormControlLayout euiFormControlLayout--fullWidth"
|
||||
>
|
||||
<div
|
||||
class="euiFormControlLayout__childrenWrapper"
|
||||
>
|
||||
<div
|
||||
class="euiComboBox__inputWrap euiComboBox__inputWrap--fullWidth euiComboBox__inputWrap--noWrap"
|
||||
data-test-subj="comboBoxInput"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="euiComboBoxPill euiComboBoxPill--plainText"
|
||||
>
|
||||
machine.os.raw
|
||||
</span>
|
||||
<div
|
||||
class="euiComboBox__input"
|
||||
style="font-size: 14px; display: inline-block;"
|
||||
>
|
||||
<input
|
||||
aria-autocomplete="list"
|
||||
aria-controls=""
|
||||
aria-expanded="false"
|
||||
data-test-subj="comboBoxSearchInput"
|
||||
disabled=""
|
||||
id="generated-id__eui-combobox-id"
|
||||
role="combobox"
|
||||
style="box-sizing: content-box; width: 2px;"
|
||||
value=""
|
||||
/>
|
||||
<div
|
||||
style="position: absolute; top: 0px; left: 0px; visibility: hidden; height: 0px; overflow: scroll; white-space: pre; font-family: -webkit-small-control; letter-spacing: normal; text-transform: none;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
|
||||
>
|
||||
<button
|
||||
aria-label="Open list of options"
|
||||
class="euiFormControlLayoutCustomIcon euiFormControlLayoutCustomIcon--clickable"
|
||||
data-test-subj="comboBoxToggleListButton"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="euiFormControlLayoutCustomIcon__icon"
|
||||
data-euiicon-type="arrowDown"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>,
|
||||
"container": <div>
|
||||
<div
|
||||
class="euiComboBox euiComboBox--fullWidth euiComboBox-isDisabled"
|
||||
data-test-subj="fieldAutocompleteComboBox"
|
||||
>
|
||||
<div
|
||||
class="euiFormControlLayout euiFormControlLayout--fullWidth"
|
||||
>
|
||||
<div
|
||||
class="euiFormControlLayout__childrenWrapper"
|
||||
>
|
||||
<div
|
||||
class="euiComboBox__inputWrap euiComboBox__inputWrap--fullWidth euiComboBox__inputWrap--noWrap"
|
||||
data-test-subj="comboBoxInput"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="euiComboBoxPill euiComboBoxPill--plainText"
|
||||
>
|
||||
machine.os.raw
|
||||
</span>
|
||||
<div
|
||||
class="euiComboBox__input"
|
||||
style="font-size: 14px; display: inline-block;"
|
||||
>
|
||||
<input
|
||||
aria-autocomplete="list"
|
||||
aria-controls=""
|
||||
aria-expanded="false"
|
||||
data-test-subj="comboBoxSearchInput"
|
||||
disabled=""
|
||||
id="generated-id__eui-combobox-id"
|
||||
role="combobox"
|
||||
style="box-sizing: content-box; width: 2px;"
|
||||
value=""
|
||||
/>
|
||||
<div
|
||||
style="position: absolute; top: 0px; left: 0px; visibility: hidden; height: 0px; overflow: scroll; white-space: pre; font-family: -webkit-small-control; letter-spacing: normal; text-transform: none;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
|
||||
>
|
||||
<button
|
||||
aria-label="Open list of options"
|
||||
class="euiFormControlLayoutCustomIcon euiFormControlLayoutCustomIcon--clickable"
|
||||
data-test-subj="comboBoxToggleListButton"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="euiFormControlLayoutCustomIcon__icon"
|
||||
data-euiicon-type="arrowDown"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
"debug": [Function],
|
||||
"findAllByAltText": [Function],
|
||||
"findAllByDisplayValue": [Function],
|
||||
"findAllByLabelText": [Function],
|
||||
"findAllByPlaceholderText": [Function],
|
||||
"findAllByRole": [Function],
|
||||
"findAllByTestId": [Function],
|
||||
"findAllByText": [Function],
|
||||
"findAllByTitle": [Function],
|
||||
"findByAltText": [Function],
|
||||
"findByDisplayValue": [Function],
|
||||
"findByLabelText": [Function],
|
||||
"findByPlaceholderText": [Function],
|
||||
"findByRole": [Function],
|
||||
"findByTestId": [Function],
|
||||
"findByText": [Function],
|
||||
"findByTitle": [Function],
|
||||
"getAllByAltText": [Function],
|
||||
"getAllByDisplayValue": [Function],
|
||||
"getAllByLabelText": [Function],
|
||||
"getAllByPlaceholderText": [Function],
|
||||
"getAllByRole": [Function],
|
||||
"getAllByTestId": [Function],
|
||||
"getAllByText": [Function],
|
||||
"getAllByTitle": [Function],
|
||||
"getByAltText": [Function],
|
||||
"getByDisplayValue": [Function],
|
||||
"getByLabelText": [Function],
|
||||
"getByPlaceholderText": [Function],
|
||||
"getByRole": [Function],
|
||||
"getByTestId": [Function],
|
||||
"getByText": [Function],
|
||||
"getByTitle": [Function],
|
||||
"queryAllByAltText": [Function],
|
||||
"queryAllByDisplayValue": [Function],
|
||||
"queryAllByLabelText": [Function],
|
||||
"queryAllByPlaceholderText": [Function],
|
||||
"queryAllByRole": [Function],
|
||||
"queryAllByTestId": [Function],
|
||||
"queryAllByText": [Function],
|
||||
"queryAllByTitle": [Function],
|
||||
"queryByAltText": [Function],
|
||||
"queryByDisplayValue": [Function],
|
||||
"queryByLabelText": [Function],
|
||||
"queryByPlaceholderText": [Function],
|
||||
"queryByRole": [Function],
|
||||
"queryByTestId": [Function],
|
||||
"queryByText": [Function],
|
||||
"queryByTitle": [Function],
|
||||
"rerender": [Function],
|
||||
"unmount": [Function],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`FieldComponent should render the component enabled and displays the selected field correctly 1`] = `
|
||||
Object {
|
||||
"asFragment": [Function],
|
||||
"baseElement": <body>
|
||||
<div>
|
||||
<div
|
||||
class="euiComboBox euiComboBox--fullWidth"
|
||||
data-test-subj="fieldAutocompleteComboBox"
|
||||
>
|
||||
<div
|
||||
class="euiFormControlLayout euiFormControlLayout--fullWidth"
|
||||
>
|
||||
<div
|
||||
class="euiFormControlLayout__childrenWrapper"
|
||||
>
|
||||
<div
|
||||
class="euiComboBox__inputWrap euiComboBox__inputWrap--fullWidth euiComboBox__inputWrap--noWrap"
|
||||
data-test-subj="comboBoxInput"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="euiComboBoxPill euiComboBoxPill--plainText"
|
||||
>
|
||||
machine.os.raw
|
||||
</span>
|
||||
<div
|
||||
class="euiComboBox__input"
|
||||
style="font-size: 14px; display: inline-block;"
|
||||
>
|
||||
<input
|
||||
aria-autocomplete="list"
|
||||
aria-controls=""
|
||||
aria-expanded="false"
|
||||
data-test-subj="comboBoxSearchInput"
|
||||
id="generated-id__eui-combobox-id"
|
||||
role="combobox"
|
||||
style="box-sizing: content-box; width: 2px;"
|
||||
value=""
|
||||
/>
|
||||
<div
|
||||
style="position: absolute; top: 0px; left: 0px; visibility: hidden; height: 0px; overflow: scroll; white-space: pre; font-family: -webkit-small-control; letter-spacing: normal; text-transform: none;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
|
||||
>
|
||||
<button
|
||||
aria-label="Open list of options"
|
||||
class="euiFormControlLayoutCustomIcon euiFormControlLayoutCustomIcon--clickable"
|
||||
data-test-subj="comboBoxToggleListButton"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="euiFormControlLayoutCustomIcon__icon"
|
||||
data-euiicon-type="arrowDown"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>,
|
||||
"container": <div>
|
||||
<div
|
||||
class="euiComboBox euiComboBox--fullWidth"
|
||||
data-test-subj="fieldAutocompleteComboBox"
|
||||
>
|
||||
<div
|
||||
class="euiFormControlLayout euiFormControlLayout--fullWidth"
|
||||
>
|
||||
<div
|
||||
class="euiFormControlLayout__childrenWrapper"
|
||||
>
|
||||
<div
|
||||
class="euiComboBox__inputWrap euiComboBox__inputWrap--fullWidth euiComboBox__inputWrap--noWrap"
|
||||
data-test-subj="comboBoxInput"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="euiComboBoxPill euiComboBoxPill--plainText"
|
||||
>
|
||||
machine.os.raw
|
||||
</span>
|
||||
<div
|
||||
class="euiComboBox__input"
|
||||
style="font-size: 14px; display: inline-block;"
|
||||
>
|
||||
<input
|
||||
aria-autocomplete="list"
|
||||
aria-controls=""
|
||||
aria-expanded="false"
|
||||
data-test-subj="comboBoxSearchInput"
|
||||
id="generated-id__eui-combobox-id"
|
||||
role="combobox"
|
||||
style="box-sizing: content-box; width: 2px;"
|
||||
value=""
|
||||
/>
|
||||
<div
|
||||
style="position: absolute; top: 0px; left: 0px; visibility: hidden; height: 0px; overflow: scroll; white-space: pre; font-family: -webkit-small-control; letter-spacing: normal; text-transform: none;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
|
||||
>
|
||||
<button
|
||||
aria-label="Open list of options"
|
||||
class="euiFormControlLayoutCustomIcon euiFormControlLayoutCustomIcon--clickable"
|
||||
data-test-subj="comboBoxToggleListButton"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="euiFormControlLayoutCustomIcon__icon"
|
||||
data-euiicon-type="arrowDown"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
"debug": [Function],
|
||||
"findAllByAltText": [Function],
|
||||
"findAllByDisplayValue": [Function],
|
||||
"findAllByLabelText": [Function],
|
||||
"findAllByPlaceholderText": [Function],
|
||||
"findAllByRole": [Function],
|
||||
"findAllByTestId": [Function],
|
||||
"findAllByText": [Function],
|
||||
"findAllByTitle": [Function],
|
||||
"findByAltText": [Function],
|
||||
"findByDisplayValue": [Function],
|
||||
"findByLabelText": [Function],
|
||||
"findByPlaceholderText": [Function],
|
||||
"findByRole": [Function],
|
||||
"findByTestId": [Function],
|
||||
"findByText": [Function],
|
||||
"findByTitle": [Function],
|
||||
"getAllByAltText": [Function],
|
||||
"getAllByDisplayValue": [Function],
|
||||
"getAllByLabelText": [Function],
|
||||
"getAllByPlaceholderText": [Function],
|
||||
"getAllByRole": [Function],
|
||||
"getAllByTestId": [Function],
|
||||
"getAllByText": [Function],
|
||||
"getAllByTitle": [Function],
|
||||
"getByAltText": [Function],
|
||||
"getByDisplayValue": [Function],
|
||||
"getByLabelText": [Function],
|
||||
"getByPlaceholderText": [Function],
|
||||
"getByRole": [Function],
|
||||
"getByTestId": [Function],
|
||||
"getByText": [Function],
|
||||
"getByTitle": [Function],
|
||||
"queryAllByAltText": [Function],
|
||||
"queryAllByDisplayValue": [Function],
|
||||
"queryAllByLabelText": [Function],
|
||||
"queryAllByPlaceholderText": [Function],
|
||||
"queryAllByRole": [Function],
|
||||
"queryAllByTestId": [Function],
|
||||
"queryAllByText": [Function],
|
||||
"queryAllByTitle": [Function],
|
||||
"queryByAltText": [Function],
|
||||
"queryByDisplayValue": [Function],
|
||||
"queryByLabelText": [Function],
|
||||
"queryByPlaceholderText": [Function],
|
||||
"queryByRole": [Function],
|
||||
"queryByTestId": [Function],
|
||||
"queryByText": [Function],
|
||||
"queryByTitle": [Function],
|
||||
"rerender": [Function],
|
||||
"unmount": [Function],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`FieldComponent should render the loading spinner if isLoading is true when clicked 1`] = `
|
||||
Object {
|
||||
"asFragment": [Function],
|
||||
"baseElement": <body>
|
||||
<div>
|
||||
<div
|
||||
class="euiComboBox euiComboBox--fullWidth euiComboBox-isDisabled"
|
||||
data-test-subj="fieldAutocompleteComboBox"
|
||||
>
|
||||
<div
|
||||
class="euiFormControlLayout euiFormControlLayout--fullWidth"
|
||||
>
|
||||
<div
|
||||
class="euiFormControlLayout__childrenWrapper"
|
||||
>
|
||||
<div
|
||||
class="euiComboBox__inputWrap euiComboBox__inputWrap--fullWidth euiComboBox__inputWrap--noWrap euiComboBox__inputWrap-isLoading"
|
||||
data-test-subj="comboBoxInput"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="euiComboBoxPill euiComboBoxPill--plainText"
|
||||
>
|
||||
machine.os.raw
|
||||
</span>
|
||||
<div
|
||||
class="euiComboBox__input"
|
||||
style="font-size: 14px; display: inline-block;"
|
||||
>
|
||||
<input
|
||||
aria-autocomplete="list"
|
||||
aria-controls=""
|
||||
aria-expanded="false"
|
||||
data-test-subj="comboBoxSearchInput"
|
||||
disabled=""
|
||||
id="generated-id__eui-combobox-id"
|
||||
role="combobox"
|
||||
style="box-sizing: content-box; width: 2px;"
|
||||
value=""
|
||||
/>
|
||||
<div
|
||||
style="position: absolute; top: 0px; left: 0px; visibility: hidden; height: 0px; overflow: scroll; white-space: pre; font-family: -webkit-small-control; letter-spacing: normal; text-transform: none;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
|
||||
>
|
||||
<span
|
||||
aria-label="Loading"
|
||||
class="euiLoadingSpinner emotion-euiLoadingSpinner-m"
|
||||
role="progressbar"
|
||||
/>
|
||||
<button
|
||||
aria-label="Open list of options"
|
||||
class="euiFormControlLayoutCustomIcon euiFormControlLayoutCustomIcon--clickable"
|
||||
data-test-subj="comboBoxToggleListButton"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="euiFormControlLayoutCustomIcon__icon"
|
||||
data-euiicon-type="arrowDown"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>,
|
||||
"container": <div>
|
||||
<div
|
||||
class="euiComboBox euiComboBox--fullWidth euiComboBox-isDisabled"
|
||||
data-test-subj="fieldAutocompleteComboBox"
|
||||
>
|
||||
<div
|
||||
class="euiFormControlLayout euiFormControlLayout--fullWidth"
|
||||
>
|
||||
<div
|
||||
class="euiFormControlLayout__childrenWrapper"
|
||||
>
|
||||
<div
|
||||
class="euiComboBox__inputWrap euiComboBox__inputWrap--fullWidth euiComboBox__inputWrap--noWrap euiComboBox__inputWrap-isLoading"
|
||||
data-test-subj="comboBoxInput"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="euiComboBoxPill euiComboBoxPill--plainText"
|
||||
>
|
||||
machine.os.raw
|
||||
</span>
|
||||
<div
|
||||
class="euiComboBox__input"
|
||||
style="font-size: 14px; display: inline-block;"
|
||||
>
|
||||
<input
|
||||
aria-autocomplete="list"
|
||||
aria-controls=""
|
||||
aria-expanded="false"
|
||||
data-test-subj="comboBoxSearchInput"
|
||||
disabled=""
|
||||
id="generated-id__eui-combobox-id"
|
||||
role="combobox"
|
||||
style="box-sizing: content-box; width: 2px;"
|
||||
value=""
|
||||
/>
|
||||
<div
|
||||
style="position: absolute; top: 0px; left: 0px; visibility: hidden; height: 0px; overflow: scroll; white-space: pre; font-family: -webkit-small-control; letter-spacing: normal; text-transform: none;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
|
||||
>
|
||||
<span
|
||||
aria-label="Loading"
|
||||
class="euiLoadingSpinner emotion-euiLoadingSpinner-m"
|
||||
role="progressbar"
|
||||
/>
|
||||
<button
|
||||
aria-label="Open list of options"
|
||||
class="euiFormControlLayoutCustomIcon euiFormControlLayoutCustomIcon--clickable"
|
||||
data-test-subj="comboBoxToggleListButton"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="euiFormControlLayoutCustomIcon__icon"
|
||||
data-euiicon-type="arrowDown"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
"debug": [Function],
|
||||
"findAllByAltText": [Function],
|
||||
"findAllByDisplayValue": [Function],
|
||||
"findAllByLabelText": [Function],
|
||||
"findAllByPlaceholderText": [Function],
|
||||
"findAllByRole": [Function],
|
||||
"findAllByTestId": [Function],
|
||||
"findAllByText": [Function],
|
||||
"findAllByTitle": [Function],
|
||||
"findByAltText": [Function],
|
||||
"findByDisplayValue": [Function],
|
||||
"findByLabelText": [Function],
|
||||
"findByPlaceholderText": [Function],
|
||||
"findByRole": [Function],
|
||||
"findByTestId": [Function],
|
||||
"findByText": [Function],
|
||||
"findByTitle": [Function],
|
||||
"getAllByAltText": [Function],
|
||||
"getAllByDisplayValue": [Function],
|
||||
"getAllByLabelText": [Function],
|
||||
"getAllByPlaceholderText": [Function],
|
||||
"getAllByRole": [Function],
|
||||
"getAllByTestId": [Function],
|
||||
"getAllByText": [Function],
|
||||
"getAllByTitle": [Function],
|
||||
"getByAltText": [Function],
|
||||
"getByDisplayValue": [Function],
|
||||
"getByLabelText": [Function],
|
||||
"getByPlaceholderText": [Function],
|
||||
"getByRole": [Function],
|
||||
"getByTestId": [Function],
|
||||
"getByText": [Function],
|
||||
"getByTitle": [Function],
|
||||
"queryAllByAltText": [Function],
|
||||
"queryAllByDisplayValue": [Function],
|
||||
"queryAllByLabelText": [Function],
|
||||
"queryAllByPlaceholderText": [Function],
|
||||
"queryAllByRole": [Function],
|
||||
"queryAllByTestId": [Function],
|
||||
"queryAllByText": [Function],
|
||||
"queryAllByTitle": [Function],
|
||||
"queryByAltText": [Function],
|
||||
"queryByDisplayValue": [Function],
|
||||
"queryByLabelText": [Function],
|
||||
"queryByPlaceholderText": [Function],
|
||||
"queryByRole": [Function],
|
||||
"queryByTestId": [Function],
|
||||
"queryByText": [Function],
|
||||
"queryByTitle": [Function],
|
||||
"rerender": [Function],
|
||||
"unmount": [Function],
|
||||
}
|
||||
`;
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { disabledTypesWithTooltipText } from '../disabled_types_with_tooltip_text';
|
||||
|
||||
jest.mock('../../translations', () => ({
|
||||
BINARY_TYPE_NOT_SUPPORTED: 'Binary fields are currently unsupported',
|
||||
}));
|
||||
describe('disabledTypesWithTooltipText', () => {
|
||||
it('should return Binary fields are currently unsupported for binary type', () => {
|
||||
const type = 'binary';
|
||||
expect(disabledTypesWithTooltipText[type]).toEqual('Binary fields are currently unsupported');
|
||||
});
|
||||
});
|
|
@ -7,14 +7,34 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui';
|
||||
import { FieldComponent } from '.';
|
||||
import { fields, getField } from '../fields/index.mock';
|
||||
import { fireEvent, render, waitFor } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
describe('field', () => {
|
||||
test('it renders disabled if "isDisabled" is true', () => {
|
||||
const wrapper = mount(
|
||||
import { FieldComponent } from '..';
|
||||
import { fields, getField } from '../../fields/index.mock';
|
||||
|
||||
describe('FieldComponent', () => {
|
||||
it('should render the component enabled and displays the selected field correctly', () => {
|
||||
const wrapper = render(
|
||||
<FieldComponent
|
||||
isClearable={false}
|
||||
isDisabled={false}
|
||||
isLoading={false}
|
||||
indexPattern={{
|
||||
fields,
|
||||
id: '1234',
|
||||
title: 'logstash-*',
|
||||
}}
|
||||
onChange={jest.fn()}
|
||||
placeholder="Placeholder text"
|
||||
selectedField={getField('machine.os.raw')}
|
||||
/>
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(wrapper.getByTestId('fieldAutocompleteComboBox')).toHaveTextContent('machine.os.raw');
|
||||
});
|
||||
it('should render the component disabled if isDisabled is true', () => {
|
||||
const wrapper = render(
|
||||
<FieldComponent
|
||||
isClearable={false}
|
||||
isDisabled={true}
|
||||
|
@ -29,38 +49,32 @@ describe('field', () => {
|
|||
selectedField={getField('machine.os.raw')}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(
|
||||
wrapper.find(`[data-test-subj="fieldAutocompleteComboBox"] input`).prop('disabled')
|
||||
).toBeTruthy();
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(wrapper.getByTestId('fieldAutocompleteComboBox').querySelector('input')).toBeDisabled();
|
||||
});
|
||||
|
||||
test('it renders loading if "isLoading" is true', () => {
|
||||
const wrapper = mount(
|
||||
it('should render the loading spinner if isLoading is true when clicked', () => {
|
||||
const wrapper = render(
|
||||
<FieldComponent
|
||||
isClearable={false}
|
||||
isDisabled={true}
|
||||
isLoading={true}
|
||||
indexPattern={{
|
||||
fields,
|
||||
id: '1234',
|
||||
title: 'logstash-*',
|
||||
}}
|
||||
isClearable={false}
|
||||
isDisabled={false}
|
||||
isLoading={true}
|
||||
onChange={jest.fn()}
|
||||
placeholder="Placeholder text"
|
||||
selectedField={getField('machine.os.raw')}
|
||||
/>
|
||||
);
|
||||
wrapper.find(`[data-test-subj="fieldAutocompleteComboBox"] button`).at(0).simulate('click');
|
||||
expect(
|
||||
wrapper
|
||||
.find(`EuiComboBoxOptionsList[data-test-subj="fieldAutocompleteComboBox-optionsList"]`)
|
||||
.prop('isLoading')
|
||||
).toBeTruthy();
|
||||
const fieldAutocompleteComboBox = wrapper.getByTestId('fieldAutocompleteComboBox');
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
fireEvent.click(fieldAutocompleteComboBox);
|
||||
expect(wrapper.getByRole('progressbar')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('it allows user to clear values if "isClearable" is true', () => {
|
||||
const wrapper = mount(
|
||||
it('should allow user to clear values if isClearable is true', () => {
|
||||
const wrapper = render(
|
||||
<FieldComponent
|
||||
indexPattern={{
|
||||
fields,
|
||||
|
@ -75,71 +89,29 @@ describe('field', () => {
|
|||
selectedField={getField('machine.os.raw')}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(
|
||||
wrapper
|
||||
.find(`[data-test-subj="comboBoxInput"]`)
|
||||
.hasClass('euiComboBox__inputWrap-isClearable')
|
||||
).toBeTruthy();
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(wrapper.getByTestId('comboBoxClearButton')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('it correctly displays selected field', () => {
|
||||
const wrapper = mount(
|
||||
it('should change the selected value', async () => {
|
||||
const wrapper = render(
|
||||
<FieldComponent
|
||||
isClearable={false}
|
||||
isDisabled={true}
|
||||
isLoading={false}
|
||||
indexPattern={{
|
||||
fields,
|
||||
id: '1234',
|
||||
title: 'logstash-*',
|
||||
}}
|
||||
isClearable={false}
|
||||
isDisabled={false}
|
||||
isLoading={false}
|
||||
onChange={jest.fn()}
|
||||
placeholder="Placeholder text"
|
||||
selectedField={getField('machine.os.raw')}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(
|
||||
wrapper.find(`[data-test-subj="fieldAutocompleteComboBox"] EuiComboBoxPill`).at(0).text()
|
||||
).toEqual('machine.os.raw');
|
||||
});
|
||||
|
||||
test('it invokes "onChange" when option selected', () => {
|
||||
const mockOnChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
<FieldComponent
|
||||
indexPattern={{
|
||||
fields,
|
||||
id: '1234',
|
||||
title: 'logstash-*',
|
||||
}}
|
||||
isClearable={false}
|
||||
isDisabled={false}
|
||||
isLoading={false}
|
||||
onChange={mockOnChange}
|
||||
placeholder="Placeholder text"
|
||||
selectedField={getField('machine.os.raw')}
|
||||
/>
|
||||
const fieldAutocompleteComboBox = wrapper.getByTestId('comboBoxSearchInput');
|
||||
fireEvent.change(fieldAutocompleteComboBox, { target: { value: '_source' } });
|
||||
await waitFor(() =>
|
||||
expect(wrapper.getByTestId('fieldAutocompleteComboBox')).toHaveTextContent('_source')
|
||||
);
|
||||
|
||||
(
|
||||
wrapper.find(EuiComboBox).props() as unknown as {
|
||||
onChange: (a: EuiComboBoxOptionOption[]) => void;
|
||||
}
|
||||
).onChange([{ label: 'machine.os' }]);
|
||||
|
||||
expect(mockOnChange).toHaveBeenCalledWith([
|
||||
{
|
||||
aggregatable: true,
|
||||
count: 0,
|
||||
esTypes: ['text'],
|
||||
name: 'machine.os',
|
||||
readFromDocValues: false,
|
||||
scripted: false,
|
||||
searchable: true,
|
||||
type: 'string',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,396 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { DataViewFieldBase } from '@kbn/es-query';
|
||||
import { ReactElement } from 'react';
|
||||
import { act } from '@testing-library/react';
|
||||
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import TestRenderer from 'react-test-renderer';
|
||||
const { act: actTestRenderer } = TestRenderer;
|
||||
|
||||
import { fields } from '../../fields/index.mock';
|
||||
import { useField } from '../use_field';
|
||||
|
||||
jest.mock('../../translations', () => ({
|
||||
BINARY_TYPE_NOT_SUPPORTED: 'Binary fields are currently unsupported',
|
||||
}));
|
||||
|
||||
const indexPattern = { fields, title: 'title' };
|
||||
const onChangeMock = jest.fn();
|
||||
const selectedField = { name: '@timestamp', type: 'date' };
|
||||
describe('useField', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
jest.resetModules();
|
||||
});
|
||||
|
||||
describe('comboOptions and selectedComboOptions', () => {
|
||||
it('should return default values', () => {
|
||||
const { result } = renderHook(() => useField({ indexPattern, onChange: onChangeMock }));
|
||||
const { isInvalid, comboOptions, selectedComboOptions, fieldWidth } = result.current;
|
||||
expect(isInvalid).toBeFalsy();
|
||||
expect(comboOptions.length).toEqual(30);
|
||||
expect(selectedComboOptions.length).toEqual(0);
|
||||
expect(fieldWidth).toEqual({});
|
||||
});
|
||||
it('should map fields to comboOptions correctly and return empty selectedComboOptions', () => {
|
||||
const newIndexPattern = {
|
||||
...indexPattern,
|
||||
fields: [
|
||||
{
|
||||
name: 'bytes',
|
||||
type: 'number',
|
||||
esTypes: ['long'],
|
||||
count: 10,
|
||||
scripted: false,
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
readFromDocValues: true,
|
||||
},
|
||||
{
|
||||
name: 'ssl',
|
||||
type: 'boolean',
|
||||
esTypes: ['boolean'],
|
||||
count: 20,
|
||||
scripted: false,
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
readFromDocValues: true,
|
||||
},
|
||||
{
|
||||
name: '@timestamp',
|
||||
type: 'date',
|
||||
esTypes: ['date'],
|
||||
count: 30,
|
||||
scripted: false,
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
readFromDocValues: true,
|
||||
},
|
||||
] as unknown as DataViewFieldBase[],
|
||||
title: 'title1',
|
||||
};
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useField({ indexPattern: newIndexPattern, onChange: onChangeMock })
|
||||
);
|
||||
const { comboOptions, selectedComboOptions } = result.current;
|
||||
expect(comboOptions).toEqual([{ label: 'bytes' }, { label: 'ssl' }, { label: '@timestamp' }]);
|
||||
expect(selectedComboOptions).toEqual([]);
|
||||
});
|
||||
it('should map fields to comboOptions correctly and return selectedComboOptions', () => {
|
||||
const newIndexPattern = {
|
||||
...indexPattern,
|
||||
fields: [
|
||||
{
|
||||
name: 'bytes',
|
||||
type: 'number',
|
||||
esTypes: ['long'],
|
||||
count: 10,
|
||||
scripted: false,
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
readFromDocValues: true,
|
||||
},
|
||||
{
|
||||
name: 'ssl',
|
||||
type: 'boolean',
|
||||
esTypes: ['boolean'],
|
||||
count: 20,
|
||||
scripted: false,
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
readFromDocValues: true,
|
||||
},
|
||||
{
|
||||
name: '@timestamp',
|
||||
type: 'date',
|
||||
esTypes: ['date'],
|
||||
count: 30,
|
||||
scripted: false,
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
readFromDocValues: true,
|
||||
},
|
||||
] as unknown as DataViewFieldBase[],
|
||||
title: 'title1',
|
||||
};
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useField({ indexPattern: newIndexPattern, onChange: onChangeMock, selectedField })
|
||||
);
|
||||
const { comboOptions, selectedComboOptions } = result.current;
|
||||
expect(comboOptions).toEqual([{ label: 'bytes' }, { label: 'ssl' }, { label: '@timestamp' }]);
|
||||
expect(selectedComboOptions).toEqual([{ label: '@timestamp' }]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDisabledLabelTooltipTexts and renderFields', () => {
|
||||
it('should return label as component and disable the binary field type if field.esType has one the disabled types', () => {
|
||||
indexPattern.fields = [
|
||||
{
|
||||
name: 'blob',
|
||||
type: 'unknown',
|
||||
esTypes: ['binary'],
|
||||
scripted: false,
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
readFromDocValues: true,
|
||||
},
|
||||
{
|
||||
name: 'bytes',
|
||||
type: 'number',
|
||||
esTypes: ['long'],
|
||||
count: 10,
|
||||
scripted: false,
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
readFromDocValues: true,
|
||||
},
|
||||
{
|
||||
name: 'ssl',
|
||||
type: 'boolean',
|
||||
esTypes: ['boolean'],
|
||||
count: 20,
|
||||
scripted: false,
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
readFromDocValues: true,
|
||||
},
|
||||
{
|
||||
name: '@timestamp',
|
||||
type: 'date',
|
||||
esTypes: ['date'],
|
||||
count: 30,
|
||||
scripted: false,
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
readFromDocValues: true,
|
||||
},
|
||||
] as unknown as DataViewFieldBase[];
|
||||
const { result } = renderHook(() => useField({ indexPattern, onChange: onChangeMock }));
|
||||
const { comboOptions, renderFields } = result.current;
|
||||
expect(comboOptions).toEqual([
|
||||
{ label: 'blob' },
|
||||
{ label: 'bytes' },
|
||||
{ label: 'ssl' },
|
||||
{ label: '@timestamp' },
|
||||
]);
|
||||
act(() => {
|
||||
const label = renderFields({ label: 'blob' }, '', '') as ReactElement;
|
||||
expect(label?.props.content).toEqual('Binary fields are currently unsupported');
|
||||
});
|
||||
});
|
||||
it('should return label as component and disable the binary field type if field.type is one of the disabled types', () => {
|
||||
indexPattern.fields = [
|
||||
{
|
||||
name: 'blob',
|
||||
type: 'binary',
|
||||
esTypes: [],
|
||||
scripted: false,
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
readFromDocValues: true,
|
||||
},
|
||||
{
|
||||
name: 'bytes',
|
||||
type: 'number',
|
||||
esTypes: ['long'],
|
||||
count: 10,
|
||||
scripted: false,
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
readFromDocValues: true,
|
||||
},
|
||||
{
|
||||
name: 'ssl',
|
||||
type: 'boolean',
|
||||
esTypes: ['boolean'],
|
||||
count: 20,
|
||||
scripted: false,
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
readFromDocValues: true,
|
||||
},
|
||||
{
|
||||
name: '@timestamp',
|
||||
type: 'date',
|
||||
esTypes: ['date'],
|
||||
count: 30,
|
||||
scripted: false,
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
readFromDocValues: true,
|
||||
},
|
||||
] as unknown as DataViewFieldBase[];
|
||||
const { result } = renderHook(() => useField({ indexPattern, onChange: onChangeMock }));
|
||||
const { comboOptions, renderFields } = result.current;
|
||||
expect(comboOptions).toEqual([
|
||||
{ label: 'blob' },
|
||||
{ label: 'bytes' },
|
||||
{ label: 'ssl' },
|
||||
{ label: '@timestamp' },
|
||||
]);
|
||||
act(() => {
|
||||
const label = renderFields({ label: 'blob' }, '', '') as ReactElement;
|
||||
expect(label?.props.content).toEqual('Binary fields are currently unsupported');
|
||||
});
|
||||
});
|
||||
it('should return label as string', () => {
|
||||
indexPattern.fields = [
|
||||
{
|
||||
name: 'bytes',
|
||||
type: 'number',
|
||||
esTypes: ['long'],
|
||||
count: 10,
|
||||
scripted: false,
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
readFromDocValues: true,
|
||||
},
|
||||
{
|
||||
name: 'ssl',
|
||||
type: 'boolean',
|
||||
esTypes: ['boolean'],
|
||||
count: 20,
|
||||
scripted: false,
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
readFromDocValues: true,
|
||||
},
|
||||
{
|
||||
name: '@timestamp',
|
||||
type: 'date',
|
||||
esTypes: ['date'],
|
||||
count: 30,
|
||||
scripted: false,
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
readFromDocValues: true,
|
||||
},
|
||||
] as unknown as DataViewFieldBase[];
|
||||
const { result } = renderHook(() => useField({ indexPattern, onChange: onChangeMock }));
|
||||
const { comboOptions, renderFields } = result.current;
|
||||
expect(comboOptions).toEqual([{ label: 'bytes' }, { label: 'ssl' }, { label: '@timestamp' }]);
|
||||
act(() => {
|
||||
const label = renderFields({ label: '@timestamp' }, '', '') as ReactElement;
|
||||
expect(label).toEqual('@timestamp');
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('handleValuesChange', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
jest.resetModules();
|
||||
});
|
||||
it('should invoke onChange with one value if one option is sent', () => {
|
||||
const { result } = renderHook(() => useField({ indexPattern, onChange: onChangeMock }));
|
||||
act(() => {
|
||||
result.current.handleValuesChange([
|
||||
{
|
||||
label: '@timestamp',
|
||||
},
|
||||
]);
|
||||
expect(onChangeMock).toHaveBeenCalledWith([
|
||||
{
|
||||
aggregatable: true,
|
||||
count: 30,
|
||||
esTypes: ['date'],
|
||||
name: '@timestamp',
|
||||
readFromDocValues: true,
|
||||
scripted: false,
|
||||
searchable: true,
|
||||
type: 'date',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
it('should invoke onChange with array value if more than an option', () => {
|
||||
const { result } = renderHook(() => useField({ indexPattern, onChange: onChangeMock }));
|
||||
act(() => {
|
||||
result.current.handleValuesChange([
|
||||
{
|
||||
label: '@timestamp',
|
||||
},
|
||||
{
|
||||
label: 'ssl',
|
||||
},
|
||||
]);
|
||||
expect(onChangeMock).toHaveBeenCalledWith([
|
||||
{
|
||||
aggregatable: true,
|
||||
count: 30,
|
||||
esTypes: ['date'],
|
||||
name: '@timestamp',
|
||||
readFromDocValues: true,
|
||||
scripted: false,
|
||||
searchable: true,
|
||||
type: 'date',
|
||||
},
|
||||
{
|
||||
name: 'ssl',
|
||||
type: 'boolean',
|
||||
esTypes: ['boolean'],
|
||||
count: 20,
|
||||
scripted: false,
|
||||
searchable: true,
|
||||
aggregatable: true,
|
||||
readFromDocValues: true,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('fieldWidth', () => {
|
||||
it('should return object has width prop', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useField({ indexPattern, onChange: onChangeMock, fieldInputWidth: 100 })
|
||||
);
|
||||
expect(result.current.fieldWidth).toEqual({ width: '100px' });
|
||||
});
|
||||
it('should return empty object', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useField({ indexPattern, onChange: onChangeMock, fieldInputWidth: 0 })
|
||||
);
|
||||
expect(result.current.fieldWidth).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('isInvalid with handleTouch', () => {
|
||||
it('should return isInvalid equals true when calling with no selectedField and isRequired is true', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useField({ indexPattern, onChange: onChangeMock, isRequired: true })
|
||||
);
|
||||
|
||||
actTestRenderer(() => {
|
||||
result.current.handleTouch();
|
||||
});
|
||||
expect(result.current.isInvalid).toBeTruthy();
|
||||
});
|
||||
it('should return isInvalid equals false with selectedField and isRequired is true', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useField({ indexPattern, onChange: onChangeMock, isRequired: true, selectedField })
|
||||
);
|
||||
|
||||
actTestRenderer(() => {
|
||||
result.current.handleTouch();
|
||||
});
|
||||
expect(result.current.isInvalid).toBeFalsy();
|
||||
});
|
||||
it('should return isInvalid equals false when isRequired is false', () => {
|
||||
const { result } = renderHook(() => useField({ indexPattern, onChange: onChangeMock }));
|
||||
|
||||
actTestRenderer(() => {
|
||||
result.current.handleTouch();
|
||||
});
|
||||
expect(result.current.isInvalid).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
interface DisabledTypesTextType {
|
||||
[typeName: string]: string;
|
||||
}
|
||||
import * as i18n from '../translations';
|
||||
|
||||
export const disabledTypesWithTooltipText: DisabledTypesTextType = {
|
||||
binary: i18n.BINARY_TYPE_NOT_SUPPORTED,
|
||||
};
|
|
@ -6,31 +6,15 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui';
|
||||
import { DataViewBase, DataViewFieldBase } from '@kbn/es-query';
|
||||
import React from 'react';
|
||||
import { EuiComboBox } from '@elastic/eui';
|
||||
|
||||
import {
|
||||
getGenericComboBoxProps,
|
||||
GetGenericComboBoxPropsReturn,
|
||||
} from '../get_generic_combo_box_props';
|
||||
import { FieldProps } from './types';
|
||||
import { useField } from './use_field';
|
||||
|
||||
const AS_PLAIN_TEXT = { asPlainText: true };
|
||||
|
||||
interface OperatorProps {
|
||||
fieldInputWidth?: number;
|
||||
fieldTypeFilter?: string[];
|
||||
indexPattern: DataViewBase | undefined;
|
||||
isClearable: boolean;
|
||||
isDisabled: boolean;
|
||||
isLoading: boolean;
|
||||
isRequired?: boolean;
|
||||
onChange: (a: DataViewFieldBase[]) => void;
|
||||
placeholder: string;
|
||||
selectedField: DataViewFieldBase | undefined;
|
||||
}
|
||||
|
||||
export const FieldComponent: React.FC<OperatorProps> = ({
|
||||
export const FieldComponent: React.FC<FieldProps> = ({
|
||||
fieldInputWidth,
|
||||
fieldTypeFilter = [],
|
||||
indexPattern,
|
||||
|
@ -42,36 +26,23 @@ export const FieldComponent: React.FC<OperatorProps> = ({
|
|||
placeholder,
|
||||
selectedField,
|
||||
}): JSX.Element => {
|
||||
const [touched, setIsTouched] = useState(false);
|
||||
|
||||
const { availableFields, selectedFields } = useMemo(
|
||||
() => getComboBoxFields(indexPattern, selectedField, fieldTypeFilter),
|
||||
[indexPattern, selectedField, fieldTypeFilter]
|
||||
);
|
||||
|
||||
const { comboOptions, labels, selectedComboOptions } = useMemo(
|
||||
() => getComboBoxProps({ availableFields, selectedFields }),
|
||||
[availableFields, selectedFields]
|
||||
);
|
||||
|
||||
const handleValuesChange = useCallback(
|
||||
(newOptions: EuiComboBoxOptionOption[]): void => {
|
||||
const newValues: DataViewFieldBase[] = newOptions.map(
|
||||
({ label }) => availableFields[labels.indexOf(label)]
|
||||
);
|
||||
onChange(newValues);
|
||||
},
|
||||
[availableFields, labels, onChange]
|
||||
);
|
||||
|
||||
const handleTouch = useCallback((): void => {
|
||||
setIsTouched(true);
|
||||
}, [setIsTouched]);
|
||||
|
||||
const fieldWidth = useMemo(() => {
|
||||
return fieldInputWidth ? { width: `${fieldInputWidth}px` } : {};
|
||||
}, [fieldInputWidth]);
|
||||
const {
|
||||
isInvalid,
|
||||
comboOptions,
|
||||
selectedComboOptions,
|
||||
fieldWidth,
|
||||
|
||||
renderFields,
|
||||
handleTouch,
|
||||
handleValuesChange,
|
||||
} = useField({
|
||||
indexPattern,
|
||||
fieldTypeFilter,
|
||||
isRequired,
|
||||
selectedField,
|
||||
fieldInputWidth,
|
||||
onChange,
|
||||
});
|
||||
return (
|
||||
<EuiComboBox
|
||||
placeholder={placeholder}
|
||||
|
@ -81,68 +52,15 @@ export const FieldComponent: React.FC<OperatorProps> = ({
|
|||
isLoading={isLoading}
|
||||
isDisabled={isDisabled}
|
||||
isClearable={isClearable}
|
||||
isInvalid={isRequired ? touched && selectedField == null : false}
|
||||
isInvalid={isInvalid}
|
||||
onFocus={handleTouch}
|
||||
singleSelection={AS_PLAIN_TEXT}
|
||||
data-test-subj="fieldAutocompleteComboBox"
|
||||
style={fieldWidth}
|
||||
fullWidth
|
||||
renderOption={renderFields}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
FieldComponent.displayName = 'Field';
|
||||
|
||||
interface ComboBoxFields {
|
||||
availableFields: DataViewFieldBase[];
|
||||
selectedFields: DataViewFieldBase[];
|
||||
}
|
||||
|
||||
const getComboBoxFields = (
|
||||
indexPattern: DataViewBase | undefined,
|
||||
selectedField: DataViewFieldBase | undefined,
|
||||
fieldTypeFilter: string[]
|
||||
): ComboBoxFields => {
|
||||
const existingFields = getExistingFields(indexPattern);
|
||||
const selectedFields = getSelectedFields(selectedField);
|
||||
const availableFields = getAvailableFields(existingFields, selectedFields, fieldTypeFilter);
|
||||
|
||||
return { availableFields, selectedFields };
|
||||
};
|
||||
|
||||
const getComboBoxProps = (fields: ComboBoxFields): GetGenericComboBoxPropsReturn => {
|
||||
const { availableFields, selectedFields } = fields;
|
||||
|
||||
return getGenericComboBoxProps<DataViewFieldBase>({
|
||||
getLabel: (field) => field.name,
|
||||
options: availableFields,
|
||||
selectedOptions: selectedFields,
|
||||
});
|
||||
};
|
||||
|
||||
const getExistingFields = (indexPattern: DataViewBase | undefined): DataViewFieldBase[] => {
|
||||
return indexPattern != null ? indexPattern.fields : [];
|
||||
};
|
||||
|
||||
const getSelectedFields = (selectedField: DataViewFieldBase | undefined): DataViewFieldBase[] => {
|
||||
return selectedField ? [selectedField] : [];
|
||||
};
|
||||
|
||||
const getAvailableFields = (
|
||||
existingFields: DataViewFieldBase[],
|
||||
selectedFields: DataViewFieldBase[],
|
||||
fieldTypeFilter: string[]
|
||||
): DataViewFieldBase[] => {
|
||||
const fieldsByName = new Map<string, DataViewFieldBase>();
|
||||
|
||||
existingFields.forEach((f) => fieldsByName.set(f.name, f));
|
||||
selectedFields.forEach((f) => fieldsByName.set(f.name, f));
|
||||
|
||||
const uniqueFields = Array.from(fieldsByName.values());
|
||||
|
||||
if (fieldTypeFilter.length > 0) {
|
||||
return uniqueFields.filter(({ type }) => fieldTypeFilter.includes(type));
|
||||
}
|
||||
|
||||
return uniqueFields;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { DataViewBase, DataViewFieldBase } from '@kbn/es-query';
|
||||
import { GetGenericComboBoxPropsReturn } from '../get_generic_combo_box_props';
|
||||
|
||||
export interface FieldProps extends FieldBaseProps {
|
||||
isClearable: boolean;
|
||||
isDisabled: boolean;
|
||||
isLoading: boolean;
|
||||
placeholder: string;
|
||||
}
|
||||
export interface FieldBaseProps {
|
||||
indexPattern: DataViewBase | undefined;
|
||||
fieldTypeFilter?: string[];
|
||||
isRequired?: boolean;
|
||||
selectedField?: DataViewFieldBase | undefined;
|
||||
fieldInputWidth?: number;
|
||||
onChange: (a: DataViewFieldBase[]) => void;
|
||||
}
|
||||
|
||||
export interface ComboBoxFields {
|
||||
availableFields: DataViewField[];
|
||||
selectedFields: DataViewField[];
|
||||
}
|
||||
|
||||
export interface GetFieldComboBoxPropsReturn extends GetGenericComboBoxPropsReturn {
|
||||
disabledLabelTooltipTexts: { [label: string]: string };
|
||||
}
|
||||
|
||||
export interface DataViewField extends DataViewFieldBase {
|
||||
esTypes?: string[];
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { EuiComboBoxOptionOption, EuiToolTip } from '@elastic/eui';
|
||||
import { DataViewBase, DataViewFieldBase } from '@kbn/es-query';
|
||||
|
||||
import { getGenericComboBoxProps } from '../get_generic_combo_box_props';
|
||||
import {
|
||||
ComboBoxFields,
|
||||
DataViewField,
|
||||
FieldBaseProps,
|
||||
GetFieldComboBoxPropsReturn,
|
||||
} from './types';
|
||||
import { disabledTypesWithTooltipText } from './disabled_types_with_tooltip_text';
|
||||
|
||||
const getExistingFields = (indexPattern: DataViewBase | undefined): DataViewFieldBase[] => {
|
||||
return indexPattern != null ? indexPattern.fields : [];
|
||||
};
|
||||
|
||||
const getSelectedFields = (selectedField: DataViewField | undefined): DataViewFieldBase[] => {
|
||||
return selectedField ? [selectedField] : [];
|
||||
};
|
||||
|
||||
const getAvailableFields = (
|
||||
existingFields: DataViewField[],
|
||||
selectedFields: DataViewField[],
|
||||
fieldTypeFilter: string[] | undefined
|
||||
): DataViewField[] => {
|
||||
const fieldsByName = new Map<string, DataViewField>();
|
||||
|
||||
existingFields.forEach((f) => fieldsByName.set(f.name, f));
|
||||
selectedFields.forEach((f) => fieldsByName.set(f.name, f));
|
||||
|
||||
const uniqueFields = Array.from(fieldsByName.values());
|
||||
|
||||
if (fieldTypeFilter && fieldTypeFilter?.length > 0) {
|
||||
return uniqueFields.filter(({ type }) => fieldTypeFilter.includes(type));
|
||||
}
|
||||
|
||||
return uniqueFields;
|
||||
};
|
||||
|
||||
const getComboBoxFields = (
|
||||
indexPattern: DataViewBase | undefined,
|
||||
selectedField: DataViewField | undefined,
|
||||
fieldTypeFilter: string[] | undefined
|
||||
): ComboBoxFields => {
|
||||
const existingFields = getExistingFields(indexPattern);
|
||||
const selectedFields = getSelectedFields(selectedField);
|
||||
const availableFields = getAvailableFields(existingFields, selectedFields, fieldTypeFilter);
|
||||
|
||||
return { availableFields, selectedFields };
|
||||
};
|
||||
|
||||
const getDisabledLabelTooltipTexts = (fields: ComboBoxFields) => {
|
||||
const disabledLabelTooltipTexts = fields.availableFields.reduce(
|
||||
(acc: { [label: string]: string }, field: DataViewField) => {
|
||||
const esTypeKey = field.esTypes?.find((type) => disabledTypesWithTooltipText[type]);
|
||||
const tooltipText =
|
||||
(esTypeKey && disabledTypesWithTooltipText[esTypeKey]) ||
|
||||
disabledTypesWithTooltipText[field.type];
|
||||
if (tooltipText) acc[field.name] = tooltipText;
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
return disabledLabelTooltipTexts;
|
||||
};
|
||||
const getComboBoxProps = (fields: ComboBoxFields): GetFieldComboBoxPropsReturn => {
|
||||
const { availableFields, selectedFields } = fields;
|
||||
|
||||
const genericProps = getGenericComboBoxProps<DataViewFieldBase>({
|
||||
getLabel: (field) => field.name,
|
||||
options: availableFields,
|
||||
selectedOptions: selectedFields,
|
||||
});
|
||||
const disabledLabelTooltipTexts = getDisabledLabelTooltipTexts(fields);
|
||||
return {
|
||||
...genericProps,
|
||||
disabledLabelTooltipTexts,
|
||||
};
|
||||
};
|
||||
|
||||
export const useField = ({
|
||||
indexPattern,
|
||||
fieldTypeFilter,
|
||||
isRequired,
|
||||
selectedField,
|
||||
fieldInputWidth,
|
||||
onChange,
|
||||
}: FieldBaseProps) => {
|
||||
const [touched, setIsTouched] = useState(false);
|
||||
|
||||
const { availableFields, selectedFields } = useMemo(
|
||||
() => getComboBoxFields(indexPattern, selectedField, fieldTypeFilter),
|
||||
[indexPattern, fieldTypeFilter, selectedField]
|
||||
);
|
||||
|
||||
const { comboOptions, labels, selectedComboOptions, disabledLabelTooltipTexts } = useMemo(
|
||||
() => getComboBoxProps({ availableFields, selectedFields }),
|
||||
[availableFields, selectedFields]
|
||||
);
|
||||
|
||||
const handleValuesChange = useCallback(
|
||||
(newOptions: EuiComboBoxOptionOption[]): void => {
|
||||
const newValues: DataViewFieldBase[] = newOptions.map(
|
||||
({ label }) => availableFields[labels.indexOf(label)]
|
||||
);
|
||||
onChange(newValues);
|
||||
},
|
||||
[availableFields, labels, onChange]
|
||||
);
|
||||
|
||||
const handleTouch = useCallback((): void => {
|
||||
setIsTouched(true);
|
||||
}, [setIsTouched]);
|
||||
|
||||
const fieldWidth = useMemo(() => {
|
||||
return fieldInputWidth ? { width: `${fieldInputWidth}px` } : {};
|
||||
}, [fieldInputWidth]);
|
||||
|
||||
const isInvalid = useMemo(
|
||||
() => (isRequired ? touched && selectedField == null : false),
|
||||
[isRequired, selectedField, touched]
|
||||
);
|
||||
|
||||
const renderFields = (
|
||||
option: EuiComboBoxOptionOption<string | number | string[] | undefined>,
|
||||
searchValue: string,
|
||||
contentClassName: string
|
||||
) => {
|
||||
const { label } = option;
|
||||
|
||||
const labelTooltipText = disabledLabelTooltipTexts[label];
|
||||
if (labelTooltipText) {
|
||||
option.disabled = true;
|
||||
return (
|
||||
<EuiToolTip
|
||||
data-test-subj="disabledLabelWithTooltip"
|
||||
content={labelTooltipText}
|
||||
position="bottom"
|
||||
>
|
||||
<>{label}</>
|
||||
</EuiToolTip>
|
||||
);
|
||||
}
|
||||
return label;
|
||||
};
|
||||
return {
|
||||
isInvalid,
|
||||
comboOptions,
|
||||
selectedComboOptions,
|
||||
fieldWidth,
|
||||
|
||||
renderFields,
|
||||
handleTouch,
|
||||
handleValuesChange,
|
||||
};
|
||||
};
|
|
@ -28,6 +28,9 @@ export const DATE_ERR = i18n.translate('autocomplete.invalidDateError', {
|
|||
defaultMessage: 'Not a valid date',
|
||||
});
|
||||
|
||||
export const BINARY_TYPE_NOT_SUPPORTED = i18n.translate('autocomplete.invalidBinaryType', {
|
||||
defaultMessage: 'Binary fields are currently unsupported',
|
||||
});
|
||||
export const FIELD_SPACE_WARNING = i18n.translate('autocomplete.fieldSpaceWarning', {
|
||||
defaultMessage: "Warning: Spaces at the start or end of this value aren't being displayed.",
|
||||
});
|
||||
|
@ -40,4 +43,5 @@ export default {
|
|||
NUMBER_ERR,
|
||||
DATE_ERR,
|
||||
FIELD_SPACE_WARNING,
|
||||
BINARY_TYPE_NOT_SUPPORTED,
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue