Skip to content

Commit 730953f

Browse files
committed
Merge branch 'kubecon-2025' of https://github.com/devtron-labs/devtron-fe-common-lib into feat/cost-config
2 parents 9138ef7 + 6e7dcf6 commit 730953f

File tree

9 files changed

+407
-178
lines changed

9 files changed

+407
-178
lines changed
Lines changed: 5 additions & 0 deletions
Loading

src/Shared/Components/ActionMenu/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ export type ActionMenuItemType<T extends string | number = string | number> = Om
6464
trailingItem?: TrailingItemType
6565
/** Prevents the menu from closing when the item is clicked. */
6666
doNotCloseMenuOnClick?: boolean
67+
dataAttributesId?: number
6768
} & ConditionalActionMenuComponentType
6869

6970
export type ActionMenuOptionType<T extends string | number> = {

src/Shared/Components/Charts/Chart.component.tsx

Lines changed: 84 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
LineController,
1414
LineElement,
1515
PointElement,
16+
TimeScale,
1617
Title,
1718
Tooltip as ChartJSTooltip,
1819
} from 'chart.js'
@@ -21,10 +22,16 @@ import { noop, useDebounce } from '@Common/Helper'
2122
import { DEVTRON_BASE_MAIN_ID } from '@Shared/constants'
2223
import { useTheme } from '@Shared/Providers'
2324

24-
import { LEGENDS_LABEL_CONFIG } from './constants'
25-
import { getAverageLinePlugin, getSeparatorLinePlugin } from './plugins'
26-
import { ChartProps, GetDefaultOptionsParams } from './types'
27-
import { buildChartTooltipFromContext, getChartJSType, getDefaultOptions, transformDataForChart } from './utils'
25+
import { drawReferenceLine } from './plugins'
26+
import { ChartProps, GetDefaultOptionsParams, TypeAndDatasetsType } from './types'
27+
import {
28+
buildChartTooltipFromContext,
29+
distanceBetweenPoints,
30+
getChartJSType,
31+
getDefaultOptions,
32+
getLegendsLabelConfig,
33+
transformDataForChart,
34+
} from './utils'
2835

2936
// Register Chart.js components
3037
ChartJS.register(
@@ -37,19 +44,48 @@ ChartJS.register(
3744
PointElement,
3845
DoughnutController,
3946
ArcElement,
47+
TimeScale,
4048
Title,
4149
ChartJSTooltip,
4250
Legend,
4351
Filler,
4452
)
4553

46-
/**
47-
* The doughnut chart overrides the default legend label configuration.
48-
* Therefore need to set the custom legend label configuration.
49-
*/
50-
ChartJS.overrides.doughnut.plugins.legend.labels = {
51-
...ChartJS.overrides.doughnut.plugins.legend.labels,
52-
...LEGENDS_LABEL_CONFIG,
54+
ChartJSTooltip.positioners.barElementCenterPositioner = (items, eventPosition) => {
55+
if (!items.length) {
56+
return false
57+
}
58+
59+
let { x } = eventPosition
60+
let { y } = eventPosition
61+
let minDistance = Number.POSITIVE_INFINITY
62+
let i: number
63+
let len: number
64+
let nearestElement: BarElement
65+
66+
for (i = 0, len = items.length; i < len; ++i) {
67+
const el = items[i].element
68+
if (el && el.hasValue()) {
69+
const center = (el as BarElement).getCenterPoint()
70+
const d = distanceBetweenPoints(eventPosition, center)
71+
72+
if (d < minDistance) {
73+
minDistance = d
74+
nearestElement = el as BarElement
75+
}
76+
}
77+
}
78+
79+
if (nearestElement) {
80+
const tp = nearestElement.getCenterPoint()
81+
x = tp.x
82+
y = tp.y
83+
}
84+
85+
return {
86+
x,
87+
y,
88+
}
5389
}
5490

5591
/**
@@ -157,15 +193,15 @@ const Chart = (props: ChartProps) => {
157193
id,
158194
xAxisLabels: labels,
159195
hideAxis = false,
160-
onChartClick,
161-
separatorIndex,
162-
averageLineValue,
196+
referenceLines,
197+
tooltipConfig,
198+
type,
199+
datasets,
163200
xAxisMax,
201+
xScaleTitle,
164202
yAxisMax,
165-
tooltipConfig,
166-
...typeAndDatasets
203+
yScaleTitle,
167204
} = props
168-
const { type, datasets } = typeAndDatasets
169205
const { getTooltipContent, placement } = tooltipConfig || { placement: 'top' }
170206

171207
const canvasRef = useRef<HTMLCanvasElement>(null)
@@ -212,24 +248,34 @@ const Chart = (props: ChartProps) => {
212248
setTooltipVisible(true)
213249
}
214250

215-
const debouncedExternalTooltipHandler = useDebounce(externalTooltipHandler, 16)
251+
const debouncedExternalTooltipHandler = useDebounce(externalTooltipHandler, 50)
216252

217253
useEffect(() => {
218254
const ctx = canvasRef.current?.getContext('2d')
219255
if (!ctx) {
220256
return noop
221257
}
222258

259+
if (type === 'pie') {
260+
/**
261+
* The doughnut chart overrides the default legend label configuration.
262+
* Therefore need to set the custom legend label configuration.
263+
*/
264+
ChartJS.overrides.doughnut.plugins.legend.labels = {
265+
...ChartJS.overrides.doughnut.plugins.legend.labels,
266+
...getLegendsLabelConfig('pie', appTheme),
267+
}
268+
}
269+
223270
// Get Chart.js type and transform data
224271
const chartJSType = getChartJSType(type)
225-
const transformedData = { labels, datasets: transformDataForChart({ ...typeAndDatasets, appTheme }) }
272+
const transformedData = {
273+
labels,
274+
datasets: transformDataForChart({ ...({ type, datasets } as TypeAndDatasetsType), appTheme }),
275+
}
226276
const defaultOptions = getDefaultOptions({
227-
type,
277+
chartProps: props,
228278
appTheme,
229-
hideAxis,
230-
onChartClick,
231-
xAxisMax,
232-
yAxisMax,
233279
externalTooltipHandler: debouncedExternalTooltipHandler,
234280
})
235281

@@ -240,15 +286,14 @@ const Chart = (props: ChartProps) => {
240286
...defaultOptions,
241287
},
242288
plugins: [
243-
...(averageLineValue ? [getAverageLinePlugin(averageLineValue, appTheme)] : []),
244-
...(separatorIndex ? [getSeparatorLinePlugin(separatorIndex, type, appTheme)] : []),
289+
...(referenceLines ?? []).map((rl, idx) => drawReferenceLine(rl, `reference-line-${idx}`, appTheme)),
245290
],
246291
})
247292

