Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
N
nexpie-grafana-theme
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Registry
Registry
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kornkitt Poolsup
nexpie-grafana-theme
Commits
f2327baf
Unverified
Commit
f2327baf
authored
Jan 22, 2021
by
Ryan McKinley
Committed by
GitHub
Jan 22, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
DataFrame: cache frame/field index in field state (#30529)
parent
f97348ff
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
259 additions
and
193 deletions
+259
-193
packages/grafana-data/src/types/dataFrame.ts
+9
-1
packages/grafana-ui/src/components/GraphNG/GraphNG.tsx
+13
-16
packages/grafana-ui/src/components/GraphNG/utils.test.ts
+194
-105
packages/grafana-ui/src/components/GraphNG/utils.ts
+21
-27
packages/grafana-ui/src/components/Sparkline/Sparkline.tsx
+1
-4
packages/grafana-ui/src/components/uPlot/Plot.test.tsx
+9
-23
packages/grafana-ui/src/components/uPlot/Plot.tsx
+4
-4
packages/grafana-ui/src/components/uPlot/context.ts
+6
-6
packages/grafana-ui/src/components/uPlot/types.ts
+2
-7
No files found.
packages/grafana-data/src/types/dataFrame.ts
View file @
f2327baf
...
...
@@ -162,6 +162,13 @@ export interface FieldState {
* Useful for assigning color to series by looking up a color in a palette using this index
*/
seriesIndex
?:
number
;
/**
* Location of this field within the context frames results
*
* @internal -- we will try to make this unnecessary
*/
origin
?:
DataFrameFieldIndex
;
}
export
interface
NumericRange
{
...
...
@@ -206,7 +213,8 @@ export const TIME_SERIES_METRIC_FIELD_NAME = 'Metric';
/**
* Describes where a specific data frame field is located within a
* dataset of type DataFrame[]
* @public
*
* @internal -- we will try to make this unnecessary
*/
export
interface
DataFrameFieldIndex
{
frameIndex
:
number
;
...
...
packages/grafana-ui/src/components/GraphNG/GraphNG.tsx
View file @
f2327baf
...
...
@@ -12,7 +12,7 @@ import {
reduceField
,
TimeRange
,
}
from
'@grafana/data'
;
import
{
alig
nDataFrames
}
from
'./utils'
;
import
{
joi
nDataFrames
}
from
'./utils'
;
import
{
useTheme
}
from
'../../themes'
;
import
{
UPlotChart
}
from
'../uPlot/Plot'
;
import
{
PlotProps
}
from
'../uPlot/types'
;
...
...
@@ -64,9 +64,7 @@ export const GraphNG: React.FC<GraphNGProps> = ({
const
theme
=
useTheme
();
const
hasLegend
=
useRef
(
legend
&&
legend
.
displayMode
!==
LegendDisplayMode
.
Hidden
);
const
alignedFrameWithGapTest
=
useMemo
(()
=>
alignDataFrames
(
data
,
fields
),
[
data
,
fields
]);
const
alignedFrame
=
alignedFrameWithGapTest
?.
frame
;
const
getDataFrameFieldIndex
=
alignedFrameWithGapTest
?.
getDataFrameFieldIndex
;
const
frame
=
useMemo
(()
=>
joinDataFrames
(
data
,
fields
),
[
data
,
fields
]);
const
compareFrames
=
useCallback
((
a
?:
DataFrame
|
null
,
b
?:
DataFrame
|
null
)
=>
{
if
(
a
&&
b
)
{
...
...
@@ -98,17 +96,17 @@ export const GraphNG: React.FC<GraphNGProps> = ({
currentTimeRange
.
current
=
timeRange
;
},
[
timeRange
]);
const
configRev
=
useRevision
(
alignedF
rame
,
compareFrames
);
const
configRev
=
useRevision
(
f
rame
,
compareFrames
);
const
configBuilder
=
useMemo
(()
=>
{
const
builder
=
new
UPlotConfigBuilder
();
if
(
!
alignedF
rame
)
{
if
(
!
f
rame
)
{
return
builder
;
}
// X is the first field in the aligned frame
const
xField
=
alignedF
rame
.
fields
[
0
];
const
xField
=
f
rame
.
fields
[
0
];
if
(
xField
.
type
===
FieldType
.
time
)
{
builder
.
addScale
({
...
...
@@ -141,8 +139,8 @@ export const GraphNG: React.FC<GraphNGProps> = ({
}
let
indexByName
:
Map
<
string
,
number
>
|
undefined
=
undefined
;
for
(
let
i
=
0
;
i
<
alignedF
rame
.
fields
.
length
;
i
++
)
{
const
field
=
alignedF
rame
.
fields
[
i
];
for
(
let
i
=
0
;
i
<
f
rame
.
fields
.
length
;
i
++
)
{
const
field
=
f
rame
.
fields
[
i
];
const
config
=
field
.
config
as
FieldConfig
<
GraphFieldConfig
>
;
const
customConfig
:
GraphFieldConfig
=
{
...
defaultConfig
,
...
...
@@ -182,14 +180,13 @@ export const GraphNG: React.FC<GraphNGProps> = ({
}
const
showPoints
=
customConfig
.
drawStyle
===
DrawStyle
.
Points
?
PointVisibility
.
Always
:
customConfig
.
showPoints
;
const
dataFrameFieldIndex
=
getDataFrameFieldIndex
?
getDataFrameFieldIndex
(
i
)
:
undefined
;
let
{
fillOpacity
}
=
customConfig
;
if
(
customConfig
.
fillBelowTo
)
{
if
(
!
indexByName
)
{
indexByName
=
getNamesToFieldIndex
(
alignedF
rame
);
indexByName
=
getNamesToFieldIndex
(
f
rame
);
}
const
t
=
indexByName
.
get
(
getFieldDisplayName
(
field
,
alignedF
rame
));
const
t
=
indexByName
.
get
(
getFieldDisplayName
(
field
,
f
rame
));
const
b
=
indexByName
.
get
(
customConfig
.
fillBelowTo
);
if
(
isNumber
(
b
)
&&
isNumber
(
t
))
{
builder
.
addBand
({
...
...
@@ -221,15 +218,15 @@ export const GraphNG: React.FC<GraphNGProps> = ({
thresholds
:
config
.
thresholds
,
// The following properties are not used in the uPlot config, but are utilized as transport for legend config
dataFrameFieldIndex
,
fieldName
:
getFieldDisplayName
(
field
,
alignedF
rame
),
dataFrameFieldIndex
:
field
.
state
?.
origin
,
fieldName
:
getFieldDisplayName
(
field
,
f
rame
),
hideInLegend
:
customConfig
.
hideFrom
?.
legend
,
});
}
return
builder
;
},
[
configRev
,
timeZone
]);
if
(
alignedFrameWithGapTest
==
null
)
{
if
(
!
frame
)
{
return
(
<
div
className=
"panel-empty"
>
<
p
>
No data found in response
</
p
>
...
...
@@ -299,7 +296,7 @@ export const GraphNG: React.FC<GraphNGProps> = ({
<
VizLayout
width=
{
width
}
height=
{
height
}
legend=
{
legendElement
}
>
{
(
vizWidth
:
number
,
vizHeight
:
number
)
=>
(
<
UPlotChart
data=
{
alignedFrameWithGapTest
}
data=
{
frame
}
config=
{
configBuilder
}
width=
{
vizWidth
}
height=
{
vizHeight
}
...
...
packages/grafana-ui/src/components/GraphNG/utils.test.ts
View file @
f2327baf
import
{
ArrayVector
,
DataFrame
,
FieldType
,
toDataFrame
}
from
'@grafana/data'
;
import
{
AlignedFrameWithGapTest
}
from
'../uPlot/types'
;
import
{
alignDataFrames
,
isLikelyAscendingVector
}
from
'./utils'
;
import
{
joinDataFrames
,
isLikelyAscendingVector
}
from
'./utils'
;
describe
(
'
alig
nDataFrames'
,
()
=>
{
describe
(
'
alig
ned frame'
,
()
=>
{
describe
(
'
joi
nDataFrames'
,
()
=>
{
describe
(
'
joi
ned frame'
,
()
=>
{
it
(
'should align multiple data frames into one data frame'
,
()
=>
{
const
data
:
DataFrame
[]
=
[
toDataFrame
({
...
...
@@ -20,37 +19,64 @@ describe('alignDataFrames', () => {
}),
];
const
aligned
=
alignDataFrames
(
data
);
expect
(
aligned
?.
frame
.
fields
).
toEqual
([
{
config
:
{},
state
:
{},
name
:
'time'
,
type
:
FieldType
.
time
,
values
:
new
ArrayVector
([
1000
,
2000
,
3000
,
4000
]),
},
{
config
:
{},
state
:
{
displayName
:
'temperature A'
,
seriesIndex
:
0
,
const
joined
=
joinDataFrames
(
data
);
expect
(
joined
?.
fields
).
toMatchInlineSnapshot
(
`
Array [
Object {
"config": Object {},
"name": "time",
"state": Object {
"origin": undefined,
},
"type": "time",
"values": Array [
1000,
2000,
3000,
4000,
],
},
Object {
"config": Object {},
"name": "temperature A",
"state": Object {
"displayName": "temperature A",
"origin": Object {
"fieldIndex": 1,
"frameIndex": 0,
},
"seriesIndex": 0,
},
"type": "number",
"values": Array [
1,
3,
5,
7,
],
},
name
:
'temperature A'
,
type
:
FieldType
.
number
,
values
:
new
ArrayVector
([
1
,
3
,
5
,
7
]),
},
{
config
:
{},
state
:
{
displayName
:
'temperature B'
,
seriesIndex
:
1
,
Object {
"config": Object {},
"name": "temperature B",
"state": Object {
"displayName": "temperature B",
"origin": Object {
"fieldIndex": 1,
"frameIndex": 1,
},
"seriesIndex": 1,
},
"type": "number",
"values": Array [
0,
2,
6,
7,
],
},
name
:
'temperature B'
,
type
:
FieldType
.
number
,
values
:
new
ArrayVector
([
0
,
2
,
6
,
7
]),
},
]);
]
`
);
});
it
(
'should align multiple data frames into one data frame but only keep first time field'
,
()
=>
{
...
...
@@ -69,37 +95,64 @@ describe('alignDataFrames', () => {
}),
];
const
aligned
=
alignDataFrames
(
data
);
expect
(
aligned
?.
frame
.
fields
).
toEqual
([
{
config
:
{},
state
:
{},
name
:
'time'
,
type
:
FieldType
.
time
,
values
:
new
ArrayVector
([
1000
,
2000
,
3000
,
4000
]),
},
{
config
:
{},
state
:
{
displayName
:
'temperature'
,
seriesIndex
:
0
,
const
aligned
=
joinDataFrames
(
data
);
expect
(
aligned
?.
fields
).
toMatchInlineSnapshot
(
`
Array [
Object {
"config": Object {},
"name": "time",
"state": Object {
"origin": undefined,
},
"type": "time",
"values": Array [
1000,
2000,
3000,
4000,
],
},
Object {
"config": Object {},
"name": "temperature",
"state": Object {
"displayName": "temperature",
"origin": Object {
"fieldIndex": 1,
"frameIndex": 0,
},
"seriesIndex": 0,
},
"type": "number",
"values": Array [
1,
3,
5,
7,
],
},
name
:
'temperature'
,
type
:
FieldType
.
number
,
values
:
new
ArrayVector
([
1
,
3
,
5
,
7
]),
},
{
config
:
{},
state
:
{
displayName
:
'temperature B'
,
seriesIndex
:
1
,
Object {
"config": Object {},
"name": "temperature B",
"state": Object {
"displayName": "temperature B",
"origin": Object {
"fieldIndex": 1,
"frameIndex": 1,
},
"seriesIndex": 1,
},
"type": "number",
"values": Array [
0,
2,
6,
7,
],
},
name
:
'temperature B'
,
type
:
FieldType
.
number
,
values
:
new
ArrayVector
([
0
,
2
,
6
,
7
]),
},
]);
]
`
);
});
it
(
'should align multiple data frames into one data frame and skip non-numeric fields'
,
()
=>
{
...
...
@@ -113,27 +166,45 @@ describe('alignDataFrames', () => {
}),
];
const
aligned
=
alignDataFrames
(
data
);
expect
(
aligned
?.
frame
.
fields
).
toEqual
([
{
config
:
{},
state
:
{},
name
:
'time'
,
type
:
FieldType
.
time
,
values
:
new
ArrayVector
([
1000
,
2000
,
3000
,
4000
]),
},
{
config
:
{},
state
:
{
displayName
:
'temperature'
,
seriesIndex
:
0
,
const
aligned
=
joinDataFrames
(
data
);
expect
(
aligned
?.
fields
).
toMatchInlineSnapshot
(
`
Array [
Object {
"config": Object {},
"name": "time",
"state": Object {
"origin": undefined,
},
"type": "time",
"values": Array [
1000,
2000,
3000,
4000,
],
},
Object {
"config": Object {},
"name": "temperature",
"state": Object {
"displayName": "temperature",
"origin": Object {
"fieldIndex": 1,
"frameIndex": 0,
},
"seriesIndex": 0,
},
"type": "number",
"values": Array [
1,
3,
5,
7,
],
},
name
:
'temperature'
,
type
:
FieldType
.
number
,
values
:
new
ArrayVector
([
1
,
3
,
5
,
7
]),
},
]);
]
`
);
});
it
(
'should align multiple data frames into one data frame and skip non-numeric fields'
,
()
=>
{
...
...
@@ -147,32 +218,50 @@ describe('alignDataFrames', () => {
}),
];
const
aligned
=
alignDataFrames
(
data
);
expect
(
aligned
?.
frame
.
fields
).
toEqual
([
{
config
:
{},
state
:
{},
name
:
'time'
,
type
:
FieldType
.
time
,
values
:
new
ArrayVector
([
1000
,
2000
,
3000
,
4000
]),
},
{
config
:
{},
state
:
{
displayName
:
'temperature'
,
seriesIndex
:
0
,
const
aligned
=
joinDataFrames
(
data
);
expect
(
aligned
?.
fields
).
toMatchInlineSnapshot
(
`
Array [
Object {
"config": Object {},
"name": "time",
"state": Object {
"origin": undefined,
},
"type": "time",
"values": Array [
1000,
2000,
3000,
4000,
],
},
Object {
"config": Object {},
"name": "temperature",
"state": Object {
"displayName": "temperature",
"origin": Object {
"fieldIndex": 1,
"frameIndex": 0,
},
"seriesIndex": 0,
},
"type": "number",
"values": Array [
1,
3,
5,
7,
],
},
name
:
'temperature'
,
type
:
FieldType
.
number
,
values
:
new
ArrayVector
([
1
,
3
,
5
,
7
]),
},
]);
]
`
);
});
});
describe
(
'getDataFrameFieldIndex'
,
()
=>
{
let
aligned
:
AlignedFrameWithGapTest
|
null
;
let
aligned
:
DataFrame
|
null
;
beforeAll
(()
=>
{
const
data
:
DataFrame
[]
=
[
...
...
@@ -197,7 +286,7 @@ describe('alignDataFrames', () => {
}),
];
aligned
=
alig
nDataFrames
(
data
);
aligned
=
joi
nDataFrames
(
data
);
});
it
.
each
`
...
...
@@ -209,7 +298,7 @@ describe('alignDataFrames', () => {
`
(
'should return correct index for yDim'
,
({
yDim
,
index
})
=>
{
const
[
frameIndex
,
fieldIndex
]
=
index
;
expect
(
aligned
?.
getDataFrameFieldIndex
(
yDim
)
).
toEqual
({
expect
(
aligned
?.
fields
[
yDim
].
state
?.
origin
).
toEqual
({
frameIndex
,
fieldIndex
,
});
...
...
packages/grafana-ui/src/components/GraphNG/utils.ts
View file @
f2327baf
...
...
@@ -12,7 +12,6 @@ import {
sortDataFrame
,
Vector
,
}
from
'@grafana/data'
;
import
{
AlignedFrameWithGapTest
}
from
'../uPlot/types'
;
import
uPlot
,
{
AlignedData
,
JoinNullMode
}
from
'uplot'
;
import
{
XYFieldMatchers
}
from
'./GraphNG'
;
...
...
@@ -44,7 +43,7 @@ export function mapDimesions(match: XYFieldMatchers, frame: DataFrame, frames?:
*
* @alpha
*/
export
function
alignDataFrames
(
frames
:
DataFrame
[],
fields
?:
XYFieldMatchers
):
AlignedFrameWithGapTest
|
null
{
export
function
joinDataFrames
(
frames
:
DataFrame
[],
fields
?:
XYFieldMatchers
):
DataFrame
|
null
{
const
valuesFromFrames
:
AlignedData
[]
=
[];
const
sourceFields
:
Field
[]
=
[];
const
sourceFieldsRefs
:
Record
<
number
,
DataFrameFieldIndex
>
=
{};
...
...
@@ -118,39 +117,34 @@ export function alignDataFrames(frames: DataFrame[], fields?: XYFieldMatchers):
}
// do the actual alignment (outerJoin on the first arrays)
let
alig
nedData
=
uPlot
.
join
(
valuesFromFrames
,
nullModes
);
let
joi
nedData
=
uPlot
.
join
(
valuesFromFrames
,
nullModes
);
if
(
alig
nedData
!
.
length
!==
sourceFields
.
length
)
{
if
(
joi
nedData
!
.
length
!==
sourceFields
.
length
)
{
throw
new
Error
(
'outerJoinValues lost a field?'
);
}
let
seriesIdx
=
0
;
// Replace the values from the outer-join field
return
{
frame
:
{
length
:
alignedData
!
[
0
].
length
,
fields
:
alignedData
!
.
map
((
vals
,
idx
)
=>
{
let
state
:
FieldState
=
{
...
sourceFields
[
idx
].
state
};
if
(
sourceFields
[
idx
].
type
!==
FieldType
.
time
)
{
state
.
seriesIndex
=
seriesIdx
;
seriesIdx
++
;
}
return
{
...
sourceFields
[
idx
],
state
,
values
:
new
ArrayVector
(
vals
),
};
}),
},
getDataFrameFieldIndex
:
(
alignedFieldIndex
:
number
)
=>
{
const
index
=
sourceFieldsRefs
[
alignedFieldIndex
];
if
(
!
index
)
{
throw
new
Error
(
`Could not find index for
${
alignedFieldIndex
}
`
);
...
frames
[
0
],
length
:
joinedData
!
[
0
].
length
,
fields
:
joinedData
!
.
map
((
vals
,
idx
)
=>
{
let
state
:
FieldState
=
{
...
sourceFields
[
idx
].
state
,
origin
:
sourceFieldsRefs
[
idx
],
};
if
(
sourceFields
[
idx
].
type
!==
FieldType
.
time
)
{
state
.
seriesIndex
=
seriesIdx
;
seriesIdx
++
;
}
return
index
;
},
return
{
...
sourceFields
[
idx
],
state
,
values
:
new
ArrayVector
(
vals
),
};
}),
};
}
...
...
packages/grafana-ui/src/components/Sparkline/Sparkline.tsx
View file @
f2327baf
...
...
@@ -160,10 +160,7 @@ export class Sparkline extends PureComponent<Props, State> {
return
(
<
UPlotChart
data=
{
{
frame
:
data
,
getDataFrameFieldIndex
:
()
=>
undefined
,
}
}
data=
{
data
}
config=
{
configBuilder
}
width=
{
width
}
height=
{
height
}
...
...
packages/grafana-ui/src/components/uPlot/Plot.test.tsx
View file @
f2327baf
import
React
from
'react'
;
import
{
UPlotChart
}
from
'./Plot'
;
import
{
act
,
render
}
from
'@testing-library/react'
;
import
{
ArrayVector
,
DataFrame
,
dateTime
,
FieldConfig
,
FieldType
,
MutableDataFrame
}
from
'@grafana/data'
;
import
{
ArrayVector
,
dateTime
,
FieldConfig
,
FieldType
,
MutableDataFrame
}
from
'@grafana/data'
;
import
{
GraphFieldConfig
,
DrawStyle
}
from
'../uPlot/config'
;
import
uPlot
from
'uplot'
;
import
createMockRaf
from
'mock-raf'
;
import
{
UPlotConfigBuilder
}
from
'./config/UPlotConfigBuilder'
;
import
{
AlignedFrameWithGapTest
}
from
'./types'
;
const
mockRaf
=
createMockRaf
();
const
setDataMock
=
jest
.
fn
();
...
...
@@ -69,11 +68,10 @@ describe('UPlotChart', () => {
it
(
'destroys uPlot instance when component unmounts'
,
()
=>
{
const
{
data
,
timeRange
,
config
}
=
mockData
();
const
uPlotData
=
createPlotData
(
data
);
const
{
unmount
}
=
render
(
<
UPlotChart
data=
{
uPlotData
}
data=
{
data
}
// mock
config=
{
config
}
timeRange=
{
timeRange
}
timeZone=
{
'browser'
}
...
...
@@ -95,11 +93,10 @@ describe('UPlotChart', () => {
describe
(
'data update'
,
()
=>
{
it
(
'skips uPlot reinitialization when there are no field config changes'
,
()
=>
{
const
{
data
,
timeRange
,
config
}
=
mockData
();
const
uPlotData
=
createPlotData
(
data
);
const
{
rerender
}
=
render
(
<
UPlotChart
data=
{
uPlotData
}
data=
{
data
}
// mock
config=
{
config
}
timeRange=
{
timeRange
}
timeZone=
{
'browser'
}
...
...
@@ -116,11 +113,10 @@ describe('UPlotChart', () => {
expect
(
uPlot
).
toBeCalledTimes
(
1
);
data
.
fields
[
1
].
values
.
set
(
0
,
1
);
uPlotData
.
frame
=
data
;
rerender
(
<
UPlotChart
data=
{
uPlotData
}
data=
{
data
}
// changed
config=
{
config
}
timeRange=
{
timeRange
}
timeZone=
{
'browser'
}
...
...
@@ -136,10 +132,9 @@ describe('UPlotChart', () => {
describe
(
'config update'
,
()
=>
{
it
(
'skips uPlot intialization for width and height equal 0'
,
async
()
=>
{
const
{
data
,
timeRange
,
config
}
=
mockData
();
const
uPlotData
=
createPlotData
(
data
);
const
{
queryAllByTestId
}
=
render
(
<
UPlotChart
data=
{
uPlotD
ata
}
config=
{
config
}
timeRange=
{
timeRange
}
timeZone=
{
'browser'
}
width=
{
0
}
height=
{
0
}
/>
<
UPlotChart
data=
{
d
ata
}
config=
{
config
}
timeRange=
{
timeRange
}
timeZone=
{
'browser'
}
width=
{
0
}
height=
{
0
}
/>
);
expect
(
queryAllByTestId
(
'uplot-main-div'
)).
toHaveLength
(
1
);
...
...
@@ -148,11 +143,10 @@ describe('UPlotChart', () => {
it
(
'reinitializes uPlot when config changes'
,
()
=>
{
const
{
data
,
timeRange
,
config
}
=
mockData
();
const
uPlotData
=
createPlotData
(
data
);
const
{
rerender
}
=
render
(
<
UPlotChart
data=
{
uPlotData
}
data=
{
data
}
// frame
config=
{
config
}
timeRange=
{
timeRange
}
timeZone=
{
'browser'
}
...
...
@@ -170,7 +164,7 @@ describe('UPlotChart', () => {
rerender
(
<
UPlotChart
data=
{
uPlotD
ata
}
data=
{
d
ata
}
config=
{
new
UPlotConfigBuilder
()
}
timeRange=
{
timeRange
}
timeZone=
{
'browser'
}
...
...
@@ -185,11 +179,10 @@ describe('UPlotChart', () => {
it
(
'skips uPlot reinitialization when only dimensions change'
,
()
=>
{
const
{
data
,
timeRange
,
config
}
=
mockData
();
const
uPlotData
=
createPlotData
(
data
);
const
{
rerender
}
=
render
(
<
UPlotChart
data=
{
uPlotData
}
data=
{
data
}
// frame
config=
{
config
}
timeRange=
{
timeRange
}
timeZone=
{
'browser'
}
...
...
@@ -205,7 +198,7 @@ describe('UPlotChart', () => {
rerender
(
<
UPlotChart
data=
{
uPlotData
}
data=
{
data
}
// frame
config=
{
new
UPlotConfigBuilder
()
}
timeRange=
{
timeRange
}
timeZone=
{
'browser'
}
...
...
@@ -220,10 +213,3 @@ describe('UPlotChart', () => {
});
});
});
const
createPlotData
=
(
frame
:
DataFrame
):
AlignedFrameWithGapTest
=>
{
return
{
frame
,
getDataFrameFieldIndex
:
()
=>
undefined
,
};
};
packages/grafana-ui/src/components/uPlot/Plot.tsx
View file @
f2327baf
...
...
@@ -39,7 +39,7 @@ export const UPlotChart: React.FC<PlotProps> = (props) => {
// 1. When config is ready and there is no uPlot instance, create new uPlot and return
if
(
isConfigReady
&&
!
plotInstance
.
current
)
{
plotInstance
.
current
=
initializePlot
(
prepareData
(
props
.
data
.
frame
),
currentConfig
.
current
,
canvasRef
.
current
);
plotInstance
.
current
=
initializePlot
(
prepareData
(
props
.
data
),
currentConfig
.
current
,
canvasRef
.
current
);
setIsPlotReady
(
true
);
return
;
}
...
...
@@ -60,12 +60,12 @@ export const UPlotChart: React.FC<PlotProps> = (props) => {
pluginLog
(
'uPlot core'
,
false
,
'destroying instance'
);
plotInstance
.
current
.
destroy
();
}
plotInstance
.
current
=
initializePlot
(
prepareData
(
props
.
data
.
frame
),
currentConfig
.
current
,
canvasRef
.
current
);
plotInstance
.
current
=
initializePlot
(
prepareData
(
props
.
data
),
currentConfig
.
current
,
canvasRef
.
current
);
return
;
}
// 4. Otherwise, assume only data has changed and update uPlot data
updateData
(
props
.
data
.
frame
,
props
.
config
,
plotInstance
.
current
,
prepareData
(
props
.
data
.
frame
));
updateData
(
props
.
data
,
props
.
config
,
plotInstance
.
current
,
prepareData
(
props
.
data
));
},
[
props
,
isConfigReady
]);
// When component unmounts, clean the existing uPlot instance
...
...
@@ -86,7 +86,7 @@ export const UPlotChart: React.FC<PlotProps> = (props) => {
);
};
function
prepareData
(
frame
:
DataFrame
)
{
function
prepareData
(
frame
:
DataFrame
)
:
AlignedData
{
return
frame
.
fields
.
map
((
f
)
=>
f
.
values
.
toArray
())
as
AlignedData
;
}
...
...
packages/grafana-ui/src/components/uPlot/context.ts
View file @
f2327baf
import
React
,
{
useCallback
,
useContext
}
from
'react'
;
import
uPlot
,
{
Series
}
from
'uplot'
;
import
{
PlotPlugin
,
AlignedFrameWithGapTest
}
from
'./types'
;
import
{
PlotPlugin
}
from
'./types'
;
import
{
DataFrame
,
Field
,
FieldConfig
}
from
'@grafana/data'
;
interface
PlotCanvasContextType
{
...
...
@@ -26,7 +26,7 @@ interface PlotContextType extends PlotPluginsContextType {
getSeries
:
()
=>
Series
[];
getCanvas
:
()
=>
PlotCanvasContextType
;
canvasRef
:
any
;
data
:
AlignedFrameWithGapTest
;
data
:
DataFrame
;
}
export
const
PlotContext
=
React
.
createContext
<
PlotContextType
>
({}
as
PlotContextType
);
...
...
@@ -76,7 +76,7 @@ export const usePlotData = (): PlotDataAPI => {
if
(
!
ctx
)
{
throwWhenNoContext
(
'usePlotData'
);
}
return
ctx
!
.
data
.
f
rame
.
f
ields
[
idx
];
return
ctx
!
.
data
.
fields
[
idx
];
},
[
ctx
]
);
...
...
@@ -109,7 +109,7 @@ export const usePlotData = (): PlotDataAPI => {
}
// by uPlot convention x-axis is always first field
// this may change when we introduce non-time x-axis and multiple x-axes (https://leeoniya.github.io/uPlot/demos/time-periods.html)
return
ctx
!
.
data
.
f
rame
.
f
ields
.
slice
(
1
);
return
ctx
!
.
data
.
fields
.
slice
(
1
);
},
[
ctx
]);
if
(
!
ctx
)
{
...
...
@@ -117,7 +117,7 @@ export const usePlotData = (): PlotDataAPI => {
}
return
{
data
:
ctx
.
data
.
frame
,
data
:
ctx
.
data
,
getField
,
getFieldValue
,
getFieldConfig
,
...
...
@@ -129,7 +129,7 @@ export const usePlotData = (): PlotDataAPI => {
export
const
buildPlotContext
=
(
isPlotReady
:
boolean
,
canvasRef
:
any
,
data
:
AlignedFrameWithGapTest
,
data
:
DataFrame
,
registerPlugin
:
any
,
getPlotInstance
:
()
=>
uPlot
|
undefined
):
PlotContextType
=>
{
...
...
packages/grafana-ui/src/components/uPlot/types.ts
View file @
f2327baf
import
React
from
'react'
;
import
uPlot
,
{
Options
,
Hooks
}
from
'uplot'
;
import
{
DataFrame
,
DataFrameFieldIndex
,
TimeRange
,
TimeZone
}
from
'@grafana/data'
;
import
{
DataFrame
,
TimeRange
,
TimeZone
}
from
'@grafana/data'
;
import
{
UPlotConfigBuilder
}
from
'./config/UPlotConfigBuilder'
;
export
type
PlotSeriesConfig
=
Pick
<
Options
,
'series'
|
'scales'
|
'axes'
|
'cursor'
|
'bands'
>
;
...
...
@@ -16,7 +16,7 @@ export interface PlotPluginProps {
}
export
interface
PlotProps
{
data
:
AlignedFrameWithGapTest
;
data
:
DataFrame
;
timeRange
:
TimeRange
;
timeZone
:
TimeZone
;
width
:
number
;
...
...
@@ -29,8 +29,3 @@ export abstract class PlotConfigBuilder<P, T> {
constructor
(
public
props
:
P
)
{}
abstract
getConfig
():
T
;
}
export
interface
AlignedFrameWithGapTest
{
frame
:
DataFrame
;
getDataFrameFieldIndex
:
(
alignedFieldIndex
:
number
)
=>
DataFrameFieldIndex
|
undefined
;
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment