From 29dc6c746d09a08cf473bc38e969c6d5914b216b Mon Sep 17 00:00:00 2001 From: Tymoteusz Czech <2625371+Tymek@users.noreply.github.com> Date: Fri, 5 Sep 2025 09:26:23 +0200 Subject: [PATCH] feat: split impact metrics label filter into sections (#10623) --- .../LabelFilterItem/LabelFilterItem.tsx | 50 +++++---- .../LabelFilterSection/LabelFilterSection.tsx | 105 ++++++++++++++++++ .../LabelFilter/LabelsFilter.tsx | 91 ++++++--------- 3 files changed, 163 insertions(+), 83 deletions(-) rename frontend/src/component/impact-metrics/ChartConfigModal/LabelFilter/{ => LabelFilterSection}/LabelFilterItem/LabelFilterItem.tsx (77%) create mode 100644 frontend/src/component/impact-metrics/ChartConfigModal/LabelFilter/LabelFilterSection/LabelFilterSection.tsx diff --git a/frontend/src/component/impact-metrics/ChartConfigModal/LabelFilter/LabelFilterItem/LabelFilterItem.tsx b/frontend/src/component/impact-metrics/ChartConfigModal/LabelFilter/LabelFilterSection/LabelFilterItem/LabelFilterItem.tsx similarity index 77% rename from frontend/src/component/impact-metrics/ChartConfigModal/LabelFilter/LabelFilterItem/LabelFilterItem.tsx rename to frontend/src/component/impact-metrics/ChartConfigModal/LabelFilter/LabelFilterSection/LabelFilterItem/LabelFilterItem.tsx index 491041bb31..af12063ccb 100644 --- a/frontend/src/component/impact-metrics/ChartConfigModal/LabelFilter/LabelFilterItem/LabelFilterItem.tsx +++ b/frontend/src/component/impact-metrics/ChartConfigModal/LabelFilter/LabelFilterSection/LabelFilterItem/LabelFilterItem.tsx @@ -48,29 +48,33 @@ export const LabelFilterItem: FC = ({ newValues.filter((v) => v !== METRIC_LABELS_SELECT_ALL), ); }} - renderOption={(props, option, { selected }) => ( -
  • - - {option === METRIC_LABELS_SELECT_ALL ? ( - - Select all - - ) : ( - option - )} -
  • - )} + renderOption={(props, option, { selected }) => { + const { key, ...listItemProps } = props as any; + + return ( +
  • + + {option === METRIC_LABELS_SELECT_ALL ? ( + + Select all + + ) : ( + option + )} +
  • + ); + }} renderTags={(value, getTagProps) => { const overflowCount = 5; const displayedValues = value.slice(-overflowCount); diff --git a/frontend/src/component/impact-metrics/ChartConfigModal/LabelFilter/LabelFilterSection/LabelFilterSection.tsx b/frontend/src/component/impact-metrics/ChartConfigModal/LabelFilter/LabelFilterSection/LabelFilterSection.tsx new file mode 100644 index 0000000000..84ec4ed4d1 --- /dev/null +++ b/frontend/src/component/impact-metrics/ChartConfigModal/LabelFilter/LabelFilterSection/LabelFilterSection.tsx @@ -0,0 +1,105 @@ +import type { FC } from 'react'; +import { Box, Typography, Chip, styled } from '@mui/material'; +import { LabelFilterItem } from './LabelFilterItem/LabelFilterItem.tsx'; + +const StyledContainer = styled(Box)(({ theme }) => ({ + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(1), + width: '100%', +})); + +const StyledHeader = styled(Box)(({ theme }) => ({ + width: '100%', + display: 'flex', + alignItems: 'center', + gap: theme.spacing(1), +})); + +const StyledGrid = styled(Box)(({ theme }) => ({ + display: 'grid', + gridTemplateColumns: 'repeat(auto-fill, minmax(250px, 1fr))', + gap: theme.spacing(2), + flexGrow: 1, +})); + +const StyledGridItem = styled(Box)({ + display: 'flex', + flexDirection: 'column', + flexGrow: 1, +}); + +const StyledTitle = styled(Typography)({ + lineHeight: 1.5, + height: '24px', +}); + +const StyledClearAll = styled(Chip)(({ theme }) => ({ + position: 'relative', + height: '20px', + margin: theme.spacing(-1, 0), +})); + +export const LabelFilterSection: FC<{ + title: string; + labels: [string, string[]][]; + labelSelectors: Record; + onLabelChange: (labelKey: string, values: string[]) => void; + onAllToggle: (labelKey: string, checked: boolean) => void; + onChange: (labels: Record) => void; +}> = ({ + title, + labels, + labelSelectors, + onLabelChange, + onAllToggle, + onChange, +}) => { + const labelKeys = labels.map(([key]) => key); + + const hasSelections = labelKeys.some((k) => labelSelectors[k]); + + const clearSection = () => { + const newLabels: Record = {}; + Object.entries(labelSelectors).forEach(([key, val]) => { + if (!labelKeys.includes(key)) { + newLabels[key] = val; + } + }); + onChange(newLabels); + }; + + return ( + + + {title} + {hasSelections && ( + + )} + + + {labels.map(([labelKey, values]) => { + const currentSelection = labelSelectors[labelKey] || []; + return ( + + + onLabelChange(labelKey, newValues) + } + handleAllToggle={onAllToggle} + /> + + ); + })} + + + ); +}; diff --git a/frontend/src/component/impact-metrics/ChartConfigModal/LabelFilter/LabelsFilter.tsx b/frontend/src/component/impact-metrics/ChartConfigModal/LabelFilter/LabelsFilter.tsx index 6d52087152..eb19179bbb 100644 --- a/frontend/src/component/impact-metrics/ChartConfigModal/LabelFilter/LabelsFilter.tsx +++ b/frontend/src/component/impact-metrics/ChartConfigModal/LabelFilter/LabelsFilter.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react'; -import { Box, Typography, Chip } from '@mui/material'; +import { Box } from '@mui/material'; import type { ImpactMetricsLabels } from 'hooks/api/getters/useImpactMetricsData/useImpactMetricsData'; -import { LabelFilterItem } from './LabelFilterItem/LabelFilterItem.tsx'; +import { LabelFilterSection } from './LabelFilterSection/LabelFilterSection.tsx'; export type LabelsFilterProps = { labelSelectors: Record; @@ -9,6 +9,8 @@ export type LabelsFilterProps = { availableLabels: ImpactMetricsLabels; }; +const STATIC_LABELS = ['environment', 'appName', 'origin']; + export const LabelsFilter: FC = ({ labelSelectors, onChange, @@ -34,71 +36,40 @@ export const LabelsFilter: FC = ({ onChange(newLabels); }; - const clearAllLabels = () => { - onChange({}); - }; - if (!availableLabels || Object.keys(availableLabels).length === 0) { return null; } + const staticLabels = Object.entries(availableLabels) + .filter(([key]) => STATIC_LABELS.includes(key)) + .sort(); + const dynamicLabels = Object.entries(availableLabels) + .filter(([key]) => !STATIC_LABELS.includes(key)) + .sort(); + return ( - - Filter by labels - {Object.keys(labelSelectors).length > 0 && ( - - )} - + {staticLabels.length > 0 && ( + + )} - - {Object.entries(availableLabels) - .sort() - .map(([labelKey, values]) => { - const currentSelection = labelSelectors[labelKey] || []; - - return ( - - - handleLabelChange(labelKey, newValues) - } - handleAllToggle={handleAllToggle} - /> - - ); - })} - + {dynamicLabels.length > 0 && ( + + )} ); };