Replace Inspector's EuiPopover with EuiComboBox (#113566)

* Replacing EuiPopover with EuiComboBox

* The combobox will help alleviate issues when the list of options is very long

* Refactoring the Combobox to listen for change events

* Added an onChange handler
* Renamed the method to render the combobox
* Commented out additional blocks of code before final refactor

* Finished refactoring the Request Selector to use EUI Combobox

* Removed three helper methods for the EUIPopover.
  * `togglePopover()`
  * `closePopover()`
  * `renderRequestDropdownItem()`
* Removed the local state object and interface (no longer needed)
* Renamed the const `options` to `selectedOptions` in `handleSelectd()`
  method to better reflect where the options array was coming from.

* Updating tests and translations

* Fixed the inspector functional test to use comboBox service
* Removed two unused translations

* Updating Combobox options to pass data-test-sub string

* Updated two tests for Combobox single option
* Updated the test expectations to the default string
* Both tests were looking for a named string instead of a default
  message

* Adding error handling to Inspector combobox
* Checking for the item status code
* Adding a " (failed)" message if the status code returns `2`
* Updating test to look for "Chart_data" instead of "Chartdata"

* Updating two tests to validate single combobox options
* Added helper method to check default text against combobox options
* Added helper method to get the selected combobox option
* Checking two inspector instances using helpers

* Adding a defensive check to helper method.

* Correct a type error in test return

* Adding back translated failLabel

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Nathan L Smith <smith@nlsmith.com>
This commit is contained in:
Trevor Pierce 2021-10-18 11:41:53 -05:00 committed by GitHub
parent 3ebfb029a2
commit dba055c654
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 85 additions and 119 deletions

View file

