-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7248004
commit 829496c
Showing
6 changed files
with
317 additions
and
0 deletions.
There are no files selected for viewing
193 changes: 193 additions & 0 deletions
193
packages/react-components/src/components/l4eWidget/constants.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
import { MockData } from './fakeData'; | ||
import { AnomalyValue } from './types'; | ||
import { formatTooltip } from './utils/formatTooltip'; | ||
const mappedEvents = MockData.map((ev) => [ev.value.timestamp, 100, ev.value]); | ||
|
||
// defines 2 x-axes: | ||
// 1. x axis for main timeline, which shows timie value | ||
// 2. hides the x-axis for the smaller timeline and hides any styling on it | ||
const L4E_X_AXIS = { | ||
xAxis: [ | ||
{ | ||
name: 'l4e-timeline-axis', | ||
type: 'time', | ||
boundaryGap: false, | ||
show: true, | ||
axisLabel: { | ||
hideOverlap: true, | ||
color: '#5f6b7a', | ||
}, | ||
axisLine: { | ||
lineStyle: { | ||
color: '#e9ebed', | ||
width: 2, | ||
}, | ||
}, | ||
splitNumber: 6, | ||
gridIndex: 0, | ||
}, | ||
{ | ||
name: 'l4e-selection-axis', | ||
type: 'time', | ||
gridIndex: 1, | ||
boundaryGap: false, | ||
axisTick: { show: false }, | ||
splitLine: { show: false }, | ||
axisLabel: { show: false }, | ||
axisLine: { | ||
onZero: false, | ||
lineStyle: { | ||
color: '#e9ebed', | ||
width: 2, | ||
}, | ||
}, | ||
}, | ||
], | ||
}; | ||
|
||
const L4E_Y_AXIS = { | ||
yAxis: [ | ||
{ | ||
type: 'value', | ||
show: false, | ||
min: 0, | ||
gridIndex: 0, | ||
}, | ||
{ | ||
gridIndex: 1, | ||
axisLabel: { show: false }, | ||
axisLine: { show: false }, | ||
axisTick: { show: false }, | ||
splitLine: { show: false }, | ||
}, | ||
], | ||
}; | ||
|
||
const L4E_TOOLTIP = { | ||
tooltip: { | ||
show: true, | ||
trigger: 'item', | ||
confine: true, | ||
borderColor: '#C5C5C5', | ||
}, | ||
}; | ||
|
||
// defines 3 data zooms: | ||
// 1. inside zoom for main timeline, which handles gestures | ||
// 2. slider zoom for main timeline, which overlaps the smaller timeline | ||
// 3. disables zoom for the smaller timeline, which will always show 100% of the data | ||
const L4E_DATA_ZOOM = { | ||
dataZoom: [ | ||
{ | ||
type: 'inside', | ||
xAxisIndex: [0], | ||
start: 0, | ||
end: 100, | ||
zoomOnMouseWheel: true, | ||
moveOnMouseMove: 'shift', | ||
moveOnMouseWheel: false, | ||
}, | ||
{ | ||
type: 'slider', | ||
backgroundColor: 'rgba(255,255,255,0)', | ||
xAxisIndex: [0], | ||
fillerColor: 'rgba(255, 110, 110, .25)', | ||
dataBackground: { | ||
lineStyle: { opacity: 0 }, | ||
areaStyle: { opacity: 0 }, | ||
}, | ||
selectedDataBackground: { | ||
lineStyle: { color: '#ffffff', opacity: 0 }, | ||
areaStyle: { color: '#ffffff', opacity: 0 }, | ||
}, | ||
moveHandleStyle: { color: '#C5C5C5' }, | ||
handleStyle: { borderColor: '#C5C5C5' }, | ||
height: 32, | ||
bottom: 12, | ||
}, | ||
{ | ||
type: 'inside', | ||
disabled: true, | ||
xAxisIndex: [1], | ||
start: 0, | ||
end: 100, | ||
}, | ||
], | ||
}; | ||
|
||
// grid layout with 2 sections: | ||
// 1. top section is the large timeline | ||
// 2. bottom section is a smaller version of the timeline | ||
// which is overlapped by the data zoom slider | ||
const L4E_GRID = { | ||
grid: [ | ||
{ | ||
left: '10px', | ||
right: '10px', | ||
top: '30px', | ||
show: true, | ||
borderColor: '#C5C5C5', | ||
}, | ||
{ | ||
left: '12px', | ||
right: '12px', | ||
height: 30, | ||
bottom: 8, | ||
}, | ||
], | ||
}; | ||
|
||
// defines 2 series: | ||
// 1. large timeline, which a user directly interacts with | ||
// 2. smaller timeline, which sits below the slider data zoom and always shows 100% of the data | ||
const L4E_SERIES = { | ||
series: [ | ||
{ | ||
id: 'l4e_timeline', | ||
type: 'bar', | ||
color: '#D13212', | ||
barMinWidth: 5, | ||
barMaxWidth: 10, | ||
xAxisIndex: 0, | ||
yAxisIndex: 0, | ||
tooltip: { | ||
formatter: ({ data }: { data: [number, number, AnomalyValue] }) => | ||
formatTooltip(data), | ||
}, | ||
encode: { | ||
x: 'time', | ||
y: 'value', | ||
}, | ||
}, | ||
{ | ||
id: 'l4e_slider', | ||
type: 'bar', | ||
color: '#D13212', | ||
silent: true, | ||
barMinWidth: 5, | ||
barMaxWidth: 5, | ||
xAxisIndex: 1, | ||
yAxisIndex: 1, | ||
tooltip: { | ||
show: false, | ||
}, | ||
encode: { | ||
x: 'time', | ||
y: 'value', | ||
}, | ||
}, | ||
], | ||
}; | ||
|
||
export const DEFAULT_L4E_WIDGET_SETTINGS = { | ||
...L4E_GRID, | ||
...L4E_DATA_ZOOM, | ||
...L4E_TOOLTIP, | ||
...L4E_X_AXIS, | ||
...L4E_Y_AXIS, | ||
...L4E_SERIES, | ||
dataset: { | ||
dimensions: ['time', 'value', 'extraData'], | ||
source: mappedEvents, | ||
}, | ||
}; |
35 changes: 35 additions & 0 deletions
35
packages/react-components/src/components/l4eWidget/fakeData.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { AnomalyResult } from './types'; | ||
|
||
const BaseAnomalyResult = { | ||
quality: 'GOOD', | ||
value: { | ||
anomalyScore: 50, | ||
predictionReason: 'bad stuff idk', | ||
diagnostics: [ | ||
{ name: 'Average Power', value: 0.4 }, | ||
{ name: 'Average Wind Speed', value: 0.22 }, | ||
{ name: 'RPM', value: 0.18 }, | ||
{ name: 'Torque', value: 0.1 }, | ||
{ name: 'Wind Direction', value: 0.05 }, | ||
{ name: 'Wind Speed', value: 0.05 }, | ||
], | ||
}, | ||
}; | ||
|
||
function getRandomDate(from: Date, to: Date) { | ||
const fromTime = from.getTime(); | ||
const toTime = to.getTime(); | ||
return new Date(fromTime + Math.random() * (toTime - fromTime)).getTime(); | ||
} | ||
|
||
// creates array of 10 random dates within last 7 days | ||
const times = new Array(10) | ||
.fill(null) | ||
.map(() => | ||
getRandomDate(new Date(Date.now() - 7 * 24 * 60 * 60 * 1000), new Date()) | ||
); | ||
|
||
export const MockData: AnomalyResult[] = times.map((time) => ({ | ||
...BaseAnomalyResult, | ||
value: { ...BaseAnomalyResult.value, timestamp: time }, | ||
})); |
32 changes: 32 additions & 0 deletions
32
packages/react-components/src/components/l4eWidget/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import React, { useEffect } from 'react'; | ||
import { useECharts } from '../../hooks/useECharts'; | ||
import { DEFAULT_L4E_WIDGET_SETTINGS } from './constants'; | ||
import { MockData } from './fakeData'; | ||
|
||
export const L4EWidget = () => { | ||
const { ref, chartRef } = useECharts(); | ||
|
||
useEffect(() => { | ||
const l4e = chartRef.current; | ||
l4e?.setOption(DEFAULT_L4E_WIDGET_SETTINGS); | ||
}, [chartRef]); | ||
|
||
return ( | ||
<div style={{ background: 'grey' }}> | ||
<div | ||
ref={ref} | ||
style={{ background: 'white', width: '1000px', height: '300px' }} | ||
/> | ||
<div | ||
style={{ | ||
background: 'lightGrey', | ||
width: '1000px', | ||
height: '500px', | ||
overflow: 'scroll', | ||
}} | ||
> | ||
<pre>{JSON.stringify(MockData, null, 2)}</pre> | ||
</div> | ||
</div> | ||
); | ||
}; |
16 changes: 16 additions & 0 deletions
16
packages/react-components/src/components/l4eWidget/types.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
export type AnomalyDiagnostic = { | ||
name: string; | ||
value: number; | ||
}; | ||
|
||
export type AnomalyValue = { | ||
anomalyScore: number; | ||
predictionReason: string; | ||
diagnostics: AnomalyDiagnostic[]; | ||
timestamp: number; | ||
}; | ||
|
||
export type AnomalyResult = { | ||
quality: string; | ||
value: AnomalyValue; | ||
}; |
20 changes: 20 additions & 0 deletions
20
packages/react-components/src/components/l4eWidget/utils/formatTooltip.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { AnomalyValue } from '../types'; | ||
|
||
export const formatTooltip = (data: [number, number, AnomalyValue]) => { | ||
const { anomalyScore, diagnostics, predictionReason, timestamp } = data[2]; | ||
const div = document.createElement('div'); | ||
const timestampString = `<div><b>${new Date( | ||
timestamp | ||
).toLocaleString()}</b></div>`; | ||
const anomalyScoreString = `<div><b>Anomaly Score</b> ${anomalyScore}</div>`; | ||
const predictionReasonString = `<div><b>Prediction reason</b> ${predictionReason}</div>`; | ||
const diagnosticString = diagnostics | ||
.map( | ||
({ name, value }: { name: string; value: number }) => | ||
`<div><span>${name}</span> - <span>${value * 100}%</span></div>` | ||
) | ||
.join(''); | ||
|
||
div.innerHTML = `${timestampString}<hr/>${anomalyScoreString}${predictionReasonString}<div><b>Contributing Properties</b></div><div>${diagnosticString}</div>`; | ||
return div; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import React from 'react'; | ||
import { ComponentMeta, ComponentStory } from '@storybook/react'; | ||
import { L4EWidget } from '../../src/components/l4eWidget'; | ||
|
||
export default { | ||
title: 'Widgets/L4E', | ||
component: L4EWidget, | ||
parameters: { | ||
layout: 'fullscreen', | ||
}, | ||
} as ComponentMeta<typeof L4EWidget>; | ||
|
||
export const MockDataKPI: ComponentStory<typeof L4EWidget> = () => { | ||
return ( | ||
<div style={{ background: 'grey' }}> | ||
<div style={{ height: '100%', width: '100%', padding: '20px' }}> | ||
<L4EWidget /> | ||
</div> | ||
</div> | ||
); | ||
}; |