diff --git a/frontend/src/components/ValueGraph/index.tsx b/frontend/src/components/ValueGraph/index.tsx
index 6f2eaa8de1..f0ee1e08d1 100644
--- a/frontend/src/components/ValueGraph/index.tsx
+++ b/frontend/src/components/ValueGraph/index.tsx
@@ -49,7 +49,10 @@ function ValueGraph({
}
>
-
+
)}
diff --git a/frontend/src/container/PanelWrapper/__tests__/TablePanelWrapper.test.tsx b/frontend/src/container/PanelWrapper/__tests__/TablePanelWrapper.test.tsx
new file mode 100644
index 0000000000..0c2389dead
--- /dev/null
+++ b/frontend/src/container/PanelWrapper/__tests__/TablePanelWrapper.test.tsx
@@ -0,0 +1,31 @@
+import { render } from 'tests/test-utils';
+import { Widgets } from 'types/api/dashboard/getAll';
+
+import TablePanelWrapper from '../TablePanelWrapper';
+import {
+ tablePanelQueryResponse,
+ tablePanelWidgetQuery,
+} from './tablePanelWrapperHelper';
+
+describe('Table panel wrappper tests', () => {
+ it('table should render fine with the query response and column units', () => {
+ const { container, getByText } = render(
+ {}}
+ />,
+ );
+ // checking the overall rendering of the table
+ expect(container).toMatchSnapshot();
+
+ // the first row of the table should have the latency value with units
+ expect(getByText('4.35 s')).toBeInTheDocument();
+
+ // the rows should have optimised value for human readability
+ expect(getByText('31.3 ms')).toBeInTheDocument();
+
+ // the applied legend should appear as the column header
+ expect(getByText('latency-per-service')).toBeInTheDocument();
+ });
+});
diff --git a/frontend/src/container/PanelWrapper/__tests__/ValuePanelWrapper.test.tsx b/frontend/src/container/PanelWrapper/__tests__/ValuePanelWrapper.test.tsx
new file mode 100644
index 0000000000..519083cd0b
--- /dev/null
+++ b/frontend/src/container/PanelWrapper/__tests__/ValuePanelWrapper.test.tsx
@@ -0,0 +1,38 @@
+import { render } from 'tests/test-utils';
+import { Widgets } from 'types/api/dashboard/getAll';
+
+import ValuePanelWrapper from '../ValuePanelWrapper';
+import {
+ thresholds,
+ valuePanelQueryResponse,
+ valuePanelWidget,
+} from './valuePanelWrapperHelper';
+
+describe('Value panel wrappper tests', () => {
+ it('should render value panel correctly with yaxis unit', () => {
+ const { getByText } = render(
+ {}}
+ />,
+ );
+
+ // selected y axis unit as miliseconds (ms)
+ expect(getByText('295 ms')).toBeInTheDocument();
+ });
+
+ it('should render tooltip when there are conflicting thresholds', () => {
+ const { getByTestId, container } = render(
+ {}}
+ />,
+ );
+
+ expect(getByTestId('conflicting-thresholds')).toBeInTheDocument();
+ // added snapshot test here for checking the thresholds color being applied properly
+ expect(container).toMatchSnapshot();
+ });
+});
diff --git a/frontend/src/container/PanelWrapper/__tests__/__snapshots__/TablePanelWrapper.test.tsx.snap b/frontend/src/container/PanelWrapper/__tests__/__snapshots__/TablePanelWrapper.test.tsx.snap
new file mode 100644
index 0000000000..d37ccf5841
--- /dev/null
+++ b/frontend/src/container/PanelWrapper/__tests__/__snapshots__/TablePanelWrapper.test.tsx.snap
@@ -0,0 +1,389 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Table panel wrappper tests table should render fine with the query response and column units 1`] = `
+.c1 {
+ position: absolute;
+ right: -0.313rem;
+ bottom: 0;
+ z-index: 1;
+ width: 0.625rem;
+ height: 100%;
+ cursor: col-resize;
+}
+
+.c0 {
+ height: 95%;
+ overflow: hidden;
+}
+
+.c0 .ant-table-wrapper {
+ height: 100%;
+}
+
+.c0 .ant-spin-nested-loading {
+ height: 100%;
+}
+
+.c0 .ant-spin-container {
+ height: 100%;
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-flex-direction: column;
+ -ms-flex-direction: column;
+ flex-direction: column;
+}
+
+.c0 .ant-table {
+ -webkit-flex: 1;
+ -ms-flex: 1;
+ flex: 1;
+ overflow: auto;
+}
+
+.c0 .ant-table > .ant-table-container > .ant-table-content > table {
+ min-width: 99% !important;
+}
+
+
+`;
diff --git a/frontend/src/container/PanelWrapper/__tests__/__snapshots__/ValuePanelWrapper.test.tsx.snap b/frontend/src/container/PanelWrapper/__tests__/__snapshots__/ValuePanelWrapper.test.tsx.snap
new file mode 100644
index 0000000000..435a7cb08d
--- /dev/null
+++ b/frontend/src/container/PanelWrapper/__tests__/__snapshots__/ValuePanelWrapper.test.tsx.snap
@@ -0,0 +1,76 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Value panel wrappper tests should render tooltip when there are conflicting thresholds 1`] = `
+.c1 {
+ height: 100%;
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-box-pack: center;
+ -webkit-justify-content: center;
+ -ms-flex-pack: center;
+ justify-content: center;
+ -webkit-align-items: center;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ -webkit-flex-direction: column;
+ -ms-flex-direction: column;
+ flex-direction: column;
+}
+
+.c0 {
+ text-align: center;
+ padding-top: 1rem;
+}
+
+
+`;
diff --git a/frontend/src/container/PanelWrapper/__tests__/tablePanelWrapperHelper.ts b/frontend/src/container/PanelWrapper/__tests__/tablePanelWrapperHelper.ts
new file mode 100644
index 0000000000..c6be13c200
--- /dev/null
+++ b/frontend/src/container/PanelWrapper/__tests__/tablePanelWrapperHelper.ts
@@ -0,0 +1,286 @@
+export const tablePanelWidgetQuery = {
+ id: '727533b0-7718-4f99-a1db-a1875649325c',
+ title: '',
+ description: '',
+ isStacked: false,
+ nullZeroValues: 'zero',
+ opacity: '1',
+ panelTypes: 'table',
+ query: {
+ clickhouse_sql: [
+ {
+ name: 'A',
+ legend: '',
+ disabled: false,
+ query: '',
+ },
+ ],
+ promql: [
+ {
+ name: 'A',
+ query: '',
+ legend: '',
+ disabled: false,
+ },
+ ],
+ builder: {
+ queryData: [
+ {
+ dataSource: 'metrics',
+ queryName: 'A',
+ aggregateOperator: 'count',
+ aggregateAttribute: {
+ key: 'signoz_latency',
+ dataType: 'float64',
+ type: 'ExponentialHistogram',
+ isColumn: true,
+ isJSON: false,
+ id: 'signoz_latency--float64--ExponentialHistogram--true',
+ },
+ timeAggregation: '',
+ spaceAggregation: 'p90',
+ functions: [],
+ filters: {
+ items: [],
+ op: 'AND',
+ },
+ expression: 'A',
+ disabled: false,
+ stepInterval: 60,
+ having: [],
+ limit: null,
+ orderBy: [],
+ groupBy: [
+ {
+ key: 'service_name',
+ dataType: 'string',
+ type: 'tag',
+ isColumn: false,
+ isJSON: false,
+ id: 'service_name--string--tag--false',
+ },
+ ],
+ legend: 'latency-per-service',
+ reduceTo: 'avg',
+ },
+ ],
+ queryFormulas: [],
+ },
+ id: '7feafec2-a450-4b5a-8897-260c1a9fe1e4',
+ queryType: 'builder',
+ },
+ timePreferance: 'GLOBAL_TIME',
+ softMax: null,
+ softMin: null,
+ selectedLogFields: [
+ {
+ dataType: 'string',
+ type: '',
+ name: 'body',
+ },
+ {
+ dataType: 'string',
+ type: '',
+ name: 'timestamp',
+ },
+ ],
+ selectedTracesFields: [
+ {
+ key: 'serviceName',
+ dataType: 'string',
+ type: 'tag',
+ isColumn: true,
+ isJSON: false,
+ id: 'serviceName--string--tag--true',
+ },
+ {
+ key: 'name',
+ dataType: 'string',
+ type: 'tag',
+ isColumn: true,
+ isJSON: false,
+ id: 'name--string--tag--true',
+ },
+ {
+ key: 'durationNano',
+ dataType: 'float64',
+ type: 'tag',
+ isColumn: true,
+ isJSON: false,
+ id: 'durationNano--float64--tag--true',
+ },
+ {
+ key: 'httpMethod',
+ dataType: 'string',
+ type: 'tag',
+ isColumn: true,
+ isJSON: false,
+ id: 'httpMethod--string--tag--true',
+ },
+ {
+ key: 'responseStatusCode',
+ dataType: 'string',
+ type: 'tag',
+ isColumn: true,
+ isJSON: false,
+ id: 'responseStatusCode--string--tag--true',
+ },
+ ],
+ yAxisUnit: 'none',
+ thresholds: [],
+ fillSpans: false,
+ columnUnits: {
+ A: 'ms',
+ },
+ bucketCount: 30,
+ stackedBarChart: false,
+ bucketWidth: 0,
+ mergeAllActiveQueries: false,
+};
+
+export const tablePanelQueryResponse = {
+ status: 'success',
+ isLoading: false,
+ isSuccess: true,
+ isError: false,
+ isIdle: false,
+ data: {
+ statusCode: 200,
+ error: null,
+ message: 'success',
+ payload: {
+ status: 'success',
+ data: {
+ resultType: '',
+ result: [
+ {
+ table: {
+ columns: [
+ {
+ name: 'service_name',
+ queryName: '',
+ isValueColumn: false,
+ },
+ {
+ name: 'A',
+ queryName: 'A',
+ isValueColumn: true,
+ },
+ ],
+ rows: [
+ {
+ data: {
+ A: 4353.81,
+ service_name: 'demo-app',
+ },
+ },
+ {
+ data: {
+ A: 431.25,
+ service_name: 'customer',
+ },
+ },
+ {
+ data: {
+ A: 431.25,
+ service_name: 'mysql',
+ },
+ },
+ {
+ data: {
+ A: 287.11,
+ service_name: 'frontend',
+ },
+ },
+ {
+ data: {
+ A: 230.02,
+ service_name: 'driver',
+ },
+ },
+ {
+ data: {
+ A: 66.37,
+ service_name: 'route',
+ },
+ },
+ {
+ data: {
+ A: 31.3,
+ service_name: 'redis',
+ },
+ },
+ ],
+ },
+ },
+ ],
+ },
+ },
+ params: {
+ start: 1721207225000,
+ end: 1721207525000,
+ step: 60,
+ variables: {},
+ formatForWeb: true,
+ compositeQuery: {
+ queryType: 'builder',
+ panelType: 'table',
+ fillGaps: false,
+ builderQueries: {
+ A: {
+ dataSource: 'metrics',
+ queryName: 'A',
+ aggregateOperator: 'count',
+ aggregateAttribute: {
+ key: 'signoz_latency',
+ dataType: 'float64',
+ type: 'ExponentialHistogram',
+ isColumn: true,
+ isJSON: false,
+ id: 'signoz_latency--float64--ExponentialHistogram--true',
+ },
+ timeAggregation: '',
+ spaceAggregation: 'p90',
+ functions: [],
+ filters: {
+ items: [],
+ op: 'AND',
+ },
+ expression: 'A',
+ disabled: false,
+ stepInterval: 60,
+ having: [],
+ limit: null,
+ orderBy: [],
+ groupBy: [
+ {
+ key: 'service_name',
+ dataType: 'string',
+ type: 'tag',
+ isColumn: false,
+ isJSON: false,
+ id: 'service_name--string--tag--false',
+ },
+ ],
+ legend: '',
+ reduceTo: 'avg',
+ },
+ },
+ },
+ },
+ },
+ dataUpdatedAt: 1721207526018,
+ error: null,
+ errorUpdatedAt: 0,
+ failureCount: 0,
+ errorUpdateCount: 0,
+ isFetched: true,
+ isFetchedAfterMount: true,
+ isFetching: false,
+ isRefetching: false,
+ isLoadingError: false,
+ isPlaceholderData: false,
+ isPreviousData: false,
+ isRefetchError: false,
+ isStale: true,
+};
diff --git a/frontend/src/container/PanelWrapper/__tests__/valuePanelWrapperHelper.ts b/frontend/src/container/PanelWrapper/__tests__/valuePanelWrapperHelper.ts
new file mode 100644
index 0000000000..1376feac06
--- /dev/null
+++ b/frontend/src/container/PanelWrapper/__tests__/valuePanelWrapperHelper.ts
@@ -0,0 +1,267 @@
+export const valuePanelWidget = {
+ id: 'b8b93086-ef01-47bf-9044-1e7abd583be4',
+ title: 'signoz latency in ms',
+ description: '',
+ isStacked: false,
+ nullZeroValues: 'zero',
+ opacity: '1',
+ panelTypes: 'value',
+ query: {
+ clickhouse_sql: [
+ {
+ name: 'A',
+ legend: '',
+ disabled: false,
+ query: '',
+ },
+ ],
+ promql: [
+ {
+ name: 'A',
+ query: '',
+ legend: '',
+ disabled: false,
+ },
+ ],
+ builder: {
+ queryData: [
+ {
+ dataSource: 'metrics',
+ queryName: 'A',
+ aggregateOperator: 'count',
+ aggregateAttribute: {
+ key: 'signoz_latency',
+ dataType: 'float64',
+ type: 'ExponentialHistogram',
+ isColumn: true,
+ isJSON: false,
+ id: 'signoz_latency--float64--ExponentialHistogram--true',
+ },
+ timeAggregation: '',
+ spaceAggregation: 'p90',
+ functions: [],
+ filters: {
+ items: [],
+ op: 'AND',
+ },
+ expression: 'A',
+ disabled: false,
+ stepInterval: 60,
+ having: [],
+ limit: null,
+ orderBy: [],
+ groupBy: [],
+ legend: '',
+ reduceTo: 'avg',
+ },
+ ],
+ queryFormulas: [],
+ },
+ id: '3bec289c-49c3-4d7e-98bb-84d47c79909c',
+ queryType: 'builder',
+ },
+ timePreferance: 'GLOBAL_TIME',
+ softMax: null,
+ softMin: null,
+ selectedLogFields: [
+ {
+ dataType: 'string',
+ type: '',
+ name: 'body',
+ },
+ {
+ dataType: 'string',
+ type: '',
+ name: 'timestamp',
+ },
+ ],
+ selectedTracesFields: [
+ {
+ key: 'serviceName',
+ dataType: 'string',
+ type: 'tag',
+ isColumn: true,
+ isJSON: false,
+ id: 'serviceName--string--tag--true',
+ },
+ {
+ key: 'name',
+ dataType: 'string',
+ type: 'tag',
+ isColumn: true,
+ isJSON: false,
+ id: 'name--string--tag--true',
+ },
+ {
+ key: 'durationNano',
+ dataType: 'float64',
+ type: 'tag',
+ isColumn: true,
+ isJSON: false,
+ id: 'durationNano--float64--tag--true',
+ },
+ {
+ key: 'httpMethod',
+ dataType: 'string',
+ type: 'tag',
+ isColumn: true,
+ isJSON: false,
+ id: 'httpMethod--string--tag--true',
+ },
+ {
+ key: 'responseStatusCode',
+ dataType: 'string',
+ type: 'tag',
+ isColumn: true,
+ isJSON: false,
+ id: 'responseStatusCode--string--tag--true',
+ },
+ ],
+ yAxisUnit: 'ms',
+ thresholds: [],
+ fillSpans: false,
+ columnUnits: {},
+ bucketCount: 30,
+ stackedBarChart: false,
+ bucketWidth: 0,
+ mergeAllActiveQueries: false,
+};
+
+export const thresholds = [
+ {
+ index: '8eb16a3a-b4f1-47c8-943a-4b1786884583',
+ isEditEnabled: false,
+ thresholdColor: 'Blue',
+ thresholdFormat: 'Text',
+ thresholdOperator: '>',
+ thresholdUnit: 'none',
+ thresholdValue: 100,
+ keyIndex: 1,
+ selectedGraph: 'value',
+ thresholdTableOptions: '',
+ thresholdLabel: '',
+ },
+ {
+ index: 'eb9c1186-ad7d-42dd-8e7f-3913a321d7cf',
+ isEditEnabled: false,
+ thresholdColor: 'Red',
+ thresholdFormat: 'Text',
+ thresholdOperator: '>',
+ thresholdUnit: 'none',
+ thresholdValue: 0,
+ keyIndex: 0,
+ selectedGraph: 'value',
+ thresholdTableOptions: '',
+ thresholdLabel: '',
+ },
+];
+
+export const valuePanelQueryResponse = {
+ status: 'success',
+ isLoading: false,
+ isSuccess: true,
+ isError: false,
+ isIdle: false,
+ data: {
+ statusCode: 200,
+ error: null,
+ message: 'success',
+ payload: {
+ data: {
+ result: [
+ {
+ metric: {
+ A: 'A',
+ },
+ values: [[0, '295.4299833508185']],
+ queryName: 'A',
+ legend: 'A',
+ },
+ ],
+ resultType: '',
+ newResult: {
+ status: 'success',
+ data: {
+ resultType: '',
+ result: [
+ {
+ queryName: 'A',
+ series: [
+ {
+ labels: {
+ A: 'A',
+ },
+ labelsArray: null,
+ values: [
+ {
+ timestamp: 0,
+ value: '295.4299833508185',
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ },
+ },
+ },
+ params: {
+ start: 1721203451000,
+ end: 1721203751000,
+ step: 60,
+ variables: {},
+ formatForWeb: false,
+ compositeQuery: {
+ queryType: 'builder',
+ panelType: 'value',
+ fillGaps: false,
+ builderQueries: {
+ A: {
+ dataSource: 'metrics',
+ queryName: 'A',
+ aggregateOperator: 'count',
+ aggregateAttribute: {
+ key: 'signoz_latency',
+ dataType: 'float64',
+ type: 'ExponentialHistogram',
+ isColumn: true,
+ isJSON: false,
+ id: 'signoz_latency--float64--ExponentialHistogram--true',
+ },
+ timeAggregation: '',
+ spaceAggregation: 'p90',
+ functions: [],
+ filters: {
+ items: [],
+ op: 'AND',
+ },
+ expression: 'A',
+ disabled: false,
+ stepInterval: 60,
+ having: [],
+ limit: null,
+ orderBy: [],
+ groupBy: [],
+ legend: '',
+ reduceTo: 'avg',
+ },
+ },
+ },
+ },
+ },
+ dataUpdatedAt: 1721203751775,
+ error: null,
+ errorUpdatedAt: 0,
+ failureCount: 0,
+ errorUpdateCount: 0,
+ isFetched: true,
+ isFetchedAfterMount: true,
+ isFetching: false,
+ isRefetching: false,
+ isLoadingError: false,
+ isPlaceholderData: false,
+ isPreviousData: false,
+ isRefetchError: false,
+ isStale: true,
+};