@ -13,118 +13,73 @@ import { i18n } from '@kbn/i18n';
import {
EuiBadge,
EuiButtonEmpty,
EuiContextMenuPanel,
EuiContextMenuItem,
EuiComboBox,
EuiComboBoxOptionOption,
EuiFlexGroup,
EuiFlexItem,
EuiLoadingSpinner,
EuiPopover,
EuiTextColor,
EuiToolTip,
} from '@elastic/eui';
import { RequestStatus } from '../../../../common/adapters';
import { Request } from '../../../../common/adapters/request/types';
interface RequestSelectorState {
isPopoverOpen: boolean;
}
interface RequestSelectorProps {
requests: Request[];
selectedRequest: Request;
onRequestChanged: Function;
onRequestChanged: (request: Request) => void;
}
export class RequestSelector extends Component<RequestSelectorProps, RequestSelectorState> {
export class RequestSelector extends Component<RequestSelectorProps> {
static propTypes = {
requests: PropTypes.array.isRequired,
selectedRequest: PropTypes.object.isRequired,
onRequestChanged: PropTypes.func,
};
state = {
isPopoverOpen: false,
handleSelected = (selectedOptions: Array<EuiComboBoxOptionOption<string>>) => {
const selectedOption = this.props.requests.find(
(request) => request.id === selectedOptions[0].value
);
if (selectedOption) {
this.props.onRequestChanged(selectedOption);
}
};
togglePopover = () => {
this.setState((prevState: RequestSelectorState) => ({
isPopoverOpen: !prevState.isPopoverOpen,
}));
};
renderRequestCombobox() {
const options = this.props.requests.map((item) => {
const hasFailed = item.status === RequestStatus.ERROR;
const testLabel = item.name.replace(/\s+/, '_');
closePopover = () => {
this.setState({
isPopoverOpen: false,
return {
'data-test-subj': `inspectorRequestChooser${testLabel}`,
label: hasFailed
? `${item.name} ${i18n.translate('inspector.requests.failedLabel', {
defaultMessage: ' (failed)',
})}`
: item.name,
value: item.id,
};
});
};
renderRequestDropdownItem = (request: Request, index: number) => {
const hasFailed = request.status === RequestStatus.ERROR;
const inProgress = request.status === RequestStatus.PENDING;
return (
<EuiContextMenuItem
key={index}
icon={request === this.props.selectedRequest ? 'check' : 'empty'}
onClick={() => {
this.props.onRequestChanged(request);
this.closePopover();
}}
toolTipContent={request.description}
toolTipPosition="left"
data-test-subj={`inspectorRequestChooser${request.name}`}
>
<EuiTextColor color={hasFailed ? 'danger' : 'default'}>
{request.name}
{hasFailed && (
<FormattedMessage id="inspector.requests.failedLabel" defaultMessage=" (failed)" />
)}
{inProgress && (
<EuiLoadingSpinner
size="s"
aria-label={i18n.translate('inspector.requests.requestInProgressAriaLabel', {
defaultMessage: 'Request in progress',
})}
className="insRequestSelector__menuSpinner"
/>
)}
</EuiTextColor>
</EuiContextMenuItem>
);
};
renderRequestDropdown() {
const button = (
<EuiButtonEmpty
iconType="arrowDown"
iconSide="right"
size="s"
onClick={this.togglePopover}
<EuiComboBox
data-test-subj="inspectorRequestChooser"
>
{this.props.selectedRequest.name}
</EuiButtonEmpty>
);
return (
<EuiPopover
fullWidth={true}
id="inspectorRequestChooser"
button={button}
isOpen={this.state.isPopoverOpen}
closePopover={this.closePopover}
panelPaddingSize="none"
anchorPosition="downLeft"
repositionOnScroll
>
<EuiContextMenuPanel
items={this.props.requests.map(this.renderRequestDropdownItem)}
data-test-subj="inspectorRequestChooserMenuPanel"
/>
</EuiPopover>
isClearable={false}
onChange={this.handleSelected}
options={options}
prepend="Request"
selectedOptions={[
{
label: this.props.selectedRequest.name,
value: this.props.selectedRequest.id,
},
]}
singleSelection={{ asPlainText: true }}
/>
);
}
@ -132,23 +87,8 @@ export class RequestSelector extends Component<RequestSelectorProps, RequestSele
const { selectedRequest, requests } = this.props;
return (
<EuiFlexGroup alignItems="center" gutterSize="xs">
<EuiFlexItem grow={false}>
<strong>
<FormattedMessage id="inspector.requests.requestLabel" defaultMessage="Request:" />
</strong>
</EuiFlexItem>
<EuiFlexItem grow={true}>
{requests.length <= 1 && (
<div
className="insRequestSelector__singleRequest"
data-test-subj="inspectorRequestName"
>
{selectedRequest.name}
</div>
)}
{requests.length > 1 && this.renderRequestDropdown()}
</EuiFlexItem>
<EuiFlexGroup alignItems="center">
<EuiFlexItem grow={true}>{requests.length && this.renderRequestCombobox()}</EuiFlexItem>
<EuiFlexItem grow={false}>
{selectedRequest.status !== RequestStatus.PENDING && (
<EuiToolTip

View file

@ -53,9 +53,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await inspector.open();
await testSubjects.click('inspectorRequestChooser');
let foundZero = false;
for (const subj of ['Documents', 'Total hits', 'Charts']) {
for (const subj of ['Documents', 'Chart_data']) {
await testSubjects.click(`inspectorRequestChooser${subj}`);
if (testSubjects.exists('inspectorRequestDetailStatistics', { timeout: 500 })) {
if (await testSubjects.exists('inspectorRequestDetailStatistics', { timeout: 500 })) {
await testSubjects.click(`inspectorRequestDetailStatistics`);
const requestStatsTotalHits = getHitCount(await inspector.getTableData());
if (requestStatsTotalHits === '0') {

View file

@ -131,9 +131,11 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
});
it('should set the default query name if not given in the schema', async () => {
const requests = await inspector.getRequestNames();
const singleExampleRequest = await inspector.hasSingleRequest();
const selectedExampleRequest = await inspector.getSelectedOption();
expect(requests).to.be('Unnamed request #0');
expect(singleExampleRequest).to.be(true);
expect(selectedExampleRequest).to.equal('Unnamed request #0');
});
it('should log the request statistic', async () => {

View file

@ -16,6 +16,7 @@ export class InspectorService extends FtrService {
private readonly flyout = this.ctx.getService('flyout');
private readonly testSubjects = this.ctx.getService('testSubjects');
private readonly find = this.ctx.getService('find');
private readonly comboBox = this.ctx.getService('comboBox');
private async getIsEnabled(): Promise<boolean> {
const ariaDisabled = await this.testSubjects.getAttribute('openInspectorButton', 'disabled');
@ -206,20 +207,29 @@ export class InspectorService extends FtrService {
}
/**
* Returns request name as the comma-separated string
* Returns the selected option value from combobox
*/
public async getSelectedOption(): Promise<string> {
await this.openInspectorRequestsView();
const selectedOption = await this.comboBox.getComboBoxSelectedOptions(
'inspectorRequestChooser'
);
if (selectedOption.length !== 1) {
return 'Combobox has multiple options';
}
return selectedOption[0];
}
/**
* Returns request name as the comma-separated string from combobox
*/
public async getRequestNames(): Promise<string> {
await this.openInspectorRequestsView();
const requestChooserExists = await this.testSubjects.exists('inspectorRequestChooser');
if (requestChooserExists) {
await this.testSubjects.click('inspectorRequestChooser');
const menu = await this.testSubjects.find('inspectorRequestChooserMenuPanel');
const requestNames = await menu.getVisibleText();
return requestNames.trim().split('\n').join(',');
}
const singleRequest = await this.testSubjects.find('inspectorRequestName');
return await singleRequest.getVisibleText();
const comboBoxOptions = await this.comboBox.getOptionsList('inspectorRequestChooser');
return comboBoxOptions.trim().split('\n').join(',');
}
public getOpenRequestStatisticButton() {
@ -233,4 +243,17 @@ export class InspectorService extends FtrService {
public getOpenRequestDetailResponseButton() {
return this.testSubjects.find('inspectorRequestDetailResponse');
}
/**
* Returns true if the value equals the combobox options list
* @param value default combobox single option text
*/
public async hasSingleRequest(
value: string = "You've selected all available options"
): Promise<boolean> {
await this.openInspectorRequestsView();
const comboBoxOptions = await this.comboBox.getOptionsList('inspectorRequestChooser');
return value === comboBoxOptions;
}
}

View file

@ -4162,7 +4162,6 @@
"inspector.requests.noRequestsLoggedTitle": "リクエストが記録されていません",
"inspector.requests.requestFailedTooltipTitle": "リクエストに失敗しました",
"inspector.requests.requestInProgressAriaLabel": "リクエストが進行中",
"inspector.requests.requestLabel": "リクエスト:",
"inspector.requests.requestsDescriptionTooltip": "データを収集したリクエストを表示します",
"inspector.requests.requestsTitle": "リクエスト",
"inspector.requests.requestSucceededTooltipTitle": "リクエスト成功",

View file

@ -4201,7 +4201,6 @@
"inspector.requests.noRequestsLoggedTitle": "未记录任何请求",
"inspector.requests.requestFailedTooltipTitle": "请求失败",
"inspector.requests.requestInProgressAriaLabel": "进行中的请求",
"inspector.requests.requestLabel": "请求:",
"inspector.requests.requestsDescriptionTooltip": "查看已收集数据的请求",
"inspector.requests.requestsTitle": "请求",
"inspector.requests.requestSucceededTooltipTitle": "请求成功",

View file

@ -77,9 +77,12 @@ export default function ({ getPageObjects, getService }) {
await inspector.close();
await dashboardPanelActions.openInspectorByTitle('geo grid vector grid example');
const gridExampleRequestNames = await inspector.getRequestNames();
const singleExampleRequest = await inspector.hasSingleRequest();
const selectedExampleRequest = await inspector.getSelectedOption();
await inspector.close();
expect(gridExampleRequestNames).to.equal('logstash-*');
expect(singleExampleRequest).to.be(true);
expect(selectedExampleRequest).to.equal('logstash-*');
});
it('should apply container state (time, query, filters) to embeddable when loaded', async () => {