Skip to content

Commit f6da71c

Browse files
authored
[Synthetics] Improve card view style and responsiveness !! (#231078)
## Summary Fixes #205112 ### XXLarge screen <img width="2560" height="997" alt="image" src="https://github.com/user-attachments/assets/356ac475-68be-4fd8-b8a5-e0b8a80a8635" /> ### Large <img width="1780" height="987" alt="image" src="https://github.com/user-attachments/assets/fafcd620-4b97-4f0d-a7b5-5e6c74d9f4af" /> ### Medium <img width="1483" height="1001" alt="image" src="https://github.com/user-attachments/assets/ed4ff084-bd5d-47e2-bd4c-647618dc5c2d" /> ### Small <img width="1088" height="1001" alt="image" src="https://github.com/user-attachments/assets/f7c74bd5-ac81-49bc-ba29-fdd5ef461c0e" />
1 parent 3be43ed commit f6da71c

File tree

4 files changed

+73
-46
lines changed

4 files changed

+73
-46
lines changed

x-pack/solutions/observability/plugins/observability_shared/public/components/tags_list/tags_list.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ export interface TagsListProps {
1818
color?: EuiBadgeProps['color'];
1919
ignoreEmpty?: boolean;
2020
disableExpand?: boolean;
21+
prependChildren?: React.ReactNode;
2122
}
22-
2323
const getFilterLabel = (tag: string) => {
2424
return i18n.translate('xpack.observabilityShared.getFilterLabel.filter', {
2525
defaultMessage: 'Click to filter list with tag {tag}',
@@ -36,6 +36,7 @@ const TagsList = ({
3636
onClick,
3737
color = 'hollow',
3838
disableExpand = false,
39+
prependChildren,
3940
}: TagsListProps) => {
4041
const [toDisplay, setToDisplay] = useState(numberOfTagsToDisplay);
4142

@@ -53,7 +54,8 @@ const TagsList = ({
5354
const tagsToDisplay = tags.slice(0, toDisplay);
5455

5556
return (
56-
<EuiFlexGroup wrap gutterSize="xs" css={{ maxWidth: 400 }} alignItems="baseline">
57+
<EuiFlexGroup wrap gutterSize="xs" css={{ maxWidth: 400 }} alignItems="center">
58+
{prependChildren}
5759
{tagsToDisplay.map((tag) => (
5860
// filtering only makes sense in monitor list, where we have summary
5961
<EuiFlexItem key={tag} grow={false}>

x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/metric_item/metric_item.tsx

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { useKibana } from '@kbn/kibana-react-plugin/public';
1414
import moment from 'moment';
1515
import React, { useState, useMemo } from 'react';
1616
import { useDispatch, useSelector } from 'react-redux';
17+
import { MetricItemExtra } from './metric_item_extra';
1718
import type { OverviewStatusMetaData } from '../../../../../../../../common/runtime_types';
1819
import type { ClientPluginsStart } from '../../../../../../../plugin';
1920
import { useLocationName, useStatusByLocationOverview } from '../../../../../hooks';
@@ -30,11 +31,10 @@ import {
3031
import { formatDuration } from '../../../../../utils/formatting';
3132
import { ActionsPopover } from '../actions_popover';
3233
import { MetricItemBody } from './metric_item_body';
33-
import { MetricItemExtra } from './metric_item_extra';
3434
import { MetricItemIcon } from './metric_item_icon';
3535
import type { FlyoutParamProps } from '../types';
3636

37-
const METRIC_ITEM_HEIGHT = 170;
37+
export const METRIC_ITEM_HEIGHT = 180;
3838

3939
export const getColor = (euiTheme: EuiThemeComputed, isEnabled: boolean, status?: string) => {
4040
if (!isEnabled) {
@@ -62,6 +62,16 @@ export const getColor = (euiTheme: EuiThemeComputed, isEnabled: boolean, status?
6262
}
6363
};
6464

65+
const truncateText = (text: string) => {
66+
// truncate from middle if longer than maxLength
67+
const maxLength = 100;
68+
if (text.length <= maxLength) {
69+
return text;
70+
}
71+
const halfLength = Math.floor(maxLength / 2);
72+
return `${text.slice(0, halfLength)}${text.slice(-halfLength)}`;
73+
};
74+
6575
export const MetricItem = ({
6676
monitor,
6777
onClick,
@@ -153,6 +163,9 @@ export const MetricItem = ({
153163
pointer-events: auto;
154164
opacity: 1;
155165
}
166+
.echMetricText__body {
167+
overflow: visible;
168+
}
156169
`}
157170
title={moment(timestamp).format('LLL')}
158171
>
@@ -184,7 +197,7 @@ export const MetricItem = ({
184197
data={[
185198
[
186199
{
187-
title: monitor.name,
200+
title: truncateText(monitor.name),
188201
subtitle: locationName,
189202
value: trendData !== 'loading' ? trendData?.median ?? 0 : 0,
190203
trendShape: MetricTrendShape.Area,
@@ -200,12 +213,12 @@ export const MetricItem = ({
200213
}}
201214
/>
202215
) : trendData === 'loading' ? (
203-
<div>
216+
<span>
204217
<FormattedMessage
205-
defaultMessage="Loading metrics"
218+
defaultMessage="..."
206219
id="xpack.synthetics.overview.metricItem.loadingMessage"
207220
/>
208-
</div>
221+
</span>
209222
) : undefined,
210223
valueFormatter: (d: number) => formatDuration(d),
211224
color: getColor(euiTheme, monitor.isEnabled, status),

x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/metric_item/metric_item_body.tsx

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import React from 'react';
99
import { useHistory } from 'react-router-dom';
1010
import { TagsList } from '@kbn/observability-shared-plugin/public';
11-
import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
11+
import { EuiFlexItem, EuiSpacer } from '@elastic/eui';
1212
import { MonitorTypeBadge } from '../../../../common/components/monitor_type_badge';
1313
import * as labels from '../../../management/monitor_list_table/labels';
1414
import type { OverviewStatusMetaData } from '../../../../../../../../common/runtime_types';
@@ -17,34 +17,40 @@ export const MetricItemBody = ({ monitor }: { monitor: OverviewStatusMetaData })
1717
const tags = monitor.tags;
1818
const history = useHistory();
1919

20+
const typeBadge = (
21+
<MonitorTypeBadge
22+
monitorType={monitor.type}
23+
ariaLabel={labels.getFilterForTypeMessage(monitor.type)}
24+
onClick={() => {
25+
history.push({
26+
search: `monitorTypes=${encodeURIComponent(JSON.stringify([monitor.type]))}`,
27+
});
28+
}}
29+
/>
30+
);
31+
if (tags.length === 0) {
32+
return (
33+
<>
34+
<EuiSpacer size="xs" />
35+
{typeBadge}
36+
</>
37+
);
38+
}
39+
2040
return (
2141
<>
2242
<EuiSpacer size="xs" />
23-
<EuiFlexGroup gutterSize="xs">
24-
<EuiFlexItem grow={false}>
25-
<MonitorTypeBadge
26-
monitorType={monitor.type}
27-
ariaLabel={labels.getFilterForTypeMessage(monitor.type)}
28-
onClick={() => {
29-
history.push({
30-
search: `monitorTypes=${encodeURIComponent(JSON.stringify([monitor.type]))}`,
31-
});
32-
}}
33-
/>
34-
</EuiFlexItem>
35-
{(tags ?? []).length > 0 && (
36-
<EuiFlexItem grow={false}>
37-
<TagsList
38-
color="default"
39-
tags={tags}
40-
disableExpand={true}
41-
onClick={(tag) => {
42-
history.push({ search: `tags=${encodeURIComponent(JSON.stringify([tag]))}` });
43-
}}
44-
/>
45-
</EuiFlexItem>
46-
)}
47-
</EuiFlexGroup>
43+
{(tags ?? []).length > 0 && (
44+
<TagsList
45+
prependChildren={<EuiFlexItem grow={false}>{typeBadge}</EuiFlexItem>}
46+
color="default"
47+
tags={tags}
48+
disableExpand={true}
49+
onClick={(tag) => {
50+
history.push({ search: `tags=${encodeURIComponent(JSON.stringify([tag]))}` });
51+
}}
52+
/>
53+
)}
4854
</>
4955
);
5056
};

x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_grid.tsx

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {
1919
EuiText,
2020
EuiAutoSizer,
2121
} from '@elastic/eui';
22-
import { MetricItem } from './metric_item/metric_item';
22+
import { METRIC_ITEM_HEIGHT, MetricItem } from './metric_item/metric_item';
2323
import { ShowAllSpaces } from '../../common/show_all_spaces';
2424
import type { OverviewStatusMetaData } from '../../../../../../../common/runtime_types';
2525
import type { TrendRequest } from '../../../../../../../common/types';
@@ -46,11 +46,11 @@ import { MaybeMonitorDetailsFlyout } from './monitor_detail_flyout';
4646
import { OverviewGridCompactView } from './compact_view/overview_grid_compact_view';
4747
import { ViewButtons } from './view_buttons/view_buttons';
4848

49-
const ITEM_HEIGHT = 182;
50-
const ROW_COUNT = 4;
49+
const ITEM_HEIGHT = METRIC_ITEM_HEIGHT + 12;
5150
const MAX_LIST_HEIGHT = 800;
5251
const MIN_BATCH_SIZE = 20;
5352
const LIST_THRESHOLD = 12;
53+
const MIN_CARD_WIDTH = 400;
5454

5555
interface ListItem {
5656
configId: string;
@@ -69,6 +69,8 @@ export const OverviewGrid = memo(
6969
groupBy: { field: groupField },
7070
} = useSelector(selectOverviewState);
7171

72+
const [rowCount, setRowCount] = useState(5);
73+
7274
const trendData = useSelector(selectOverviewTrends);
7375
const { perPage } = pageState;
7476

@@ -98,17 +100,17 @@ export const OverviewGrid = memo(
98100
}, [dispatch, maxItem, monitorsSortedByStatus, trendData]);
99101

100102
const listHeight = Math.min(
101-
ITEM_HEIGHT * Math.ceil(monitorsSortedByStatus.length / ROW_COUNT),
103+
ITEM_HEIGHT * Math.ceil(monitorsSortedByStatus.length / rowCount),
102104
MAX_LIST_HEIGHT
103105
);
104106

105107
const listItems: ListItem[][] = useMemo(() => {
106108
const acc: ListItem[][] = [];
107-
for (let i = 0; i < monitorsSortedByStatus.length; i += ROW_COUNT) {
108-
acc.push(monitorsSortedByStatus.slice(i, i + ROW_COUNT));
109+
for (let i = 0; i < monitorsSortedByStatus.length; i += rowCount) {
110+
acc.push(monitorsSortedByStatus.slice(i, i + rowCount));
109111
}
110112
return acc;
111-
}, [monitorsSortedByStatus]);
113+
}, [monitorsSortedByStatus, rowCount]);
112114

113115
useEffect(() => {
114116
dispatch(refreshOverviewTrends.get());
@@ -168,6 +170,10 @@ export const OverviewGrid = memo(
168170
threshold={LIST_THRESHOLD}
169171
>
170172
{({ onItemsRendered, ref }) => {
173+
// set min row count to based on width to ensure cards are not too small
174+
// min is 1 and max is 5
175+
setRowCount(Math.max(1, Math.min(5, Math.floor(width / MIN_CARD_WIDTH))));
176+
171177
return (
172178
<FixedSizeList
173179
// pad computed height to avoid clipping last row's drop shadow
@@ -196,20 +202,20 @@ export const OverviewGrid = memo(
196202
{listData[listIndex].map((_, idx) => (
197203
<EuiFlexItem
198204
data-test-subj="syntheticsOverviewGridItem"
199-
key={listIndex * ROW_COUNT + idx}
205+
key={listIndex * rowCount + idx}
200206
>
201207
<MetricItem
202208
monitor={
203-
monitorsSortedByStatus[listIndex * ROW_COUNT + idx]
209+
monitorsSortedByStatus[listIndex * rowCount + idx]
204210
}
205211
onClick={setFlyoutConfigCallback}
206212
/>
207213
</EuiFlexItem>
208214
))}
209-
{listData[listIndex].length % ROW_COUNT !== 0 &&
215+
{listData[listIndex].length % rowCount !== 0 &&
210216
// Adds empty items to fill out row
211217
Array.from({
212-
length: ROW_COUNT - listData[listIndex].length,
218+
length: rowCount - listData[listIndex].length,
213219
}).map((_, idx) => <EuiFlexItem key={idx} />)}
214220
</EuiFlexGroup>
215221
);
@@ -231,7 +237,7 @@ export const OverviewGrid = memo(
231237
{groupField === 'none' &&
232238
loaded &&
233239
// display this footer when user scrolls to end of list
234-
currentIndex * ROW_COUNT + ROW_COUNT >= monitorsSortedByStatus.length && (
240+
currentIndex * rowCount + rowCount >= monitorsSortedByStatus.length && (
235241
<>
236242
<EuiSpacer />
237243
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">

0 commit comments

Comments
 (0)