Skip to content

Commit

Permalink
Replace Inspector's EuiPopover with EuiComboBox (#113566)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
Co-authored-by: Nathan L Smith <[email protected]>
  • Loading branch information
3 people authored Oct 18, 2021
1 parent 3ebfb02 commit dba055c
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 119 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,142 +13,82 @@ 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
);

togglePopover = () => {
this.setState((prevState: RequestSelectorState) => ({
isPopoverOpen: !prevState.isPopoverOpen,
}));
if (selectedOption) {
this.props.onRequestChanged(selectedOption);
}
};

closePopover = () => {
this.setState({
isPopoverOpen: false,
renderRequestCombobox() {
const options = this.props.requests.map((item) => {
const hasFailed = item.status === RequestStatus.ERROR;
const testLabel = item.name.replace(/\s+/, '_');

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 }}
/>
);
}

render() {
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
Expand Down
4 changes: 2 additions & 2 deletions test/functional/apps/discover/_inspector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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') {
Expand Down
6 changes: 4 additions & 2 deletions test/functional/apps/visualize/_vega_chart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down
43 changes: 33 additions & 10 deletions test/functional/services/inspector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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 getRequestNames(): Promise<string> {
public async getSelectedOption(): 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 selectedOption = await this.comboBox.getComboBoxSelectedOptions(
'inspectorRequestChooser'
);

if (selectedOption.length !== 1) {
return 'Combobox has multiple options';
}

const singleRequest = await this.testSubjects.find('inspectorRequestName');
return await singleRequest.getVisibleText();
return selectedOption[0];
}

/**
* Returns request name as the comma-separated string from combobox
*/
public async getRequestNames(): Promise<string> {
await this.openInspectorRequestsView();

const comboBoxOptions = await this.comboBox.getOptionsList('inspectorRequestChooser');
return comboBoxOptions.trim().split('\n').join(',');
}

public getOpenRequestStatisticButton() {
Expand All @@ -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;
}
}
1 change: 0 additions & 1 deletion x-pack/plugins/translations/translations/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": "リクエスト成功",
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/translations/translations/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": "请求成功",
Expand Down
7 changes: 5 additions & 2 deletions x-pack/test/functional/apps/maps/embeddable/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down

0 comments on commit dba055c

Please sign in to comment.