diff --git a/x-pack/plugins/infra/common/inventory_models/shared/components/metrics_and_groupby_toolbar_items.tsx b/x-pack/plugins/infra/common/inventory_models/shared/components/metrics_and_groupby_toolbar_items.tsx
index bf37828ed0856..4a4accc6edfed 100644
--- a/x-pack/plugins/infra/common/inventory_models/shared/components/metrics_and_groupby_toolbar_items.tsx
+++ b/x-pack/plugins/infra/common/inventory_models/shared/components/metrics_and_groupby_toolbar_items.tsx
@@ -7,6 +7,7 @@
import React, { useMemo } from 'react';
import { EuiFlexItem } from '@elastic/eui';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { WaffleSortControls } from '../../../../public/pages/metrics/inventory_view/components/waffle/waffle_sort_controls';
import { ToolbarProps } from '../../../../public/pages/metrics/inventory_view/components/toolbars/toolbar';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { WaffleMetricControls } from '../../../../public/pages/metrics/inventory_view/components/waffle/metric_control';
@@ -58,6 +59,11 @@ export const MetricsAndGroupByToolbarItems = (props: Props) => {
customOptions={props.customOptions}
/>
+ {props.view === 'map' && (
+
+
+
+ )}
>
);
};
diff --git a/x-pack/plugins/infra/common/saved_objects/inventory_view.ts b/x-pack/plugins/infra/common/saved_objects/inventory_view.ts
index c14b75efc6887..15874f2eee348 100644
--- a/x-pack/plugins/infra/common/saved_objects/inventory_view.ts
+++ b/x-pack/plugins/infra/common/saved_objects/inventory_view.ts
@@ -21,6 +21,16 @@ export const inventoryViewSavedObjectType: SavedObjectsType = {
name: {
type: 'keyword',
},
+ sort: {
+ properties: {
+ by: {
+ type: 'keyword',
+ },
+ direction: {
+ type: 'keyword',
+ },
+ },
+ },
metric: {
properties: {
type: {
diff --git a/x-pack/plugins/infra/public/lib/lib.ts b/x-pack/plugins/infra/public/lib/lib.ts
index 9043b4d9f6979..6bd4c10c881e3 100644
--- a/x-pack/plugins/infra/public/lib/lib.ts
+++ b/x-pack/plugins/infra/public/lib/lib.ts
@@ -18,6 +18,7 @@ import {
SnapshotNodeMetric,
SnapshotNodePath,
} from '../../common/http_api/snapshot_api';
+import { WaffleSortOption } from '../pages/metrics/inventory_view/hooks/use_waffle_options';
export interface InfraFrontendLibs {
apolloClient: InfraApolloClient;
@@ -163,6 +164,7 @@ export interface InfraWaffleMapOptions {
metric: SnapshotMetricInput;
groupBy: SnapshotGroupBy;
legend: InfraWaffleMapLegend;
+ sort: WaffleSortOption;
}
export interface InfraOptions {
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx
index a71e43874b480..32f8e47fe9b7d 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx
@@ -32,6 +32,7 @@ export const Layout = () => {
const {
metric,
groupBy,
+ sort,
nodeType,
accountId,
region,
@@ -64,6 +65,7 @@ export const Layout = () => {
],
} as InfraWaffleMapGradientLegend,
metric,
+ sort,
fields: source?.configuration?.fields,
groupBy,
};
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/toolbars/toolbar.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/toolbars/toolbar.tsx
index e8485fb812586..6a68599aee38c 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/toolbars/toolbar.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/toolbars/toolbar.tsx
@@ -19,17 +19,17 @@ import { ToolbarWrapper } from './toolbar_wrapper';
import { InfraGroupByOptions } from '../../../../../lib/lib';
import { IIndexPattern } from '../../../../../../../../../src/plugins/data/public';
import { InventoryItemType } from '../../../../../../common/inventory_models/types';
-import { WaffleOptionsState } from '../../hooks/use_waffle_options';
+import { WaffleOptionsState, WaffleSortOption } from '../../hooks/use_waffle_options';
import { useInventoryMeta } from '../../hooks/use_inventory_meta';
-export interface ToolbarProps
- extends Omit {
+export interface ToolbarProps extends Omit {
createDerivedIndexPattern: (type: 'logs' | 'metrics' | 'both') => IIndexPattern;
changeMetric: (payload: SnapshotMetricInput) => void;
changeGroupBy: (payload: SnapshotGroupBy) => void;
changeCustomOptions: (payload: InfraGroupByOptions[]) => void;
changeAccount: (id: string) => void;
changeRegion: (name: string) => void;
+ changeSort: (sort: WaffleSortOption) => void;
accounts: InventoryCloudAccount[];
regions: string[];
changeCustomMetrics: (payload: SnapshotCustomMetricInput[]) => void;
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/toolbars/toolbar_wrapper.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/toolbars/toolbar_wrapper.tsx
index ea53122984161..3606bcc6944d1 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/toolbars/toolbar_wrapper.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/toolbars/toolbar_wrapper.tsx
@@ -25,12 +25,15 @@ export const ToolbarWrapper = (props: Props) => {
changeCustomOptions,
changeAccount,
changeRegion,
+ changeSort,
customOptions,
groupBy,
metric,
nodeType,
accountId,
+ view,
region,
+ sort,
customMetrics,
changeCustomMetrics,
} = useWaffleOptionsContext();
@@ -47,8 +50,11 @@ export const ToolbarWrapper = (props: Props) => {
changeAccount,
changeRegion,
changeCustomOptions,
+ changeSort,
customOptions,
groupBy,
+ sort,
+ view,
metric,
nodeType,
region,
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/map.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/map.tsx
index b1f816fb46d2e..eee8c69091ef0 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/map.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/map.tsx
@@ -15,6 +15,7 @@ import { GroupOfNodes } from './group_of_nodes';
import { applyWaffleMapLayout } from '../../lib/apply_wafflemap_layout';
import { SnapshotNode } from '../../../../../../common/http_api/snapshot_api';
import { InventoryItemType } from '../../../../../../common/inventory_models/types';
+import { sortNodes } from '../../lib/sort_nodes';
interface Props {
nodes: SnapshotNode[];
@@ -37,7 +38,8 @@ export const Map: React.FC = ({
nodeType,
dataBounds,
}) => {
- const map = nodesToWaffleMap(nodes);
+ const sortedNodes = sortNodes(options.sort, nodes);
+ const map = nodesToWaffleMap(sortedNodes);
return (
{({ measureRef, content: { width = 0, height = 0 } }) => {
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/index.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/index.tsx
index f91e9a4034bc2..0cf1faec06c11 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/index.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/index.tsx
@@ -132,7 +132,10 @@ export const WaffleMetricControls = ({
}
const button = (
-
+
{currentLabel}
);
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_accounts_controls.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_accounts_controls.tsx
index 3e4ff1de8291d..5ac6320510257 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_accounts_controls.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_accounts_controls.tsx
@@ -58,7 +58,10 @@ export const WaffleAccountsControls = (props: Props) => {
);
const button = (
-
+
{currentLabel
? currentLabel.name
: i18n.translate('xpack.infra.waffle.accountAllTitle', {
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_group_by_controls.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_group_by_controls.tsx
index c1f406f31e85e..cc09ce226b2fe 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_group_by_controls.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_group_by_controls.tsx
@@ -130,7 +130,10 @@ export const WaffleGroupByControls = class extends React.PureComponent
+
{buttonBody}
);
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_inventory_switcher.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_inventory_switcher.tsx
index e534c97eda090..c31b7e0a3e6ff 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_inventory_switcher.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_inventory_switcher.tsx
@@ -7,6 +7,7 @@
import { EuiPopover, EuiContextMenu, EuiContextMenuPanelDescriptor } from '@elastic/eui';
import React, { useCallback, useState, useMemo } from 'react';
+import { i18n } from '@kbn/i18n';
import { findInventoryModel } from '../../../../../../common/inventory_models';
import { InventoryItemType } from '../../../../../../common/inventory_models/types';
import { useWaffleOptionsContext } from '../../hooks/use_waffle_options';
@@ -115,7 +116,10 @@ export const WaffleInventorySwitcher: React.FC = () => {
}, [nodeType]);
const button = (
-
+
{selectedText}
);
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_region_controls.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_region_controls.tsx
index 9d759424cdc93..9c28f0798b519 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_region_controls.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_region_controls.tsx
@@ -57,7 +57,10 @@ export const WaffleRegionControls = (props: Props) => {
);
const button = (
-
+
{currentLabel ||
i18n.translate('xpack.infra.waffle.region', {
defaultMessage: 'All',
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_sort_controls.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_sort_controls.tsx
new file mode 100644
index 0000000000000..b5e6aacd0e6f4
--- /dev/null
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_sort_controls.tsx
@@ -0,0 +1,125 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { useCallback, useMemo, useState, ReactNode } from 'react';
+import { EuiSwitch, EuiContextMenuPanelDescriptor, EuiPopover, EuiContextMenu } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { EuiTheme, withTheme } from '../../../../../../../observability/public';
+import { WaffleSortOption } from '../../hooks/use_waffle_options';
+import { DropdownButton } from '../dropdown_button';
+
+interface Props {
+ sort: WaffleSortOption;
+ onChange: (sort: WaffleSortOption) => void;
+}
+
+const LABELS = {
+ name: i18n.translate('xpack.infra.waffle.sortNameLabel', { defaultMessage: 'Name' }),
+ value: i18n.translate('xpack.infra.waffle.sort.valueLabel', { defaultMessage: 'Metric value' }),
+};
+
+export const WaffleSortControls = ({ sort, onChange }: Props) => {
+ const [isOpen, setIsOpen] = useState(false);
+
+ const showPopover = useCallback(() => {
+ setIsOpen(true);
+ }, [setIsOpen]);
+
+ const closePopover = useCallback(() => {
+ setIsOpen(false);
+ }, [setIsOpen]);
+
+ const label = LABELS[sort.by];
+
+ const button = (
+
+ {label}
+
+ );
+
+ const selectName = useCallback(() => {
+ onChange({ ...sort, by: 'name' });
+ closePopover();
+ }, [closePopover, onChange, sort]);
+
+ const selectValue = useCallback(() => {
+ onChange({ ...sort, by: 'value' });
+ closePopover();
+ }, [closePopover, onChange, sort]);
+
+ const toggleSort = useCallback(() => {
+ onChange({
+ ...sort,
+ direction: sort.direction === 'asc' ? 'desc' : 'asc',
+ });
+ }, [sort, onChange]);
+
+ const panels = useMemo(
+ () => [
+ {
+ id: 0,
+ title: '',
+ items: [
+ {
+ name: LABELS.name,
+ icon: sort.by === 'name' ? 'check' : 'empty',
+ onClick: selectName,
+ },
+ {
+ name: LABELS.value,
+ icon: sort.by === 'value' ? 'check' : 'empty',
+ onClick: selectValue,
+ },
+ ],
+ },
+ ],
+ [sort.by, selectName, selectValue]
+ );
+
+ return (
+
+
+
+
+
+
+ );
+};
+
+interface SwitchContainerProps {
+ theme: EuiTheme;
+ children: ReactNode;
+}
+
+const SwitchContainer = withTheme(({ children, theme }: SwitchContainerProps) => {
+ return (
+
+ {children}
+
+ );
+});
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_options.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_options.ts
index 32bfe6e085b4e..92f9ee4897f2d 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_options.ts
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_options.ts
@@ -32,6 +32,7 @@ export const DEFAULT_WAFFLE_OPTIONS_STATE: WaffleOptionsState = {
accountId: '',
region: '',
customMetrics: [],
+ sort: { by: 'name', direction: 'desc' },
};
export const useWaffleOptions = () => {
@@ -99,7 +100,15 @@ export const useWaffleOptions = () => {
[setState]
);
+ const changeSort = useCallback(
+ (sort: WaffleSortOption) => {
+ setState(previous => ({ ...previous, sort }));
+ },
+ [setState]
+ );
+
return {
+ ...DEFAULT_WAFFLE_OPTIONS_STATE,
...state,
changeMetric,
changeGroupBy,
@@ -111,10 +120,16 @@ export const useWaffleOptions = () => {
changeAccount,
changeRegion,
changeCustomMetrics,
+ changeSort,
setWaffleOptionsState: setState,
};
};
+export const WaffleSortOptionRT = rt.type({
+ by: rt.keyof({ name: null, value: null }),
+ direction: rt.keyof({ asc: null, desc: null }),
+});
+
export const WaffleOptionsStateRT = rt.type({
metric: SnapshotMetricInputRT,
groupBy: SnapshotGroupByRT,
@@ -134,8 +149,10 @@ export const WaffleOptionsStateRT = rt.type({
accountId: rt.string,
region: rt.string,
customMetrics: rt.array(SnapshotCustomMetricInputRT),
+ sort: WaffleSortOptionRT,
});
+export type WaffleSortOption = rt.TypeOf;
export type WaffleOptionsState = rt.TypeOf;
const encodeUrlState = (state: WaffleOptionsState) => {
return WaffleOptionsStateRT.encode(state);
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_view_state.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_view_state.ts
index 869560b2b8709..306c30228596f 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_view_state.ts
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_view_state.ts
@@ -28,6 +28,7 @@ export const useWaffleViewState = () => {
autoBounds,
accountId,
region,
+ sort,
setWaffleOptionsState,
} = useWaffleOptionsContext();
const { currentTime, isAutoReloading, setWaffleTimeState } = useWaffleTimeContext();
@@ -35,6 +36,7 @@ export const useWaffleViewState = () => {
const viewState: WaffleViewState = {
metric,
+ sort,
groupBy,
nodeType,
view,
@@ -59,6 +61,7 @@ export const useWaffleViewState = () => {
const onViewChange = useCallback(
(newState: WaffleViewState) => {
setWaffleOptionsState({
+ sort: newState.sort,
metric: newState.metric,
groupBy: newState.groupBy,
nodeType: newState.nodeType,
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/create_uptime_link.test.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/create_uptime_link.test.ts
index 5f760cf2f591e..fe9b2aebe27f7 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/create_uptime_link.test.ts
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/create_uptime_link.test.ts
@@ -21,6 +21,7 @@ const options: InfraWaffleMapOptions = {
formatTemplate: '{{value}}',
metric: { type: 'cpu' },
groupBy: [],
+ sort: { by: 'name', direction: 'asc' },
legend: {
type: 'gradient',
rules: [],
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/sort_nodes.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/sort_nodes.ts
new file mode 100644
index 0000000000000..e676fb3e09c2b
--- /dev/null
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/sort_nodes.ts
@@ -0,0 +1,23 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { sortBy, last } from 'lodash';
+import { SnapshotNode } from '../../../../../common/http_api/snapshot_api';
+import { WaffleSortOption } from '../hooks/use_waffle_options';
+
+const SORT_PATHS = {
+ name: (node: SnapshotNode) => last(node.path),
+ value: 'metric.value',
+};
+
+export const sortNodes = (sort: WaffleSortOption, nodes: SnapshotNode[]) => {
+ const sortPath = SORT_PATHS[sort.by];
+ const sortedNodes = sortBy(nodes, sortPath);
+ if (sort.direction === 'desc') {
+ return sortedNodes.reverse();
+ }
+ return sortedNodes;
+};