fix(UI): graph legends is fixed (#461)

* fix(UI): graph legends is fixed

* chore(UI): some changes regarding the color of the chart is updated

* full view css is fixed

* usage explorer graph is fixed

* default query is removed

* fix: scroll is removed
This commit is contained in:
pal-sig 2021-12-10 14:28:31 +05:30 committed by GitHub
parent d9a99827c0
commit c79223742f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 183 additions and 117 deletions

View File

@ -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 (
<Container>
<ColorContainer color={color}></ColorContainer>
<Typography>{text}</Typography>
</Container>
);
};
interface LegendProps {
text: string;
color: string;
}
export default Legend;

View File

@ -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<Props>`
background-color: ${({ color }): string => color};
border-radius: 50%;
width: 20px;
height: 20px;
margin-right: 0.5rem;
`;

View File

@ -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<ChartType> => {
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);
}
});
},
};
};

View File

@ -0,0 +1 @@
export * from './Legend';

View File

@ -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<AppState, AppReducer>((state) => state.app);
const chartRef = useRef<HTMLCanvasElement>(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 <canvas ref={chartRef} />;
return (
<div style={{ height: '85%' }}>
<canvas ref={chartRef} />
<LegendsContainer id={name} />
</div>
);
};
interface GraphProps {
@ -174,6 +165,7 @@ interface GraphProps {
isStacked?: boolean;
label?: string[];
onClickHandler?: graphOnClickHandler;
name: string;
}
export type graphOnClickHandler = (

View File

@ -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 */
}
`;

View File

@ -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;

View File

@ -27,6 +27,7 @@ const FullView = ({
fullViewOptions = true,
onClickHandler,
noDataGraph = false,
name,
}: FullViewProps): JSX.Element => {
const { minTime, maxTime } = useSelector<AppState, GlobalTime>(
(state) => state.globalTime,
@ -189,7 +190,7 @@ const FullView = ({
</TimeContainer>
)}
<GraphContainer>
{/* <GraphContainer> */}
<GridGraphComponent
{...{
GRAPH_TYPES: widget.panelTypes,
@ -198,9 +199,10 @@ const FullView = ({
opacity: widget.opacity,
title: widget.title,
onClickHandler: onClickHandler,
name,
}}
/>
</GraphContainer>
{/* </GraphContainer> */}
</>
);
};
@ -217,6 +219,7 @@ interface FullViewProps {
fullViewOptions?: boolean;
onClickHandler?: graphOnClickHandler;
noDataGraph?: boolean;
name: string;
}
export default FullView;

View File

@ -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<GridCardGraphState>({
loading: true,
@ -166,7 +167,9 @@ const GridCardGraph = ({
width="85%"
destroyOnClose
>
<FullView widget={widget} />
<FullViewContainer>
<FullView name={name} widget={widget} />
</FullViewContainer>
</Modal>
<GridGraphComponent
@ -176,6 +179,7 @@ const GridCardGraph = ({
isStacked: widget.isStacked,
opacity: widget.opacity,
title: widget.title,
name,
}}
/>
</>
@ -198,6 +202,7 @@ interface DispatchProps {
interface GridCardGraphProps extends DispatchProps {
widget: Widgets;
isDeleted: React.MutableRefObject<boolean>;
name: string;
}
const mapDispatchToProps = (

View File

@ -11,3 +11,7 @@ export const Modal = styled(ModalComponent)<Props>`
min-height: ${({ height = '80vh' }): string => height};
}
`;
export const FullViewContainer = styled.div`
height: 70vh;
`;

View File

@ -60,7 +60,11 @@ const GridGraph = (): JSX.Element => {
i: (index + 1).toString(),
x: (index % 2) * 6,
Component: (): JSX.Element => (
<Graph isDeleted={isDeleted} widget={widgets[index]} />
<Graph
name={e.id + index}
isDeleted={isDeleted}
widget={widgets[index]}
/>
),
};
});
@ -69,7 +73,7 @@ const GridGraph = (): JSX.Element => {
...e,
y: 0,
Component: (): JSX.Element => (
<Graph isDeleted={isDeleted} widget={widgets[index]} />
<Graph name={e.i + index} isDeleted={isDeleted} widget={widgets[index]} />
),
}));
}

View File

@ -13,7 +13,7 @@ export const Card = styled(CardComponent)<Props>`
}
.ant-card-body {
height: 100%;
height: 95%;
padding: 0;
}
`;

View File

@ -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 => {
<GraphTitle>Request per sec</GraphTitle>
<GraphContainer>
<FullView
name="request_per_sec"
noDataGraph
fullViewOptions={false}
onClickHandler={(event, element, chart, data): void => {
@ -206,11 +208,11 @@ const Application = ({ getWidget }: DashboardProps): JSX.Element => {
View Traces
</Button>
<Card>
<Card>
<GraphTitle>Error Percentage (%)</GraphTitle>
<GraphContainer>
<FullView
name="error_percentage_%"
noDataGraph
fullViewOptions={false}
onClickHandler={(ChartEvent, activeElements, chart, data): void => {
@ -225,7 +227,6 @@ const Application = ({ getWidget }: DashboardProps): JSX.Element => {
/>
</GraphContainer>
</Card>
</Card>
</Col>
<Col span={12}>

View File

@ -17,6 +17,7 @@ const DBCall = ({ getWidget }: DBCallProps): JSX.Element => {
<GraphTitle>Database Calls RPS</GraphTitle>
<GraphContainer>
<FullView
name="database_call_rps"
noDataGraph
fullViewOptions={false}
widget={getWidget([
@ -35,6 +36,7 @@ const DBCall = ({ getWidget }: DBCallProps): JSX.Element => {
<GraphTitle>Database Calls Avg Duration (in ms)</GraphTitle>
<GraphContainer>
<FullView
name="database_call_avg_duration"
noDataGraph
fullViewOptions={false}
widget={getWidget([

View File

@ -17,6 +17,7 @@ const External = ({ getWidget }: ExternalProps): JSX.Element => {
<GraphTitle>External Call Error Percentage (%)</GraphTitle>
<GraphContainer>
<FullView
name="external_call_error_percentage"
fullViewOptions={false}
noDataGraph
widget={getWidget([
@ -35,6 +36,7 @@ const External = ({ getWidget }: ExternalProps): JSX.Element => {
<GraphTitle>External Call duration</GraphTitle>
<GraphContainer>
<FullView
name="external_call_duration"
noDataGraph
fullViewOptions={false}
widget={getWidget([
@ -55,6 +57,7 @@ const External = ({ getWidget }: ExternalProps): JSX.Element => {
<GraphTitle>External Call RPS(by Address)</GraphTitle>
<GraphContainer>
<FullView
name="external_call_rps_by_address"
noDataGraph
fullViewOptions={false}
widget={getWidget([
@ -74,6 +77,7 @@ const External = ({ getWidget }: ExternalProps): JSX.Element => {
<GraphContainer>
<FullView
noDataGraph
name="external_call_duration_by_address"
fullViewOptions={false}
widget={getWidget([
{

View File

@ -30,13 +30,7 @@ export const Col = styled(ColComponent)`
`;
export const GraphContainer = styled.div`
min-height: 40vh;
max-height: 40vh;
div {
min-height: 40vh;
max-height: 40vh;
}
height: 40vh;
`;
export const GraphTitle = styled(Typography)`

View File

@ -50,6 +50,7 @@ const WidgetGraph = ({ selectedGraph }: WidgetGraphProps): JSX.Element => {
opacity={opacity}
data={chartDataSet}
GRAPH_TYPES={selectedGraph}
name={widgetId || 'legend_widget'}
/>
);
};

View File

@ -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)`

View File

@ -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;

View File

@ -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,
};

View File

@ -186,7 +186,7 @@ const _UsageExplorer = (props: UsageExplorerProps): JSX.Element => {
</Space>
<Card>
<Graph data={data} type="bar" />
<Graph name="usage" data={data} type="bar" />
</Card>
</React.Fragment>
);

View File

@ -8,7 +8,6 @@ export const Card = styled(CardComponent)`
}
.ant-card-body {
height: 100%;
min-height: 70vh;
height: 70vh;
}
`;