248293
return () => {
249294
chartRef.current.destroy()
250295
}
251-
}, [type, datasets, labels, appTheme, hideAxis, averageLineValue, separatorIndex])
296+
}, [type, datasets, labels, appTheme, hideAxis, referenceLines, xAxisMax, xScaleTitle, yAxisMax, yScaleTitle])
252297

253298
return (
254299
<div className="h-100 w-100 dc__position-rel">
@@ -260,8 +305,19 @@ const Chart = (props: ChartProps) => {
260305
placement={placement}
261306
appendTo={document.getElementById(DEVTRON_BASE_MAIN_ID)}
262307
className="default-tt dc__mxw-400 dc__word-break"
308+
moveTransition="transform 200ms cubic-bezier(.42,.61,.64,.81)"
263309
>
264-
<span ref={tooltipRef} style={{ position: 'absolute', left: 0, top: 0, width: 0, height: 0 }} />
310+
<span
311+
ref={tooltipRef}
312+
style={{
313+
position: 'absolute',
314+
left: 0,
315+
top: 0,
316+
width: 0,
317+
height: 0,
318+
willChange: 'transform',
319+
}}
320+
/>
265321
</Tippy>
266322
</div>
267323
)

src/Shared/Components/Charts/constants.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,6 @@ import { AppThemeType } from '@Shared/Providers'
22

33
import { ChartColorKey } from './types'
44

5-
export const LEGENDS_LABEL_CONFIG = {
6-
usePointStyle: true,
7-
pointStyle: 'rectRounded',
8-
pointStyleWidth: 0,
9-
font: {
10-
family: "'IBM Plex Sans', 'Open Sans', 'Roboto'",
11-
size: 13,
12-
lineHeight: '150%',
13-
weight: 400,
14-
},
15-
} as const
16-
175
export const CHART_COLORS: Record<AppThemeType, Record<ChartColorKey, string>> = {
186
[AppThemeType.light]: {
197
// Sky Blue
@@ -306,11 +294,23 @@ export const CHART_COLORS: Record<AppThemeType, Record<ChartColorKey, string>> =
306294
} as const
307295

308296
export const CHART_GRID_LINES_COLORS: Record<AppThemeType, string> = {
309-
[AppThemeType.light]: '#f1f5f9',
310-
[AppThemeType.dark]: '#1e293b',
297+
[AppThemeType.light]: 'rgb(237, 241, 245)',
298+
[AppThemeType.dark]: 'rgb(40, 43, 51)',
299+
}
300+
301+
export const CHART_AXIS_COLORS: Record<AppThemeType, string> = {
302+
[AppThemeType.light]: 'rgb(208, 212, 217)',
303+
[AppThemeType.dark]: 'rgb(60, 63, 71)',
304+
}
305+
306+
export const CHART_AXIS_LABELS_COLOR: Record<AppThemeType, string> = {
307+
[AppThemeType.dark]: 'rgb(248, 248, 249)',
308+
[AppThemeType.light]: 'rgb(0, 10, 20)',
311309
}
312310

313311
export const CHART_CANVAS_BACKGROUND_COLORS: Record<AppThemeType, string> = {
314-
[AppThemeType.light]: '#ffffff',
315-
[AppThemeType.dark]: '#1e293b',
312+
[AppThemeType.light]: 'rgb(255, 255, 255)',
313+
[AppThemeType.dark]: 'rgb(21, 22, 31)',
316314
}
315+
316+
export const LINE_DASH = [6, 6]
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
export { default as Chart } from './Chart.component'
22
export { CHART_COLORS } from './constants'
3-
export type { ChartColorKey, ChartProps, ChartType, SimpleDataset, SimpleDatasetForPie } from './types'
3+
export type {
4+
ChartColorKey,
5+
ChartProps,
6+
ChartType,
7+
ReferenceLineConfigType,
8+
SimpleDataset,
9+
SimpleDatasetForPie,
10+
} from './types'
411
export { chartColorGenerator } from './utils'
Lines changed: 8 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,27 @@
1-
import { Chart, Plugin } from 'chart.js'
1+
import { Plugin } from 'chart.js'
22

33
import { AppThemeType } from '@Shared/Providers'
44

55
import { CHART_COLORS } from './constants'
6-
import { ChartType } from './types'
6+
import { ReferenceLineConfigType } from './types'
77

8-
export const getAverageLinePlugin = (averageValue: number, appTheme: AppThemeType): Plugin => ({
9-
id: 'averageLine',
8+
export const drawReferenceLine = (config: ReferenceLineConfigType, id: string, appTheme: AppThemeType): Plugin => ({
9+
id,
1010
afterDraw: (chart) => {
1111
const { ctx, chartArea, scales } = chart
12-
if (!scales || !scales.y || !averageValue) {
12+
if (!scales || !scales.y || !config?.value) {
1313
return
1414
}
15-
const yValue = scales.y.getPixelForValue(averageValue)
15+
const yValue = scales.y.getPixelForValue(config.value)
1616
ctx.save()
1717
ctx.beginPath()
1818
ctx.setLineDash([6, 6])
19-
ctx.strokeStyle = CHART_COLORS[appTheme].CharcoalGray700
20-
ctx.lineWidth = 1
19+
ctx.strokeStyle = CHART_COLORS[appTheme][config.color ?? 'CharcoalGray700']
20+
ctx.lineWidth = config.strokeWidth ?? 1
2121
ctx.moveTo(chartArea.left, yValue)
2222
ctx.lineTo(chartArea.right, yValue)
2323
ctx.stroke()
2424
ctx.setLineDash([])
2525
ctx.restore()
2626
},
2727
})
28-
29-
const drawSeparator = (separatorIndex: number, chartType: ChartType, appTheme: AppThemeType) => (chart: Chart) => {
30-
const { ctx, scales } = chart
31-
ctx.save()
32-
ctx.beginPath()
33-
ctx.strokeStyle = CHART_COLORS[appTheme].CharcoalGray700
34-
ctx.lineWidth = 1
35-
36-
if (chartType === 'stackedBar') {
37-
// Draw vertical separator after separatorIndex, extend beyond chart area for labels
38-
if (scales && scales.x) {
39-
const xValue = scales.x.getPixelForValue(separatorIndex + 0.5)
40-
// Try to extend line from top to bottom of canvas
41-
ctx.moveTo(xValue, 0)
42-
ctx.lineTo(xValue, chart.height)
43-
}
44-
} else if (scales && scales.y) {
45-
// Draw horizontal separator after separatorIndex, extend beyond chart area for labels
46-
const yValue = scales.y.getPixelForValue(separatorIndex + 0.5)
47-
ctx.moveTo(0, yValue)
48-
ctx.lineTo(chart.width, yValue)
49-
}
50-
51-
ctx.stroke()
52-
ctx.restore()
53-
}
54-
55-
export const getSeparatorLinePlugin = (
56-
separatorIndex: number,
57-
chartType: ChartType,
58-
appTheme: AppThemeType,
59-
): Plugin => ({
60-
id: 'separatorLine',
61-
afterDraw: drawSeparator(separatorIndex, chartType, appTheme),
62-
afterDatasetsDraw: drawSeparator(separatorIndex, chartType, appTheme),
63-
})

0 commit comments

Comments
 (0)