diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_of_nodes.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_of_nodes.tsx index af5a2ebd1d280..762c73a226b40 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_of_nodes.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_of_nodes.tsx @@ -17,7 +17,6 @@ import { import { GroupName } from './group_name'; import { Node } from './node'; import { InventoryItemType } from '../../../../../../common/inventory_models/types'; -import { useAssetDetailsFlyoutState } from '../../hooks/use_asset_details_flyout_url_state'; interface Props { onDrilldown: (filter: string) => void; @@ -45,7 +44,6 @@ const isEqualGroupOfNodes = (prevProps: Props, nextProps: Props) => { export const GroupOfNodes = React.memo( ({ group, options, formatter, onDrilldown, isChild = false, bounds, nodeType, currentTime }) => { const width = group.width > 200 ? group.width : 200; - const [_, setProperties] = useAssetDetailsFlyoutState(); return ( @@ -62,7 +60,6 @@ export const GroupOfNodes = React.memo( bounds={bounds} nodeType={nodeType} currentTime={currentTime} - setShowAssetDetailsFlyout={setProperties} /> )) ) : ( diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node.tsx index 18861610c8e06..602fafb8ad7d0 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node.tsx @@ -5,14 +5,11 @@ * 2.0. */ -import { darken, readableColor } from 'polished'; import React from 'react'; -import { i18n } from '@kbn/i18n'; - import { first } from 'lodash'; import { EuiPopover, EuiToolTip } from '@elastic/eui'; -import { euiStyled } from '@kbn/kibana-react-plugin/common'; +import { useBoolean } from '../../../../../hooks/use_boolean'; import { InfraWaffleMapBounds, InfraWaffleMapNode, @@ -23,15 +20,8 @@ import { colorFromValue } from '../../lib/color_from_value'; import { InventoryItemType } from '../../../../../../common/inventory_models/types'; import { NodeContextMenu } from './node_context_menu'; - -const initialState = { - isPopoverOpen: false, - isOverlayOpen: false, - isAlertFlyoutVisible: false, - isToolTipOpen: false, -}; - -type State = Readonly; +import { NodeSquare } from './node_square'; +import { useAssetDetailsFlyoutState } from '../../hooks/use_asset_details_flyout_url_state'; interface Props { squareSize: number; @@ -41,206 +31,75 @@ interface Props { bounds: InfraWaffleMapBounds; nodeType: InventoryItemType; currentTime: number; - setShowAssetDetailsFlyout: ({ detailsItemId }: { detailsItemId: string | null }) => void; } -export class Node extends React.PureComponent { - public readonly state: State = initialState; - public render() { - const { nodeType, node, options, squareSize, bounds, formatter, currentTime } = this.props; - const { isPopoverOpen, isToolTipOpen } = this.state; - const metric = first(node.metrics); - const valueMode = squareSize > 70; - const ellipsisMode = squareSize > 30; - const rawValue = (metric && metric.value) || 0; - const color = colorFromValue(options.legend, rawValue, bounds); - const value = formatter(rawValue); - const nodeAriaLabel = i18n.translate('xpack.infra.node.ariaLabel', { - defaultMessage: '{nodeName}, click to open menu', - values: { nodeName: node.name }, - }); - - const nodeBorder = this.state.isOverlayOpen ? { border: 'solid 4px #000' } : undefined; - - const bigSquare = ( - - - - {valueMode ? ( - - - - {value} - - - ) : ( - ellipsisMode && ( - - - - ) - )} - - - - ); - - const smallSquare = ( - - ); - - const nodeSquare = valueMode || ellipsisMode ? bigSquare : smallSquare; - - return ( - <> - {isPopoverOpen ? ( - - - - ) : isToolTipOpen ? ( - - } - > - {nodeSquare} - - ) : ( - nodeSquare - )} - - ); - } - - private togglePopover = () => { - const { nodeType, node, setShowAssetDetailsFlyout } = this.props; +export const Node = ({ + nodeType, + node, + options, + squareSize, + bounds, + formatter, + currentTime, +}: Props) => { + const [isToolTipOpen, { off: hideToolTip, on: showToolTip }] = useBoolean(false); + const [isPopoverOpen, { off: closePopover, toggle: togglePopover }] = useBoolean(false); + const [{ detailsItemId }, setProperties] = useAssetDetailsFlyoutState(); + + const metric = first(node.metrics); + const rawValue = (metric && metric.value) || 0; + const color = colorFromValue(options.legend, rawValue, bounds); + const value = formatter(rawValue); + + const toggleAssetPopover = () => { if (nodeType === 'host') { - setShowAssetDetailsFlyout({ detailsItemId: node.name }); + setProperties({ detailsItemId: node.name }); } else { - this.setState((prevState) => ({ isPopoverOpen: !prevState.isPopoverOpen })); + togglePopover(); } }; - private closePopover = () => { - if (this.state.isPopoverOpen) { - this.setState({ isPopoverOpen: false }); - } - }; - private showToolTip = () => { - this.setState({ isToolTipOpen: true }); - }; - private hideToolTip = () => { - this.setState({ isToolTipOpen: false }); - }; -} - -const NodeContainer = euiStyled.div` - position: relative; - cursor: pointer; -`; -const NodeContainerSmall = euiStyled.div` - cursor: pointer; - position: relative; - background-color: ${(props) => darken(0.1, props.color)}; - border-radius: 3px; - margin: 2px; - box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.2); -`; - -interface ColorProps { - color: string; -} - -const SquareOuter = euiStyled.div` - position: absolute; - top: 4px; - left: 4px; - bottom: 4px; - right: 4px; - background-color: ${(props) => darken(0.1, props.color)}; - border-radius: 3px; - box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.2); -`; - -const SquareInner = euiStyled.div` - position: absolute; - top: 0; - right: 0; - bottom: 2px; - left: 0; - border-radius: 3px; - background-color: ${(props) => props.color}; -`; - -const ValueInner = euiStyled.button` - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - display: flex; - line-height: 1.2em; - align-items: center; - align-content: center; - padding: 1em; - overflow: hidden; - flex-wrap: wrap; - width: 100%; - border: none; - &:focus { - outline: none !important; - border: ${(params) => params.theme?.eui.euiFocusRingSize} solid - ${(params) => params.theme?.eui.euiFocusRingColor}; - box-shadow: none; - } -`; - -const SquareTextContent = euiStyled.div` - text-align: center; - width: 100%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - flex: 1 0 auto; - color: ${(props) => readableColor(props.color)}; -`; - -const Value = euiStyled(SquareTextContent)` - font-weight: bold; - font-size: 0.9em; - line-height: 1.2em; -`; - -const Label = euiStyled(SquareTextContent)` - font-size: 0.7em; - margin-bottom: 0.7em; -`; + const nodeSquare = ( + + ); + + return ( + <> + {isPopoverOpen ? ( + + + + ) : isToolTipOpen ? ( + } + > + {nodeSquare} + + ) : ( + nodeSquare + )} + + ); +}; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_square.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_square.tsx new file mode 100644 index 0000000000000..71d4d6576be4a --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_square.tsx @@ -0,0 +1,165 @@ +/* + * 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 { darken, readableColor } from 'polished'; +import React from 'react'; + +import { i18n } from '@kbn/i18n'; + +import { euiStyled } from '@kbn/kibana-react-plugin/common'; +import { DispatchWithOptionalAction } from '../../../../../hooks/use_boolean'; + +const NodeContainer = euiStyled.div` + position: relative; + cursor: pointer; +`; +const NodeContainerSmall = euiStyled.div` + cursor: pointer; + position: relative; + background-color: ${(props) => darken(0.1, props.color)}; + border-radius: 3px; + margin: 2px; + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.2); +`; + +interface ColorProps { + color: string; +} + +const SquareOuter = euiStyled.div` + position: absolute; + top: 4px; + left: 4px; + bottom: 4px; + right: 4px; + background-color: ${(props) => darken(0.1, props.color)}; + border-radius: 3px; + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.2); +`; + +const SquareInner = euiStyled.div` + position: absolute; + top: 0; + right: 0; + bottom: 2px; + left: 0; + border-radius: 3px; + background-color: ${(props) => props.color}; +`; + +const ValueInner = euiStyled.button` + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + display: flex; + line-height: 1.2em; + align-items: center; + align-content: center; + padding: 1em; + overflow: hidden; + flex-wrap: wrap; + width: 100%; + border: none; + &:focus { + outline: none !important; + border: ${(params) => params.theme?.eui.euiFocusRingSize} solid + ${(params) => params.theme?.eui.euiFocusRingColor}; + box-shadow: none; + } +`; + +const SquareTextContent = euiStyled.div` + text-align: center; + width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + flex: 1 0 auto; + color: ${(props) => readableColor(props.color)}; +`; + +const Value = euiStyled(SquareTextContent)` + font-weight: bold; + font-size: 0.9em; + line-height: 1.2em; +`; + +const Label = euiStyled(SquareTextContent)` + font-size: 0.7em; + margin-bottom: 0.7em; +`; + +export const NodeSquare = ({ + squareSize, + togglePopover, + showToolTip, + hideToolTip, + color, + nodeName, + value, + isOverlayOpen, +}: { + squareSize: number; + togglePopover: DispatchWithOptionalAction; + showToolTip: () => void; + hideToolTip: () => void; + color: string; + nodeName: string; + value: string; + isOverlayOpen: boolean; +}) => { + const valueMode = squareSize > 70; + const ellipsisMode = squareSize > 30; + const nodeAriaLabel = i18n.translate('xpack.infra.node.ariaLabel', { + defaultMessage: '{nodeName}, click to open menu', + values: { nodeName }, + }); + const nodeBorder = isOverlayOpen ? { border: 'solid 4px #000' } : undefined; + + return valueMode || ellipsisMode ? ( + + + + {valueMode ? ( + + + + {value} + + + ) : ( + ellipsisMode && ( + + + + ) + )} + + + + ) : ( + + ); +};