[APM] Improve service groups error messages and validatons (#162556)

## Summary

closes https://github.com/elastic/kibana/issues/150288
closes https://github.com/elastic/kibana/issues/160713

### Before 
<img width="1344" alt="image"
src="6a1b2f0a-9f62-48f1-a14d-fbd9bc0b41d6">


### After


f2f90dba-a6c2-46e4-87c6-7f4a7cb59839

#### Invalid query syntax 
![Screenshot 2023-07-26 at 16 37
43](f5a2b6ea-c527-4744-9575-2f7726e06fa6)

#### Not supported field
<img width="1652" alt="image"
src="e6401a74-24f7-4544-86a5-8ac0fce11f01">


#### Invalid HEX
<img width="1652" alt="image"
src="d92a999f-9a34-460d-9a06-aedc445cf684">




## Notes
1. The validations run when the user refreshes/submits a new search.
Previously it was running every time the query was changing.
2. change the width from 600 to 700 
2. The layout issues were attributed to the error message, so I changed
the error message as a callout, similar to the style used by Discover.
This change allows the user to easily spot the error and address it more
effectively.
This commit is contained in:
Katerina 2023-08-03 15:37:02 +02:00 committed by GitHub
parent 68fec4bc64
commit 73d53dc6d6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 62 additions and 37 deletions

View file

@ -18,6 +18,7 @@ import {
EuiFormRow,
useColorPickerState,
EuiText,
isValidHex,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { useEffect, useRef, useState } from 'react';
@ -44,6 +45,7 @@ export function GroupDetails({
const [color, setColor, colorPickerErrors] = useColorPickerState(
serviceGroup?.color || '#5094C4'
);
const [description, setDescription] = useState<string | undefined>(
serviceGroup?.description
);
@ -53,7 +55,7 @@ export function GroupDetails({
if (serviceGroup.color) {
setColor(serviceGroup.color, {
hex: serviceGroup.color,
isValid: true,
isValid: isValidHex(color),
});
}
setDescription(serviceGroup.description);
@ -61,7 +63,7 @@ export function GroupDetails({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [serviceGroup]); // setColor omitted: new reference each render
const isInvalidColor = !!colorPickerErrors?.length;
const isInvalidColor = !!colorPickerErrors?.length || !isValidHex(color);
const isInvalidName = !name;
const isInvalid = isInvalidName || isInvalidColor;
@ -121,13 +123,17 @@ export function GroupDetails({
'xpack.apm.serviceGroups.groupDetailsForm.invalidColorError',
{
defaultMessage:
'Please provide a valid color value',
'Please provide a valid HEX color value',
}
)
: undefined
}
>
<EuiColorPicker onChange={setColor} color={color} />
<EuiColorPicker
onChange={setColor}
color={color}
isInvalid={isInvalidColor}
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>

View file

@ -7,7 +7,9 @@
import {
EuiButton,
EuiButtonEmpty,
EuiCallOut,
EuiFlexGroup,
useEuiTheme,
EuiFlexItem,
EuiLoadingSpinner,
EuiModalBody,
@ -19,6 +21,7 @@ import {
EuiText,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { css } from '@emotion/react';
import React, { useEffect, useState, useMemo } from 'react';
import styled from 'styled-components';
import { isEmpty } from 'lodash';
@ -44,7 +47,6 @@ const MODAL_HEADER_HEIGHT = 180;
const MODAL_FOOTER_HEIGHT = 80;
const Container = styled.div`
width: 600px;
height: ${MAX_CONTAINER_HEIGHT}px;
`;
@ -71,6 +73,8 @@ export function SelectServices({
string | undefined
>();
const { euiTheme } = useEuiTheme();
useEffect(() => {
if (isEdit) {
setKuery(serviceGroup.kuery);
@ -78,14 +82,6 @@ export function SelectServices({
}
}, [isEdit, serviceGroup.kuery]);
useEffect(() => {
if (!stagedKuery) {
return;
}
const { message } = validateServiceGroupKuery(stagedKuery);
setKueryValidationMessage(message);
}, [stagedKuery]);
const { start, end } = useMemo(
() =>
getDateRange({
@ -110,6 +106,18 @@ export function SelectServices({
const isServiceListPreviewLoading = status === FETCH_STATUS.LOADING;
const handleOnSubmit = () => {
if (!stagedKuery) {
return;
}
const { message } = validateServiceGroupKuery(stagedKuery);
setKueryValidationMessage(message);
if (!message) {
setKuery(stagedKuery);
}
};
return (
<Container>
<EuiModalHeader>
@ -132,11 +140,6 @@ export function SelectServices({
}
)}
</EuiText>
{kueryValidationMessage && (
<EuiText color="danger" size="s">
{kueryValidationMessage}
</EuiText>
)}
<EuiSpacer size="s" />
<EuiFlexGroup gutterSize="s">
<EuiFlexItem>
@ -145,9 +148,7 @@ export function SelectServices({
'xpack.apm.serviceGroups.selectServicesForm.kql',
{ defaultMessage: 'E.g. labels.team: "web"' }
)}
onSubmit={(value) => {
setKuery(value);
}}
onSubmit={handleOnSubmit}
onChange={(value) => {
setStagedKuery(value);
}}
@ -169,9 +170,7 @@ export function SelectServices({
<EuiFlexItem grow={false}>
<EuiButton
data-test-subj="apmSelectServicesButton"
onClick={() => {
setKuery(stagedKuery);
}}
onClick={handleOnSubmit}
iconType={!kuery ? 'search' : 'refresh'}
isDisabled={isServiceListPreviewLoading || !stagedKuery}
>
@ -215,7 +214,36 @@ export function SelectServices({
>
<EuiFlexItem>
<EuiPanel hasShadow={false} hasBorder paddingSize="s">
{!kuery && (
{!data && isServiceListPreviewLoading && <EuiLoadingSpinner />}
{kueryValidationMessage?.length ? (
<CentralizedContainer>
<EuiCallOut
title={i18n.translate(
'xpack.apm.serviceGroups.searchResults.error',
{
defaultMessage: 'Error retrieving search results',
}
)}
color="danger"
iconType="error"
>
<p
css={css`
white-space: break-spaces;
font-family: ${euiTheme.font.familyCode};
`}
data-test-subj="discoverErrorCalloutMessage"
>
{kueryValidationMessage}
</p>
</EuiCallOut>
</CentralizedContainer>
) : kuery && data ? (
<ServiceListPreview
items={data.items}
isLoading={isServiceListPreviewLoading}
/>
) : (
<CentralizedContainer>
<EuiText size="s" color="subdued">
{i18n.translate(
@ -225,17 +253,6 @@ export function SelectServices({
</EuiText>
</CentralizedContainer>
)}
{!data && isServiceListPreviewLoading && (
<CentralizedContainer>
<EuiLoadingSpinner />
</CentralizedContainer>
)}
{kuery && data && (
<ServiceListPreview
items={data.items}
isLoading={isServiceListPreviewLoading}
/>
)}
</EuiPanel>
</EuiFlexItem>
</EuiFlexGroup>
@ -279,7 +296,9 @@ export function SelectServices({
onClick={() => {
onSaveClick({ ...serviceGroup, kuery });
}}
isDisabled={isLoading || !kuery}
isDisabled={
isLoading || !kuery || !isEmpty(kueryValidationMessage)
}
>
{i18n.translate(
'xpack.apm.serviceGroups.selectServicesForm.saveGroup',