diff --git a/x-pack/plugins/observability/public/pages/slos/components/card_view/badges_portal.tsx b/x-pack/plugins/observability/public/pages/slos/components/card_view/badges_portal.tsx new file mode 100644 index 0000000000000..aace661240d23 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slos/components/card_view/badges_portal.tsx @@ -0,0 +1,34 @@ +/* + * 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, { ReactNode, useEffect, useMemo } from 'react'; +import { createHtmlPortalNode, InPortal, OutPortal } from 'react-reverse-portal'; +import ReactDOM from 'react-dom'; +export interface Props { + children: ReactNode; + containerRef: React.RefObject; +} + +export function SloCardBadgesPortal({ children, containerRef }: Props) { + const portalNode = useMemo(() => createHtmlPortalNode(), []); + + useEffect(() => { + if (containerRef?.current) { + setTimeout(() => { + const gapDiv = containerRef?.current?.querySelector('.echMetricText__gap'); + if (!gapDiv) return; + ReactDOM.render(, gapDiv); + }, 100); + } + + return () => { + portalNode.unmount(); + }; + }, [portalNode, containerRef]); + + return {children}; +} diff --git a/x-pack/plugins/observability/public/pages/slos/components/card_view/slo_card_item.tsx b/x-pack/plugins/observability/public/pages/slos/components/card_view/slo_card_item.tsx index 07ad3caf1fd00..ac649a71e3600 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/card_view/slo_card_item.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/card_view/slo_card_item.tsx @@ -9,16 +9,17 @@ import React, { useState } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { Chart, + DARK_THEME, isMetricElementEvent, Metric, - Settings, - DARK_THEME, MetricTrendShape, + Settings, } from '@elastic/charts'; import { EuiIcon, EuiPanel, useEuiBackgroundColor } from '@elastic/eui'; -import { SLOWithSummaryResponse, HistoricalSummaryResponse, ALL_VALUE } from '@kbn/slo-schema'; +import { ALL_VALUE, HistoricalSummaryResponse, SLOWithSummaryResponse } from '@kbn/slo-schema'; import { Rule } from '@kbn/triggers-actions-ui-plugin/public'; import { i18n } from '@kbn/i18n'; +import { SloCardBadgesPortal } from './badges_portal'; import { useSloListActions } from '../../hooks/use_slo_list_actions'; import { BurnRateRuleFlyout } from '../common/burn_rate_rule_flyout'; import { formatHistoricalData } from '../../../../utils/slo/chart_data_formatter'; @@ -52,12 +53,7 @@ const useCardColor = (status?: SLOWithSummaryResponse['summary']['status']) => { }; const getSubTitle = (slo: SLOWithSummaryResponse, cardsPerRow: number) => { - const subTitle = - slo.groupBy && slo.groupBy !== ALL_VALUE ? `${slo.groupBy}: ${slo.instanceId}` : ''; - if (cardsPerRow === 4) { - return subTitle.substring(0, 30) + (subTitle.length > 30 ? '..' : ''); - } - return subTitle.substring(0, 40) + (subTitle.length > 40 ? '..' : ''); + return slo.groupBy && slo.groupBy !== ALL_VALUE ? `${slo.groupBy}: ${slo.instanceId}` : ''; }; export function SloCardItem({ slo, rules, activeAlerts, historicalSummary, cardsPerRow }: Props) { @@ -65,6 +61,8 @@ export function SloCardItem({ slo, rules, activeAlerts, historicalSummary, cards application: { navigateToUrl }, } = useKibana().services; + const containerRef = React.useRef(null); + const [isMouseOver, setIsMouseOver] = useState(false); const [isActionsPopoverOpen, setIsActionsPopoverOpen] = useState(false); const [isAddRuleFlyoutOpen, setIsAddRuleFlyoutOpen] = useState(false); @@ -88,6 +86,7 @@ export function SloCardItem({ slo, rules, activeAlerts, historicalSummary, cards return ( <> } onMouseOver={() => { if (!isMouseOver) { setIsMouseOver(true); @@ -145,13 +144,6 @@ export function SloCardItem({ slo, rules, activeAlerts, historicalSummary, cards ]} /> - {(isMouseOver || isActionsPopoverOpen) && ( )} + + + void; } -const Container = styled.div<{ hasGroupBy: boolean }>` - position: absolute; +const Container = styled.div` display: inline-block; - top: ${({ hasGroupBy }) => (hasGroupBy ? '55px' : '35px')}; - left: 7px; - z-index: 1; - border-radius: ${({ theme }) => theme.eui.euiBorderRadius}; + margin-top: 5px; `; -export function SloCardItemBadges({ - slo, - activeAlerts, - rules, - handleCreateRule, - hasGroupBy, -}: Props) { +export function SloCardItemBadges({ slo, activeAlerts, rules, handleCreateRule }: Props) { return ( - - + + {!slo.summary ? ( ) : ( diff --git a/x-pack/plugins/observability/public/pages/slos/components/card_view/slos_card_view.tsx b/x-pack/plugins/observability/public/pages/slos/components/card_view/slos_card_view.tsx index 3768bdbb7dac3..4e4df93967fde 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/card_view/slos_card_view.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/card_view/slos_card_view.tsx @@ -6,7 +6,13 @@ */ import React from 'react'; -import { EuiFlexGrid, EuiFlexItem, EuiPanel, EuiSkeletonText } from '@elastic/eui'; +import { + EuiFlexGrid, + EuiFlexItem, + EuiPanel, + EuiSkeletonText, + useIsWithinBreakpoints, +} from '@elastic/eui'; import { SLOWithSummaryResponse, ALL_VALUE } from '@kbn/slo-schema'; import { EuiFlexGridProps } from '@elastic/eui/src/components/flex/flex_grid'; import { ActiveAlerts } from '../../../../hooks/slo/use_fetch_active_alerts'; @@ -23,6 +29,25 @@ export interface Props { rulesBySlo?: UseFetchRulesForSloResponse['data']; } +const useColumns = (cardsPerRow: string | undefined) => { + const isMobile = useIsWithinBreakpoints(['xs', 's']); + const isMedium = useIsWithinBreakpoints(['m']); + const isLarge = useIsWithinBreakpoints(['l']); + + const columns = (Number(cardsPerRow) as EuiFlexGridProps['columns']) ?? 3; + + switch (true) { + case isMobile: + return 1; + case isMedium: + return columns > 2 ? 2 : columns; + case isLarge: + return columns > 3 ? 3 : columns; + default: + return columns; + } +}; + export function SloListCardView({ sloList, loading, @@ -36,12 +61,14 @@ export function SloListCardView({ list: sloList.map((slo) => ({ sloId: slo.id, instanceId: slo.instanceId ?? ALL_VALUE })), }); + const columns = useColumns(cardsPerRow); + if (loading && sloList.length === 0) { return ; } return ( - + {sloList.map((slo) => (