diff --git a/frontend/src/components/Graph/Legend/index.tsx b/frontend/src/components/Graph/Legend/index.tsx
deleted file mode 100644
index 41255241db..0000000000
--- a/frontend/src/components/Graph/Legend/index.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { Typography } from 'antd';
-import React from 'react';
-
-import { ColorContainer, Container } from './styles';
-
-const Legend = ({ text, color }: LegendProps): JSX.Element => {
- if (text.length === 0) {
- return <>>;
- }
-
- return (
-
-
- {text}
-
- );
-};
-
-interface LegendProps {
- text: string;
- color: string;
-}
-
-export default Legend;
diff --git a/frontend/src/components/Graph/Legend/styles.ts b/frontend/src/components/Graph/Legend/styles.ts
deleted file mode 100644
index ac627bf0ea..0000000000
--- a/frontend/src/components/Graph/Legend/styles.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import styled from 'styled-components';
-
-export const Container = styled.div`
- margin-left: 2rem;
- margin-right: 2rem;
- display: flex;
- cursor: pointer;
-`;
-
-interface Props {
- color: string;
-}
-
-export const ColorContainer = styled.div`
- background-color: ${({ color }): string => color};
- border-radius: 50%;
- width: 20px;
- height: 20px;
- margin-right: 0.5rem;
-`;
diff --git a/frontend/src/components/Graph/Plugin/Legend.ts b/frontend/src/components/Graph/Plugin/Legend.ts
new file mode 100644
index 0000000000..6c4826e5f7
--- /dev/null
+++ b/frontend/src/components/Graph/Plugin/Legend.ts
@@ -0,0 +1,91 @@
+import { Plugin, ChartType, Chart, ChartOptions } from 'chart.js';
+import { colors } from 'lib/getRandomColor';
+
+const getOrCreateLegendList = (chart: Chart, id: string, isLonger: boolean) => {
+ const legendContainer = document.getElementById(id);
+ let listContainer = legendContainer?.querySelector('ul');
+
+ if (!listContainer) {
+ listContainer = document.createElement('ul');
+ listContainer.style.display = 'flex';
+ // listContainer.style.flexDirection = isLonger ? 'column' : 'row';
+ listContainer.style.margin = '0';
+ listContainer.style.padding = '0';
+ listContainer.style.overflowY = 'scroll';
+ listContainer.style.justifyContent = isLonger ? 'start' : 'center';
+ listContainer.style.alignItems = isLonger ? 'start' : 'center';
+ listContainer.style.height = '100%';
+ listContainer.style.flexWrap = 'wrap';
+ listContainer.style.justifyContent = 'center';
+
+ legendContainer?.appendChild(listContainer);
+ }
+
+ return listContainer;
+};
+
+export const legend = (id: string, isLonger: boolean): Plugin => {
+ return {
+ id: 'htmlLegend',
+ afterUpdate(chart, args, options: ChartOptions) {
+ const ul = getOrCreateLegendList(chart, id || 'legend', isLonger);
+
+ // Remove old legend items
+ while (ul.firstChild) {
+ ul.firstChild.remove();
+ }
+
+ // Reuse the built-in legendItems generator
+ const items = chart?.options?.plugins?.legend?.labels?.generateLabels(chart);
+
+ items?.forEach((item, index) => {
+ const li = document.createElement('li');
+ li.style.alignItems = 'center';
+ li.style.cursor = 'pointer';
+ li.style.display = 'flex';
+ li.style.marginLeft = '10px';
+ li.style.marginTop = '5px';
+
+ li.onclick = () => {
+ const { type } = chart.config;
+ if (type === 'pie' || type === 'doughnut') {
+ // Pie and doughnut charts only have a single dataset and visibility is per item
+ chart.toggleDataVisibility(index);
+ } else {
+ chart.setDatasetVisibility(
+ item.datasetIndex,
+ !chart.isDatasetVisible(item.datasetIndex),
+ );
+ }
+ chart.update();
+ };
+
+ // Color box
+ const boxSpan = document.createElement('span');
+ boxSpan.style.background = item.strokeStyle || colors[0];
+ boxSpan.style.borderColor = item?.strokeStyle;
+ boxSpan.style.borderWidth = item.lineWidth + 'px';
+ boxSpan.style.display = 'inline-block';
+ boxSpan.style.minHeight = '20px';
+ boxSpan.style.marginRight = '10px';
+ boxSpan.style.minWidth = '20px';
+ boxSpan.style.borderRadius = '50%';
+
+ if (item.text) {
+ // Text
+ const textContainer = document.createElement('span');
+ textContainer.style.margin = '0';
+ textContainer.style.padding = '0';
+ textContainer.style.textDecoration = item.hidden ? 'line-through' : '';
+
+ const text = document.createTextNode(item.text);
+ textContainer.appendChild(text);
+
+ li.appendChild(boxSpan);
+ li.appendChild(textContainer);
+ ul.appendChild(li);
+ }
+ });
+ },
+ };
+};
diff --git a/frontend/src/components/Graph/Plugin/index.ts b/frontend/src/components/Graph/Plugin/index.ts
new file mode 100644
index 0000000000..6adb3cc7d3
--- /dev/null
+++ b/frontend/src/components/Graph/Plugin/index.ts
@@ -0,0 +1 @@
+export * from './Legend';
diff --git a/frontend/src/components/Graph/index.tsx b/frontend/src/components/Graph/index.tsx
index 45041ad505..b6905495d4 100644
--- a/frontend/src/components/Graph/index.tsx
+++ b/frontend/src/components/Graph/index.tsx
@@ -11,7 +11,6 @@ import {
Decimation,
Filler,
Legend,
- // LegendItem,
LinearScale,
LineController,
LineElement,
@@ -23,15 +22,11 @@ import {
Tooltip,
} from 'chart.js';
import * as chartjsAdapter from 'chartjs-adapter-date-fns';
-// import { colors } from 'lib/getRandomColor';
-// import stringToHTML from 'lib/stringToHTML';
import React, { useCallback, useEffect, useRef } from 'react';
import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
import AppReducer from 'types/reducer/app';
-// import Legends from './Legend';
-// import { LegendsContainer } from './styles';
Chart.register(
LineElement,
PointElement,
@@ -49,6 +44,8 @@ Chart.register(
BarController,
BarElement,
);
+import { legend } from './Plugin';
+import { LegendsContainer } from './styles';
const Graph = ({
data,
@@ -56,6 +53,7 @@ const Graph = ({
title,
isStacked,
onClickHandler,
+ name,
}: GraphProps): JSX.Element => {
const { isDarkMode } = useSelector((state) => state.app);
const chartRef = useRef(null);
@@ -95,20 +93,7 @@ const Graph = ({
text: title,
},
legend: {
- // just making sure that label is present
- display: !(
- data.datasets.find((e) => {
- if (e.label?.length === 0) {
- return false;
- }
- return e.label !== undefined;
- }) === undefined
- ),
- labels: {
- usePointStyle: true,
- pointStyle: 'circle',
- },
- position: 'bottom',
+ display: false,
},
},
layout: {
@@ -156,6 +141,7 @@ const Graph = ({
type: type,
data: data,
options,
+ plugins: [legend(name, data.datasets.length > 3)],
});
}
}, [chartRef, data, type, title, isStacked, getGridColor, onClickHandler]);
@@ -164,7 +150,12 @@ const Graph = ({
buildChart();
}, [buildChart]);
- return ;
+ return (
+
+
+
+
+ );
};
interface GraphProps {
@@ -174,6 +165,7 @@ interface GraphProps {
isStacked?: boolean;
label?: string[];
onClickHandler?: graphOnClickHandler;
+ name: string;
}
export type graphOnClickHandler = (
diff --git a/frontend/src/components/Graph/styles.ts b/frontend/src/components/Graph/styles.ts
index 204a78a57f..e0fa56dee1 100644
--- a/frontend/src/components/Graph/styles.ts
+++ b/frontend/src/components/Graph/styles.ts
@@ -1,8 +1,14 @@
import styled from 'styled-components';
export const LegendsContainer = styled.div`
- display: flex;
- overflow-y: scroll;
- margin-right: 1rem;
- margin-bottom: 1rem;
+ height: 15%;
+
+ * {
+ ::-webkit-scrollbar {
+ display: none !important;
+ }
+
+ -ms-overflow-style: none !important; /* IE and Edge */
+ scrollbar-width: none !important; /* Firefox */
+ }
`;
diff --git a/frontend/src/container/GridGraphComponent/index.tsx b/frontend/src/container/GridGraphComponent/index.tsx
index 6849903c2f..ba3107f96b 100644
--- a/frontend/src/container/GridGraphComponent/index.tsx
+++ b/frontend/src/container/GridGraphComponent/index.tsx
@@ -15,6 +15,7 @@ const GridGraphComponent = ({
opacity,
isStacked,
onClickHandler,
+ name,
}: GridGraphComponentProps): JSX.Element | null => {
const location = history.location.pathname;
@@ -31,6 +32,7 @@ const GridGraphComponent = ({
opacity,
xAxisType: 'time',
onClickHandler: onClickHandler,
+ name,
}}
/>
);
@@ -69,6 +71,7 @@ export interface GridGraphComponentProps {
opacity?: string;
isStacked?: boolean;
onClickHandler?: graphOnClickHandler;
+ name: string;
}
export default GridGraphComponent;
diff --git a/frontend/src/container/GridGraphLayout/Graph/FullView/index.tsx b/frontend/src/container/GridGraphLayout/Graph/FullView/index.tsx
index 1d66a60488..b2cbc0366d 100644
--- a/frontend/src/container/GridGraphLayout/Graph/FullView/index.tsx
+++ b/frontend/src/container/GridGraphLayout/Graph/FullView/index.tsx
@@ -27,6 +27,7 @@ const FullView = ({
fullViewOptions = true,
onClickHandler,
noDataGraph = false,
+ name,
}: FullViewProps): JSX.Element => {
const { minTime, maxTime } = useSelector(
(state) => state.globalTime,
@@ -189,18 +190,19 @@ const FullView = ({
)}
-
-
-
+ {/* */}
+
+ {/* */}
>
);
};
@@ -217,6 +219,7 @@ interface FullViewProps {
fullViewOptions?: boolean;
onClickHandler?: graphOnClickHandler;
noDataGraph?: boolean;
+ name: string;
}
export default FullView;
diff --git a/frontend/src/container/GridGraphLayout/Graph/index.tsx b/frontend/src/container/GridGraphLayout/Graph/index.tsx
index e1a21c383c..60a086befa 100644
--- a/frontend/src/container/GridGraphLayout/Graph/index.tsx
+++ b/frontend/src/container/GridGraphLayout/Graph/index.tsx
@@ -23,12 +23,13 @@ import { Widgets } from 'types/api/dashboard/getAll';
import Bar from './Bar';
import FullView from './FullView';
-import { Modal } from './styles';
+import { Modal, FullViewContainer } from './styles';
const GridCardGraph = ({
widget,
deleteWidget,
isDeleted,
+ name,
}: GridCardGraphProps): JSX.Element => {
const [state, setState] = useState({
loading: true,
@@ -166,7 +167,9 @@ const GridCardGraph = ({
width="85%"
destroyOnClose
>
-
+
+
+
>
@@ -198,6 +202,7 @@ interface DispatchProps {
interface GridCardGraphProps extends DispatchProps {
widget: Widgets;
isDeleted: React.MutableRefObject;
+ name: string;
}
const mapDispatchToProps = (
diff --git a/frontend/src/container/GridGraphLayout/Graph/styles.ts b/frontend/src/container/GridGraphLayout/Graph/styles.ts
index 156b7553d7..61f4c7c9c9 100644
--- a/frontend/src/container/GridGraphLayout/Graph/styles.ts
+++ b/frontend/src/container/GridGraphLayout/Graph/styles.ts
@@ -11,3 +11,7 @@ export const Modal = styled(ModalComponent)`
min-height: ${({ height = '80vh' }): string => height};
}
`;
+
+export const FullViewContainer = styled.div`
+ height: 70vh;
+`;
diff --git a/frontend/src/container/GridGraphLayout/index.tsx b/frontend/src/container/GridGraphLayout/index.tsx
index 49fbd0df25..c72885f9ef 100644
--- a/frontend/src/container/GridGraphLayout/index.tsx
+++ b/frontend/src/container/GridGraphLayout/index.tsx
@@ -60,7 +60,11 @@ const GridGraph = (): JSX.Element => {
i: (index + 1).toString(),
x: (index % 2) * 6,
Component: (): JSX.Element => (
-
+
),
};
});
@@ -69,7 +73,7 @@ const GridGraph = (): JSX.Element => {
...e,
y: 0,
Component: (): JSX.Element => (
-
+
),
}));
}
diff --git a/frontend/src/container/GridGraphLayout/styles.ts b/frontend/src/container/GridGraphLayout/styles.ts
index d8172e4ebf..0b8f4cfc89 100644
--- a/frontend/src/container/GridGraphLayout/styles.ts
+++ b/frontend/src/container/GridGraphLayout/styles.ts
@@ -13,7 +13,7 @@ export const Card = styled(CardComponent)`
}
.ant-card-body {
- height: 100%;
+ height: 95%;
padding: 0;
}
`;
diff --git a/frontend/src/container/MetricsApplication/Tabs/Application.tsx b/frontend/src/container/MetricsApplication/Tabs/Application.tsx
index d31a12a8a7..90e85230a1 100644
--- a/frontend/src/container/MetricsApplication/Tabs/Application.tsx
+++ b/frontend/src/container/MetricsApplication/Tabs/Application.tsx
@@ -114,6 +114,7 @@ const Application = ({ getWidget }: DashboardProps): JSX.Element => {
onClickHandler={(ChartEvent, activeElements, chart, data): void => {
onClickhandler(ChartEvent, activeElements, chart, data, 'Application');
}}
+ name="application_latency"
type="line"
data={{
datasets: [
@@ -177,6 +178,7 @@ const Application = ({ getWidget }: DashboardProps): JSX.Element => {
Request per sec
{
@@ -207,24 +209,23 @@ const Application = ({ getWidget }: DashboardProps): JSX.Element => {
-
- Error Percentage (%)
-
- {
- onClickhandler(ChartEvent, activeElements, chart, data, 'Error');
- }}
- widget={getWidget([
- {
- query: `sum(rate(signoz_calls_total{service_name="${servicename}", span_kind="SPAN_KIND_SERVER", status_code="STATUS_CODE_ERROR"}[1m]) OR rate(signoz_calls_total{service_name="${servicename}", span_kind="SPAN_KIND_SERVER", http_status_code=~"5.."}[1m]) OR vector(0))*100/sum(rate(signoz_calls_total{service_name="${servicename}", span_kind="SPAN_KIND_SERVER"}[1m]))`,
- legend: 'Error Percentage (%)',
- },
- ])}
- />
-
-
+ Error Percentage (%)
+
+ {
+ onClickhandler(ChartEvent, activeElements, chart, data, 'Error');
+ }}
+ widget={getWidget([
+ {
+ query: `sum(rate(signoz_calls_total{service_name="${servicename}", span_kind="SPAN_KIND_SERVER", status_code="STATUS_CODE_ERROR"}[1m]) OR rate(signoz_calls_total{service_name="${servicename}", span_kind="SPAN_KIND_SERVER", http_status_code=~"5.."}[1m]) OR vector(0))*100/sum(rate(signoz_calls_total{service_name="${servicename}", span_kind="SPAN_KIND_SERVER"}[1m]))`,
+ legend: 'Error Percentage (%)',
+ },
+ ])}
+ />
+
diff --git a/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx b/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx
index 45c081d1da..44fda1e230 100644
--- a/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx
+++ b/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx
@@ -17,6 +17,7 @@ const DBCall = ({ getWidget }: DBCallProps): JSX.Element => {
Database Calls RPS
{
Database Calls Avg Duration (in ms)
{
External Call Error Percentage (%)
{
External Call duration
{
External Call RPS(by Address)
{
{
opacity={opacity}
data={chartDataSet}
GRAPH_TYPES={selectedGraph}
+ name={widgetId || 'legend_widget'}
/>
);
};
diff --git a/frontend/src/container/TraceCustomVisualization/styles.ts b/frontend/src/container/TraceCustomVisualization/styles.ts
index 6a06cbd717..069feaad73 100644
--- a/frontend/src/container/TraceCustomVisualization/styles.ts
+++ b/frontend/src/container/TraceCustomVisualization/styles.ts
@@ -7,7 +7,7 @@ import {
import styled from 'styled-components';
export const CustomGraphContainer = styled.div`
- min-height: 30vh;
+ height: 30vh;
`;
export const Card = styled(CardComponent)`
diff --git a/frontend/src/lib/stringToHTML.ts b/frontend/src/lib/JSXtoHTML.ts
similarity index 68%
rename from frontend/src/lib/stringToHTML.ts
rename to frontend/src/lib/JSXtoHTML.ts
index b5fb77afbf..48a758d085 100644
--- a/frontend/src/lib/stringToHTML.ts
+++ b/frontend/src/lib/JSXtoHTML.ts
@@ -1,9 +1,9 @@
import { renderToString } from 'react-dom/server';
-const stringToHTML = function (str: JSX.Element): HTMLElement {
+const JSXtoHTML = function (str: JSX.Element): HTMLElement {
const parser = new DOMParser();
const doc = parser.parseFromString(renderToString(str), 'text/html');
return doc.body.firstChild as HTMLElement;
};
-export default stringToHTML;
+export default JSXtoHTML;
diff --git a/frontend/src/lib/getChartData.ts b/frontend/src/lib/getChartData.ts
index e20df3db49..d745706a94 100644
--- a/frontend/src/lib/getChartData.ts
+++ b/frontend/src/lib/getChartData.ts
@@ -47,7 +47,7 @@ const getChartData = ({ queryData }: GetChartDataProps): ChartData => {
borderWidth: 1.5,
spanGaps: true,
animations: false,
- borderColor: colors[index] || 'red',
+ borderColor: colors[index % colors.length] || 'red',
showLine: true,
pointRadius: 0,
};
diff --git a/frontend/src/modules/Usage/UsageExplorer.tsx b/frontend/src/modules/Usage/UsageExplorer.tsx
index f6bbe06c7b..a7566b1bd0 100644
--- a/frontend/src/modules/Usage/UsageExplorer.tsx
+++ b/frontend/src/modules/Usage/UsageExplorer.tsx
@@ -186,7 +186,7 @@ const _UsageExplorer = (props: UsageExplorerProps): JSX.Element => {
-
+
);
diff --git a/frontend/src/modules/Usage/styles.ts b/frontend/src/modules/Usage/styles.ts
index 26efebc856..4bb27fc585 100644
--- a/frontend/src/modules/Usage/styles.ts
+++ b/frontend/src/modules/Usage/styles.ts
@@ -8,7 +8,6 @@ export const Card = styled(CardComponent)`
}
.ant-card-body {
- height: 100%;
- min-height: 70vh;
+ height: 70vh;
}
`;