mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-12-13 00:22:23 +00:00
fix: show all the category for cardinality distribution graph (#24098)
* fix: show all the category for cardinality distribution graph * feat: enhance CardinalityDistributionChart with category selection and custom Y-axis ticks * fix: update cursor fill color in visualisation charts for better visibility --------- Co-authored-by: Harsh Vador <58542468+harsh-vador@users.noreply.github.com>
This commit is contained in:
parent
4da984cd56
commit
dd8b6481b1
@ -13,13 +13,13 @@
|
|||||||
|
|
||||||
import { Box, Card, Divider, Typography, useTheme } from '@mui/material';
|
import { Box, Card, Divider, Typography, useTheme } from '@mui/material';
|
||||||
import { isUndefined } from 'lodash';
|
import { isUndefined } from 'lodash';
|
||||||
import { useMemo } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
Bar,
|
Bar,
|
||||||
BarChart,
|
BarChart,
|
||||||
CartesianGrid,
|
CartesianGrid,
|
||||||
Legend,
|
Cell,
|
||||||
ResponsiveContainer,
|
ResponsiveContainer,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
TooltipProps,
|
TooltipProps,
|
||||||
@ -52,6 +52,7 @@ const CardinalityDistributionChart = ({
|
|||||||
}: CardinalityDistributionChartProps) => {
|
}: CardinalityDistributionChartProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
|
||||||
|
|
||||||
const firstDayAllUnique =
|
const firstDayAllUnique =
|
||||||
data.firstDayData?.cardinalityDistribution?.allValuesUnique ?? false;
|
data.firstDayData?.cardinalityDistribution?.allValuesUnique ?? false;
|
||||||
@ -170,6 +171,52 @@ const CardinalityDistributionChart = ({
|
|||||||
'message.all-values-unique-no-distribution-available'
|
'message.all-values-unique-no-distribution-available'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleCategoryClick = (categoryName: string) => {
|
||||||
|
setSelectedCategory((prev) =>
|
||||||
|
prev === categoryName ? null : categoryName
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const CustomYAxisTick = (props: {
|
||||||
|
x?: number;
|
||||||
|
y?: number;
|
||||||
|
payload?: { value: string };
|
||||||
|
}) => {
|
||||||
|
const { x, y, payload } = props;
|
||||||
|
if (!payload) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const categoryName = payload.value;
|
||||||
|
const isSelected = selectedCategory === categoryName;
|
||||||
|
const isHighlighted = selectedCategory && selectedCategory !== categoryName;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<g transform={`translate(${x},${y})`}>
|
||||||
|
<text
|
||||||
|
cursor="pointer"
|
||||||
|
dy={4}
|
||||||
|
fill={
|
||||||
|
isSelected
|
||||||
|
? theme.palette.primary.main
|
||||||
|
: isHighlighted
|
||||||
|
? theme.palette.grey[400]
|
||||||
|
: theme.palette.grey[700]
|
||||||
|
}
|
||||||
|
fontSize={12}
|
||||||
|
fontWeight={isSelected ? 600 : 400}
|
||||||
|
opacity={isHighlighted ? 0.5 : 1}
|
||||||
|
textAnchor="end"
|
||||||
|
x={-8}
|
||||||
|
onClick={() => handleCategoryClick(categoryName)}>
|
||||||
|
{categoryName.length > 15
|
||||||
|
? `${categoryName.slice(0, 15)}...`
|
||||||
|
: categoryName}
|
||||||
|
</text>
|
||||||
|
</g>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
data-testid="chart-container"
|
data-testid="chart-container"
|
||||||
@ -203,6 +250,8 @@ const CardinalityDistributionChart = ({
|
|||||||
'MMM dd, yyyy'
|
'MMM dd, yyyy'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const containerHeight = Math.max(350, graphData.length * 30);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
key={key}
|
key={key}
|
||||||
@ -236,16 +285,21 @@ const CardinalityDistributionChart = ({
|
|||||||
})}: ${cardinalityData.categories?.length || 0}`}
|
})}: ${cardinalityData.categories?.length || 0}`}
|
||||||
</DataPill>
|
</DataPill>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ flex: 1, minHeight: 350 }}>
|
<Box
|
||||||
|
sx={{
|
||||||
|
flex: 1,
|
||||||
|
minHeight: 350,
|
||||||
|
overflowX: 'hidden',
|
||||||
|
}}>
|
||||||
<ResponsiveContainer
|
<ResponsiveContainer
|
||||||
debounce={200}
|
debounce={200}
|
||||||
|
height={containerHeight}
|
||||||
id={`${key}-cardinality`}
|
id={`${key}-cardinality`}
|
||||||
minHeight={300}>
|
width="100%">
|
||||||
<BarChart
|
<BarChart
|
||||||
className="w-full"
|
className="w-full"
|
||||||
data={graphData}
|
data={graphData}
|
||||||
layout="vertical"
|
layout="vertical">
|
||||||
margin={{ left: 16 }}>
|
|
||||||
<CartesianGrid
|
<CartesianGrid
|
||||||
horizontal={renderHorizontalGridLine}
|
horizontal={renderHorizontalGridLine}
|
||||||
stroke={GRAPH_BACKGROUND_COLOR}
|
stroke={GRAPH_BACKGROUND_COLOR}
|
||||||
@ -267,29 +321,49 @@ const CardinalityDistributionChart = ({
|
|||||||
axisLine={false}
|
axisLine={false}
|
||||||
dataKey="name"
|
dataKey="name"
|
||||||
padding={{ top: 16, bottom: 16 }}
|
padding={{ top: 16, bottom: 16 }}
|
||||||
tick={{ fontSize: 12 }}
|
tick={<CustomYAxisTick />}
|
||||||
tickFormatter={(value: string) =>
|
|
||||||
value?.length > 15
|
|
||||||
? `${value.slice(0, 15)}...`
|
|
||||||
: value
|
|
||||||
}
|
|
||||||
tickLine={false}
|
tickLine={false}
|
||||||
type="category"
|
type="category"
|
||||||
width={120}
|
width={120}
|
||||||
/>
|
/>
|
||||||
<Legend />
|
|
||||||
<Tooltip
|
<Tooltip
|
||||||
content={renderTooltip}
|
content={renderTooltip}
|
||||||
cursor={{
|
cursor={{
|
||||||
|
fill: theme.palette.grey[100],
|
||||||
stroke: theme.palette.grey[200],
|
stroke: theme.palette.grey[200],
|
||||||
strokeDasharray: '3 3',
|
strokeDasharray: '3 3',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Bar
|
<Bar
|
||||||
|
barSize={22}
|
||||||
dataKey="percentage"
|
dataKey="percentage"
|
||||||
fill={CHART_BLUE_1}
|
radius={[0, 8, 8, 0]}>
|
||||||
radius={[0, 8, 8, 0]}
|
{graphData.map((entry) => {
|
||||||
/>
|
const isSelected =
|
||||||
|
selectedCategory === entry.name;
|
||||||
|
const isHighlighted =
|
||||||
|
selectedCategory &&
|
||||||
|
selectedCategory !== entry.name;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Cell
|
||||||
|
cursor="pointer"
|
||||||
|
fill={
|
||||||
|
isSelected
|
||||||
|
? theme.palette.primary.main
|
||||||
|
: isHighlighted
|
||||||
|
? theme.palette.grey[300]
|
||||||
|
: CHART_BLUE_1
|
||||||
|
}
|
||||||
|
key={`cell-${entry.name}`}
|
||||||
|
opacity={isHighlighted ? 0.3 : 1}
|
||||||
|
onClick={() =>
|
||||||
|
handleCategoryClick(entry.name)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Bar>
|
||||||
</BarChart>
|
</BarChart>
|
||||||
</ResponsiveContainer>
|
</ResponsiveContainer>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@ -117,6 +117,7 @@ const CustomBarChart = ({
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
cursor={{
|
cursor={{
|
||||||
|
fill: theme.palette.grey[100],
|
||||||
stroke: theme.palette.grey[200],
|
stroke: theme.palette.grey[200],
|
||||||
strokeDasharray: '3 3',
|
strokeDasharray: '3 3',
|
||||||
}}
|
}}
|
||||||
|
|||||||
@ -180,6 +180,7 @@ const DataDistributionHistogram = ({
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
cursor={{
|
cursor={{
|
||||||
|
fill: theme.palette.grey[100],
|
||||||
stroke: theme.palette.grey[200],
|
stroke: theme.palette.grey[200],
|
||||||
strokeDasharray: '3 3',
|
strokeDasharray: '3 3',
|
||||||
}}
|
}}
|
||||||
|
|||||||
@ -15,11 +15,17 @@ import React from 'react';
|
|||||||
window.React = React;
|
window.React = React;
|
||||||
|
|
||||||
jest.mock('recharts', () => ({
|
jest.mock('recharts', () => ({
|
||||||
Bar: jest.fn().mockImplementation(() => <div>Bar</div>),
|
Bar: jest.fn().mockImplementation(({ children }) => (
|
||||||
|
<div>
|
||||||
|
<p>Bar</p>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)),
|
||||||
Line: jest.fn().mockImplementation(() => <div>Line</div>),
|
Line: jest.fn().mockImplementation(() => <div>Line</div>),
|
||||||
Brush: jest.fn().mockImplementation(() => <div>Brush</div>),
|
Brush: jest.fn().mockImplementation(() => <div>Brush</div>),
|
||||||
Area: jest.fn().mockImplementation(() => <div>Area</div>),
|
Area: jest.fn().mockImplementation(() => <div>Area</div>),
|
||||||
Scatter: jest.fn().mockImplementation(() => <div>Scatter</div>),
|
Scatter: jest.fn().mockImplementation(() => <div>Scatter</div>),
|
||||||
|
Cell: jest.fn().mockImplementation(() => <div>Cell</div>),
|
||||||
CartesianGrid: jest.fn().mockImplementation(() => <div>CartesianGrid</div>),
|
CartesianGrid: jest.fn().mockImplementation(() => <div>CartesianGrid</div>),
|
||||||
Legend: jest.fn().mockImplementation(() => <div>Legend</div>),
|
Legend: jest.fn().mockImplementation(() => <div>Legend</div>),
|
||||||
Tooltip: jest.fn().mockImplementation(() => <div>Tooltip</div>),
|
Tooltip: jest.fn().mockImplementation(() => <div>Tooltip</div>),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user