Skip to content

Commit

Permalink
[8.16] [DataUsage][Serverless] Data usage charts enhancements (#196559)…
Browse files Browse the repository at this point in the history
… (#196876)

# Backport

This will backport the following commits from `main` to `8.16`:
- [[DataUsage][Serverless] Data usage charts enhancements
(#196559)](#196559)

<!--- Backport version: 9.4.3 -->

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

<!--BACKPORT
[{"author":{"name":"Ash","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-10-18T13:10:30Z","message":"[DataUsage][Serverless]
Data usage charts enhancements (#196559)\n\n## Summary\r\n\r\nfollow up
of:\r\n- /pull/195556\r\n\r\nAdds a lot of enhancements to
the datastream dropdown including:\r\n\r\n- [x] shows storage sizes on
the data stream dropdown\r\n- [x] preselects all data streams on the
first page load\r\n- [x] updates selected data streams to URL
params\r\n- [x] selects data streams based on URL load\r\n- [x] doesn't
allow deselecting all data streams\r\n- [x] cancels older API
requests\r\n\r\n### screen\r\n![Screenshot 2024-10-16 at 16
57\r\n43](https://github.com/user-attachments/assets/38db2d93-f531-4269-88ea-51b4926b6a72)\r\n\r\n###
clip\r\n\r\n![metrics-ux-16-10](https://github.com/user-attachments/assets/7913d1b6-31df-48e6-a3a9-f4dad0dc1b1e)\r\n\r\nrelated
PRs\r\n- /pull/193966 \r\n \r\n### Checklist\r\n- [x] Any
text added follows [EUI's
writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\r\nsentence case text and includes
[i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n-
[ ] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n- [ ] [Flaky
Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\r\nused on any tests changed\r\n- [x] Any UI touched in this PR does
not create any new axe failures\r\n(run axe in
browser:\r\n[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),\r\n[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))\r\n-
[x] This renders correctly on smaller devices using a
responsive\r\nlayout. (You can test this [in
your\r\nbrowser](https://www.browserstack.com/guide/responsive-testing-on-local-server))\r\n-
[x] This was checked for
[cross-browser\r\ncompatibility](https://www.elastic.co/support/matrix#matrix_browsers)\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<[email protected]>","sha":"13e19cb645e3e3b037ea40809dfbfdaf93529169","branchLabelMapping":{"^v9.0.0$":"main","^v8.17.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","v8.16.0","backport:version"],"title":"[DataUsage][Serverless]
Data usage charts
enhancements","number":196559,"url":"https://github.com/elastic/kibana/pull/196559","mergeCommit":{"message":"[DataUsage][Serverless]
Data usage charts enhancements (#196559)\n\n## Summary\r\n\r\nfollow up
of:\r\n- /pull/195556\r\n\r\nAdds a lot of enhancements to
the datastream dropdown including:\r\n\r\n- [x] shows storage sizes on
the data stream dropdown\r\n- [x] preselects all data streams on the
first page load\r\n- [x] updates selected data streams to URL
params\r\n- [x] selects data streams based on URL load\r\n- [x] doesn't
allow deselecting all data streams\r\n- [x] cancels older API
requests\r\n\r\n### screen\r\n![Screenshot 2024-10-16 at 16
57\r\n43](https://github.com/user-attachments/assets/38db2d93-f531-4269-88ea-51b4926b6a72)\r\n\r\n###
clip\r\n\r\n![metrics-ux-16-10](https://github.com/user-attachments/assets/7913d1b6-31df-48e6-a3a9-f4dad0dc1b1e)\r\n\r\nrelated
PRs\r\n- /pull/193966 \r\n \r\n### Checklist\r\n- [x] Any
text added follows [EUI's
writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\r\nsentence case text and includes
[i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n-
[ ] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n- [ ] [Flaky
Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\r\nused on any tests changed\r\n- [x] Any UI touched in this PR does
not create any new axe failures\r\n(run axe in
browser:\r\n[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),\r\n[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))\r\n-
[x] This renders correctly on smaller devices using a
responsive\r\nlayout. (You can test this [in
your\r\nbrowser](https://www.browserstack.com/guide/responsive-testing-on-local-server))\r\n-
[x] This was checked for
[cross-browser\r\ncompatibility](https://www.elastic.co/support/matrix#matrix_browsers)\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<[email protected]>","sha":"13e19cb645e3e3b037ea40809dfbfdaf93529169"}},"sourceBranch":"main","suggestedTargetBranches":["8.16"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/196559","number":196559,"mergeCommit":{"message":"[DataUsage][Serverless]
Data usage charts enhancements (#196559)\n\n## Summary\r\n\r\nfollow up
of:\r\n- /pull/195556\r\n\r\nAdds a lot of enhancements to
the datastream dropdown including:\r\n\r\n- [x] shows storage sizes on
the data stream dropdown\r\n- [x] preselects all data streams on the
first page load\r\n- [x] updates selected data streams to URL
params\r\n- [x] selects data streams based on URL load\r\n- [x] doesn't
allow deselecting all data streams\r\n- [x] cancels older API
requests\r\n\r\n### screen\r\n![Screenshot 2024-10-16 at 16
57\r\n43](https://github.com/user-attachments/assets/38db2d93-f531-4269-88ea-51b4926b6a72)\r\n\r\n###
clip\r\n\r\n![metrics-ux-16-10](https://github.com/user-attachments/assets/7913d1b6-31df-48e6-a3a9-f4dad0dc1b1e)\r\n\r\nrelated
PRs\r\n- /pull/193966 \r\n \r\n### Checklist\r\n- [x] Any
text added follows [EUI's
writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\r\nsentence case text and includes
[i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n-
[ ] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n- [ ] [Flaky
Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\r\nused on any tests changed\r\n- [x] Any UI touched in this PR does
not create any new axe failures\r\n(run axe in
browser:\r\n[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),\r\n[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))\r\n-
[x] This renders correctly on smaller devices using a
responsive\r\nlayout. (You can test this [in
your\r\nbrowser](https://www.browserstack.com/guide/responsive-testing-on-local-server))\r\n-
[x] This was checked for
[cross-browser\r\ncompatibility](https://www.elastic.co/support/matrix#matrix_browsers)\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<[email protected]>","sha":"13e19cb645e3e3b037ea40809dfbfdaf93529169"}},{"branch":"8.16","label":"v8.16.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Ash <[email protected]>
  • Loading branch information
kibanamachine and ashokaditya authored Oct 18, 2024
1 parent 23315ac commit 79e7c86
Show file tree
Hide file tree
Showing 14 changed files with 160 additions and 195 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/
import React, { useCallback, useMemo } from 'react';
import numeral from '@elastic/numeral';

import { EuiFlexItem, EuiPanel, EuiTitle, useEuiTheme } from '@elastic/eui';
import {
Chart,
Expand All @@ -20,6 +20,7 @@ import {
import { i18n } from '@kbn/i18n';
import { LegendAction } from './legend_action';
import { MetricTypes, MetricSeries } from '../../../common/rest_types';
import { formatBytes } from '../../utils/format_bytes';

// TODO: Remove this when we have a title for each metric type
type ChartKey = Extract<MetricTypes, 'ingest_rate' | 'storage_retained'>;
Expand Down Expand Up @@ -118,7 +119,3 @@ export const ChartPanel: React.FC<ChartPanelProps> = ({
</EuiFlexItem>
);
};

const formatBytes = (bytes: number) => {
return numeral(bytes).format('0.0 b');
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
* 2.0.
*/

import React, { useCallback, useEffect, memo, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { css } from '@emotion/react';
import { EuiFlexGroup, EuiFlexItem, EuiLoadingElastic, EuiCallOut } from '@elastic/eui';
import { EuiFlexGroup, EuiFlexItem, EuiLoadingElastic } from '@elastic/eui';
import { Charts } from './charts';
import { useBreadcrumbs } from '../../utils/use_breadcrumbs';
import { useKibanaContextForPlugin } from '../../utils/use_kibana';
Expand All @@ -16,31 +16,40 @@ import { useGetDataUsageMetrics } from '../../hooks/use_get_usage_metrics';
import { useDataUsageMetricsUrlParams } from '../hooks/use_charts_url_params';
import { DEFAULT_DATE_RANGE_OPTIONS, useDateRangePicker } from '../hooks/use_date_picker';
import { DEFAULT_METRIC_TYPES, UsageMetricsRequestBody } from '../../../common/rest_types';
import { ChartFilters } from './filters/charts_filters';
import { UX_LABELS } from '../translations';
import { ChartFilters, ChartFiltersProps } from './filters/charts_filters';
import { useGetDataUsageDataStreams } from '../../hooks/use_get_data_streams';

const EuiItemCss = css`
width: 100%;
`;

const FlexItemWithCss = memo(({ children }: { children: React.ReactNode }) => (
const FlexItemWithCss = ({ children }: { children: React.ReactNode }) => (
<EuiFlexItem css={EuiItemCss}>{children}</EuiFlexItem>
));
);

export const DataUsageMetrics = () => {
const {
services: { chrome, appParams },
} = useKibanaContextForPlugin();
useBreadcrumbs([{ text: PLUGIN_NAME }], appParams, chrome);

const {
metricTypes: metricTypesFromUrl,
dataStreams: dataStreamsFromUrl,
startDate: startDateFromUrl,
endDate: endDateFromUrl,
setUrlMetricTypesFilter,
setUrlDataStreamsFilter,
setUrlDateRangeFilter,
} = useDataUsageMetricsUrlParams();

const { data: dataStreams, isFetching: isFetchingDataStreams } = useGetDataUsageDataStreams({
selectedDataStreams: dataStreamsFromUrl,
options: {
enabled: true,
},
});

const [metricsFilters, setMetricsFilters] = useState<UsageMetricsRequestBody>({
metricTypes: [...DEFAULT_METRIC_TYPES],
dataStreams: [],
Expand All @@ -52,15 +61,22 @@ export const DataUsageMetrics = () => {
if (!metricTypesFromUrl) {
setUrlMetricTypesFilter(metricsFilters.metricTypes.join(','));
}
if (!dataStreamsFromUrl && dataStreams) {
setUrlDataStreamsFilter(dataStreams.map((ds) => ds.name).join(','));
}
if (!startDateFromUrl || !endDateFromUrl) {
setUrlDateRangeFilter({ startDate: metricsFilters.from, endDate: metricsFilters.to });
}
}, [
dataStreams,
dataStreamsFromUrl,
endDateFromUrl,
metricTypesFromUrl,
metricsFilters.dataStreams,
metricsFilters.from,
metricsFilters.metricTypes,
metricsFilters.to,
setUrlDataStreamsFilter,
setUrlDateRangeFilter,
setUrlMetricTypesFilter,
startDateFromUrl,
Expand All @@ -77,7 +93,6 @@ export const DataUsageMetrics = () => {
const { dateRangePickerState, onRefreshChange, onTimeChange } = useDateRangePicker();

const {
error,
data,
isFetching,
isFetched,
Expand All @@ -90,6 +105,7 @@ export const DataUsageMetrics = () => {
},
{
retry: false,
enabled: !!metricsFilters.dataStreams.length,
}
);

Expand All @@ -111,33 +127,51 @@ export const DataUsageMetrics = () => {
[setMetricsFilters]
);

useBreadcrumbs([{ text: PLUGIN_NAME }], appParams, chrome);
const filterOptions: ChartFiltersProps['filterOptions'] = useMemo(() => {
const dataStreamsOptions = dataStreams?.reduce<Record<string, number>>((acc, ds) => {
acc[ds.name] = ds.storageSizeBytes;
return acc;
}, {});

return {
dataStreams: {
filterName: 'dataStreams',
options: dataStreamsOptions ? Object.keys(dataStreamsOptions) : metricsFilters.dataStreams,
appendOptions: dataStreamsOptions,
selectedOptions: metricsFilters.dataStreams,
onChangeFilterOptions: onChangeDataStreamsFilter,
isFilterLoading: isFetchingDataStreams,
},
metricTypes: {
filterName: 'metricTypes',
options: metricsFilters.metricTypes,
onChangeFilterOptions: onChangeMetricTypesFilter,
},
};
}, [
dataStreams,
isFetchingDataStreams,
metricsFilters.dataStreams,
metricsFilters.metricTypes,
onChangeDataStreamsFilter,
onChangeMetricTypesFilter,
]);

return (
<EuiFlexGroup alignItems="flexStart" direction="column">
<FlexItemWithCss>
<ChartFilters
dateRangePickerState={dateRangePickerState}
isDataLoading={isFetching}
isDataLoading={isFetchingDataStreams}
onClick={refetchDataUsageMetrics}
onRefresh={onRefresh}
onRefreshChange={onRefreshChange}
onTimeChange={onTimeChange}
onChangeDataStreamsFilter={onChangeDataStreamsFilter}
onChangeMetricTypesFilter={onChangeMetricTypesFilter}
filterOptions={filterOptions}
showMetricsTypesFilter={false}
/>
</FlexItemWithCss>
{!isFetching && error?.message && (
<FlexItemWithCss>
<EuiCallOut
size="s"
title={UX_LABELS.noDataStreamsSelected}
iconType="iInCircle"
color="warning"
/>
</FlexItemWithCss>
)}

<FlexItemWithCss>
{isFetched && data?.metrics ? (
<Charts data={data} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,14 @@

import { orderBy } from 'lodash/fp';
import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiPopoverTitle, EuiSelectable } from '@elastic/eui';
import { EuiPopoverTitle, EuiSelectable } from '@elastic/eui';

import { useTestIdGenerator } from '../../../hooks/use_test_id_generator';
import {
METRIC_TYPE_API_VALUES_TO_UI_OPTIONS_MAP,
type MetricTypes,
} from '../../../../common/rest_types';

import { ClearAllButton } from './clear_all_button';
import { UX_LABELS } from '../../translations';
import { ChartsFilterPopover } from './charts_filter_popover';
import { FilterItems, FilterName, useChartsFilter } from '../../hooks';
Expand All @@ -27,20 +26,34 @@ const getSearchPlaceholder = (filterName: FilterName) => {
return UX_LABELS.filterSearchPlaceholder('metric types');
};

export const ChartsFilter = memo(
export interface ChartsFilterProps {
filterOptions: {
filterName: FilterName;
options: string[];
appendOptions?: Record<string, number>;
selectedOptions?: string[];
onChangeFilterOptions: (selectedOptions: string[]) => void;
isFilterLoading?: boolean;
};
'data-test-subj'?: string;
}

export const ChartsFilter = memo<ChartsFilterProps>(
({
filterName,
onChangeFilterOptions,
filterOptions: {
filterName,
options,
appendOptions,
selectedOptions,
onChangeFilterOptions,
isFilterLoading = false,
},
'data-test-subj': dataTestSubj,
}: {
filterName: FilterName;
onChangeFilterOptions?: (selectedOptions: string[]) => void;
'data-test-subj'?: string;
}) => {
const getTestId = useTestIdGenerator(dataTestSubj);

const isMetricsFilter = filterName === 'metricTypes';
const isDataStreamsFilter = filterName === 'dataStreams';

// popover states and handlers
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
const onPopoverButtonClick = useCallback(() => {
Expand All @@ -50,11 +63,8 @@ export const ChartsFilter = memo(
setIsPopoverOpen(false);
}, [setIsPopoverOpen]);

// search string state
const [searchString, setSearchString] = useState('');
const {
areDataStreamsSelectedOnMount,
isLoading,
items,
setItems,
hasActiveFilters,
Expand All @@ -64,17 +74,18 @@ export const ChartsFilter = memo(
setUrlDataStreamsFilter,
setUrlMetricTypesFilter,
} = useChartsFilter({
filterName,
searchString,
filterOptions: {
filterName,
options,
appendOptions,
selectedOptions,
onChangeFilterOptions,
isFilterLoading,
},
});

// track popover state to pin selected options
const wasPopoverOpen = useRef(isPopoverOpen);
useEffect(() => {
return () => {
wasPopoverOpen.current = isPopoverOpen;
};
}, [isPopoverOpen, wasPopoverOpen]);

// compute if selected dataStreams should be pinned
const shouldPinSelectedDataStreams = useCallback(
Expand Down Expand Up @@ -104,8 +115,16 @@ export const ChartsFilter = memo(

const onOptionsChange = useCallback(
(newOptions: FilterItems) => {
const optionItemsToSet = newOptions.map((option) => option);
const currChecks = optionItemsToSet.filter((option) => option.checked === 'on');

// don't update filter state if trying to uncheck all options
if (currChecks.length < 1) {
return;
}

// update filter UI options state
setItems(newOptions.map((option) => option));
setItems(optionItemsToSet);

// compute a selected list of options
const selectedItems = newOptions.reduce<string[]>((acc, curr) => {
Expand All @@ -129,10 +148,7 @@ export const ChartsFilter = memo(
shouldPinSelectedDataStreams(false);
setAreDataStreamsSelectedOnMount(false);

// update overall query state
if (typeof onChangeFilterOptions !== 'undefined') {
onChangeFilterOptions(selectedItems);
}
onChangeFilterOptions(selectedItems);
},
[
setItems,
Expand All @@ -146,35 +162,11 @@ export const ChartsFilter = memo(
]
);

// clear all selected options
const onClearAll = useCallback(() => {
// update filter UI options state
setItems(
items.map((option) => {
option.checked = undefined;
return option;
})
);

// update URL params based on filter on page
if (isMetricsFilter) {
setUrlMetricTypesFilter('');
} else if (isDataStreamsFilter) {
setUrlDataStreamsFilter('');
}

if (typeof onChangeFilterOptions !== 'undefined') {
onChangeFilterOptions([]);
}
}, [
setItems,
items,
isMetricsFilter,
isDataStreamsFilter,
onChangeFilterOptions,
setUrlMetricTypesFilter,
setUrlDataStreamsFilter,
]);
useEffect(() => {
return () => {
wasPopoverOpen.current = isPopoverOpen;
};
}, [isPopoverOpen, wasPopoverOpen]);

return (
<ChartsFilterPopover
Expand All @@ -190,14 +182,13 @@ export const ChartsFilter = memo(
<EuiSelectable
aria-label={`${filterName}`}
emptyMessage={UX_LABELS.filterEmptyMessage(filterName)}
isLoading={isLoading}
isLoading={isFilterLoading}
onChange={onOptionsChange}
options={sortedHostsFilterOptions}
searchable={isSearchable ? true : undefined}
searchProps={{
placeholder: getSearchPlaceholder(filterName),
compressed: true,
onChange: (searchValue) => setSearchString(searchValue.trim()),
}}
>
{(list, search) => {
Expand All @@ -215,17 +206,6 @@ export const ChartsFilter = memo(
</EuiPopoverTitle>
)}
{list}
{!isMetricsFilter && (
<EuiFlexGroup>
<EuiFlexItem>
<ClearAllButton
data-test-subj={getTestId(`${filterName}-filter-clearAllButton`)}
isDisabled={!hasActiveFilters}
onClick={onClearAll}
/>
</EuiFlexItem>
</EuiFlexGroup>
)}
</div>
);
}}
Expand Down
Loading

0 comments on commit 79e7c86

Please sign in to comment.