mirror of
https://github.com/Unleash/unleash.git
synced 2025-09-01 13:47:27 +02:00
feat: series query warning (#10413)
Co-authored-by: Thomas Heartman <thomas@getunleash.io>
This commit is contained in:
parent
15449e83d3
commit
8554eee37a
@ -1,13 +1,19 @@
|
|||||||
import {
|
import {
|
||||||
|
Alert,
|
||||||
Autocomplete,
|
Autocomplete,
|
||||||
Box,
|
|
||||||
Checkbox,
|
Checkbox,
|
||||||
Chip,
|
Chip,
|
||||||
FormControlLabel,
|
FormControlLabel,
|
||||||
|
styled,
|
||||||
TextField,
|
TextField,
|
||||||
|
Typography,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
|
|
||||||
|
const StyledSelectAllLabel = styled('span')(({ theme }) => ({
|
||||||
|
fontSize: theme.fontSizes.smallBody,
|
||||||
|
}));
|
||||||
|
|
||||||
type LabelFilterItemProps = {
|
type LabelFilterItemProps = {
|
||||||
labelKey: string;
|
labelKey: string;
|
||||||
options: string[];
|
options: string[];
|
||||||
@ -25,6 +31,7 @@ export const LabelFilterItem: FC<LabelFilterItemProps> = ({
|
|||||||
const isAllSelected = value.includes('*');
|
const isAllSelected = value.includes('*');
|
||||||
const autocompleteId = `autocomplete-${labelKey}`;
|
const autocompleteId = `autocomplete-${labelKey}`;
|
||||||
const selectAllId = `select-all-${labelKey}`;
|
const selectAllId = `select-all-${labelKey}`;
|
||||||
|
const isTruncated = options.length >= 1_000;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -46,16 +53,7 @@ export const LabelFilterItem: FC<LabelFilterItemProps> = ({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
label={
|
label={<StyledSelectAllLabel>Select all</StyledSelectAllLabel>}
|
||||||
<Box
|
|
||||||
component='span'
|
|
||||||
sx={(theme) => ({
|
|
||||||
fontSize: theme.fontSizes.smallBody,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
Select all
|
|
||||||
</Box>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
multiple
|
multiple
|
||||||
@ -66,37 +64,68 @@ export const LabelFilterItem: FC<LabelFilterItemProps> = ({
|
|||||||
onChange(newValues);
|
onChange(newValues);
|
||||||
}}
|
}}
|
||||||
disabled={isAllSelected}
|
disabled={isAllSelected}
|
||||||
renderTags={(value, getTagProps) =>
|
renderTags={(value, getTagProps) => {
|
||||||
value.map((option, index) => {
|
const overflowCount = 5;
|
||||||
const { key, ...chipProps } = getTagProps({
|
const displayedValues = value.slice(-overflowCount);
|
||||||
index,
|
const remainingCount = value.length - overflowCount;
|
||||||
});
|
|
||||||
return (
|
return (
|
||||||
<Chip
|
<>
|
||||||
{...chipProps}
|
{displayedValues.map((option, index) => {
|
||||||
key={key}
|
const { key, ...chipProps } = getTagProps({
|
||||||
label={option}
|
index,
|
||||||
size='small'
|
});
|
||||||
/>
|
return (
|
||||||
);
|
<Chip
|
||||||
})
|
{...chipProps}
|
||||||
}
|
key={key}
|
||||||
|
label={option}
|
||||||
|
size='small'
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{remainingCount > 0 ? (
|
||||||
|
<Typography
|
||||||
|
component='span'
|
||||||
|
sx={{ color: 'text.secondary' }}
|
||||||
|
>
|
||||||
|
{' '}
|
||||||
|
(+{remainingCount})
|
||||||
|
</Typography>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}}
|
||||||
renderInput={(params) => (
|
renderInput={(params) => (
|
||||||
<TextField
|
<>
|
||||||
{...params}
|
<TextField
|
||||||
label={labelKey}
|
{...params}
|
||||||
placeholder={
|
label={labelKey}
|
||||||
isAllSelected ? undefined : 'Select values…'
|
placeholder={
|
||||||
}
|
isAllSelected ? undefined : 'Select values…'
|
||||||
variant='outlined'
|
}
|
||||||
size='small'
|
variant='outlined'
|
||||||
inputProps={{
|
size='small'
|
||||||
...params.inputProps,
|
inputProps={{
|
||||||
'aria-describedby': isAllSelected
|
...params.inputProps,
|
||||||
? `${selectAllId}-description`
|
'aria-describedby': isAllSelected
|
||||||
: undefined,
|
? `${selectAllId}-description`
|
||||||
}}
|
: undefined,
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
{isTruncated && (
|
||||||
|
<Alert
|
||||||
|
severity='warning'
|
||||||
|
sx={(theme) => ({
|
||||||
|
padding: theme.spacing(1, 2),
|
||||||
|
marginTop: theme.spacing(1),
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
Maximum of 1000 values loaded due to
|
||||||
|
performance.
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
@ -72,30 +72,32 @@ export const LabelsFilter: FC<LabelsFilterProps> = ({
|
|||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{Object.entries(availableLabels).map(([labelKey, values]) => {
|
{Object.entries(availableLabels)
|
||||||
const currentSelection = selectedLabels[labelKey] || [];
|
.sort()
|
||||||
|
.map(([labelKey, values]) => {
|
||||||
|
const currentSelection = selectedLabels[labelKey] || [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
key={labelKey}
|
key={labelKey}
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<LabelFilterItem
|
<LabelFilterItem
|
||||||
labelKey={labelKey}
|
labelKey={labelKey}
|
||||||
options={values}
|
options={values}
|
||||||
value={currentSelection}
|
value={currentSelection}
|
||||||
onChange={(newValues) =>
|
onChange={(newValues) =>
|
||||||
handleLabelChange(labelKey, newValues)
|
handleLabelChange(labelKey, newValues)
|
||||||
}
|
}
|
||||||
handleAllToggle={handleAllToggle}
|
handleAllToggle={handleAllToggle}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
@ -202,6 +202,19 @@ export const ImpactMetricsChart: FC<ImpactMetricsChartProps> = ({
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
) : null}
|
) : null}
|
||||||
|
{isPreview && debug?.isTruncated ? (
|
||||||
|
<Box
|
||||||
|
sx={(theme) => ({
|
||||||
|
padding: theme.spacing(0, 2),
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Alert severity='warning'>
|
||||||
|
Showing only {timeSeriesData.length} series due to
|
||||||
|
performance. Please change filters for more accurate
|
||||||
|
results.
|
||||||
|
</Alert>
|
||||||
|
</Box>
|
||||||
|
) : null}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -10,6 +10,7 @@ export type ImpactMetricsSeries = {
|
|||||||
data: TimeSeriesData;
|
data: TimeSeriesData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO(impactMetrics): use OpenAPI types
|
||||||
export type ImpactMetricsResponse = {
|
export type ImpactMetricsResponse = {
|
||||||
start?: string;
|
start?: string;
|
||||||
end?: string;
|
end?: string;
|
||||||
@ -18,6 +19,7 @@ export type ImpactMetricsResponse = {
|
|||||||
labels?: ImpactMetricsLabels;
|
labels?: ImpactMetricsLabels;
|
||||||
debug?: {
|
debug?: {
|
||||||
query?: string;
|
query?: string;
|
||||||
|
isTruncated?: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user