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
e0ecbc4c
Commit
e0ecbc4c
authored
Mar 22, 2019
by
Torkel Ödegaard
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'table-reducer' of
https://github.com/ryantxu/grafana
into ryantxu-table-reducer
parents
a0d2e586
78a92437
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
317 additions
and
37 deletions
+317
-37
packages/grafana-ui/src/components/StatsPicker/StatsPicker.story.tsx
+79
-0
packages/grafana-ui/src/components/StatsPicker/StatsPicker.tsx
+98
-0
packages/grafana-ui/src/components/index.ts
+1
-0
packages/grafana-ui/src/utils/index.ts
+1
-0
packages/grafana-ui/src/utils/statsCalculator.test.ts
+92
-0
packages/grafana-ui/src/utils/statsCalculator.ts
+0
-0
public/app/features/dashboard/dashgrid/DashboardPanel.tsx
+5
-0
public/app/plugins/panel/bargauge/types.ts
+2
-2
public/app/plugins/panel/gauge/types.ts
+2
-2
public/app/plugins/panel/singlestat2/SingleStatPanel.tsx
+15
-10
public/app/plugins/panel/singlestat2/SingleStatValueEditor.tsx
+12
-20
public/app/plugins/panel/singlestat2/module.tsx
+8
-1
public/app/plugins/panel/singlestat2/types.ts
+2
-2
No files found.
packages/grafana-ui/src/components/StatsPicker/StatsPicker.story.tsx
0 → 100644
View file @
e0ecbc4c
import
React
,
{
PureComponent
}
from
'react'
;
import
{
storiesOf
}
from
'@storybook/react'
;
import
{
action
}
from
'@storybook/addon-actions'
;
import
{
withCenteredStory
}
from
'../../utils/storybook/withCenteredStory'
;
import
{
StatsPicker
}
from
'./StatsPicker'
;
import
{
text
,
boolean
}
from
'@storybook/addon-knobs'
;
const
getKnobs
=
()
=>
{
return
{
placeholder
:
text
(
'Placeholder Text'
,
''
),
defaultStat
:
text
(
'Default Stat'
,
''
),
allowMultiple
:
boolean
(
'Allow Multiple'
,
false
),
initialStats
:
text
(
'Initial Stats'
,
''
),
};
};
interface
State
{
stats
:
string
[];
}
export
class
WrapperWithState
extends
PureComponent
<
any
,
State
>
{
constructor
(
props
:
any
)
{
super
(
props
);
this
.
state
=
{
stats
:
this
.
toStatsArray
(
props
.
initialReducers
),
};
}
toStatsArray
=
(
txt
:
string
):
string
[]
=>
{
if
(
!
txt
)
{
return
[];
}
return
txt
.
split
(
','
).
map
(
v
=>
v
.
trim
());
};
componentDidUpdate
(
prevProps
:
any
)
{
const
{
initialReducers
}
=
this
.
props
;
if
(
initialReducers
!==
prevProps
.
initialReducers
)
{
console
.
log
(
'Changing initial reducers'
);
this
.
setState
({
stats
:
this
.
toStatsArray
(
initialReducers
)
});
}
}
render
()
{
const
{
placeholder
,
defaultStat
,
allowMultiple
}
=
this
.
props
;
const
{
stats
}
=
this
.
state
;
return
(
<
StatsPicker
placeholder=
{
placeholder
}
defaultStat=
{
defaultStat
}
allowMultiple=
{
allowMultiple
}
stats=
{
stats
}
onChange=
{
(
stats
:
string
[])
=>
{
action
(
'Picked:'
)(
stats
);
this
.
setState
({
stats
});
}
}
/>
);
}
}
const
story
=
storiesOf
(
'UI/StatsPicker'
,
module
);
story
.
addDecorator
(
withCenteredStory
);
story
.
add
(
'picker'
,
()
=>
{
const
{
placeholder
,
defaultStat
,
allowMultiple
,
initialStats
}
=
getKnobs
();
return
(
<
div
>
<
WrapperWithState
placeholder=
{
placeholder
}
defaultStat=
{
defaultStat
}
allowMultiple=
{
allowMultiple
}
initialStats=
{
initialStats
}
/>
</
div
>
);
});
packages/grafana-ui/src/components/StatsPicker/StatsPicker.tsx
0 → 100644
View file @
e0ecbc4c
import
React
,
{
PureComponent
}
from
'react'
;
import
isArray
from
'lodash/isArray'
;
import
difference
from
'lodash/difference'
;
import
{
Select
}
from
'../index'
;
import
{
getStatsCalculators
}
from
'../../utils/statsCalculator'
;
import
{
SelectOptionItem
}
from
'../Select/Select'
;
interface
Props
{
placeholder
?:
string
;
onChange
:
(
stats
:
string
[])
=>
void
;
stats
:
string
[];
width
?:
number
;
allowMultiple
?:
boolean
;
defaultStat
?:
string
;
}
export
class
StatsPicker
extends
PureComponent
<
Props
>
{
static
defaultProps
=
{
width
:
12
,
allowMultiple
:
false
,
};
componentDidMount
()
{
this
.
checkInput
();
}
componentDidUpdate
(
prevProps
:
Props
)
{
this
.
checkInput
();
}
checkInput
=
()
=>
{
const
{
stats
,
allowMultiple
,
defaultStat
,
onChange
}
=
this
.
props
;
const
current
=
getStatsCalculators
(
stats
);
if
(
current
.
length
!==
stats
.
length
)
{
const
found
=
current
.
map
(
v
=>
v
.
id
);
const
notFound
=
difference
(
stats
,
found
);
console
.
warn
(
'Unknown stats'
,
notFound
,
stats
);
onChange
(
current
.
map
(
stat
=>
stat
.
id
));
}
// Make sure there is only one
if
(
!
allowMultiple
&&
stats
.
length
>
1
)
{
console
.
warn
(
'Removing extra stat'
,
stats
);
onChange
([
stats
[
0
]]);
}
// Set the reducer from callback
if
(
defaultStat
&&
stats
.
length
<
1
)
{
onChange
([
defaultStat
]);
}
};
onSelectionChange
=
(
item
:
SelectOptionItem
)
=>
{
const
{
onChange
}
=
this
.
props
;
if
(
isArray
(
item
))
{
onChange
(
item
.
map
(
v
=>
v
.
value
));
}
else
{
onChange
([
item
.
value
]);
}
};
render
()
{
const
{
width
,
stats
,
allowMultiple
,
defaultStat
,
placeholder
}
=
this
.
props
;
const
options
=
getStatsCalculators
().
map
(
s
=>
{
return
{
value
:
s
.
id
,
label
:
s
.
name
,
description
:
s
.
description
,
};
});
const
value
:
SelectOptionItem
[]
=
[];
stats
.
forEach
(
s
=>
{
const
o
=
options
.
find
(
v
=>
v
.
value
===
s
);
if
(
o
)
{
value
.
push
(
o
);
}
});
//getStatsCalculators(stats);
return
(
<
Select
width=
{
width
}
value=
{
value
}
isClearable=
{
!
defaultStat
}
isMulti=
{
allowMultiple
}
isSearchable=
{
true
}
options=
{
options
}
placeholder=
{
placeholder
}
onChange=
{
this
.
onSelectionChange
}
/>
);
}
}
packages/grafana-ui/src/components/index.ts
View file @
e0ecbc4c
...
...
@@ -27,6 +27,7 @@ export { Switch } from './Switch/Switch';
export
{
EmptySearchResult
}
from
'./EmptySearchResult/EmptySearchResult'
;
export
{
PieChart
,
PieChartDataPoint
,
PieChartType
}
from
'./PieChart/PieChart'
;
export
{
UnitPicker
}
from
'./UnitPicker/UnitPicker'
;
export
{
StatsPicker
}
from
'./StatsPicker/StatsPicker'
;
export
{
Input
,
InputStatus
}
from
'./Input/Input'
;
// Visualizations
...
...
packages/grafana-ui/src/utils/index.ts
View file @
e0ecbc4c
...
...
@@ -5,6 +5,7 @@ export * from './colors';
export
*
from
'./namedColorsPalette'
;
export
*
from
'./thresholds'
;
export
*
from
'./string'
;
export
*
from
'./statsCalculator'
;
export
*
from
'./displayValue'
;
export
*
from
'./deprecationWarning'
;
export
{
getMappedValue
}
from
'./valueMappings'
;
...
...
packages/grafana-ui/src/utils/statsCalculator.test.ts
0 → 100644
View file @
e0ecbc4c
import
{
parseCSV
}
from
'./processTableData'
;
import
{
getStatsCalculators
,
StatID
,
calculateStats
}
from
'./statsCalculator'
;
import
_
from
'lodash'
;
describe
(
'Stats Calculators'
,
()
=>
{
const
basicTable
=
parseCSV
(
'a,b,c
\
n10,20,30
\
n20,30,40'
);
it
(
'should load all standard stats'
,
()
=>
{
const
names
=
[
StatID
.
sum
,
StatID
.
max
,
StatID
.
min
,
StatID
.
logmin
,
StatID
.
mean
,
StatID
.
last
,
StatID
.
first
,
StatID
.
count
,
StatID
.
range
,
StatID
.
diff
,
StatID
.
step
,
StatID
.
delta
,
// StatID.allIsZero,
// StatID.allIsNull,
];
const
stats
=
getStatsCalculators
(
names
);
expect
(
stats
.
length
).
toBe
(
names
.
length
);
});
it
(
'should fail to load unknown stats'
,
()
=>
{
const
names
=
[
'not a stat'
,
StatID
.
max
,
StatID
.
min
,
'also not a stat'
];
const
stats
=
getStatsCalculators
(
names
);
expect
(
stats
.
length
).
toBe
(
2
);
const
found
=
stats
.
map
(
v
=>
v
.
id
);
const
notFound
=
_
.
difference
(
names
,
found
);
expect
(
notFound
.
length
).
toBe
(
2
);
expect
(
notFound
[
0
]).
toBe
(
'not a stat'
);
});
it
(
'should calculate basic stats'
,
()
=>
{
const
stats
=
calculateStats
({
table
:
basicTable
,
columnIndex
:
0
,
stats
:
[
'first'
,
'last'
,
'mean'
],
});
// First
expect
(
stats
.
first
).
toEqual
(
10
);
// Last
expect
(
stats
.
last
).
toEqual
(
20
);
// Mean
expect
(
stats
.
mean
).
toEqual
(
15
);
});
it
(
'should support a single stat also'
,
()
=>
{
const
stats
=
calculateStats
({
table
:
basicTable
,
columnIndex
:
0
,
stats
:
[
'first'
],
});
// Should do the simple version that just looks up value
expect
(
Object
.
keys
(
stats
).
length
).
toEqual
(
1
);
expect
(
stats
.
first
).
toEqual
(
10
);
});
it
(
'should get non standard stats'
,
()
=>
{
const
stats
=
calculateStats
({
table
:
basicTable
,
columnIndex
:
0
,
stats
:
[
StatID
.
distinctCount
,
StatID
.
changeCount
],
});
expect
(
stats
.
distinctCount
).
toEqual
(
2
);
expect
(
stats
.
changeCount
).
toEqual
(
1
);
});
it
(
'should calculate step'
,
()
=>
{
const
stats
=
calculateStats
({
table
:
{
columns
:
[{
text
:
'A'
}],
rows
:
[[
100
],
[
200
],
[
300
],
[
400
]]
},
columnIndex
:
0
,
stats
:
[
StatID
.
step
,
StatID
.
delta
],
});
expect
(
stats
.
step
).
toEqual
(
100
);
expect
(
stats
.
delta
).
toEqual
(
300
);
});
});
packages/grafana-ui/src/utils/statsCalculator.ts
0 → 100644
View file @
e0ecbc4c
This diff is collapsed.
Click to expand it.
public/app/features/dashboard/dashgrid/DashboardPanel.tsx
View file @
e0ecbc4c
...
...
@@ -98,6 +98,11 @@ export class DashboardPanel extends PureComponent<Props, State> {
}
panel
.
changeType
(
pluginId
,
hook
);
}
}
else
if
(
plugin
.
exports
&&
plugin
.
exports
.
reactPanel
)
{
const
hook
=
plugin
.
exports
.
reactPanel
.
panelTypeChangedHook
;
if
(
hook
)
{
panel
.
options
=
hook
(
panel
.
options
||
{},
null
,
null
);
}
}
this
.
setState
({
plugin
,
angularPanel
:
null
});
...
...
public/app/plugins/panel/bargauge/types.ts
View file @
e0ecbc4c
import
{
VizOrientation
,
SelectOptionItem
}
from
'@grafana/ui'
;
import
{
VizOrientation
,
SelectOptionItem
,
StatID
}
from
'@grafana/ui'
;
import
{
SingleStatBaseOptions
}
from
'../singlestat2/types'
;
export
interface
BarGaugeOptions
extends
SingleStatBaseOptions
{
...
...
@@ -25,7 +25,7 @@ export const defaults: BarGaugeOptions = {
orientation
:
VizOrientation
.
Horizontal
,
valueOptions
:
{
unit
:
'none'
,
stat
:
'avg'
,
stat
:
StatID
.
mean
,
prefix
:
''
,
suffix
:
''
,
decimals
:
null
,
...
...
public/app/plugins/panel/gauge/types.ts
View file @
e0ecbc4c
import
{
SingleStatBaseOptions
}
from
'../singlestat2/types'
;
import
{
VizOrientation
}
from
'@grafana/ui'
;
import
{
VizOrientation
,
StatID
}
from
'@grafana/ui'
;
export
interface
GaugeOptions
extends
SingleStatBaseOptions
{
maxValue
:
number
;
...
...
@@ -17,7 +17,7 @@ export const defaults: GaugeOptions = {
prefix
:
''
,
suffix
:
''
,
decimals
:
null
,
stat
:
'avg'
,
stat
:
StatID
.
mean
,
unit
:
'none'
,
},
valueMappings
:
[],
...
...
public/app/plugins/panel/singlestat2/SingleStatPanel.tsx
View file @
e0ecbc4c
...
...
@@ -4,7 +4,7 @@ import React, { PureComponent, CSSProperties } from 'react';
// Types
import
{
SingleStatOptions
,
SingleStatBaseOptions
}
from
'./types'
;
import
{
DisplayValue
,
PanelProps
,
processTimeSeries
,
NullValueMode
,
ColumnType
}
from
'@grafana/ui'
;
import
{
DisplayValue
,
PanelProps
,
NullValueMode
,
ColumnType
,
calculateStats
}
from
'@grafana/ui'
;
import
{
config
}
from
'app/core/config'
;
import
{
getDisplayProcessor
}
from
'@grafana/ui'
;
import
{
ProcessedValuesRepeater
}
from
'./ProcessedValuesRepeater'
;
...
...
@@ -14,7 +14,7 @@ export const getSingleStatValues = (props: PanelProps<SingleStatBaseOptions>): D
const
{
valueOptions
,
valueMappings
}
=
options
;
const
{
unit
,
decimals
,
stat
}
=
valueOptions
;
const
processor
=
getDisplayProcessor
({
const
display
=
getDisplayProcessor
({
unit
,
decimals
,
mappings
:
valueMappings
,
...
...
@@ -25,21 +25,25 @@ export const getSingleStatValues = (props: PanelProps<SingleStatBaseOptions>): D
});
const
values
:
DisplayValue
[]
=
[];
for
(
const
table
of
data
)
{
if
(
stat
===
'name'
)
{
values
.
push
(
display
(
table
.
name
));
}
for
(
let
i
=
0
;
i
<
table
.
columns
.
length
;
i
++
)
{
const
column
=
table
.
columns
[
i
];
// Show all columns that are not 'time'
if
(
column
.
type
===
ColumnType
.
number
)
{
const
s
eries
=
processTimeSerie
s
({
data
:
[
table
]
,
xColumn
:
i
,
yColumn
:
i
,
const
s
tats
=
calculateStat
s
({
table
,
columnIndex
:
i
,
// Hardcoded for now!
stats
:
[
stat
],
// The stats to calculate
nullValueMode
:
NullValueMode
.
Null
,
})[
0
];
const
value
=
stat
!==
'name'
?
series
.
stats
[
stat
]
:
series
.
label
;
values
.
push
(
processor
(
value
));
});
const
displayValue
=
display
(
stats
[
stat
]);
values
.
push
(
displayValue
);
}
}
}
...
...
@@ -47,6 +51,7 @@ export const getSingleStatValues = (props: PanelProps<SingleStatBaseOptions>): D
if
(
values
.
length
===
0
)
{
throw
{
message
:
'Could not find numeric data'
};
}
return
values
;
};
...
...
public/app/plugins/panel/singlestat2/SingleStatValueEditor.tsx
View file @
e0ecbc4c
...
...
@@ -2,25 +2,11 @@
import
React
,
{
PureComponent
}
from
'react'
;
// Components
import
{
FormField
,
FormLabel
,
PanelOptionsGroup
,
S
elect
,
UnitPicker
}
from
'@grafana/ui'
;
import
{
FormField
,
FormLabel
,
PanelOptionsGroup
,
S
tatsPicker
,
UnitPicker
,
StatID
}
from
'@grafana/ui'
;
// Types
import
{
SingleStatValueOptions
}
from
'./types'
;
const
statOptions
=
[
{
value
:
'min'
,
label
:
'Min'
},
{
value
:
'max'
,
label
:
'Max'
},
{
value
:
'avg'
,
label
:
'Average'
},
{
value
:
'current'
,
label
:
'Current'
},
{
value
:
'total'
,
label
:
'Total'
},
{
value
:
'name'
,
label
:
'Name'
},
{
value
:
'first'
,
label
:
'First'
},
{
value
:
'delta'
,
label
:
'Delta'
},
{
value
:
'diff'
,
label
:
'Difference'
},
{
value
:
'range'
,
label
:
'Range'
},
{
value
:
'last_time'
,
label
:
'Time of last point'
},
];
const
labelWidth
=
6
;
export
interface
Props
{
...
...
@@ -30,7 +16,11 @@ export interface Props {
export
class
SingleStatValueEditor
extends
PureComponent
<
Props
>
{
onUnitChange
=
unit
=>
this
.
props
.
onChange
({
...
this
.
props
.
options
,
unit
:
unit
.
value
});
onStatChange
=
stat
=>
this
.
props
.
onChange
({
...
this
.
props
.
options
,
stat
:
stat
.
value
});
onStatsChange
=
stats
=>
{
const
stat
=
stats
[
0
]
||
StatID
.
mean
;
this
.
props
.
onChange
({
...
this
.
props
.
options
,
stat
});
};
onDecimalChange
=
event
=>
{
if
(
!
isNaN
(
event
.
target
.
value
))
{
...
...
@@ -61,11 +51,13 @@ export class SingleStatValueEditor extends PureComponent<Props> {
<
PanelOptionsGroup
title=
"Value"
>
<
div
className=
"gf-form"
>
<
FormLabel
width=
{
labelWidth
}
>
Stat
</
FormLabel
>
<
S
elect
<
S
tatsPicker
width=
{
12
}
options=
{
statOptions
}
onChange=
{
this
.
onStatChange
}
value=
{
statOptions
.
find
(
option
=>
option
.
value
===
stat
)
}
placeholder=
"Choose Stat"
defaultStat=
{
StatID
.
mean
}
allowMultiple=
{
false
}
stats=
{
[
stat
]
}
onChange=
{
this
.
onStatsChange
}
/>
</
div
>
<
div
className=
"gf-form"
>
...
...
public/app/plugins/panel/singlestat2/module.tsx
View file @
e0ecbc4c
import
{
ReactPanelPlugin
}
from
'@grafana/ui'
;
import
{
ReactPanelPlugin
,
getStatsCalculators
}
from
'@grafana/ui'
;
import
{
SingleStatOptions
,
defaults
,
SingleStatBaseOptions
}
from
'./types'
;
import
{
SingleStatPanel
}
from
'./SingleStatPanel'
;
import
cloneDeep
from
'lodash/cloneDeep'
;
...
...
@@ -21,6 +21,13 @@ export const singleStatBaseOptionsCheck = (
});
}
// 6.1 renamed some stats, This makes sure they are up to date
// avg -> mean, current -> last, total -> sum
const
{
valueOptions
}
=
options
;
if
(
valueOptions
&&
valueOptions
.
stat
)
{
valueOptions
.
stat
=
getStatsCalculators
([
valueOptions
.
stat
]).
map
(
s
=>
s
.
id
)[
0
];
console
.
log
(
'CHANGED'
,
valueOptions
);
}
return
options
;
};
...
...
public/app/plugins/panel/singlestat2/types.ts
View file @
e0ecbc4c
import
{
VizOrientation
,
ValueMapping
,
Threshold
}
from
'@grafana/ui'
;
import
{
VizOrientation
,
ValueMapping
,
Threshold
,
StatID
}
from
'@grafana/ui'
;
export
interface
SingleStatBaseOptions
{
valueMappings
:
ValueMapping
[];
...
...
@@ -24,7 +24,7 @@ export const defaults: SingleStatOptions = {
prefix
:
''
,
suffix
:
''
,
decimals
:
null
,
stat
:
'avg'
,
stat
:
StatID
.
mean
,
unit
:
'none'
,
},
valueMappings
:
[],
...
...
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