diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/Widgets/TotalDataAssetsWidget/TotalDataAssetsWidget.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/Widgets/TotalDataAssetsWidget/TotalDataAssetsWidget.component.tsx index 94fb79225fa..f8681eb85f0 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/Widgets/TotalDataAssetsWidget/TotalDataAssetsWidget.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/Widgets/TotalDataAssetsWidget/TotalDataAssetsWidget.component.tsx @@ -13,7 +13,15 @@ import { Typography } from 'antd'; import { AxiosError } from 'axios'; import classNames from 'classnames'; -import { groupBy, isEmpty, omit, reduce, sortBy, startCase } from 'lodash'; +import { + groupBy, + isEmpty, + omit, + orderBy, + reduce, + sortBy, + startCase, +} from 'lodash'; import { useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; @@ -75,13 +83,11 @@ const TotalDataAssetsWidget = ({ applicationConfig?.customTheme?.primaryColor ?? DEFAULT_THEME.primaryColor; + // Generate palette and arrange from dark to light for high-to-low data const fullPalette = generatePalette(primaryColor); - const reversed = fullPalette.slice().reverse(); - const firstTwo = reversed.slice(0, 2); - const remaining = reversed.slice(2); - - return [...remaining, ...firstTwo]; + // Reverse the palette to ensure dark-to-light order for high-to-low data + return fullPalette.slice().reverse(); }, [applicationConfig?.customTheme?.primaryColor]); const widgetData = useMemo(() => { @@ -92,67 +98,74 @@ const TotalDataAssetsWidget = ({ return currentLayout?.find((item) => item.i === widgetKey)?.w === 2; }, [currentLayout, widgetKey]); - const { graphData, rightSideEntityList, dataByDate, availableDates } = - useMemo(() => { - const results = chartData?.results ?? []; + const { graphData, dataByDate, availableDates } = useMemo(() => { + const results = chartData?.results ?? []; - const groupedByDay = groupBy(results, 'day'); - const labels: string[] = []; + const groupedByDay = groupBy(results, 'day'); + const labels: string[] = []; - const graphData = Object.entries(groupedByDay).map( - ([dayKey, entries]) => { - const day = Number(dayKey); - const values = entries.reduce((acc, curr) => { - if (curr.group) { - labels.push(curr.group); - } - - return { - ...acc, - [curr.group ?? 'count']: curr.count, - }; - }, {}); - - return { - day, - dayString: customFormatDateTime(day, 'dd MMM'), - ...values, - }; + const graphData = Object.entries(groupedByDay).map(([dayKey, entries]) => { + const day = Number(dayKey); + const values = entries.reduce((acc, curr) => { + if (curr.group) { + labels.push(curr.group); } - ); - const sortedData = sortBy(graphData, 'day'); - const uniqueLabels = Array.from(new Set(labels)); - - const dataByDate: Record> = {}; - sortedData.forEach((item) => { - dataByDate[item.day] = omit(item, ['day', 'dayString']); - }); - - const availableDates = sortedData.map(({ day, dayString }) => ({ - day, - dayString, - })); + return { + ...acc, + [curr.group ?? 'count']: curr.count, + }; + }, {}); return { - graphData: sortedData, - rightSideEntityList: uniqueLabels, - dataByDate, - availableDates, + day, + dayString: customFormatDateTime(day, 'dd MMM'), + ...values, }; - }, [chartData?.results]); + }); - const selectedDateData = useMemo(() => { + const sortedData = sortBy(graphData, 'day'); + + const dataByDate: Record> = {}; + sortedData.forEach((item) => { + dataByDate[item.day] = omit(item, ['day', 'dayString']); + }); + + const availableDates = sortedData.map(({ day, dayString }) => ({ + day, + dayString, + })); + + return { + graphData: sortedData, + dataByDate, + availableDates, + }; + }, [chartData?.results]); + + const { selectedDateData, sortedEntityList, totalDatAssets } = useMemo(() => { if (!selectedDate) { - return {}; + return { selectedDateData: {}, sortedEntityList: [], totalDatAssets: 0 }; } - return dataByDate[selectedDate] ?? {}; - }, [selectedDate, dataByDate]); + const rawData = dataByDate[selectedDate] ?? {}; - const totalDatAssets = useMemo(() => { - return reduce(selectedDateData, (acc, value) => acc + value, 0); - }, [selectedDateData]); + // Sort data by count (high to low) and create sorted structures + const sortedEntries = orderBy( + Object.entries(rawData), + ([, value]) => value, + 'desc' + ); + const sortedData = Object.fromEntries(sortedEntries); + const entityList = Object.keys(sortedData); + const total = reduce(sortedData, (acc, value) => acc + value, 0); + + return { + selectedDateData: sortedData, + sortedEntityList: entityList, + totalDatAssets: total, + }; + }, [selectedDate, dataByDate]); const fetchData = async () => { setIsLoading(true); @@ -222,7 +235,7 @@ const TotalDataAssetsWidget = ({ nameKey="name" outerRadius={117} paddingAngle={1}> - {rightSideEntityList.map((label, index) => ( + {sortedEntityList.map((label, index) => ( - {rightSideEntityList.map((label, index) => ( -
+
+ {sortedEntityList.map((label, index) => ( +
{startCase(label)} - + {selectedDateData[label] ?? 0}
@@ -298,8 +319,9 @@ const TotalDataAssetsWidget = ({ selectedDate, selectedDateData, totalDatAssets, - rightSideEntityList, + sortedEntityList, isFullSizeWidget, + pieChartColors, ]); useEffect(() => { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/Widgets/TotalDataAssetsWidget/TotalDataAssetsWidget.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/Widgets/TotalDataAssetsWidget/TotalDataAssetsWidget.test.tsx index 8147470bde4..ef081c34315 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/Widgets/TotalDataAssetsWidget/TotalDataAssetsWidget.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/Widgets/TotalDataAssetsWidget/TotalDataAssetsWidget.test.tsx @@ -547,6 +547,79 @@ describe('TotalDataAssetsWidget', () => { }); describe('Data Processing', () => { + it('should sort data by count in descending order', async () => { + const sortedTestData: DataInsightCustomChartResult = { + results: [ + { + count: 50, + day: 1640995200000, + group: 'dashboard', + term: 'dashboard', + }, + { + count: 200, + day: 1640995200000, + group: 'table', + term: 'table', + }, + { + count: 100, + day: 1640995200000, + group: 'topic', + term: 'topic', + }, + ], + }; + + (getChartPreviewByName as jest.Mock).mockResolvedValueOnce( + sortedTestData + ); + + await act(async () => { + renderTotalDataAssetsWidget(); + }); + + await waitFor(() => { + expect(screen.getByText('350')).toBeInTheDocument(); // Total count + }); + + // Verify legend items are sorted by count (high to low) + await waitFor(() => { + // Verify specific legend items exist with correct counts using data-testid + expect(screen.getByTestId('legend-count-table')).toHaveTextContent( + '200' + ); + expect(screen.getByTestId('legend-count-topic')).toHaveTextContent( + '100' + ); + expect(screen.getByTestId('legend-count-dashboard')).toHaveTextContent( + '50' + ); + + // Verify legend items appear in sorted order (highest count first) + const legendContainer = screen.getByTestId('assets-legend'); + const legendItems = legendContainer.querySelectorAll( + '[data-testid^="legend-item-"]' + ); + + // First item should be table (highest count) + expect(legendItems[0]).toHaveAttribute( + 'data-testid', + 'legend-item-table' + ); + // Second item should be topic (medium count) + expect(legendItems[1]).toHaveAttribute( + 'data-testid', + 'legend-item-topic' + ); + // Third item should be dashboard (lowest count) + expect(legendItems[2]).toHaveAttribute( + 'data-testid', + 'legend-item-dashboard' + ); + }); + }); + it('should correctly group data by date', async () => { await act(async () => { renderTotalDataAssetsWidget();