1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-06-09 01:17:06 +02:00

fix: make no data look like no data (#8396)

This PR adjusts what we show for no data to make it clearer that we
actually have no data.

It also makes the graph slightly smaller.


![image](https://github.com/user-attachments/assets/12a009a6-a24d-4821-bb03-c408417011a7)
This commit is contained in:
Thomas Heartman 2024-10-09 13:16:57 +02:00 committed by GitHub
parent eb2d1fb905
commit ad35fa9a48
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 48 additions and 48 deletions

View File

@ -11,7 +11,7 @@ import annotationPlugin from 'chartjs-plugin-annotation';
import { Bar } from 'react-chartjs-2'; import { Bar } from 'react-chartjs-2';
import useTheme from '@mui/material/styles/useTheme'; import useTheme from '@mui/material/styles/useTheme';
import { type FC, useEffect, useMemo, useState } from 'react'; import { type FC, useEffect, useMemo, useState } from 'react';
import { Box, styled, Typography } from '@mui/material'; import { Box, styled } from '@mui/material';
import { FeatureMetricsHours } from '../feature/FeatureView/FeatureMetrics/FeatureMetricsHours/FeatureMetricsHours'; import { FeatureMetricsHours } from '../feature/FeatureView/FeatureMetrics/FeatureMetricsHours/FeatureMetricsHours';
import GeneralSelect from '../common/GeneralSelect/GeneralSelect'; import GeneralSelect from '../common/GeneralSelect/GeneralSelect';
import { useFeatureMetricsRaw } from 'hooks/api/getters/useFeatureMetricsRaw/useFeatureMetricsRaw'; import { useFeatureMetricsRaw } from 'hooks/api/getters/useFeatureMetricsRaw/useFeatureMetricsRaw';
@ -24,39 +24,24 @@ import {
} from './createChartOptions'; } from './createChartOptions';
import { useFeature } from 'hooks/api/getters/useFeature/useFeature'; import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
const defaultYes = [ const defaultYes = [0, 14, 28, 21, 33, 31, 31, 22, 26, 37, 31, 14, 21, 14, 0];
45_000_000, 28_000_000, 28_000_000, 25_000_000, 50_000_000, 27_000_000,
26_000_000, 50_000_000, 32_000_000, 12_000_000, 13_000_000, 31_000_000,
12_000_000, 47_000_000, 29_000_000, 46_000_000, 45_000_000, 28_000_000,
28_000_000, 25_000_000, 50_000_000, 27_000_000, 26_000_000, 50_000_000,
32_000_000, 12_000_000, 13_000_000, 31_000_000, 12_000_000, 47_000_000,
];
const defaultNo = [
5_000_000, 8_000_000, 3_000_000, 2_000_000, 2_000_000, 5_000_000, 9_000_000,
3_000_000, 7_000_000, 2_000_000, 5_000_000, 8_000_000, 3_000_000, 2_000_000,
2_000_000, 5_000_000, 1_000_000, 3_000_000, 12_000_000, 2_000_000,
1_000_000, 1_000_000, 3_000_000, 2_000_000, 2_000_000, 5_000_000, 1_000_000,
3_000_000, 8_000_000, 2_000_000,
];
const placeholderData = { const placeholderData = {
labels: Array.from({ length: 30 }, (_, i) => i + 1), labels: Array.from({ length: 15 }, (_, i) => i + 1),
datasets: [ datasets: [
{ {
data: defaultYes, data: defaultYes,
label: 'yes', backgroundColor: '#EAEAED',
backgroundColor: '#BEBEBE', hoverBackgroundColor: '#EAEAED',
hoverBackgroundColor: '#BEBEBE', label: 'No metrics for this feature flag in the selected environment and time period',
},
{
data: defaultNo,
label: 'no',
backgroundColor: '#9A9A9A',
hoverBackgroundColor: '#9A9A9A',
}, },
], ],
}; };
const ChartWrapper = styled('div')({
width: '90%',
});
export const PlaceholderFlagMetricsChart = () => { export const PlaceholderFlagMetricsChart = () => {
const theme = useTheme(); const theme = useTheme();
@ -65,14 +50,13 @@ export const PlaceholderFlagMetricsChart = () => {
}, [theme]); }, [theme]);
return ( return (
<> <ChartWrapper>
<Typography sx={{ mb: 4 }}>No feature flag metrics data</Typography>
<Bar <Bar
data={placeholderData} data={placeholderData}
options={options} options={options}
aria-label='A placeholder bar chart with a single feature flag exposure metrics' aria-label='A placeholder bar chart with a single feature flag exposure metrics'
/> />
</> </ChartWrapper>
); );
}; };
@ -160,7 +144,14 @@ const MetricsSelectors = styled(Box)(({ theme }) => ({
display: 'flex', display: 'flex',
justifyContent: 'flex-end', justifyContent: 'flex-end',
gap: theme.spacing(2), gap: theme.spacing(2),
mb: theme.spacing(6), width: '100%',
}));
const ChartContainer = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(3),
alignItems: 'center',
})); }));
export const FlagMetricsChart: FC<{ export const FlagMetricsChart: FC<{
@ -176,7 +167,7 @@ export const FlagMetricsChart: FC<{
const noData = data.datasets[0].data.length === 0; const noData = data.datasets[0].data.length === 0;
return ( return (
<> <ChartContainer>
<MetricsSelectors> <MetricsSelectors>
{environment ? ( {environment ? (
<EnvironmentSelect <EnvironmentSelect
@ -194,13 +185,15 @@ export const FlagMetricsChart: FC<{
{noData ? ( {noData ? (
<PlaceholderFlagMetricsChart /> <PlaceholderFlagMetricsChart />
) : ( ) : (
<ChartWrapper>
<Bar <Bar
data={data} data={data}
options={options} options={options}
aria-label='A bar chart with a single feature flag exposure metrics' aria-label='A bar chart with a single feature flag exposure metrics'
/> />
</ChartWrapper>
)} )}
</> </ChartContainer>
); );
}; };

View File

@ -145,7 +145,6 @@ test('Render personal dashboard for a long running project', async () => {
await screen.findByText('13 feature flags'); // stale flags await screen.findByText('13 feature flags'); // stale flags
await screen.findByText('14 feature flags'); // potentially stale flags await screen.findByText('14 feature flags'); // potentially stale flags
await screen.findByText('myFlag'); await screen.findByText('myFlag');
await screen.findByText('No feature flag metrics data');
await screen.findByText('production'); await screen.findByText('production');
await screen.findByText('Last 48 hours'); await screen.findByText('Last 48 hours');
}); });
@ -166,6 +165,4 @@ test('Render personal dashboard for a new project', async () => {
await screen.findByText( await screen.findByText(
'You have not created or favorited any feature flags. Once you do, they will show up here.', 'You have not created or favorited any feature flags. Once you do, they will show up here.',
); );
await screen.findByText('No feature flag metrics data');
}); });

View File

@ -1,6 +1,5 @@
import type { Theme } from '@mui/material/styles/createTheme'; import type { Theme } from '@mui/material/styles/createTheme';
import type { ChartOptions } from 'chart.js'; import type { ChartOptions } from 'chart.js';
import { formatTickValue } from '../common/Chart/formatTickValue';
import type { ILocationSettings } from '../../hooks/useLocationSettings'; import type { ILocationSettings } from '../../hooks/useLocationSettings';
import type { IPoint } from '../feature/FeatureView/FeatureMetrics/FeatureMetricsChart/createChartData'; import type { IPoint } from '../feature/FeatureView/FeatureMetrics/FeatureMetricsChart/createChartData';
import { import {
@ -24,16 +23,17 @@ export const createPlaceholderBarChartOptions = (
): ChartOptions<'bar'> => ({ ): ChartOptions<'bar'> => ({
plugins: { plugins: {
legend: { legend: {
position: 'bottom', position: 'top',
labels: { labels: {
color: theme.palette.text.primary, color: theme.palette.text.primary,
pointStyle: 'circle',
usePointStyle: true, usePointStyle: true,
boxHeight: 6, pointStyle: 'none',
boxHeight: 0,
padding: 15, padding: 15,
boxPadding: 5, boxPadding: 5,
}, },
}, },
tooltip: { tooltip: {
enabled: false, enabled: false,
}, },
@ -43,7 +43,7 @@ export const createPlaceholderBarChartOptions = (
x: { x: {
stacked: true, stacked: true,
ticks: { ticks: {
color: theme.palette.text.secondary, display: false,
}, },
grid: { grid: {
display: false, display: false,
@ -52,9 +52,8 @@ export const createPlaceholderBarChartOptions = (
y: { y: {
stacked: true, stacked: true,
ticks: { ticks: {
color: theme.palette.text.secondary,
maxTicksLimit: 5, maxTicksLimit: 5,
callback: formatTickValue, display: false,
}, },
grid: { grid: {
drawBorder: false, drawBorder: false,
@ -77,11 +76,22 @@ export const createBarChartOptions = (
hoursBack: number, hoursBack: number,
locationSettings: ILocationSettings, locationSettings: ILocationSettings,
): ChartOptions<'bar'> => { ): ChartOptions<'bar'> => {
const { plugins, responsive, elements, interaction, scales } = const { responsive, elements, interaction, scales } =
createPlaceholderBarChartOptions(theme); createPlaceholderBarChartOptions(theme);
return { return {
plugins: { plugins: {
legend: plugins?.legend, legend: {
position: 'bottom',
labels: {
color: theme.palette.text.primary,
pointStyle: 'circle',
usePointStyle: true,
boxHeight: 6,
padding: 15,
boxPadding: 5,
},
},
// required to avoid the highlight plugin highlighting empty annotation // required to avoid the highlight plugin highlighting empty annotation
annotation: { annotation: {
clip: false, clip: false,