feat: per column unit option for table type (#5134)

* feat: base setup for individual column type units

* feat: added logic for y axis unit selection

* fix: light mode design

* feat: fix the mutation of original datasource array
This commit is contained in:
Vikrant Gupta 2024-06-04 11:14:54 +05:30 committed by GitHub
parent be9c3f0697
commit 2145e353c8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 169 additions and 8 deletions

View File

@ -1,12 +1,13 @@
import { ExclamationCircleFilled } from '@ant-design/icons';
import { Space, Tooltip } from 'antd';
import { getYAxisFormattedValue } from 'components/Graph/yAxisConfig';
import { Events } from 'constants/events';
import { QueryTable } from 'container/QueryTable';
import {
createTableColumnsFromQuery,
RowData,
} from 'lib/query/createTableColumnsFromQuery';
import { get, set } from 'lodash-es';
import { cloneDeep, get, isEmpty, set } from 'lodash-es';
import { memo, ReactNode, useCallback, useEffect, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { eventEmitter } from 'utils/getEventEmitter';
@ -19,11 +20,12 @@ function GridTableComponent({
data,
query,
thresholds,
columnUnits,
tableProcessedDataRef,
...props
}: GridTableComponentProps): JSX.Element {
const { t } = useTranslation(['valueGraph']);
const { columns, dataSource } = useMemo(
const { columns, dataSource: originalDataSource } = useMemo(
() =>
createTableColumnsFromQuery({
query,
@ -31,7 +33,6 @@ function GridTableComponent({
}),
[data, query],
);
const createDataInCorrectFormat = useCallback(
(dataSource: RowData[]): RowData[] =>
dataSource.map((d) => {
@ -52,6 +53,35 @@ function GridTableComponent({
[columns],
);
const applyColumnUnits = useCallback(
(dataSource: RowData[]): RowData[] => {
let mutateDataSource = cloneDeep(dataSource);
if (isEmpty(columnUnits)) {
return mutateDataSource;
}
mutateDataSource = mutateDataSource.map(
(val): RowData => {
const newValue = val;
Object.keys(val).forEach((k) => {
if (columnUnits[k]) {
newValue[k] = getYAxisFormattedValue(String(val[k]), columnUnits[k]);
}
});
return newValue;
},
);
return mutateDataSource;
},
[columnUnits],
);
const dataSource = useMemo(() => applyColumnUnits(originalDataSource), [
applyColumnUnits,
originalDataSource,
]);
useEffect(() => {
if (tableProcessedDataRef) {
// eslint-disable-next-line no-param-reassign

View File

@ -5,11 +5,13 @@ import {
ThresholdProps,
} from 'container/NewWidget/RightContainer/Threshold/types';
import { RowData } from 'lib/query/createTableColumnsFromQuery';
import { ColumnUnit } from 'types/api/dashboard/getAll';
import { Query } from 'types/api/queryBuilder/queryBuilderData';
export type GridTableComponentProps = {
query: Query;
thresholds?: ThresholdProps[];
columnUnits?: ColumnUnit;
tableProcessedDataRef?: React.MutableRefObject<RowData[]>;
} & Pick<LogsExplorerTableProps, 'data'> &
Omit<TableProps<RowData>, 'columns' | 'dataSource'>;

View File

@ -0,0 +1,27 @@
.column-unit-selector {
margin-top: 16px;
.heading {
color: var(--bg-vanilla-400);
font-family: 'Space Mono';
font-size: 13px;
font-style: normal;
font-weight: 400;
line-height: 18px; /* 138.462% */
letter-spacing: 0.52px;
text-transform: uppercase;
}
.y-axis-unit-selector {
flex-direction: row !important;
align-items: center;
}
}
.lightMode {
.column-unit-selector {
.heading {
color: var(--bg-ink-400);
}
}
}

View File

@ -0,0 +1,49 @@
import './ColumnUnitSelector.styles.scss';
import { Typography } from 'antd';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { Dispatch, SetStateAction } from 'react';
import { ColumnUnit } from 'types/api/dashboard/getAll';
import YAxisUnitSelector from '../YAxisUnitSelector';
interface ColumnUnitSelectorProps {
columnUnits: ColumnUnit;
setColumnUnits: Dispatch<SetStateAction<ColumnUnit>>;
}
export function ColumnUnitSelector(
props: ColumnUnitSelectorProps,
): JSX.Element {
const { currentQuery } = useQueryBuilder();
function getAggregateColumnsNamesAndLabels(): string[] {
return currentQuery.builder.queryData.map((q) => q.queryName);
}
const { columnUnits, setColumnUnits } = props;
const aggregationQueries = getAggregateColumnsNamesAndLabels();
function handleColumnUnitSelect(queryName: string, value: string): void {
setColumnUnits((prev) => ({
...prev,
[queryName]: value,
}));
}
return (
<section className="column-unit-selector">
<Typography.Text className="heading">Column Units</Typography.Text>
{aggregationQueries.map((query) => (
<YAxisUnitSelector
defaultValue={columnUnits[query]}
onSelect={(value: string): void => handleColumnUnitSelect(query, value)}
fieldLabel={query}
key={query}
handleClear={(): void => {
handleColumnUnitSelect(query, '');
}}
/>
))}
</section>
);
}

View File

@ -13,14 +13,17 @@ const findCategoryByName = (
): Record<string, string> | undefined =>
find(flattenedCategories, (option) => option.name === searchValue);
type OnSelectType = Dispatch<SetStateAction<string>> | ((val: string) => void);
function YAxisUnitSelector({
defaultValue,
onSelect,
fieldLabel,
handleClear,
}: {
defaultValue: string;
onSelect: Dispatch<SetStateAction<string>>;
onSelect: OnSelectType;
fieldLabel: string;
handleClear?: () => void;
}): JSX.Element {
const onSelectHandler = (selectedValue: string): void => {
onSelect(findCategoryByName(selectedValue)?.id || '');
@ -35,7 +38,9 @@ function YAxisUnitSelector({
style={{ width: '100%' }}
rootClassName="y-axis-root-popover"
options={options}
allowClear
defaultValue={findCategoryById(defaultValue)?.name}
onClear={handleClear}
onSelect={onSelectHandler}
filterOption={(inputValue, option): boolean => {
if (option) {
@ -46,10 +51,14 @@ function YAxisUnitSelector({
return false;
}}
>
<Input placeholder="Unit" allowClear rootClassName="input" />
<Input placeholder="Unit" rootClassName="input" />
</AutoComplete>
</div>
);
}
export default YAxisUnitSelector;
YAxisUnitSelector.defaultProps = {
handleClear: (): void => {},
};

View File

@ -68,7 +68,7 @@ export const panelTypeVsFillSpan: { [key in PANEL_TYPES]: boolean } = {
export const panelTypeVsYAxisUnit: { [key in PANEL_TYPES]: boolean } = {
[PANEL_TYPES.TIME_SERIES]: true,
[PANEL_TYPES.VALUE]: true,
[PANEL_TYPES.TABLE]: true,
[PANEL_TYPES.TABLE]: false,
[PANEL_TYPES.LIST]: false,
[PANEL_TYPES.PIE]: false,
[PANEL_TYPES.BAR]: true,
@ -99,3 +99,16 @@ export const panelTypeVsPanelTimePreferences: {
[PANEL_TYPES.TRACE]: false,
[PANEL_TYPES.EMPTY_WIDGET]: false,
} as const;
export const panelTypeVsColumnUnitPreferences: {
[key in PANEL_TYPES]: boolean;
} = {
[PANEL_TYPES.TIME_SERIES]: false,
[PANEL_TYPES.VALUE]: false,
[PANEL_TYPES.TABLE]: true,
[PANEL_TYPES.LIST]: false,
[PANEL_TYPES.PIE]: false,
[PANEL_TYPES.BAR]: false,
[PANEL_TYPES.TRACE]: false,
[PANEL_TYPES.EMPTY_WIDGET]: false,
} as const;

View File

@ -18,10 +18,12 @@ import {
useEffect,
useState,
} from 'react';
import { Widgets } from 'types/api/dashboard/getAll';
import { ColumnUnit, Widgets } from 'types/api/dashboard/getAll';
import { DataSource } from 'types/common/queryBuilder';
import { ColumnUnitSelector } from './ColumnUnitSelector/ColumnUnitSelector';
import {
panelTypeVsColumnUnitPreferences,
panelTypeVsCreateAlert,
panelTypeVsFillSpan,
panelTypeVsPanelTimePreferences,
@ -57,6 +59,8 @@ function RightContainer({
softMin,
setSoftMax,
setSoftMin,
columnUnits,
setColumnUnits,
}: RightContainerProps): JSX.Element {
const onChangeHandler = useCallback(
(setFunc: Dispatch<SetStateAction<string>>, value: string) => {
@ -78,6 +82,9 @@ function RightContainer({
const allowPanelTimePreference =
panelTypeVsPanelTimePreferences[selectedGraph];
const allowPanelColumnPreference =
panelTypeVsColumnUnitPreferences[selectedGraph];
const { currentQuery } = useQueryBuilder();
const [graphTypes, setGraphTypes] = useState<ItemsProps[]>(GraphTypes);
@ -179,6 +186,13 @@ function RightContainer({
</>
)}
{allowPanelColumnPreference && (
<ColumnUnitSelector
columnUnits={columnUnits}
setColumnUnits={setColumnUnits}
/>
)}
{allowYAxisUnit && (
<YAxisUnitSelector
defaultValue={yAxisUnit}
@ -258,6 +272,8 @@ interface RightContainerProps {
setIsFillSpans: Dispatch<SetStateAction<boolean>>;
softMin: number | null;
softMax: number | null;
columnUnits: ColumnUnit;
setColumnUnits: Dispatch<SetStateAction<ColumnUnit>>;
setSoftMin: Dispatch<SetStateAction<number | null>>;
setSoftMax: Dispatch<SetStateAction<number | null>>;
}

View File

@ -30,7 +30,7 @@ import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';
import { generatePath, useParams } from 'react-router-dom';
import { AppState } from 'store/reducers';
import { Dashboard, Widgets } from 'types/api/dashboard/getAll';
import { ColumnUnit, Dashboard, Widgets } from 'types/api/dashboard/getAll';
import { IField } from 'types/api/logs/fields';
import { EQueryType } from 'types/common/dashboard';
import { DataSource } from 'types/common/queryBuilder';
@ -156,6 +156,10 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
: selectedWidget?.softMax || 0,
);
const [columnUnits, setColumnUnits] = useState<ColumnUnit>(
selectedWidget?.columnUnits || {},
);
useEffect(() => {
setSelectedWidget((prev) => {
if (!prev) {
@ -174,11 +178,13 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
softMin,
softMax,
fillSpans: isFillSpans,
columnUnits,
selectedLogFields,
selectedTracesFields,
};
});
}, [
columnUnits,
currentQuery,
description,
isFillSpans,
@ -284,6 +290,7 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
panelTypes: graphType,
query: currentQuery,
thresholds: selectedWidget?.thresholds,
columnUnits: selectedWidget?.columnUnits,
softMin: selectedWidget?.softMin || 0,
softMax: selectedWidget?.softMax || 0,
fillSpans: selectedWidget?.fillSpans,
@ -305,6 +312,7 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
panelTypes: graphType,
query: currentQuery,
thresholds: selectedWidget?.thresholds,
columnUnits: selectedWidget?.columnUnits,
softMin: selectedWidget?.softMin || 0,
softMax: selectedWidget?.softMax || 0,
fillSpans: selectedWidget?.fillSpans,
@ -483,6 +491,8 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
setStacked={setStacked}
opacity={opacity}
yAxisUnit={yAxisUnit}
columnUnits={columnUnits}
setColumnUnits={setColumnUnits}
setOpacity={setOpacity}
selectedNullZeroValue={selectedNullZeroValue}
setSelectedNullZeroValue={setSelectedNullZeroValue}

View File

@ -16,6 +16,7 @@ function TablePanelWrapper({
data={panelData}
query={widget.query}
thresholds={thresholds}
columnUnits={widget.columnUnits}
tableProcessedDataRef={tableProcessedDataRef}
// eslint-disable-next-line react/jsx-props-no-spreading
{...GRID_TABLE_CONFIG}

View File

@ -83,6 +83,9 @@ export interface WidgetRow {
description: string;
}
export interface ColumnUnit {
[key: string]: string;
}
export interface IBaseWidget {
isStacked: boolean;
id: string;
@ -98,6 +101,7 @@ export interface IBaseWidget {
softMin: number | null;
softMax: number | null;
fillSpans?: boolean;
columnUnits?: ColumnUnit;
selectedLogFields: IField[] | null;
selectedTracesFields: BaseAutocompleteData[] | null;
}