mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[SLO] Fix card view title overlap (#172344)
This commit is contained in:
parent
abac134908
commit
4fd75a9436
4 changed files with 85 additions and 33 deletions
|
@ -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<HTMLDivElement>;
|
||||
}
|
||||
|
||||
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(<OutPortal node={portalNode} />, gapDiv);
|
||||
}, 100);
|
||||
}
|
||||
|
||||
return () => {
|
||||
portalNode.unmount();
|
||||
};
|
||||
}, [portalNode, containerRef]);
|
||||
|
||||
return <InPortal node={portalNode}>{children}</InPortal>;
|
||||
}
|
|
@ -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<HTMLDivElement>(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 (
|
||||
<>
|
||||
<EuiPanel
|
||||
panelRef={containerRef as React.Ref<HTMLDivElement>}
|
||||
onMouseOver={() => {
|
||||
if (!isMouseOver) {
|
||||
setIsMouseOver(true);
|
||||
|
@ -145,13 +144,6 @@ export function SloCardItem({ slo, rules, activeAlerts, historicalSummary, cards
|
|||
]}
|
||||
/>
|
||||
</Chart>
|
||||
<SloCardItemBadges
|
||||
slo={slo}
|
||||
rules={rules}
|
||||
activeAlerts={activeAlerts}
|
||||
handleCreateRule={handleCreateRule}
|
||||
hasGroupBy={Boolean(slo.groupBy && slo.groupBy !== ALL_VALUE)}
|
||||
/>
|
||||
{(isMouseOver || isActionsPopoverOpen) && (
|
||||
<SloCardItemActions
|
||||
slo={slo}
|
||||
|
@ -162,6 +154,15 @@ export function SloCardItem({ slo, rules, activeAlerts, historicalSummary, cards
|
|||
/>
|
||||
)}
|
||||
</EuiPanel>
|
||||
<SloCardBadgesPortal containerRef={containerRef}>
|
||||
<SloCardItemBadges
|
||||
slo={slo}
|
||||
rules={rules}
|
||||
activeAlerts={activeAlerts}
|
||||
handleCreateRule={handleCreateRule}
|
||||
hasGroupBy={Boolean(slo.groupBy && slo.groupBy !== ALL_VALUE)}
|
||||
/>
|
||||
</SloCardBadgesPortal>
|
||||
|
||||
<BurnRateRuleFlyout
|
||||
slo={slo}
|
||||
|
|
|
@ -25,25 +25,15 @@ interface Props {
|
|||
handleCreateRule: () => 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 (
|
||||
<Container hasGroupBy={hasGroupBy}>
|
||||
<EuiFlexGroup direction="row" responsive={false} gutterSize="s" alignItems="center" wrap>
|
||||
<Container>
|
||||
<EuiFlexGroup direction="row" responsive={false} gutterSize="xs" alignItems="center" wrap>
|
||||
{!slo.summary ? (
|
||||
<LoadingBadges />
|
||||
) : (
|
||||
|
|
|
@ -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 <LoadingSloGrid gridSize={Number(cardsPerRow)} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiFlexGrid columns={Number(cardsPerRow) as EuiFlexGridProps['columns']}>
|
||||
<EuiFlexGrid columns={columns}>
|
||||
{sloList.map((slo) => (
|
||||
<EuiFlexItem key={`${slo.id}-${slo.instanceId ?? 'ALL_VALUE'}`}>
|
||||
<SloCardItem
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue