[8.x] [Synthetics] Added drilldown to synthetics stats overview embeddable (#217688) (#218387)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Synthetics] Added drilldown to synthetics stats overview embeddable
(#217688)](https://github.com/elastic/kibana/pull/217688)

<!--- Backport version: 9.6.6 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sorenlouv/backport)

<!--BACKPORT [{"author":{"name":"Francesco
Fagnani","email":"fagnani.francesco@gmail.com"},"sourceCommit":{"committedDate":"2025-04-16T07:52:16Z","message":"[Synthetics]
Added drilldown to synthetics stats overview embeddable
(#217688)\n\nThis PR closes #208066 by adding drilldowns to the
synthetics stats\noverview
embeddable.\n\n\n\nhttps://github.com/user-attachments/assets/fe8d9eb0-ecdc-449d-93af-7d165c541d46","sha":"ec88cca373b879b265df08965a9a590a7dc65217","branchLabelMapping":{"^v9.1.0$":"main","^v8.19.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:feature","Team:obs-ux-management","backport:version","v9.1.0","v8.19.0"],"title":"[Synthetics]
Added drilldown to synthetics stats overview
embeddable","number":217688,"url":"https://github.com/elastic/kibana/pull/217688","mergeCommit":{"message":"[Synthetics]
Added drilldown to synthetics stats overview embeddable
(#217688)\n\nThis PR closes #208066 by adding drilldowns to the
synthetics stats\noverview
embeddable.\n\n\n\nhttps://github.com/user-attachments/assets/fe8d9eb0-ecdc-449d-93af-7d165c541d46","sha":"ec88cca373b879b265df08965a9a590a7dc65217"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/217688","number":217688,"mergeCommit":{"message":"[Synthetics]
Added drilldown to synthetics stats overview embeddable
(#217688)\n\nThis PR closes #208066 by adding drilldowns to the
synthetics stats\noverview
embeddable.\n\n\n\nhttps://github.com/user-attachments/assets/fe8d9eb0-ecdc-449d-93af-7d165c541d46","sha":"ec88cca373b879b265df08965a9a590a7dc65217"}},{"branch":"8.x","label":"v8.19.0","branchLabelMappingKey":"^v8.19.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->
This commit is contained in:
Francesco Fagnani 2025-04-16 18:25:29 +02:00 committed by GitHub
parent 6458959c21
commit 54d58acf36
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 175 additions and 49 deletions

View file

@ -56,7 +56,8 @@
"spaces",
"telemetry",
"licenseManagement",
"observabilityAIAssistant"
"observabilityAIAssistant",
"embeddableEnhanced",
],
"requiredBundles": [
"data",

View file

@ -62,6 +62,7 @@ const WithFiltersComponent = ({ filters }: { filters: MonitorFilters }) => {
<OverviewStatus
titleAppend={hasFilters ? <ShowSelectedFilters filters={filters ?? {}} /> : null}
hideTitle={true}
areStatsClickable
/>
);
};

View file

@ -17,9 +17,16 @@ import {
PublishesTitle,
SerializedTitles,
HasEditCapabilities,
getUnchangingComparator,
HasSupportedTriggers,
} from '@kbn/presentation-publishing';
import { BehaviorSubject, Subject } from 'rxjs';
import type { StartServicesAccessor } from '@kbn/core-lifecycle-browser';
import {
DynamicActionsSerializedState,
ReactEmbeddableDynamicActionsApi,
} from '@kbn/embeddable-enhanced-plugin/public/plugin';
import { HasDynamicActions } from '@kbn/embeddable-enhanced-plugin/public';
import { MonitorFilters } from '../monitors_overview/types';
import { SYNTHETICS_STATS_OVERVIEW_EMBEDDABLE } from '../constants';
import { ClientPluginsStart } from '../../../plugin';
@ -31,14 +38,50 @@ export const getOverviewPanelTitle = () =>
defaultMessage: 'Synthetics Stats Overview',
});
export type OverviewEmbeddableState = SerializedTitles & {
filters: MonitorFilters;
};
export type OverviewEmbeddableState = SerializedTitles &
DynamicActionsSerializedState & {
filters: MonitorFilters;
};
export type StatsOverviewApi = DefaultEmbeddableApi<OverviewEmbeddableState> &
PublishesWritableTitle &
PublishesTitle &
HasEditCapabilities;
HasEditCapabilities &
HasDynamicActions &
HasSupportedTriggers;
type DynamicActionsData = {
serializedDynamicActions: DynamicActionsSerializedState;
stopDynamicActions?: () => void;
} & Pick<ReactEmbeddableDynamicActionsApi, 'dynamicActionsApi' | 'dynamicActionsComparator'>;
const getDynamicActionsData = (
extractor?: () => ReactEmbeddableDynamicActionsApi
): DynamicActionsData => {
if (extractor) {
const {
serializeDynamicActions,
startDynamicActions,
dynamicActionsApi,
dynamicActionsComparator,
} = extractor();
return {
serializedDynamicActions: serializeDynamicActions(),
stopDynamicActions: startDynamicActions().stopDynamicActions,
dynamicActionsApi,
dynamicActionsComparator,
};
}
return {
serializedDynamicActions: {},
stopDynamicActions: undefined,
dynamicActionsApi: {},
dynamicActionsComparator: {
enhancements: getUnchangingComparator(),
},
};
};
export const getStatsOverviewEmbeddableFactory = (
getStartServices: StartServicesAccessor<ClientPluginsStart>
@ -52,7 +95,7 @@ export const getStatsOverviewEmbeddableFactory = (
deserializeState: (state) => {
return state.rawState as OverviewEmbeddableState;
},
buildEmbeddable: async (state, buildApi, uuid, parentApi) => {
buildEmbeddable: async (state, buildApi, uuid) => {
const [coreStart, pluginStart] = await getStartServices();
const titleManager = initializeTitleManager(state);
@ -60,9 +103,28 @@ export const getStatsOverviewEmbeddableFactory = (
const reload$ = new Subject<boolean>();
const filters$ = new BehaviorSubject(state.filters);
const { embeddableEnhanced } = pluginStart;
const {
dynamicActionsApi,
dynamicActionsComparator,
serializedDynamicActions,
stopDynamicActions,
} = getDynamicActionsData(
embeddableEnhanced
? () =>
embeddableEnhanced.initializeReactEmbeddableDynamicActions(
uuid,
() => titleManager.api.title$.getValue(),
state
)
: undefined
);
const api = buildApi(
{
...titleManager.api,
...dynamicActionsApi,
supportedTriggers: () => [],
defaultTitle$,
getTypeDisplayName: () =>
i18n.translate('xpack.synthetics.editSloOverviewEmbeddableTitle.typeDisplayName', {
@ -92,6 +154,7 @@ export const getStatsOverviewEmbeddableFactory = (
rawState: {
...titleManager.serialize(),
filters: filters$.getValue(),
...serializedDynamicActions,
},
};
},
@ -99,6 +162,7 @@ export const getStatsOverviewEmbeddableFactory = (
{
...titleManager.comparators,
filters: [filters$, (value) => filters$.next(value)],
...dynamicActionsComparator,
}
);
@ -116,6 +180,7 @@ export const getStatsOverviewEmbeddableFactory = (
useEffect(() => {
return () => {
fetchSubscription.unsubscribe();
if (stopDynamicActions) stopDynamicActions();
};
}, []);
return (

View file

@ -5,28 +5,69 @@
* 2.0.
*/
import { EuiFlexGroup, EuiFlexItem, EuiStat } from '@elastic/eui';
import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiStat } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { useEffect, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useDispatch } from 'react-redux';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import { EmbeddablePanelWrapper } from '../../../common/components/embeddable_panel_wrapper';
import { clearOverviewStatusErrorAction } from '../../../../state/overview_status';
import { kibanaService } from '../../../../../../utils/kibana_service';
import { useGetUrlParams } from '../../../../hooks/use_url_params';
import { useOverviewStatus } from '../../hooks/use_overview_status';
import { PLUGIN } from '../../../../../../../common/constants/plugin';
function title(t?: number) {
return t ?? '-';
}
interface MonitorStatProps {
dataTestSubj: string;
statName: string;
statNo: number | '-';
numberColor: string;
isClickable: boolean;
onClickStat: () => void;
}
const MonitorStat = ({
dataTestSubj,
statName,
statNo,
numberColor,
isClickable,
onClickStat,
}: MonitorStatProps) => {
const statComponent = (
<EuiStat
data-test-subj={dataTestSubj}
description={statName}
reverse
title={statNo}
titleColor={numberColor}
titleSize="m"
/>
);
return isClickable ? (
<EuiButtonEmpty data-test-subj={`${dataTestSubj}Btn`} onClick={onClickStat}>
{statComponent}
</EuiButtonEmpty>
) : (
statComponent
);
};
export function OverviewStatus({
titleAppend,
hideTitle,
areStatsClickable = false,
}: {
titleAppend?: React.ReactNode;
hideTitle?: boolean;
areStatsClickable?: boolean;
}) {
const { statusFilter } = useGetUrlParams();
const { application } = useKibana().services;
const {
status,
@ -97,6 +138,58 @@ export function OverviewStatus({
}
}, [status, statusFilter]);
const getOnClickStat = useCallback(
(statusFilterName: string) => {
return () => {
application?.navigateToApp(PLUGIN.SYNTHETICS_PLUGIN_ID, {
path: `?statusFilter=${statusFilterName}`,
});
};
},
[application]
);
const monitorStatData = useMemo(() => {
const stats: MonitorStatProps[] = [
{
dataTestSubj: 'syntheticsOverviewUp',
statName: upDescription,
statNo: title(statusConfig?.up),
numberColor: 'success',
isClickable: areStatsClickable,
onClickStat: getOnClickStat('up'),
},
{
dataTestSubj: 'syntheticsOverviewDown',
statName: downDescription,
statNo: title(statusConfig?.down),
numberColor: 'danger',
isClickable: areStatsClickable,
onClickStat: getOnClickStat('down'),
},
{
dataTestSubj: 'xpack.uptime.synthetics.overview.status.disabled',
statName: disabledDescription,
statNo: title(statusConfig?.disabledCount),
numberColor: 'subdued',
isClickable: areStatsClickable,
onClickStat: getOnClickStat('disabled'),
},
];
if (statusConfig?.pending) {
stats.push({
dataTestSubj: 'xpack.uptime.synthetics.overview.status.pending',
statName: pendingDescription,
statNo: title(statusConfig.pending),
numberColor: 'subdued',
isClickable: areStatsClickable,
onClickStat: getOnClickStat('pending'),
});
}
return stats;
}, [areStatsClickable, getOnClickStat, statusConfig]);
return (
<EmbeddablePanelWrapper
title={headingText}
@ -105,48 +198,11 @@ export function OverviewStatus({
hideTitle={hideTitle}
>
<EuiFlexGroup gutterSize="xl" justifyContent="spaceAround">
<EuiFlexItem grow={false}>
<EuiStat
data-test-subj="syntheticsOverviewUp"
description={upDescription}
reverse
title={title(statusConfig?.up)}
titleColor="success"
titleSize="m"
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiStat
data-test-subj="syntheticsOverviewDown"
description={downDescription}
reverse
title={title(statusConfig?.down)}
titleColor="danger"
titleSize="m"
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiStat
data-test-subj="xpack.uptime.synthetics.overview.status.disabled"
description={disabledDescription}
reverse
title={title(statusConfig?.disabledCount)}
titleColor="subdued"
titleSize="m"
/>
</EuiFlexItem>
{(statusConfig?.pending || 0) > 0 && (
<EuiFlexItem grow={false}>
<EuiStat
data-test-subj="xpack.uptime.synthetics.overview.status.pending"
description={pendingDescription}
reverse
title={title(statusConfig?.pending)}
titleColor="subdued"
titleSize="m"
/>
{monitorStatData.map((props) => (
<EuiFlexItem grow={false} key={props.dataTestSubj}>
<MonitorStat {...props} />
</EuiFlexItem>
)}
))}
</EuiFlexGroup>
</EmbeddablePanelWrapper>
);

View file

@ -63,6 +63,7 @@ import { DashboardStart } from '@kbn/dashboard-plugin/public';
import { SLOPublicStart } from '@kbn/slo-plugin/public';
import type { ChartsPluginStart } from '@kbn/charts-plugin/public';
import { FieldsMetadataPublicStart } from '@kbn/fields-metadata-plugin/public';
import { EmbeddableEnhancedPluginStart } from '@kbn/embeddable-enhanced-plugin/public';
import { registerSyntheticsEmbeddables } from './apps/embeddables/register_embeddables';
import { kibanaService } from './utils/kibana_service';
import { PLUGIN } from '../common/constants/plugin';
@ -97,6 +98,7 @@ export interface ClientPluginsStart {
discover: DiscoverStart;
inspector: InspectorPluginStart;
embeddable: EmbeddableStart;
embeddableEnhanced?: EmbeddableEnhancedPluginStart;
exploratoryView: ExploratoryViewPublicStart;
observability: ObservabilityPublicStart;
observabilityShared: ObservabilitySharedPluginStart;

View file

@ -27,6 +27,7 @@
"@kbn/discover-plugin",
"@kbn/home-plugin",
"@kbn/embeddable-plugin",
"@kbn/embeddable-enhanced-plugin",
"@kbn/data-plugin",
"@kbn/kibana-utils-plugin",
"@kbn/inspector-plugin",