Skip to content

Commit

Permalink
[Uptime] Convert ui monitor management combobox to checkboxes (#124917)
Browse files Browse the repository at this point in the history
Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
dominiqueclarke and kibanamachine authored Feb 11, 2022
1 parent 63dffb1 commit 0766f18
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,8 @@ export function monitorManagementPageProvider({
},

async selectLocations({ locations }: { locations: string[] }) {
await this.clickByTestSubj('syntheticsServiceLocationsComboBox');
for (let i = 0; i < locations.length; i++) {
await page.click(`text=${locations[i]}`);
await page.check(`text=${locations[i]}`);
}
},

Expand Down
4 changes: 4 additions & 0 deletions x-pack/plugins/uptime/e2e/page_objects/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ export function utilsPageProvider({ page }: { page: Page }) {
await page.selectOption(`[data-test-subj=${dataTestSubj}]`, value);
},

async checkByTestSubj(dataTestSubj: string, value: string) {
await page.check(`[data-test-subj=${dataTestSubj}]`);
},

async clickByTestSubj(dataTestSubj: string) {
await page.click(`[data-test-subj=${dataTestSubj}]`);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
import React from 'react';
import { screen } from '@testing-library/react';
import { render } from '../../../lib/helper/rtl_helpers';
import { ServiceLocations, LOCATIONS_LABEL } from './locations';
import userEvent from '@testing-library/user-event';
import { ServiceLocations } from './locations';

describe('<ActionBar />', () => {
const setLocations = jest.fn();
Expand Down Expand Up @@ -52,47 +51,7 @@ describe('<ActionBar />', () => {
{ state }
);

expect(screen.getByText(LOCATIONS_LABEL)).toBeInTheDocument();
expect(screen.queryByText('US Central')).not.toBeInTheDocument();
});

it('shows location options when clicked', async () => {
render(
<ServiceLocations selectedLocations={[]} setLocations={setLocations} isInvalid={false} />,
{ state }
);

userEvent.click(screen.getByRole('button'));

expect(screen.getByText('US Central')).toBeInTheDocument();
});

it('prevents bad inputs', async () => {
render(
<ServiceLocations selectedLocations={[]} setLocations={setLocations} isInvalid={false} />,
{ state }
);

userEvent.click(screen.getByRole('button'));
userEvent.type(screen.getByRole('textbox'), 'fake location');

expect(screen.getByText("doesn't match any options")).toBeInTheDocument();

userEvent.keyboard(`{enter}`);

expect(screen.getByText('"fake location" is not a valid option')).toBeInTheDocument();
});

it('calls setLocations', async () => {
render(
<ServiceLocations selectedLocations={[]} setLocations={setLocations} isInvalid={false} />,
{ state }
);

userEvent.click(screen.getByRole('button'));
userEvent.click(screen.getByText('US Central'));

expect(setLocations).toBeCalledWith([location]);
expect(screen.queryByText('US Central')).toBeInTheDocument();
});

it('shows invalid error', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
* 2.0.
*/

import React, { useState } from 'react';
import React, { useState, useEffect } from 'react';
import { useSelector } from 'react-redux';
import { i18n } from '@kbn/i18n';
import { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow } from '@elastic/eui';
import { EuiCheckboxGroup, EuiFormRow } from '@elastic/eui';
import { monitorManagementListSelector } from '../../../state/selectors';
import { ServiceLocation } from '../../../../common/runtime_types';

Expand All @@ -20,66 +20,56 @@ interface Props {

export const ServiceLocations = ({ selectedLocations, setLocations, isInvalid }: Props) => {
const [error, setError] = useState<string | null>(null);
const [checkboxIdToSelectedMap, setCheckboxIdToSelectedMap] = useState<Record<string, boolean>>(
{}
);
const { locations } = useSelector(monitorManagementListSelector);

const onLocationChange = (
selectedLocationOptions: Array<EuiComboBoxOptionOption<ServiceLocation>>
) => {
setLocations(selectedLocationOptions as ServiceLocation[]);
setError(null);
};

const onSearchChange = (value: string, hasMatchingOptions?: boolean) => {
setError(value.length === 0 || hasMatchingOptions ? null : getInvalidOptionError(value));
};

const onBlur = (event: unknown) => {
const inputElement = (event as FocusEvent)?.target as HTMLInputElement;
if (inputElement) {
const { value } = inputElement;
setError(value.length === 0 ? null : getInvalidOptionError(value));
const onLocationChange = (optionId: string) => {
const isSelected = !checkboxIdToSelectedMap[optionId];
const location = locations.find((loc) => loc.id === optionId);
if (isSelected) {
setLocations((prevLocations) => (location ? [...prevLocations, location] : prevLocations));
} else {
setLocations((prevLocations) => [...prevLocations].filter((loc) => loc.id !== optionId));
}
setError(null);
};

const errorMessage = error ?? (isInvalid ? VALIDATION_ERROR : null);

useEffect(() => {
const newCheckboxIdToSelectedMap = selectedLocations.reduce<Record<string, boolean>>(
(acc, location) => {
acc[location.id] = true;
return acc;
},
{}
);
setCheckboxIdToSelectedMap(newCheckboxIdToSelectedMap);
}, [selectedLocations]);

return (
<EuiFormRow label={LOCATIONS_LABEL} error={errorMessage} isInvalid={errorMessage !== null}>
<EuiComboBox
placeholder={PLACEHOLDER_LABEL}
options={locations}
selectedOptions={selectedLocations}
onChange={onLocationChange}
onSearchChange={onSearchChange}
onBlur={onBlur}
data-test-subj="syntheticsServiceLocationsComboBox"
<EuiCheckboxGroup
options={locations.map((location) => ({
...location,
'data-test-subj': `syntheticsServiceLocation--${location.id}`,
}))}
idToSelectedMap={checkboxIdToSelectedMap}
onChange={(id) => onLocationChange(id)}
/>
</EuiFormRow>
);
};

const PLACEHOLDER_LABEL = i18n.translate(
'xpack.uptime.monitorManagement.serviceLocationsPlaceholderLabel',
{
defaultMessage: 'Select one or more locations to run your monitor.',
}
);

const VALIDATION_ERROR = i18n.translate(
'xpack.uptime.monitorManagement.serviceLocationsValidationError',
{
defaultMessage: 'At least one service location must be specified',
}
);

const getInvalidOptionError = (value: string) =>
i18n.translate('xpack.uptime.monitorManagement.serviceLocationsOptionError', {
defaultMessage: '"{value}" is not a valid option',
values: {
value,
},
});

export const LOCATIONS_LABEL = i18n.translate(
'xpack.uptime.monitorManagement.monitorLocationsLabel',
{
Expand Down

0 comments on commit 0766f18

Please sign in to comment.