mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-09-27 09:55:36 +00:00
fix: improve application robustness and UI consistency
- Set default value for plugins in AppRouter to prevent potential errors. - Add conditional rendering for ProfilerLatestValue to avoid rendering issues when props are undefined. - Make profilerLatestValueProps optional in ProfilerStateWrapper interface for better flexibility. - Refactor ColumnProfileTable to remove unused imports and optimize rendering logic. - Replace Button with Typography for better styling in ColumnProfileTable. - Update SingleColumnProfile to use Stack for layout consistency and include ColumnSummary when available. - Enhance CardinalityDistributionChart and DataDistributionHistogram with new styling and layout using MUI components. - Introduce DataPill styled component for consistent data display. - Update color constants for improved visual consistency across charts. - Modify data insight tooltip to conditionally display date in header for better clarity.
This commit is contained in:
parent
f062908e56
commit
7ef9508f0a
@ -43,7 +43,7 @@ const AppRouter = () => {
|
||||
isApplicationLoading,
|
||||
isAuthenticating,
|
||||
} = useApplicationStore();
|
||||
const { plugins } = useApplicationsProvider();
|
||||
const { plugins = [] } = useApplicationsProvider();
|
||||
|
||||
useEffect(() => {
|
||||
const { pathname } = location;
|
||||
|
@ -57,7 +57,9 @@ const ProfilerStateWrapper = ({
|
||||
boxShadow: 'none',
|
||||
}}>
|
||||
<Stack spacing={4}>
|
||||
<ProfilerLatestValue {...profilerLatestValueProps} />
|
||||
{profilerLatestValueProps && (
|
||||
<ProfilerLatestValue {...profilerLatestValueProps} />
|
||||
)}
|
||||
<Box flexGrow={1}>{children}</Box>
|
||||
</Stack>
|
||||
</Card>
|
||||
|
@ -16,6 +16,6 @@ export interface ProfilerStateWrapperProps {
|
||||
isLoading: boolean;
|
||||
children: React.ReactNode;
|
||||
title: string;
|
||||
profilerLatestValueProps: ProfilerLatestValueProps;
|
||||
profilerLatestValueProps?: ProfilerLatestValueProps;
|
||||
dataTestId?: string;
|
||||
}
|
||||
|
@ -12,19 +12,8 @@
|
||||
*/
|
||||
|
||||
import { Grid, Stack, Typography, useTheme } from '@mui/material';
|
||||
import { Button, Col, Row } from 'antd';
|
||||
import { ColumnsType } from 'antd/lib/table';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
filter,
|
||||
find,
|
||||
groupBy,
|
||||
isEmpty,
|
||||
isUndefined,
|
||||
map,
|
||||
round,
|
||||
toLower,
|
||||
} from 'lodash';
|
||||
import { isEmpty, round } from 'lodash';
|
||||
import Qs from 'qs';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -38,10 +27,6 @@ import {
|
||||
Table as TableType,
|
||||
} from '../../../../../generated/entity/data/table';
|
||||
import { Operation } from '../../../../../generated/entity/policies/policy';
|
||||
import {
|
||||
TestCase,
|
||||
TestCaseStatus,
|
||||
} from '../../../../../generated/tests/testCase';
|
||||
import { usePaging } from '../../../../../hooks/paging/usePaging';
|
||||
import useCustomLocation from '../../../../../hooks/useCustomLocation/useCustomLocation';
|
||||
import { useFqn } from '../../../../../hooks/useFqn';
|
||||
@ -49,26 +34,21 @@ import {
|
||||
getTableColumnsByFQN,
|
||||
searchTableColumnsByFQN,
|
||||
} from '../../../../../rest/tableAPI';
|
||||
import { getListTestCaseBySearch } from '../../../../../rest/testAPI';
|
||||
import {
|
||||
formatNumberWithComma,
|
||||
getTableFQNFromColumnFQN,
|
||||
} from '../../../../../utils/CommonUtils';
|
||||
import { getEntityName } from '../../../../../utils/EntityUtils';
|
||||
import {
|
||||
generateEntityLink,
|
||||
getTableExpandableConfig,
|
||||
pruneEmptyChildren,
|
||||
} from '../../../../../utils/TableUtils';
|
||||
import ErrorPlaceHolder from '../../../../common/ErrorWithPlaceholder/ErrorPlaceHolder';
|
||||
import FilterTablePlaceHolder from '../../../../common/ErrorWithPlaceholder/FilterTablePlaceHolder';
|
||||
import { PagingHandlerParams } from '../../../../common/NextPrevious/NextPrevious.interface';
|
||||
import { SummaryCard } from '../../../../common/SummaryCard/SummaryCard.component';
|
||||
import { SummaryCardProps } from '../../../../common/SummaryCard/SummaryCard.interface';
|
||||
import SummaryCardV1 from '../../../../common/SummaryCard/SummaryCardV1';
|
||||
import Table from '../../../../common/Table/Table';
|
||||
import { TableProfilerTab } from '../../ProfilerDashboard/profilerDashboard.interface';
|
||||
import ColumnSummary from '../ColumnSummary';
|
||||
import NoProfilerBanner from '../NoProfilerBanner/NoProfilerBanner.component';
|
||||
import SingleColumnProfile from '../SingleColumnProfile';
|
||||
import { ModifiedColumn } from '../TableProfiler.interface';
|
||||
@ -95,8 +75,6 @@ const ColumnProfileTable = () => {
|
||||
const isLoading = isTestsLoading || isProfilerDataLoading;
|
||||
const [searchText, setSearchText] = useState<string>('');
|
||||
const [data, setData] = useState<ModifiedColumn[]>([]);
|
||||
const [isTestCaseLoading, setIsTestCaseLoading] = useState(false);
|
||||
const [columnTestCases, setColumnTestCases] = useState<TestCase[]>([]);
|
||||
const [isColumnsLoading, setIsColumnsLoading] = useState(false);
|
||||
const {
|
||||
currentPage,
|
||||
@ -138,14 +116,20 @@ const ColumnProfileTable = () => {
|
||||
fixed: 'left',
|
||||
render: (_, record) => {
|
||||
return (
|
||||
<Button
|
||||
<Typography
|
||||
className="break-word p-0"
|
||||
type="link"
|
||||
sx={{
|
||||
color: theme.palette.primary.main,
|
||||
fontSize: theme.typography.pxToRem(14),
|
||||
fontWeight: theme.typography.fontWeightMedium,
|
||||
cursor: 'pointer',
|
||||
'&:hover': { textDecoration: 'underline' },
|
||||
}}
|
||||
onClick={() =>
|
||||
updateActiveColumnFqn(record.fullyQualifiedName || '')
|
||||
}>
|
||||
{getEntityName(record)}
|
||||
</Button>
|
||||
</Typography>
|
||||
);
|
||||
},
|
||||
sorter: (col1, col2) => col1.name.localeCompare(col2.name),
|
||||
@ -157,7 +141,11 @@ const ColumnProfileTable = () => {
|
||||
width: 150,
|
||||
render: (dataTypeDisplay: string) => {
|
||||
return (
|
||||
<Typography className="break-word">
|
||||
<Typography
|
||||
className="break-word"
|
||||
sx={{
|
||||
fontSize: theme.typography.pxToRem(14),
|
||||
}}>
|
||||
{dataTypeDisplay || 'N/A'}
|
||||
</Typography>
|
||||
);
|
||||
@ -286,51 +274,11 @@ const ColumnProfileTable = () => {
|
||||
];
|
||||
}, [testCaseSummary]);
|
||||
|
||||
const selectedColumn = useMemo(() => {
|
||||
return find(
|
||||
data,
|
||||
(column: Column) => column.fullyQualifiedName === activeColumnFqn
|
||||
);
|
||||
}, [data, activeColumnFqn]);
|
||||
|
||||
const selectedColumnTestsObj = useMemo(() => {
|
||||
const temp = filter(
|
||||
columnTestCases,
|
||||
(test: TestCase) => !isUndefined(test.testCaseResult)
|
||||
);
|
||||
|
||||
const statusDict = {
|
||||
[TestCaseStatus.Success]: [],
|
||||
[TestCaseStatus.Aborted]: [],
|
||||
[TestCaseStatus.Failed]: [],
|
||||
...groupBy(temp, 'testCaseResult.testCaseStatus'),
|
||||
};
|
||||
|
||||
return { statusDict, totalTests: temp.length };
|
||||
}, [columnTestCases]);
|
||||
|
||||
const handleSearchAction = (searchText: string) => {
|
||||
setSearchText(searchText);
|
||||
handlePageChange(1);
|
||||
};
|
||||
|
||||
const fetchColumnTestCase = async (activeColumnFqn: string) => {
|
||||
setIsTestCaseLoading(true);
|
||||
try {
|
||||
const { data } = await getListTestCaseBySearch({
|
||||
fields: TabSpecificField.TEST_CASE_RESULT,
|
||||
entityLink: generateEntityLink(activeColumnFqn),
|
||||
limit: PAGE_SIZE_LARGE,
|
||||
});
|
||||
|
||||
setColumnTestCases(data);
|
||||
} catch {
|
||||
setColumnTestCases([]);
|
||||
} finally {
|
||||
setIsTestCaseLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchTableColumnWithProfiler = useCallback(
|
||||
async (page: number, searchText: string) => {
|
||||
if (!tableFqn) {
|
||||
@ -382,14 +330,6 @@ const ColumnProfileTable = () => {
|
||||
}
|
||||
}, [tableFqn, currentPage, searchText, pageSize]);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeColumnFqn) {
|
||||
fetchColumnTestCase(activeColumnFqn);
|
||||
} else {
|
||||
setColumnTestCases([]);
|
||||
}
|
||||
}, [activeColumnFqn]);
|
||||
|
||||
const pagingProps = useMemo(() => {
|
||||
return {
|
||||
currentPage: currentPage,
|
||||
@ -424,77 +364,43 @@ const ColumnProfileTable = () => {
|
||||
return (
|
||||
<Stack data-testid="column-profile-table-container" spacing="30px">
|
||||
{!isLoading && !isProfilingEnabled && <NoProfilerBanner />}
|
||||
<Col span={24}>
|
||||
<Grid container spacing={5}>
|
||||
{overallSummary?.map((summary) => (
|
||||
<Grid key={summary.title} size="grow">
|
||||
<SummaryCardV1
|
||||
extra={summary.extra}
|
||||
icon={summary.icon}
|
||||
isLoading={isLoading}
|
||||
title={summary.title}
|
||||
value={summary.value}
|
||||
/>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
<Row gutter={[16, 16]}>
|
||||
{!isUndefined(selectedColumn) && (
|
||||
<Col span={10}>
|
||||
<ColumnSummary column={selectedColumn} />
|
||||
</Col>
|
||||
)}
|
||||
|
||||
<Col span={selectedColumn ? 14 : 24}>
|
||||
<Row
|
||||
wrap
|
||||
className={classNames(
|
||||
activeColumnFqn ? 'justify-start' : 'justify-between'
|
||||
)}
|
||||
gutter={[16, 16]}>
|
||||
{!isEmpty(activeColumnFqn) &&
|
||||
map(selectedColumnTestsObj.statusDict, (data, key) => (
|
||||
<Col key={key}>
|
||||
<SummaryCard
|
||||
showProgressBar
|
||||
isLoading={isTestCaseLoading}
|
||||
title={key}
|
||||
total={selectedColumnTestsObj.totalTests}
|
||||
type={toLower(key) as SummaryCardProps['type']}
|
||||
value={data.length}
|
||||
/>
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
<Grid container spacing={5}>
|
||||
{overallSummary?.map((summary) => (
|
||||
<Grid key={summary.title} size="grow">
|
||||
<SummaryCardV1
|
||||
extra={summary.extra}
|
||||
icon={summary.icon}
|
||||
isLoading={isLoading}
|
||||
title={summary.title}
|
||||
value={summary.value}
|
||||
/>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
|
||||
{isEmpty(activeColumnFqn) ? (
|
||||
<Col span={24}>
|
||||
<Table
|
||||
columns={tableColumn}
|
||||
customPaginationProps={pagingProps}
|
||||
dataSource={data}
|
||||
expandable={getTableExpandableConfig<Column>()}
|
||||
loading={isColumnsLoading || isLoading}
|
||||
locale={{
|
||||
emptyText: <FilterTablePlaceHolder />,
|
||||
}}
|
||||
pagination={false}
|
||||
rowKey="name"
|
||||
scroll={{ x: true }}
|
||||
searchProps={searchProps}
|
||||
size="small"
|
||||
/>
|
||||
</Col>
|
||||
<Table
|
||||
columns={tableColumn}
|
||||
customPaginationProps={pagingProps}
|
||||
dataSource={data}
|
||||
expandable={getTableExpandableConfig<Column>()}
|
||||
loading={isColumnsLoading || isLoading}
|
||||
locale={{
|
||||
emptyText: <FilterTablePlaceHolder />,
|
||||
}}
|
||||
pagination={false}
|
||||
rowKey="name"
|
||||
scroll={{ x: true }}
|
||||
searchProps={searchProps}
|
||||
size="small"
|
||||
/>
|
||||
) : (
|
||||
<Col span={24}>
|
||||
<SingleColumnProfile
|
||||
activeColumnFqn={activeColumnFqn}
|
||||
dateRangeObject={dateRangeObject}
|
||||
tableDetails={tableDetailsWithColumns}
|
||||
/>
|
||||
</Col>
|
||||
<SingleColumnProfile
|
||||
activeColumnFqn={activeColumnFqn}
|
||||
dateRangeObject={dateRangeObject}
|
||||
tableDetails={tableDetailsWithColumns}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
|
@ -10,9 +10,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Card, Col, Row, Typography } from 'antd';
|
||||
import { Stack } from '@mui/material';
|
||||
import { AxiosError } from 'axios';
|
||||
import { first, isString, last, pick } from 'lodash';
|
||||
import { find, first, isString, last, pick } from 'lodash';
|
||||
import { DateRangeObject } from 'Models';
|
||||
import { FC, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -20,7 +20,10 @@ import {
|
||||
DEFAULT_RANGE_DATA,
|
||||
INITIAL_COLUMN_METRICS_VALUE,
|
||||
} from '../../../../constants/profiler.constant';
|
||||
import { ColumnProfile } from '../../../../generated/entity/data/container';
|
||||
import {
|
||||
Column,
|
||||
ColumnProfile,
|
||||
} from '../../../../generated/entity/data/container';
|
||||
import { Table } from '../../../../generated/entity/data/table';
|
||||
import { getColumnProfilerList } from '../../../../rest/tableAPI';
|
||||
import { Transi18next } from '../../../../utils/CommonUtils';
|
||||
@ -35,6 +38,8 @@ import { showErrorToast } from '../../../../utils/ToastUtils';
|
||||
import CardinalityDistributionChart from '../../../Visualisations/Chart/CardinalityDistributionChart.component';
|
||||
import DataDistributionHistogram from '../../../Visualisations/Chart/DataDistributionHistogram.component';
|
||||
import ProfilerDetailsCard from '../ProfilerDetailsCard/ProfilerDetailsCard';
|
||||
import ProfilerStateWrapper from '../ProfilerStateWrapper/ProfilerStateWrapper.component';
|
||||
import ColumnSummary from './ColumnSummary';
|
||||
import CustomMetricGraphs from './CustomMetricGraphs/CustomMetricGraphs.component';
|
||||
import { useTableProfiler } from './TableProfilerProvider';
|
||||
|
||||
@ -63,6 +68,13 @@ const SingleColumnProfile: FC<SingleColumnProfileProps> = ({
|
||||
[]
|
||||
);
|
||||
|
||||
const selectedColumn = useMemo(() => {
|
||||
return find(
|
||||
tableDetails?.columns ?? [],
|
||||
(column: Column) => column.fullyQualifiedName === activeColumnFqn
|
||||
);
|
||||
}, [tableDetails, activeColumnFqn]);
|
||||
|
||||
const customMetrics = useMemo(
|
||||
() =>
|
||||
getColumnCustomMetric(
|
||||
@ -151,115 +163,85 @@ const SingleColumnProfile: FC<SingleColumnProfileProps> = ({
|
||||
}, [activeColumnFqn, dateRangeObject]);
|
||||
|
||||
return (
|
||||
<Row
|
||||
<Stack
|
||||
className="m-b-lg"
|
||||
data-testid="profiler-tab-container"
|
||||
gutter={[16, 16]}>
|
||||
<Col span={24}>
|
||||
<ProfilerDetailsCard
|
||||
chartCollection={columnMetric.countMetrics}
|
||||
isLoading={isLoading}
|
||||
name="count"
|
||||
noDataPlaceholderText={noProfilerMessage}
|
||||
title={t('label.data-count-plural')}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<ProfilerDetailsCard
|
||||
chartCollection={columnMetric.proportionMetrics}
|
||||
isLoading={isLoading}
|
||||
name="proportion"
|
||||
noDataPlaceholderText={noProfilerMessage}
|
||||
tickFormatter="%"
|
||||
title={t('label.data-proportion-plural')}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<ProfilerDetailsCard
|
||||
chartCollection={columnMetric.mathMetrics}
|
||||
isLoading={isLoading}
|
||||
name="math"
|
||||
noDataPlaceholderText={noProfilerMessage}
|
||||
showYAxisCategory={isMinMaxStringData}
|
||||
// only min/max category can be string
|
||||
title={t('label.data-range')}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<ProfilerDetailsCard
|
||||
chartCollection={columnMetric.sumMetrics}
|
||||
isLoading={isLoading}
|
||||
name="sum"
|
||||
noDataPlaceholderText={noProfilerMessage}
|
||||
title={t('label.data-aggregate')}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<ProfilerDetailsCard
|
||||
chartCollection={columnMetric.quartileMetrics}
|
||||
isLoading={isLoading}
|
||||
name="quartile"
|
||||
noDataPlaceholderText={noProfilerMessage}
|
||||
title={t('label.data-quartile-plural')}
|
||||
/>
|
||||
</Col>
|
||||
spacing="30px">
|
||||
{selectedColumn && <ColumnSummary column={selectedColumn} />}
|
||||
<ProfilerDetailsCard
|
||||
chartCollection={columnMetric.countMetrics}
|
||||
isLoading={isLoading}
|
||||
name="count"
|
||||
noDataPlaceholderText={noProfilerMessage}
|
||||
title={t('label.data-count-plural')}
|
||||
/>
|
||||
<ProfilerDetailsCard
|
||||
chartCollection={columnMetric.proportionMetrics}
|
||||
isLoading={isLoading}
|
||||
name="proportion"
|
||||
noDataPlaceholderText={noProfilerMessage}
|
||||
tickFormatter="%"
|
||||
title={t('label.data-proportion-plural')}
|
||||
/>
|
||||
<ProfilerDetailsCard
|
||||
chartCollection={columnMetric.mathMetrics}
|
||||
isLoading={isLoading}
|
||||
name="math"
|
||||
noDataPlaceholderText={noProfilerMessage}
|
||||
showYAxisCategory={isMinMaxStringData}
|
||||
// only min/max category can be string
|
||||
title={t('label.data-range')}
|
||||
/>
|
||||
<ProfilerDetailsCard
|
||||
chartCollection={columnMetric.sumMetrics}
|
||||
chartType="area"
|
||||
isLoading={isLoading}
|
||||
name="sum"
|
||||
noDataPlaceholderText={noProfilerMessage}
|
||||
title={t('label.data-aggregate')}
|
||||
/>
|
||||
<ProfilerDetailsCard
|
||||
chartCollection={columnMetric.quartileMetrics}
|
||||
isLoading={isLoading}
|
||||
name="quartile"
|
||||
noDataPlaceholderText={noProfilerMessage}
|
||||
title={t('label.data-quartile-plural')}
|
||||
/>
|
||||
{firstDay?.histogram || currentDay?.histogram ? (
|
||||
<Col span={24}>
|
||||
<Card
|
||||
className="shadow-none global-border-radius"
|
||||
data-testid="histogram-metrics"
|
||||
loading={isLoading}>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={24}>
|
||||
<Typography.Title
|
||||
data-testid="data-distribution-title"
|
||||
level={5}>
|
||||
{t('label.data-distribution')}
|
||||
</Typography.Title>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<DataDistributionHistogram
|
||||
data={{ firstDayData: firstDay, currentDayData: currentDay }}
|
||||
noDataPlaceholderText={noProfilerMessage}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
</Col>
|
||||
<ProfilerStateWrapper
|
||||
dataTestId="histogram-metrics"
|
||||
isLoading={isLoading}
|
||||
title={t('label.data-distribution')}>
|
||||
<DataDistributionHistogram
|
||||
data={{
|
||||
firstDayData: firstDay,
|
||||
currentDayData: currentDay,
|
||||
}}
|
||||
noDataPlaceholderText={noProfilerMessage}
|
||||
/>
|
||||
</ProfilerStateWrapper>
|
||||
) : null}
|
||||
{firstDay?.cardinalityDistribution ||
|
||||
currentDay?.cardinalityDistribution ? (
|
||||
<Col span={24}>
|
||||
<Card
|
||||
className="shadow-none global-border-radius"
|
||||
data-testid="cardinality-distribution-metrics"
|
||||
loading={isLoading}>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={24}>
|
||||
<Typography.Title
|
||||
data-testid="cardinality-distribution-title"
|
||||
level={5}>
|
||||
{t('label.cardinality')}
|
||||
</Typography.Title>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<CardinalityDistributionChart
|
||||
data={{ firstDayData: firstDay, currentDayData: currentDay }}
|
||||
noDataPlaceholderText={noProfilerMessage}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
</Col>
|
||||
<ProfilerStateWrapper
|
||||
dataTestId="cardinality-distribution-metrics"
|
||||
isLoading={isLoading}
|
||||
title={t('label.cardinality')}>
|
||||
<CardinalityDistributionChart
|
||||
data={{
|
||||
firstDayData: firstDay,
|
||||
currentDayData: currentDay,
|
||||
}}
|
||||
noDataPlaceholderText={noProfilerMessage}
|
||||
/>
|
||||
</ProfilerStateWrapper>
|
||||
) : null}
|
||||
<Col span={24}>
|
||||
<CustomMetricGraphs
|
||||
customMetrics={customMetrics}
|
||||
customMetricsGraphData={columnCustomMetrics}
|
||||
isLoading={isLoading || isProfilerDataLoading}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<CustomMetricGraphs
|
||||
customMetrics={customMetrics}
|
||||
customMetricsGraphData={columnCustomMetrics}
|
||||
isLoading={isLoading || isProfilerDataLoading}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -11,8 +11,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Card, Col, Row, Tag } from 'antd';
|
||||
import { isUndefined, map } from 'lodash';
|
||||
import { Box, Card, Divider, Typography, useTheme } from '@mui/material';
|
||||
import { isUndefined } from 'lodash';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
Bar,
|
||||
@ -30,6 +30,7 @@ import { GRAPH_BACKGROUND_COLOR } from '../../../constants/constants';
|
||||
import { ColumnProfile } from '../../../generated/entity/data/table';
|
||||
import { axisTickFormatter, tooltipFormatter } from '../../../utils/ChartUtils';
|
||||
import { customFormatDateTime } from '../../../utils/date-time/DateTimeUtils';
|
||||
import { DataPill } from '../../common/DataPill/DataPill.styled';
|
||||
import ErrorPlaceHolder from '../../common/ErrorWithPlaceholder/ErrorPlaceHolder';
|
||||
|
||||
export interface CardinalityDistributionChartProps {
|
||||
@ -44,6 +45,7 @@ const CardinalityDistributionChart = ({
|
||||
data,
|
||||
noDataPlaceholderText,
|
||||
}: CardinalityDistributionChartProps) => {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
const showSingleGraph =
|
||||
isUndefined(data.firstDayData?.cardinalityDistribution) ||
|
||||
@ -54,11 +56,16 @@ const CardinalityDistributionChart = ({
|
||||
isUndefined(data.currentDayData?.cardinalityDistribution)
|
||||
) {
|
||||
return (
|
||||
<Row align="middle" className="h-full w-full" justify="center">
|
||||
<Col>
|
||||
<ErrorPlaceHolder placeholderText={noDataPlaceholderText} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
}}>
|
||||
<ErrorPlaceHolder placeholderText={noDataPlaceholderText} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@ -70,16 +77,60 @@ const CardinalityDistributionChart = ({
|
||||
const data = payload[0].payload;
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<p className="font-semibold text-sm mb-1">{`${t('label.category')}: ${
|
||||
data.name
|
||||
}`}</p>
|
||||
<p className="text-sm mb-1">{`${t('label.count')}: ${tooltipFormatter(
|
||||
data.count
|
||||
)}`}</p>
|
||||
<p className="text-sm">{`${t('label.percentage')}: ${
|
||||
data.percentage
|
||||
}%`}</p>
|
||||
<Card
|
||||
sx={{
|
||||
p: '10px',
|
||||
bgcolor: theme.palette.allShades.white,
|
||||
}}>
|
||||
<Typography
|
||||
sx={{
|
||||
color: theme.palette.allShades.gray[900],
|
||||
fontWeight: theme.typography.fontWeightMedium,
|
||||
fontSize: theme.typography.pxToRem(12),
|
||||
}}>
|
||||
{data.name}
|
||||
</Typography>
|
||||
<Divider
|
||||
sx={{
|
||||
my: 2,
|
||||
borderStyle: 'dashed',
|
||||
borderColor: theme.palette.allShades.gray[300],
|
||||
}}
|
||||
/>
|
||||
<Box className="d-flex items-center justify-between gap-6 p-b-xss text-sm">
|
||||
<Typography
|
||||
sx={(theme) => ({
|
||||
color: theme.palette.allShades.gray[700],
|
||||
fontSize: theme.typography.pxToRem(11),
|
||||
})}>
|
||||
{t('label.count')}
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={(theme) => ({
|
||||
color: theme.palette.allShades.gray[900],
|
||||
fontWeight: theme.typography.fontWeightMedium,
|
||||
fontSize: theme.typography.pxToRem(11),
|
||||
})}>
|
||||
{tooltipFormatter(data.count)}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box className="d-flex items-center justify-between gap-6 p-b-xss text-sm">
|
||||
<Typography
|
||||
sx={(theme) => ({
|
||||
color: theme.palette.allShades.gray[700],
|
||||
fontSize: theme.typography.pxToRem(11),
|
||||
})}>
|
||||
{t('label.percentage')}
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={(theme) => ({
|
||||
color: theme.palette.allShades.gray[900],
|
||||
fontWeight: theme.typography.fontWeightMedium,
|
||||
fontSize: theme.typography.pxToRem(11),
|
||||
})}>
|
||||
{`${data.percentage}%`}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@ -87,9 +138,19 @@ const CardinalityDistributionChart = ({
|
||||
return null;
|
||||
};
|
||||
|
||||
const dataEntries = Object.entries(data).filter(
|
||||
([, columnProfile]) => !isUndefined(columnProfile?.cardinalityDistribution)
|
||||
);
|
||||
|
||||
return (
|
||||
<Row className="w-full" data-testid="chart-container">
|
||||
{map(data, (columnProfile, key) => {
|
||||
<Box
|
||||
data-testid="chart-container"
|
||||
sx={{
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
gap: 0,
|
||||
}}>
|
||||
{dataEntries.map(([key, columnProfile], index) => {
|
||||
if (
|
||||
isUndefined(columnProfile) ||
|
||||
isUndefined(columnProfile?.cardinalityDistribution)
|
||||
@ -108,62 +169,96 @@ const CardinalityDistributionChart = ({
|
||||
|
||||
const graphDate = customFormatDateTime(
|
||||
columnProfile?.timestamp || 0,
|
||||
'MMM dd'
|
||||
'MMM dd, yyyy'
|
||||
);
|
||||
|
||||
return (
|
||||
<Col key={key} span={showSingleGraph ? 24 : 12}>
|
||||
<Row gutter={[8, 8]}>
|
||||
<Col
|
||||
data-testid="date"
|
||||
offset={showSingleGraph ? 1 : 2}
|
||||
span={24}>
|
||||
{graphDate}
|
||||
</Col>
|
||||
<Col offset={showSingleGraph ? 1 : 2} span={24}>
|
||||
<Tag data-testid="cardinality-tag">{`${t('label.total-entity', {
|
||||
<Box
|
||||
key={key}
|
||||
sx={{
|
||||
flex: showSingleGraph ? '1 1 100%' : '1 1 50%',
|
||||
minWidth: 0,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
px: showSingleGraph ? 4 : 6,
|
||||
py: 2,
|
||||
borderRight:
|
||||
!showSingleGraph && index === 0
|
||||
? `1px solid ${theme.palette.grey[200]}`
|
||||
: 'none',
|
||||
}}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
mb: 5,
|
||||
}}>
|
||||
<DataPill data-testid="date">{graphDate}</DataPill>
|
||||
<DataPill data-testid="cardinality-tag">
|
||||
{`${t('label.total-entity', {
|
||||
entity: t('label.category-plural'),
|
||||
})}: ${cardinalityData.categories?.length || 0}`}</Tag>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<ResponsiveContainer
|
||||
debounce={200}
|
||||
id={`${key}-cardinality`}
|
||||
minHeight={300}>
|
||||
<BarChart
|
||||
className="w-full"
|
||||
data={graphData}
|
||||
layout="vertical"
|
||||
margin={{ left: 16 }}>
|
||||
<CartesianGrid stroke={GRAPH_BACKGROUND_COLOR} />
|
||||
<XAxis
|
||||
padding={{ left: 16, right: 16 }}
|
||||
tick={{ fontSize: 12 }}
|
||||
tickFormatter={(props) => axisTickFormatter(props, '%')}
|
||||
type="number"
|
||||
/>
|
||||
<YAxis
|
||||
allowDataOverflow
|
||||
dataKey="name"
|
||||
padding={{ top: 16, bottom: 16 }}
|
||||
tick={{ fontSize: 12 }}
|
||||
tickFormatter={(value: string) =>
|
||||
value?.length > 15 ? `${value.slice(0, 15)}...` : value
|
||||
}
|
||||
type="category"
|
||||
width={120}
|
||||
/>
|
||||
<Legend />
|
||||
<Tooltip content={renderTooltip} />
|
||||
<Bar dataKey="percentage" fill={CHART_BLUE_1} />
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
})}: ${cardinalityData.categories?.length || 0}`}
|
||||
</DataPill>
|
||||
</Box>
|
||||
<Box sx={{ flex: 1, minHeight: 350 }}>
|
||||
<ResponsiveContainer
|
||||
debounce={200}
|
||||
id={`${key}-cardinality`}
|
||||
minHeight={300}>
|
||||
<BarChart
|
||||
className="w-full"
|
||||
data={graphData}
|
||||
layout="vertical"
|
||||
margin={{ left: 16 }}>
|
||||
<CartesianGrid
|
||||
stroke={GRAPH_BACKGROUND_COLOR}
|
||||
strokeDasharray="3 3"
|
||||
vertical={false}
|
||||
/>
|
||||
<XAxis
|
||||
axisLine={{
|
||||
stroke: theme.palette.grey[200],
|
||||
}}
|
||||
padding={{ left: 16, right: 16 }}
|
||||
tick={{ fontSize: 12 }}
|
||||
tickFormatter={(props) => axisTickFormatter(props, '%')}
|
||||
tickLine={false}
|
||||
type="number"
|
||||
/>
|
||||
<YAxis
|
||||
allowDataOverflow
|
||||
axisLine={false}
|
||||
dataKey="name"
|
||||
padding={{ top: 16, bottom: 16 }}
|
||||
tick={{ fontSize: 12 }}
|
||||
tickFormatter={(value: string) =>
|
||||
value?.length > 15 ? `${value.slice(0, 15)}...` : value
|
||||
}
|
||||
tickLine={false}
|
||||
type="category"
|
||||
width={120}
|
||||
/>
|
||||
<Legend />
|
||||
<Tooltip
|
||||
content={renderTooltip}
|
||||
cursor={{
|
||||
stroke: theme.palette.grey[200],
|
||||
strokeDasharray: '3 3',
|
||||
}}
|
||||
/>
|
||||
<Bar
|
||||
dataKey="percentage"
|
||||
fill={CHART_BLUE_1}
|
||||
radius={[0, 8, 8, 0]}
|
||||
/>
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</Row>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -11,8 +11,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Col, Row, Tag } from 'antd';
|
||||
import { isUndefined, map } from 'lodash';
|
||||
import { Box, useTheme } from '@mui/material';
|
||||
import { isUndefined } from 'lodash';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
Bar,
|
||||
@ -29,7 +29,9 @@ import { GRAPH_BACKGROUND_COLOR } from '../../../constants/constants';
|
||||
import { DEFAULT_HISTOGRAM_DATA } from '../../../constants/profiler.constant';
|
||||
import { HistogramClass } from '../../../generated/entity/data/table';
|
||||
import { axisTickFormatter, tooltipFormatter } from '../../../utils/ChartUtils';
|
||||
import { CustomDQTooltip } from '../../../utils/DataQuality/DataQualityUtils';
|
||||
import { customFormatDateTime } from '../../../utils/date-time/DateTimeUtils';
|
||||
import { DataPill } from '../../common/DataPill/DataPill.styled';
|
||||
import ErrorPlaceHolder from '../../common/ErrorWithPlaceholder/ErrorPlaceHolder';
|
||||
import { DataDistributionHistogramProps } from './Chart.interface';
|
||||
|
||||
@ -37,7 +39,9 @@ const DataDistributionHistogram = ({
|
||||
data,
|
||||
noDataPlaceholderText,
|
||||
}: DataDistributionHistogramProps) => {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const showSingleGraph =
|
||||
isUndefined(data.firstDayData?.histogram) ||
|
||||
isUndefined(data.currentDayData?.histogram);
|
||||
@ -47,21 +51,32 @@ const DataDistributionHistogram = ({
|
||||
isUndefined(data.currentDayData?.histogram)
|
||||
) {
|
||||
return (
|
||||
<Row align="middle" className="h-full w-full" justify="center">
|
||||
<Col>
|
||||
<ErrorPlaceHolder placeholderText={noDataPlaceholderText} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
}}>
|
||||
<ErrorPlaceHolder placeholderText={noDataPlaceholderText} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Row className="w-full" data-testid="chart-container">
|
||||
{map(data, (columnProfile, key) => {
|
||||
if (isUndefined(columnProfile?.histogram)) {
|
||||
return;
|
||||
}
|
||||
const dataEntries = Object.entries(data).filter(
|
||||
([, columnProfile]) => !isUndefined(columnProfile?.histogram)
|
||||
);
|
||||
|
||||
return (
|
||||
<Box
|
||||
data-testid="chart-container"
|
||||
sx={{
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
gap: 0,
|
||||
}}>
|
||||
{dataEntries.map(([key, columnProfile], index) => {
|
||||
const histogramData =
|
||||
(columnProfile?.histogram as HistogramClass) ||
|
||||
DEFAULT_HISTOGRAM_DATA;
|
||||
@ -73,59 +88,105 @@ const DataDistributionHistogram = ({
|
||||
|
||||
const graphDate = customFormatDateTime(
|
||||
columnProfile?.timestamp || 0,
|
||||
'MMM dd'
|
||||
'MMM dd, yyyy'
|
||||
);
|
||||
|
||||
const skewColorTheme = columnProfile?.nonParametricSkew
|
||||
? columnProfile?.nonParametricSkew > 0
|
||||
? theme.palette.allShades.success
|
||||
: theme.palette.allShades.error
|
||||
: theme.palette.allShades.info;
|
||||
|
||||
return (
|
||||
<Col key={key} span={showSingleGraph ? 24 : 12}>
|
||||
<Row gutter={[8, 8]}>
|
||||
<Col
|
||||
data-testid="date"
|
||||
offset={showSingleGraph ? 1 : 2}
|
||||
span={24}>
|
||||
{graphDate}
|
||||
</Col>
|
||||
<Col offset={showSingleGraph ? 1 : 2} span={24}>
|
||||
<Tag data-testid="skew-tag">{`${t('label.skew')}: ${
|
||||
<Box
|
||||
key={key}
|
||||
sx={{
|
||||
flex: showSingleGraph ? '1 1 100%' : '1 1 50%',
|
||||
minWidth: 0,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
px: showSingleGraph ? 4 : 3,
|
||||
py: 2,
|
||||
borderRight:
|
||||
!showSingleGraph && index === 0
|
||||
? `1px solid ${theme.palette.grey[200]}`
|
||||
: 'none',
|
||||
}}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
mb: 5,
|
||||
}}>
|
||||
<DataPill>{graphDate}</DataPill>
|
||||
<DataPill
|
||||
sx={{
|
||||
backgroundColor: skewColorTheme[100],
|
||||
color: skewColorTheme[900],
|
||||
}}>
|
||||
{`${t('label.skew')}: ${
|
||||
columnProfile?.nonParametricSkew || '--'
|
||||
}`}</Tag>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<ResponsiveContainer
|
||||
debounce={200}
|
||||
id={`${key}-histogram`}
|
||||
minHeight={300}>
|
||||
<BarChart
|
||||
className="w-full"
|
||||
data={graphData}
|
||||
margin={{ left: 16 }}>
|
||||
<CartesianGrid stroke={GRAPH_BACKGROUND_COLOR} />
|
||||
<XAxis
|
||||
dataKey="name"
|
||||
padding={{ left: 16, right: 16 }}
|
||||
tick={{ fontSize: 12 }}
|
||||
/>
|
||||
<YAxis
|
||||
allowDataOverflow
|
||||
padding={{ top: 16, bottom: 16 }}
|
||||
tick={{ fontSize: 12 }}
|
||||
tickFormatter={(props) => axisTickFormatter(props)}
|
||||
/>
|
||||
<Legend />
|
||||
<Tooltip
|
||||
formatter={(value: number | string) =>
|
||||
tooltipFormatter(value)
|
||||
}
|
||||
/>
|
||||
<Bar dataKey="frequency" fill={CHART_BLUE_1} />
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
}`}
|
||||
</DataPill>
|
||||
</Box>
|
||||
<Box sx={{ flex: 1, minHeight: 350 }}>
|
||||
<ResponsiveContainer
|
||||
debounce={200}
|
||||
height="100%"
|
||||
id={`${key}-histogram`}
|
||||
width="100%">
|
||||
<BarChart
|
||||
data={graphData}
|
||||
margin={{ top: 10, right: 10, bottom: 10, left: 10 }}>
|
||||
<CartesianGrid
|
||||
stroke={GRAPH_BACKGROUND_COLOR}
|
||||
strokeDasharray="3 3"
|
||||
vertical={false}
|
||||
/>
|
||||
<XAxis
|
||||
axisLine={{
|
||||
stroke: theme.palette.grey[200],
|
||||
}}
|
||||
dataKey="name"
|
||||
padding={{ left: 16, right: 16 }}
|
||||
tick={{ fontSize: 12 }}
|
||||
tickLine={false}
|
||||
/>
|
||||
<YAxis
|
||||
allowDataOverflow
|
||||
axisLine={false}
|
||||
padding={{ top: 16, bottom: 16 }}
|
||||
tick={{ fontSize: 12 }}
|
||||
tickFormatter={(props) => axisTickFormatter(props)}
|
||||
tickLine={false}
|
||||
/>
|
||||
<Legend />
|
||||
<Tooltip
|
||||
content={
|
||||
<CustomDQTooltip
|
||||
displayDateInHeader={false}
|
||||
timeStampKey="name"
|
||||
valueFormatter={(value) => tooltipFormatter(value)}
|
||||
/>
|
||||
}
|
||||
cursor={{
|
||||
stroke: theme.palette.grey[200],
|
||||
strokeDasharray: '3 3',
|
||||
}}
|
||||
/>
|
||||
<Bar
|
||||
dataKey="frequency"
|
||||
fill={CHART_BLUE_1}
|
||||
radius={[8, 8, 0, 0]}
|
||||
/>
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</Row>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2023 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Box, styled } from '@mui/material';
|
||||
|
||||
export const DataPill = styled(Box)(({ theme }) => ({
|
||||
backgroundColor: theme.palette.grey[100],
|
||||
color: theme.palette.grey[900],
|
||||
borderRadius: '6px',
|
||||
padding: '6px 12px',
|
||||
fontSize: theme.typography.pxToRem(14),
|
||||
fontWeight: theme.typography.fontWeightBold,
|
||||
display: 'inline-block',
|
||||
}));
|
@ -34,7 +34,7 @@ export const BLUE_2 = '#3ca2f4';
|
||||
export const BLUE_500 = '#2E90FA';
|
||||
export const BLUE_800 = '#1849A9';
|
||||
export const BLUE_50 = '#EFF8FF';
|
||||
export const CHART_BLUE_1 = '#1890FF';
|
||||
export const CHART_BLUE_1 = '#4689FF';
|
||||
export const RIPTIDE = '#76E9C6';
|
||||
export const MY_SIN = '#FEB019';
|
||||
export const SAN_MARINO = '#416BB3';
|
||||
|
@ -123,28 +123,28 @@ export const INITIAL_COUNT_METRIC_VALUE = {
|
||||
entity: t('label.distinct'),
|
||||
}),
|
||||
dataKey: 'distinctCount',
|
||||
color: '#1890FF',
|
||||
color: '#467DDC',
|
||||
},
|
||||
{
|
||||
title: t('label.entity-count', {
|
||||
entity: t('label.null'),
|
||||
}),
|
||||
dataKey: 'nullCount',
|
||||
color: '#7147E8',
|
||||
color: '#3488B5',
|
||||
},
|
||||
{
|
||||
title: t('label.entity-count', {
|
||||
entity: t('label.unique'),
|
||||
}),
|
||||
dataKey: 'uniqueCount',
|
||||
color: '#008376',
|
||||
color: '#685997',
|
||||
},
|
||||
{
|
||||
title: t('label.entity-count', {
|
||||
entity: t('label.value-plural'),
|
||||
}),
|
||||
dataKey: 'valuesCount',
|
||||
color: '#B02AAC',
|
||||
color: '#464A52',
|
||||
},
|
||||
],
|
||||
data: [],
|
||||
@ -157,21 +157,21 @@ export const INITIAL_PROPORTION_METRIC_VALUE = {
|
||||
entity: t('label.distinct'),
|
||||
}),
|
||||
dataKey: 'distinctProportion',
|
||||
color: '#1890FF',
|
||||
color: '#6B97E3',
|
||||
},
|
||||
{
|
||||
title: t('label.entity-proportion', {
|
||||
entity: t('label.null'),
|
||||
}),
|
||||
dataKey: 'nullProportion',
|
||||
color: '#7147E8',
|
||||
color: '#867AAC',
|
||||
},
|
||||
{
|
||||
title: t('label.entity-proportion', {
|
||||
entity: t('label.unique'),
|
||||
}),
|
||||
dataKey: 'uniqueProportion',
|
||||
color: '#008376',
|
||||
color: '#6B6E75',
|
||||
},
|
||||
],
|
||||
data: [],
|
||||
@ -182,17 +182,17 @@ export const INITIAL_MATH_METRIC_VALUE = {
|
||||
{
|
||||
title: t('label.max'),
|
||||
dataKey: 'max',
|
||||
color: '#1890FF',
|
||||
color: '#6B97E3',
|
||||
},
|
||||
{
|
||||
title: t('label.mean'),
|
||||
dataKey: 'mean',
|
||||
color: '#7147E8',
|
||||
color: '#6B6E75',
|
||||
},
|
||||
{
|
||||
title: t('label.min'),
|
||||
dataKey: 'min',
|
||||
color: '#008376',
|
||||
color: '#867AAC',
|
||||
},
|
||||
],
|
||||
data: [],
|
||||
@ -203,7 +203,8 @@ export const INITIAL_SUM_METRIC_VALUE = {
|
||||
{
|
||||
title: t('label.sum'),
|
||||
dataKey: 'sum',
|
||||
color: '#1890FF',
|
||||
color: BLUE_500,
|
||||
fill: BLUE_50,
|
||||
},
|
||||
],
|
||||
data: [],
|
||||
@ -213,22 +214,22 @@ export const INITIAL_QUARTILE_METRIC_VALUE = {
|
||||
{
|
||||
title: t('label.first-quartile'),
|
||||
dataKey: 'firstQuartile',
|
||||
color: '#1890FF',
|
||||
color: '#467DDC',
|
||||
},
|
||||
{
|
||||
title: t('label.median'),
|
||||
dataKey: 'median',
|
||||
color: '#7147E8',
|
||||
color: '#3488B5',
|
||||
},
|
||||
{
|
||||
title: t('label.inter-quartile-range'),
|
||||
dataKey: 'interQuartileRange',
|
||||
color: '#008376',
|
||||
color: '#685997',
|
||||
},
|
||||
{
|
||||
title: t('label.third-quartile'),
|
||||
dataKey: 'thirdQuartile',
|
||||
color: '#B02AAC',
|
||||
color: '#464A52',
|
||||
},
|
||||
],
|
||||
data: [],
|
||||
|
@ -41,6 +41,7 @@ export interface ChartFilter {
|
||||
export interface DataInsightChartTooltipProps extends TooltipProps<any, any> {
|
||||
cardStyles?: React.CSSProperties;
|
||||
customValueKey?: string;
|
||||
displayDateInHeader?: boolean;
|
||||
dateTimeFormatter?: (date?: number, format?: string) => string;
|
||||
isPercentage?: boolean;
|
||||
isTier?: boolean;
|
||||
|
@ -369,11 +369,15 @@ export const CustomDQTooltip = (props: DataInsightChartTooltipProps) => {
|
||||
timeStampKey = 'timestampValue',
|
||||
transformLabel = true,
|
||||
valueFormatter,
|
||||
displayDateInHeader = true,
|
||||
} = props;
|
||||
|
||||
if (active && payload && payload.length) {
|
||||
// we need to check if the xAxis is a date or not.
|
||||
const timestamp = dateTimeFormatter(payload[0].payload[timeStampKey] || 0);
|
||||
const timestamp = displayDateInHeader
|
||||
? dateTimeFormatter(payload[0].payload[timeStampKey] || 0)
|
||||
: payload[0].payload[timeStampKey];
|
||||
|
||||
const payloadValue = uniqBy(payload, 'dataKey');
|
||||
|
||||
return (
|
||||
|
Loading…
x
Reference in New Issue
Block a user