Synthetics design cleanup (#144954)

## Summary

Made a few small changes to cleanup the UI.
- Fixed some gutter spacing for EuiFlexGroup and added
`responsive={false}` where appropriate
- Updated Monitor Detail Flyout due to an EUI bug. Now uses a fixed
600px width, and transitions between a `push` and `overlay` flyout
depending on breakpoint.
- Updated visual style of location selector in monitor detail flyout to
match other instances of this. Text is now wrapped in `EuiLink`
- Added "Create Monitor" button to monitors overview page.
- Changed monitors overview page title from "Overview" to "Monitors"
- Fixed size of Sort by context menu items
- Cleaned up some code here and there 😅

## Before:
Overview page. Wrong page title. Missing "Create Monitor" button

![image](https://user-images.githubusercontent.com/847805/200962551-f4e760fd-46cd-4cd0-ae31-cb2cc278d579.png)

EUI related bug: flyout is too big

![image](https://user-images.githubusercontent.com/847805/200962641-bcb22c75-ddb9-4b24-b530-2e24cb67dd60.png)

Sort by context menu items wrong size

![image](https://user-images.githubusercontent.com/847805/200962819-b88cb5f2-affd-493b-9c53-895a8347f42f.png)


## After:
Overview page. Updated page title / added button

![image](https://user-images.githubusercontent.com/847805/200962153-4ca94b13-0146-45b0-8143-187e225ac3b3.png)

Flyout header. Rearranged order and changed style of location selector

![image](https://user-images.githubusercontent.com/847805/200962281-27de5410-6983-4b25-b53d-538cc610004c.png)

Push flyout at larger breakpoints

![image](https://user-images.githubusercontent.com/847805/200962011-4ac822b5-b9bf-4f7d-9338-0c05dc0b61a3.png)

Overlay flyout at smaller breakpoints

![image](https://user-images.githubusercontent.com/847805/200962062-000c35c4-202e-4b6d-8c1f-3c8f6ea2f03a.png)

Sort by context menu items fixed

![image](https://user-images.githubusercontent.com/847805/200963090-c73ad0e5-403b-4175-a439-6a1acbe3b0ff.png)


****


### Checklist

Delete any items that are not applicable to this PR.

- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
were updated or added to match the most common scenarios
- [x] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [x] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: shahzad31 <shahzad.muhammad@elastic.co>
This commit is contained in:
Henry Harding 2022-11-10 11:03:03 -05:00 committed by GitHub
parent c0d87b4fac
commit d5ed159eb5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 136 additions and 142 deletions

View file

@ -5,9 +5,8 @@
* 2.0.
*/
import React, { useEffect, useMemo } from 'react';
import styled from 'styled-components';
import { EuiPageHeaderProps, EuiPageTemplateProps, useIsWithinMaxBreakpoint } from '@elastic/eui';
import React, { useEffect } from 'react';
import { EuiPageHeaderProps, EuiPageTemplateProps } from '@elastic/eui';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import { useInspectorContext } from '@kbn/observability-plugin/public';
import { useSyntheticsDataView } from '../../../contexts';
@ -20,12 +19,6 @@ interface Props {
pageHeader?: EuiPageHeaderProps;
}
const mobileCenteredHeader = `
.euiPageHeaderContent > .euiFlexGroup > .euiFlexItem {
align-items: center;
}
`;
export const SyntheticsPageTemplateComponent: React.FC<Props & EuiPageTemplateProps> = ({
path,
pageHeader,
@ -35,18 +28,8 @@ export const SyntheticsPageTemplateComponent: React.FC<Props & EuiPageTemplatePr
const {
services: { observability },
} = useKibana<ClientPluginsStart>();
const isMobile = useIsWithinMaxBreakpoint('s');
const PageTemplateComponent = observability.navigation.PageTemplate;
const StyledPageTemplateComponent = useMemo(() => {
return styled(PageTemplateComponent)<{ isMobile: boolean }>`
.euiPageHeaderContent > .euiFlexGroup {
flex-wrap: wrap;
}
${(props) => (props.isMobile ? mobileCenteredHeader : '')}
`;
}, [PageTemplateComponent]);
const { loading, error, hasData } = useSyntheticsDataView();
const { inspectorAdapters } = useInspectorContext();
@ -63,8 +46,7 @@ export const SyntheticsPageTemplateComponent: React.FC<Props & EuiPageTemplatePr
return (
<>
<StyledPageTemplateComponent
isMobile={isMobile}
<PageTemplateComponent
pageHeader={pageHeader}
data-test-subj={'synthetics-page-template'}
isPageDataLoaded={loading === false}
@ -72,7 +54,7 @@ export const SyntheticsPageTemplateComponent: React.FC<Props & EuiPageTemplatePr
>
{showLoading && <EmptyStateLoading />}
<div style={{ visibility: showLoading ? 'hidden' : 'initial' }}>{children}</div>
</StyledPageTemplateComponent>
</PageTemplateComponent>
</>
);
};

View file

@ -14,7 +14,7 @@ export const MonitorDetailsPageTitle = () => {
const { monitor } = useSelectedMonitor();
return (
<EuiFlexGroup gutterSize="m">
<EuiFlexGroup gutterSize="s" alignItems="center" responsive={false}>
<EuiFlexItem grow={false}> {monitor?.name}</EuiFlexItem>
<EuiFlexItem>
<MonitorSelector />

View file

@ -65,7 +65,12 @@ export const MonitorSelector = () => {
};
const button = (
<EuiButtonIcon iconType="arrowDown" onClick={onButtonClick} aria-label={SELECT_MONITOR} />
<EuiButtonIcon
size="s"
iconType="arrowDown"
onClick={onButtonClick}
aria-label={SELECT_MONITOR}
/>
);
return (

View file

@ -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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React, { useContext } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { EuiButton } from '@elastic/eui';
import { useEnablement } from '../../hooks';
import { MONITOR_ADD_ROUTE } from '../../../../../common/constants';
import { SyntheticsSettingsContext } from '../../contexts/synthetics_settings_context';
export const CreateMonitorButton: React.FC = () => {
const { basePath } = useContext(SyntheticsSettingsContext);
const {
enablement: { isEnabled },
} = useEnablement();
return (
<EuiButton
color="primary"
fill
iconSide="left"
iconType="plusInCircleFilled"
href={`${basePath}/app/synthetics${MONITOR_ADD_ROUTE}`}
isDisabled={!isEnabled}
data-test-subj="syntheticsAddMonitorBtn"
>
<FormattedMessage
id="xpack.synthetics.monitors.pageHeader.createButton.label"
defaultMessage="Create Monitor"
/>
</EuiButton>
);
};

View file

@ -5,53 +5,21 @@
* 2.0.
*/
import React, { useContext } from 'react';
import React from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { EuiBetaBadge, EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { useEnablement } from '../../../../hooks';
import { MONITOR_ADD_ROUTE } from '../../../../../../../common/constants';
import { SyntheticsSettingsContext } from '../../../../contexts/synthetics_settings_context';
import { EuiBetaBadge, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { BETA_TOOLTIP_MESSAGE } from '../labels';
export const MonitorsPageHeader = () => {
const { basePath } = useContext(SyntheticsSettingsContext);
const {
enablement: { isEnabled },
} = useEnablement();
return (
<EuiFlexGroup alignItems="center" gutterSize="xs">
<EuiFlexItem grow={false}>
<FormattedMessage
id="xpack.synthetics.monitors.pageHeader.title"
defaultMessage="Monitors"
/>
</EuiFlexItem>
<EuiFlexItem grow={true}>
<div>
<EuiBetaBadge label="Beta" tooltipContent={BETA_TOOLTIP_MESSAGE} />
</div>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
color="primary"
fill
iconSide="left"
iconType="plusInCircleFilled"
href={`${basePath}/app/synthetics${MONITOR_ADD_ROUTE}`}
isDisabled={!isEnabled}
data-test-subj="syntheticsAddMonitorBtn"
>
<FormattedMessage
id="xpack.synthetics.monitors.pageHeader.createButton.label"
defaultMessage="Create Monitor"
/>
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
);
};
export const MonitorsPageHeader = () => (
<EuiFlexGroup alignItems="center" gutterSize="xs" responsive={false}>
<EuiFlexItem grow={false}>
<FormattedMessage id="xpack.synthetics.monitors.pageHeader.title" defaultMessage="Monitors" />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<div>
<EuiBetaBadge label="Beta" tooltipContent={BETA_TOOLTIP_MESSAGE} />
</div>
</EuiFlexItem>
</EuiFlexGroup>
);

View file

@ -46,14 +46,10 @@ export const MetricItem = ({
const theme = useTheme();
return (
<div
data-test-subj={`${monitor.name}-metric-item`}
style={{
height: '160px',
}}
>
<div data-test-subj={`${monitor.name}-metric-item`} style={{ height: '160px' }}>
{loaded ? (
<EuiPanel
paddingSize="none"
onMouseOver={() => {
if (!isMouseOver) {
setIsMouseOver(true);
@ -65,7 +61,6 @@ export const MetricItem = ({
}
}}
style={{
padding: '0px',
height: '100%',
overflow: 'hidden',
}}
@ -88,7 +83,7 @@ export const MetricItem = ({
extra: (
<span>
{i18n.translate('xpack.synthetics.overview.duration.label', {
defaultMessage: 'Duration',
defaultMessage: 'Duration Avg.',
})}
</span>
),

View file

@ -10,7 +10,6 @@ import {
EuiBadgeGroup,
EuiButton,
EuiButtonEmpty,
EuiButtonIcon,
EuiContextMenu,
EuiDescriptionList,
EuiDescriptionListDescription,
@ -24,6 +23,7 @@ import {
EuiFlyoutFooter,
EuiFlyoutHeader,
EuiHealth,
EuiIcon,
EuiLink,
EuiLoadingSpinner,
EuiPageSection,
@ -31,6 +31,7 @@ import {
EuiPopover,
EuiSpacer,
EuiTitle,
useIsWithinMaxBreakpoint,
} from '@elastic/eui';
import { SavedObject } from '@kbn/core/public';
import { FetcherResult } from '@kbn/observability-plugin/public/hooks/use_fetcher';
@ -175,30 +176,26 @@ function LocationSelect({
const [isOpen, setIsOpen] = useState(false);
const isDown = !!locations.find((l) => l.observer?.geo?.name === currentLocation)?.summary?.down;
return (
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiDescriptionList align="left" compressed>
<EuiDescriptionListTitle>{ENABLED_ITEM_TEXT}</EuiDescriptionListTitle>
<EuiDescriptionListDescription>
<MonitorEnabled id={id} monitor={monitor} reloadPage={onEnabledChange} />
</EuiDescriptionListDescription>
</EuiDescriptionList>
</EuiFlexItem>
<EuiFlexGroup wrap={true} responsive={false}>
<EuiFlexItem grow={false}>
<EuiDescriptionList compressed>
<EuiDescriptionListTitle>{LOCATION_TITLE_TEXT}</EuiDescriptionListTitle>
<EuiDescriptionListDescription>
{currentLocation}
<EuiPopover
button={
<EuiButtonIcon
aria-label={LOCATION_SELECT_POPOVER_ICON_BUTTON_LABEL}
color="primary"
display="empty"
iconType="arrowDown"
onClick={() => setIsOpen(!isOpen)}
size="xs"
/>
<>
<EuiLink
aria-label={LOCATION_SELECT_POPOVER_LINK_LABEL}
onClick={() => setIsOpen(!isOpen)}
>
<EuiFlexGroup gutterSize="xs" alignItems="center" responsive={false}>
<EuiFlexItem grow={false}>{currentLocation}</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiIcon type="arrowDown" size="s" color="inherit" />
</EuiFlexItem>
</EuiFlexGroup>
</EuiLink>
</>
}
isOpen={isOpen}
closePopover={() => setIsOpen(false)}
@ -239,6 +236,24 @@ function LocationSelect({
</EuiDescriptionListDescription>
</EuiDescriptionList>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiDescriptionList align="left" compressed>
<EuiDescriptionListTitle>{ENABLED_ITEM_TEXT}</EuiDescriptionListTitle>
<EuiDescriptionListDescription>
<MonitorEnabled id={id} monitor={monitor} reloadPage={onEnabledChange} />
</EuiDescriptionListDescription>
</EuiDescriptionList>
</EuiFlexItem>
</EuiFlexGroup>
);
}
function LoadingState() {
return (
<EuiFlexGroup alignItems="center" justifyContent="center" style={{ height: '100%' }}>
<EuiFlexItem grow={false}>
<EuiLoadingSpinner size="xl" />
</EuiFlexItem>
</EuiFlexGroup>
);
}
@ -278,15 +293,22 @@ export function MonitorDetailFlyout(props: Props) {
const locationStatuses = useStatusByLocation(id);
const locations = locationStatuses.locations?.filter((l: any) => !!l?.observer?.geo?.name) ?? [];
const isOverlay = useIsWithinMaxBreakpoint('xl');
return (
<EuiFlyout size="s" type="push" onClose={props.onClose} paddingSize="none">
<EuiFlyout
size="600px"
type={isOverlay ? 'overlay' : 'push'}
onClose={props.onClose}
paddingSize="none"
>
{status === FETCH_STATUS.FAILURE && <EuiErrorBoundary>{error?.message}</EuiErrorBoundary>}
{status === FETCH_STATUS.LOADING && <EuiLoadingSpinner size="xl" />}
{status === FETCH_STATUS.LOADING && <LoadingState />}
{status === FETCH_STATUS.SUCCESS && monitorSavedObject && (
<>
<EuiFlyoutHeader hasBorder>
<EuiPanel hasBorder={false} hasShadow={false} paddingSize="l">
<EuiFlexGroup gutterSize="s">
<EuiFlexGroup responsive={false} gutterSize="s">
<EuiFlexItem grow={false}>
<EuiTitle size="s">
<h2>{monitorSavedObject?.attributes[ConfigKey.NAME]}</h2>
@ -403,6 +425,7 @@ export function MonitorDetailFlyout(props: Props) {
href={detailLink}
iconType="sortRight"
iconSide="right"
fill
>
{GO_TO_MONITOR_LINK_TEXT}
</EuiButton>
@ -551,11 +574,11 @@ const GO_TO_LOCATIONS_LABEL = i18n.translate(
}
);
const LOCATION_SELECT_POPOVER_ICON_BUTTON_LABEL = i18n.translate(
const LOCATION_SELECT_POPOVER_LINK_LABEL = i18n.translate(
'xpack.synthetics.monitorList.flyout.locationSelect.iconButton.label',
{
defaultMessage:
"This icon button opens a context menu that will allow you to change the monitor's selected location. If you change the location, the flyout will display metrics for the monitor's performance in that location.",
"This button opens a context menu that will allow you to change the monitor's selected location. If you change the location, the flyout will display metrics for the monitor's performance in that location.",
}
);

View file

@ -100,7 +100,7 @@ export const OverviewGrid = memo(() => {
return (
<>
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
<EuiFlexGroup justifyContent="spaceBetween" alignItems="baseline" responsive={false}>
<EuiFlexItem grow={false}>
<OverviewPaginationInfo
page={page}
@ -112,9 +112,13 @@ export const OverviewGrid = memo(() => {
<SortFields onSortChange={() => setPage(1)} />
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer />
<EuiSpacer size="m" />
{loaded && currentMonitors.length ? (
<EuiFlexGrid columns={4} data-test-subj="syntheticsOverviewGridItemContainer">
<EuiFlexGrid
columns={4}
gutterSize="m"
data-test-subj="syntheticsOverviewGridItemContainer"
>
{currentMonitors.map((monitor) => (
<EuiFlexItem
key={`${monitor.id}-${monitor.location?.id}`}

View file

@ -9,12 +9,7 @@ import { EuiPanel, EuiLoadingContent } from '@elastic/eui';
export const OverviewGridItemLoader = () => {
return (
<EuiPanel
style={{
height: '160px',
}}
hasBorder={true}
>
<EuiPanel style={{ height: '160px' }} hasBorder={true}>
<EuiLoadingContent lines={2} />
</EuiPanel>
);

View file

@ -15,7 +15,7 @@ export const OverviewLoader = () => {
const loaders = Array(ROWS * COLUMNS).fill(null);
return (
<>
<EuiFlexGrid columns={COLUMNS}>
<EuiFlexGrid gutterSize="m" columns={COLUMNS}>
{loaders.map((_, i) => (
<EuiFlexItem key={i}>
<OverviewGridItemLoader />

View file

@ -146,7 +146,7 @@ const getOrderContent = (sortField: string) => {
};
const SORT_TITLE = i18n.translate('xpack.synthetics.overview.sortPopover.sort.title', {
defaultMessage: 'Sort',
defaultMessage: 'Sort by',
});
const SORT_ALPHABETICAL_ASC = i18n.translate(

View file

@ -84,7 +84,7 @@ export const SortMenu = ({ sortOptions, orderOptions, sortField }: Props) => {
panelPaddingSize="none"
anchorPosition="downLeft"
>
<EuiContextMenuPanel size="s" items={items} />
<EuiContextMenuPanel size="s" items={items} style={{ minWidth: 160 }} />
</EuiPopover>
);
};
@ -102,15 +102,16 @@ const ContextMenuItem = ({
return (
<EuiContextMenuItem
size="s"
key={option.value}
icon={getIconType(option.checked)}
onClick={() => {
onClosePopover();
option.onClick();
}}
style={{
marginRight: 24,
}}
// style={{
// marginRight: 24,
// }}
>
{option.label}
</EuiContextMenuItem>

View file

@ -7,14 +7,7 @@
import { EuiThemeComputed } from '@elastic/eui/src/services/theme/types';
import React, { FC, useEffect } from 'react';
import {
EuiFlexGroup,
EuiFlexItem,
EuiIcon,
EuiLink,
EuiPageHeaderProps,
useEuiTheme,
} from '@elastic/eui';
import { EuiIcon, EuiLink, EuiPageHeaderProps, useEuiTheme } from '@elastic/eui';
import { Route, Switch, useHistory, useRouteMatch } from 'react-router-dom';
import { OutPortal } from 'react-reverse-portal';
import { FormattedMessage } from '@kbn/i18n-react';
@ -33,6 +26,7 @@ import { MonitorDetailsPageTitle } from './components/monitor_details/monitor_de
import { MonitorDetailsPage } from './components/monitor_details/monitor_details_page';
import { GettingStartedPage } from './components/getting_started/getting_started_page';
import { MonitorsPageHeader } from './components/monitors_page/management/page_header/monitors_page_header';
import { CreateMonitorButton } from './components/monitors_page/create_monitor_button';
import { OverviewPage } from './components/monitors_page/overview/overview_page';
import { SyntheticsPageTemplateComponent } from './components/common/pages/synthetics_page_template';
import { NotFoundPage } from './components/common/pages/not_found';
@ -155,19 +149,8 @@ const getRoutes = (
component: OverviewPage,
dataTestSubj: 'syntheticsOverviewPage',
pageHeader: {
pageTitle: (
<EuiFlexGroup alignItems="center" gutterSize="xs">
<EuiFlexItem grow={false}>
<FormattedMessage
id="xpack.synthetics.overview.pageHeader.title"
defaultMessage="Overview"
/>
</EuiFlexItem>
</EuiFlexGroup>
),
rightSideItems: [
/* <AddMonitorBtn />*/
],
pageTitle: <MonitorsPageHeader />,
rightSideItems: [<CreateMonitorButton />],
tabs: [
{
label: (
@ -206,6 +189,7 @@ const getRoutes = (
dataTestSubj: 'syntheticsMonitorManagementPage',
pageHeader: {
pageTitle: <MonitorsPageHeader />,
rightSideItems: [<CreateMonitorButton />],
tabs: [
{
label: (
@ -240,6 +224,7 @@ const getRoutes = (
</ServiceAllowedWrapper>
),
dataTestSubj: 'syntheticsMonitorAddPage',
restrictWidth: true,
pageHeader: {
pageTitle: (
<FormattedMessage
@ -277,6 +262,7 @@ const getRoutes = (
</ServiceAllowedWrapper>
),
dataTestSubj: 'syntheticsMonitorEditPage',
restrictWidth: true,
pageHeader: {
pageTitle: (
<FormattedMessage
@ -377,7 +363,7 @@ const getMonitorSummaryHeader = (
),
color: 'primary',
'aria-current': false,
href: `${syntheticsPath}${MONITORS_ROUTE}`,
href: `${syntheticsPath}${OVERVIEW_ROUTE}`,
},
],
rightSideItems: [

View file

@ -31209,7 +31209,6 @@
"xpack.synthetics.overview.duration.label": "Durée",
"xpack.synthetics.overview.heading": "Moniteurs",
"xpack.synthetics.overview.monitors.label": "Moniteurs",
"xpack.synthetics.overview.pageHeader.title": "Aperçu",
"xpack.synthetics.overviewPage.overviewCrumb": "Aperçu",
"xpack.synthetics.overviewPageLink.disabled.ariaLabel": "Bouton de pagination désactivé indiquant qu'aucune autre navigation ne peut être effectuée dans la liste des moniteurs.",
"xpack.synthetics.overviewPageLink.next.ariaLabel": "Page de résultats suivante",

View file

@ -31185,7 +31185,6 @@
"xpack.synthetics.overview.duration.label": "期間",
"xpack.synthetics.overview.heading": "監視",
"xpack.synthetics.overview.monitors.label": "監視",
"xpack.synthetics.overview.pageHeader.title": "概要",
"xpack.synthetics.overviewPage.overviewCrumb": "概要",
"xpack.synthetics.overviewPageLink.disabled.ariaLabel": "無効になったページ付けボタンです。モニターリストがこれ以上ナビゲーションできないことを示しています。",
"xpack.synthetics.overviewPageLink.next.ariaLabel": "次の結果ページ",

View file

@ -31220,7 +31220,6 @@
"xpack.synthetics.overview.duration.label": "持续时间",
"xpack.synthetics.overview.heading": "监测",
"xpack.synthetics.overview.monitors.label": "监测",
"xpack.synthetics.overview.pageHeader.title": "概览",
"xpack.synthetics.overviewPage.overviewCrumb": "概览",
"xpack.synthetics.overviewPageLink.disabled.ariaLabel": "禁用的分页按钮表示在监测列表中无法进行进一步导航。",
"xpack.synthetics.overviewPageLink.next.ariaLabel": "下页结果",