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
dd0afd0a
Commit
dd0afd0a
authored
Feb 06, 2019
by
Torkel Ödegaard
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Big refactoring for dashboard init redux actions
parent
8574dca0
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
225 additions
and
212 deletions
+225
-212
public/app/core/components/AlertBox/AlertBox.tsx
+41
-0
public/app/core/components/AppNotifications/AppNotificationItem.tsx
+8
-12
public/app/core/copy/appNotification.ts
+2
-13
public/app/core/utils/errors.ts
+15
-0
public/app/features/dashboard/components/DashNav/DashNavCtrl.ts
+0
-117
public/app/features/dashboard/components/DashNav/index.ts
+0
-1
public/app/features/dashboard/containers/DashboardPage.test.tsx
+6
-6
public/app/features/dashboard/containers/DashboardPage.tsx
+40
-14
public/app/features/dashboard/containers/SoloPanelPage.tsx
+0
-1
public/app/features/dashboard/state/actions.ts
+22
-6
public/app/features/dashboard/state/initDashboard.test.ts
+4
-6
public/app/features/dashboard/state/initDashboard.ts
+15
-18
public/app/features/dashboard/state/reducers.ts
+54
-12
public/app/types/dashboard.ts
+13
-6
public/sass/pages/_dashboard.scss
+5
-0
No files found.
public/app/core/components/AlertBox/AlertBox.tsx
0 → 100644
View file @
dd0afd0a
import
React
,
{
FunctionComponent
}
from
'react'
;
import
{
AppNotificationSeverity
}
from
'app/types'
;
interface
Props
{
title
:
string
;
icon
?:
string
;
text
?:
string
;
severity
:
AppNotificationSeverity
;
onClose
?:
()
=>
void
;
}
function
getIconFromSeverity
(
severity
:
AppNotificationSeverity
):
string
{
switch
(
severity
)
{
case
AppNotificationSeverity
.
Error
:
{
return
'fa fa-exclamation-triangle'
;
}
case
AppNotificationSeverity
.
Success
:
{
return
'fa fa-check'
;
}
default
:
return
null
;
}
}
export
const
AlertBox
:
FunctionComponent
<
Props
>
=
({
title
,
icon
,
text
,
severity
,
onClose
})
=>
{
return
(
<
div
className=
{
`alert alert-${severity}`
}
>
<
div
className=
"alert-icon"
>
<
i
className=
{
icon
||
getIconFromSeverity
(
severity
)
}
/>
</
div
>
<
div
className=
"alert-body"
>
<
div
className=
"alert-title"
>
{
title
}
</
div
>
{
text
&&
<
div
className=
"alert-text"
>
{
text
}
</
div
>
}
</
div
>
{
onClose
&&
(
<
button
type=
"button"
className=
"alert-close"
onClick=
{
onClose
}
>
<
i
className=
"fa fa fa-remove"
/>
</
button
>
)
}
</
div
>
);
};
public/app/core/components/AppNotifications/AppNotificationItem.tsx
View file @
dd0afd0a
import
React
,
{
Component
}
from
'react'
;
import
React
,
{
Component
}
from
'react'
;
import
{
AppNotification
}
from
'app/types'
;
import
{
AppNotification
}
from
'app/types'
;
import
{
AlertBox
}
from
'../AlertBox/AlertBox'
;
interface
Props
{
interface
Props
{
appNotification
:
AppNotification
;
appNotification
:
AppNotification
;
...
@@ -22,18 +23,13 @@ export default class AppNotificationItem extends Component<Props> {
...
@@ -22,18 +23,13 @@ export default class AppNotificationItem extends Component<Props> {
const
{
appNotification
,
onClearNotification
}
=
this
.
props
;
const
{
appNotification
,
onClearNotification
}
=
this
.
props
;
return
(
return
(
<
div
className=
{
`alert-${appNotification.severity} alert`
}
>
<
AlertBox
<
div
className=
"alert-icon"
>
severity=
{
appNotification
.
severity
}
<
i
className=
{
appNotification
.
icon
}
/>
title=
{
appNotification
.
title
}
</
div
>
text=
{
appNotification
.
text
}
<
div
className=
"alert-body"
>
icon=
{
appNotification
.
icon
}
<
div
className=
"alert-title"
>
{
appNotification
.
title
}
</
div
>
onClose=
{
()
=>
onClearNotification
(
appNotification
.
id
)
}
<
div
className=
"alert-text"
>
{
appNotification
.
text
}
</
div
>
/>
</
div
>
<
button
type=
"button"
className=
"alert-close"
onClick=
{
()
=>
onClearNotification
(
appNotification
.
id
)
}
>
<
i
className=
"fa fa fa-remove"
/>
</
button
>
</
div
>
);
);
}
}
}
}
public/app/core/copy/appNotification.ts
View file @
dd0afd0a
import
_
from
'lodash'
;
import
{
AppNotification
,
AppNotificationSeverity
,
AppNotificationTimeout
}
from
'app/types'
;
import
{
AppNotification
,
AppNotificationSeverity
,
AppNotificationTimeout
}
from
'app/types'
;
import
{
getMessageFromError
}
from
'app/core/utils/errors'
;
const
defaultSuccessNotification
:
AppNotification
=
{
const
defaultSuccessNotification
:
AppNotification
=
{
title
:
''
,
title
:
''
,
...
@@ -33,21 +33,10 @@ export const createSuccessNotification = (title: string, text?: string): AppNoti
...
@@ -33,21 +33,10 @@ export const createSuccessNotification = (title: string, text?: string): AppNoti
});
});
export
const
createErrorNotification
=
(
title
:
string
,
text
?:
any
):
AppNotification
=>
{
export
const
createErrorNotification
=
(
title
:
string
,
text
?:
any
):
AppNotification
=>
{
// Handling if text is an error object
if
(
text
&&
!
_
.
isString
(
text
))
{
if
(
text
.
message
)
{
text
=
text
.
message
;
}
else
if
(
text
.
data
&&
text
.
data
.
message
)
{
text
=
text
.
data
.
message
;
}
else
{
text
=
text
.
toString
();
}
}
return
{
return
{
...
defaultErrorNotification
,
...
defaultErrorNotification
,
title
:
title
,
title
:
title
,
text
:
text
,
text
:
getMessageFromError
(
text
)
,
id
:
Date
.
now
(),
id
:
Date
.
now
(),
};
};
};
};
...
...
public/app/core/utils/errors.ts
0 → 100644
View file @
dd0afd0a
import
_
from
'lodash'
;
export
function
getMessageFromError
(
err
:
any
):
string
|
null
{
if
(
err
&&
!
_
.
isString
(
err
))
{
if
(
err
.
message
)
{
return
err
.
message
;
}
else
if
(
err
.
data
&&
err
.
data
.
message
)
{
return
err
.
data
.
message
;
}
else
{
return
JSON
.
stringify
(
err
);
}
}
return
null
;
}
public/app/features/dashboard/components/DashNav/DashNavCtrl.ts
deleted
100644 → 0
View file @
8574dca0
import
moment
from
'moment'
;
import
angular
from
'angular'
;
import
{
appEvents
,
NavModel
}
from
'app/core/core'
;
import
{
DashboardModel
}
from
'../../state/DashboardModel'
;
export
class
DashNavCtrl
{
dashboard
:
DashboardModel
;
navModel
:
NavModel
;
titleTooltip
:
string
;
/** @ngInject */
constructor
(
private
$scope
,
private
dashboardSrv
,
private
$location
,
public
playlistSrv
)
{
if
(
this
.
dashboard
.
meta
.
isSnapshot
)
{
const
meta
=
this
.
dashboard
.
meta
;
this
.
titleTooltip
=
'Created: '
+
moment
(
meta
.
created
).
calendar
();
if
(
meta
.
expires
)
{
this
.
titleTooltip
+=
'<br>Expires: '
+
moment
(
meta
.
expires
).
fromNow
()
+
'<br>'
;
}
}
}
toggleSettings
()
{
const
search
=
this
.
$location
.
search
();
if
(
search
.
editview
)
{
delete
search
.
editview
;
}
else
{
search
.
editview
=
'settings'
;
}
this
.
$location
.
search
(
search
);
}
toggleViewMode
()
{
appEvents
.
emit
(
'toggle-kiosk-mode'
);
}
close
()
{
const
search
=
this
.
$location
.
search
();
if
(
search
.
editview
)
{
delete
search
.
editview
;
}
else
if
(
search
.
fullscreen
)
{
delete
search
.
fullscreen
;
delete
search
.
edit
;
delete
search
.
tab
;
delete
search
.
panelId
;
}
this
.
$location
.
search
(
search
);
}
starDashboard
()
{
this
.
dashboardSrv
.
starDashboard
(
this
.
dashboard
.
id
,
this
.
dashboard
.
meta
.
isStarred
).
then
(
newState
=>
{
this
.
dashboard
.
meta
.
isStarred
=
newState
;
});
}
shareDashboard
(
tabIndex
)
{
const
modalScope
=
this
.
$scope
.
$new
();
modalScope
.
tabIndex
=
tabIndex
;
modalScope
.
dashboard
=
this
.
dashboard
;
appEvents
.
emit
(
'show-modal'
,
{
src
:
'public/app/features/dashboard/components/ShareModal/template.html'
,
scope
:
modalScope
,
});
}
hideTooltip
(
evt
)
{
angular
.
element
(
evt
.
currentTarget
).
tooltip
(
'hide'
);
}
saveDashboard
()
{
return
this
.
dashboardSrv
.
saveDashboard
();
}
showSearch
()
{
if
(
this
.
dashboard
.
meta
.
fullscreen
)
{
this
.
close
();
return
;
}
appEvents
.
emit
(
'show-dash-search'
);
}
addPanel
()
{
appEvents
.
emit
(
'dash-scroll'
,
{
animate
:
true
,
evt
:
0
});
if
(
this
.
dashboard
.
panels
.
length
>
0
&&
this
.
dashboard
.
panels
[
0
].
type
===
'add-panel'
)
{
return
;
// Return if the "Add panel" exists already
}
this
.
dashboard
.
addPanel
({
type
:
'add-panel'
,
gridPos
:
{
x
:
0
,
y
:
0
,
w
:
12
,
h
:
8
},
title
:
'Panel Title'
,
});
}
navItemClicked
(
navItem
,
evt
)
{
if
(
navItem
.
clickHandler
)
{
navItem
.
clickHandler
();
evt
.
preventDefault
();
}
}
}
export
function
dashNavDirective
()
{
return
{
restrict
:
'E'
,
templateUrl
:
'public/app/features/dashboard/components/DashNav/template.html'
,
controller
:
DashNavCtrl
,
bindToController
:
true
,
controllerAs
:
'ctrl'
,
transclude
:
true
,
scope
:
{
dashboard
:
'='
},
};
}
angular
.
module
(
'grafana.directives'
).
directive
(
'dashnav'
,
dashNavDirective
);
public/app/features/dashboard/components/DashNav/index.ts
View file @
dd0afd0a
export
{
DashNavCtrl
}
from
'./DashNavCtrl'
;
import
DashNav
from
'./DashNav'
;
import
DashNav
from
'./DashNav'
;
export
{
DashNav
};
export
{
DashNav
};
public/app/features/dashboard/containers/DashboardPage.test.tsx
View file @
dd0afd0a
...
@@ -2,8 +2,8 @@ import React from 'react';
...
@@ -2,8 +2,8 @@ import React from 'react';
import
{
shallow
,
ShallowWrapper
}
from
'enzyme'
;
import
{
shallow
,
ShallowWrapper
}
from
'enzyme'
;
import
{
DashboardPage
,
Props
,
State
}
from
'./DashboardPage'
;
import
{
DashboardPage
,
Props
,
State
}
from
'./DashboardPage'
;
import
{
DashboardModel
}
from
'../state'
;
import
{
DashboardModel
}
from
'../state'
;
import
{
setDashboardModel
}
from
'../state/actions'
;
import
{
cleanUpDashboard
}
from
'../state/actions'
;
import
{
DashboardRouteInfo
,
Dashboard
LoadingStat
e
}
from
'app/types'
;
import
{
DashboardRouteInfo
,
Dashboard
InitPhas
e
}
from
'app/types'
;
jest
.
mock
(
'sass/_variables.scss'
,
()
=>
({
jest
.
mock
(
'sass/_variables.scss'
,
()
=>
({
panelhorizontalpadding
:
10
,
panelhorizontalpadding
:
10
,
...
@@ -22,13 +22,13 @@ function setup(propOverrides?: Partial<Props>): ShallowWrapper<Props, State, Das
...
@@ -22,13 +22,13 @@ function setup(propOverrides?: Partial<Props>): ShallowWrapper<Props, State, Das
routeInfo
:
DashboardRouteInfo
.
Normal
,
routeInfo
:
DashboardRouteInfo
.
Normal
,
urlEdit
:
false
,
urlEdit
:
false
,
urlFullscreen
:
false
,
urlFullscreen
:
false
,
loadingState
:
DashboardLoadingState
.
Done
,
initPhase
:
DashboardInitPhase
.
Completed
,
is
Loading
Slow
:
false
,
is
Init
Slow
:
false
,
initDashboard
:
jest
.
fn
(),
initDashboard
:
jest
.
fn
(),
updateLocation
:
jest
.
fn
(),
updateLocation
:
jest
.
fn
(),
notifyApp
:
jest
.
fn
(),
notifyApp
:
jest
.
fn
(),
dashboard
:
null
,
dashboard
:
null
,
setDashboardModel
:
setDashboardModel
,
cleanUpDashboard
:
cleanUpDashboard
,
};
};
Object
.
assign
(
props
,
propOverrides
);
Object
.
assign
(
props
,
propOverrides
);
...
@@ -66,7 +66,7 @@ describe('DashboardPage', () => {
...
@@ -66,7 +66,7 @@ describe('DashboardPage', () => {
canEdit
:
true
,
canEdit
:
true
,
canSave
:
true
,
canSave
:
true
,
});
});
wrapper
.
setProps
({
dashboard
,
loadingState
:
DashboardLoadingState
.
Done
});
wrapper
.
setProps
({
dashboard
,
initPhase
:
DashboardInitPhase
.
Completed
});
});
});
it
(
'Should update title'
,
()
=>
{
it
(
'Should update title'
,
()
=>
{
...
...
public/app/features/dashboard/containers/DashboardPage.tsx
View file @
dd0afd0a
...
@@ -7,6 +7,7 @@ import classNames from 'classnames';
...
@@ -7,6 +7,7 @@ import classNames from 'classnames';
// Services & Utils
// Services & Utils
import
{
createErrorNotification
}
from
'app/core/copy/appNotification'
;
import
{
createErrorNotification
}
from
'app/core/copy/appNotification'
;
import
{
getMessageFromError
}
from
'app/core/utils/errors'
;
// Components
// Components
import
{
DashboardGrid
}
from
'../dashgrid/DashboardGrid'
;
import
{
DashboardGrid
}
from
'../dashgrid/DashboardGrid'
;
...
@@ -14,15 +15,22 @@ import { DashNav } from '../components/DashNav';
...
@@ -14,15 +15,22 @@ import { DashNav } from '../components/DashNav';
import
{
SubMenu
}
from
'../components/SubMenu'
;
import
{
SubMenu
}
from
'../components/SubMenu'
;
import
{
DashboardSettings
}
from
'../components/DashboardSettings'
;
import
{
DashboardSettings
}
from
'../components/DashboardSettings'
;
import
{
CustomScrollbar
}
from
'@grafana/ui'
;
import
{
CustomScrollbar
}
from
'@grafana/ui'
;
import
{
AlertBox
}
from
'app/core/components/AlertBox/AlertBox'
;
// Redux
// Redux
import
{
initDashboard
}
from
'../state/initDashboard'
;
import
{
initDashboard
}
from
'../state/initDashboard'
;
import
{
setDashboardModel
}
from
'../state/actions'
;
import
{
cleanUpDashboard
}
from
'../state/actions'
;
import
{
updateLocation
}
from
'app/core/actions'
;
import
{
updateLocation
}
from
'app/core/actions'
;
import
{
notifyApp
}
from
'app/core/actions'
;
import
{
notifyApp
}
from
'app/core/actions'
;
// Types
// Types
import
{
StoreState
,
DashboardLoadingState
,
DashboardRouteInfo
}
from
'app/types'
;
import
{
StoreState
,
DashboardInitPhase
,
DashboardRouteInfo
,
DashboardInitError
,
AppNotificationSeverity
,
}
from
'app/types'
;
import
{
DashboardModel
,
PanelModel
}
from
'app/features/dashboard/state'
;
import
{
DashboardModel
,
PanelModel
}
from
'app/features/dashboard/state'
;
export
interface
Props
{
export
interface
Props
{
...
@@ -37,11 +45,12 @@ export interface Props {
...
@@ -37,11 +45,12 @@ export interface Props {
routeInfo
:
DashboardRouteInfo
;
routeInfo
:
DashboardRouteInfo
;
urlEdit
:
boolean
;
urlEdit
:
boolean
;
urlFullscreen
:
boolean
;
urlFullscreen
:
boolean
;
loadingState
:
DashboardLoadingStat
e
;
initPhase
:
DashboardInitPhas
e
;
is
Loading
Slow
:
boolean
;
is
Init
Slow
:
boolean
;
dashboard
:
DashboardModel
|
null
;
dashboard
:
DashboardModel
|
null
;
initError
?:
DashboardInitError
;
initDashboard
:
typeof
initDashboard
;
initDashboard
:
typeof
initDashboard
;
setDashboardModel
:
typeof
setDashboardModel
;
cleanUpDashboard
:
typeof
cleanUpDashboard
;
notifyApp
:
typeof
notifyApp
;
notifyApp
:
typeof
notifyApp
;
updateLocation
:
typeof
updateLocation
;
updateLocation
:
typeof
updateLocation
;
}
}
...
@@ -83,7 +92,7 @@ export class DashboardPage extends PureComponent<Props, State> {
...
@@ -83,7 +92,7 @@ export class DashboardPage extends PureComponent<Props, State> {
componentWillUnmount
()
{
componentWillUnmount
()
{
if
(
this
.
props
.
dashboard
)
{
if
(
this
.
props
.
dashboard
)
{
this
.
props
.
dashboard
.
destroy
();
this
.
props
.
dashboard
.
destroy
();
this
.
props
.
setDashboardModel
(
null
);
this
.
props
.
cleanUpDashboard
(
);
}
}
}
}
...
@@ -204,23 +213,37 @@ export class DashboardPage extends PureComponent<Props, State> {
...
@@ -204,23 +213,37 @@ export class DashboardPage extends PureComponent<Props, State> {
this
.
setState
({
scrollTop
:
0
});
this
.
setState
({
scrollTop
:
0
});
};
};
render
Loading
State
()
{
render
SlowInit
State
()
{
return
(
return
(
<
div
className=
"dashboard-loading"
>
<
div
className=
"dashboard-loading"
>
<
div
className=
"dashboard-loading__text"
>
<
div
className=
"dashboard-loading__text"
>
<
i
className=
"fa fa-spinner fa-spin"
/>
Dashboard
{
this
.
props
.
loadingStat
e
}
<
i
className=
"fa fa-spinner fa-spin"
/>
{
this
.
props
.
initPhas
e
}
</
div
>
</
div
>
</
div
>
</
div
>
);
);
}
}
renderInitFailedState
()
{
const
{
initError
}
=
this
.
props
;
return
(
<
div
className=
"dashboard-loading"
>
<
AlertBox
severity=
{
AppNotificationSeverity
.
Error
}
title=
{
initError
.
message
}
text=
{
getMessageFromError
(
initError
.
error
)
}
/>
</
div
>
);
}
render
()
{
render
()
{
const
{
dashboard
,
editview
,
$injector
,
is
LoadingSlow
}
=
this
.
props
;
const
{
dashboard
,
editview
,
$injector
,
is
InitSlow
,
initError
}
=
this
.
props
;
const
{
isSettingsOpening
,
isEditing
,
isFullscreen
,
scrollTop
}
=
this
.
state
;
const
{
isSettingsOpening
,
isEditing
,
isFullscreen
,
scrollTop
}
=
this
.
state
;
if
(
!
dashboard
)
{
if
(
!
dashboard
)
{
if
(
is
Loading
Slow
)
{
if
(
is
Init
Slow
)
{
return
this
.
render
Loading
State
();
return
this
.
render
SlowInit
State
();
}
}
return
null
;
return
null
;
}
}
...
@@ -249,6 +272,8 @@ export class DashboardPage extends PureComponent<Props, State> {
...
@@ -249,6 +272,8 @@ export class DashboardPage extends PureComponent<Props, State> {
<
CustomScrollbar
autoHeightMin=
{
'100%'
}
setScrollTop=
{
this
.
setScrollTop
}
scrollTop=
{
scrollTop
}
>
<
CustomScrollbar
autoHeightMin=
{
'100%'
}
setScrollTop=
{
this
.
setScrollTop
}
scrollTop=
{
scrollTop
}
>
{
editview
&&
<
DashboardSettings
dashboard=
{
dashboard
}
/>
}
{
editview
&&
<
DashboardSettings
dashboard=
{
dashboard
}
/>
}
{
initError
&&
this
.
renderInitFailedState
()
}
<
div
className=
{
gridWrapperClasses
}
>
<
div
className=
{
gridWrapperClasses
}
>
{
dashboard
.
meta
.
submenuEnabled
&&
<
SubMenu
dashboard=
{
dashboard
}
/>
}
{
dashboard
.
meta
.
submenuEnabled
&&
<
SubMenu
dashboard=
{
dashboard
}
/>
}
<
DashboardGrid
dashboard=
{
dashboard
}
isEditing=
{
isEditing
}
isFullscreen=
{
isFullscreen
}
/>
<
DashboardGrid
dashboard=
{
dashboard
}
isEditing=
{
isEditing
}
isFullscreen=
{
isFullscreen
}
/>
...
@@ -269,14 +294,15 @@ const mapStateToProps = (state: StoreState) => ({
...
@@ -269,14 +294,15 @@ const mapStateToProps = (state: StoreState) => ({
urlFolderId
:
state
.
location
.
query
.
folderId
,
urlFolderId
:
state
.
location
.
query
.
folderId
,
urlFullscreen
:
state
.
location
.
query
.
fullscreen
===
true
,
urlFullscreen
:
state
.
location
.
query
.
fullscreen
===
true
,
urlEdit
:
state
.
location
.
query
.
edit
===
true
,
urlEdit
:
state
.
location
.
query
.
edit
===
true
,
loadingState
:
state
.
dashboard
.
loadingState
,
initPhase
:
state
.
dashboard
.
initPhase
,
isLoadingSlow
:
state
.
dashboard
.
isLoadingSlow
,
isInitSlow
:
state
.
dashboard
.
isInitSlow
,
initError
:
state
.
dashboard
.
initError
,
dashboard
:
state
.
dashboard
.
model
as
DashboardModel
,
dashboard
:
state
.
dashboard
.
model
as
DashboardModel
,
});
});
const
mapDispatchToProps
=
{
const
mapDispatchToProps
=
{
initDashboard
,
initDashboard
,
setDashboardModel
,
cleanUpDashboard
,
notifyApp
,
notifyApp
,
updateLocation
,
updateLocation
,
};
};
...
...
public/app/features/dashboard/containers/SoloPanelPage.tsx
View file @
dd0afd0a
...
@@ -100,7 +100,6 @@ const mapStateToProps = (state: StoreState) => ({
...
@@ -100,7 +100,6 @@ const mapStateToProps = (state: StoreState) => ({
urlSlug
:
state
.
location
.
routeParams
.
slug
,
urlSlug
:
state
.
location
.
routeParams
.
slug
,
urlType
:
state
.
location
.
routeParams
.
type
,
urlType
:
state
.
location
.
routeParams
.
type
,
urlPanelId
:
state
.
location
.
query
.
panelId
,
urlPanelId
:
state
.
location
.
query
.
panelId
,
loadingState
:
state
.
dashboard
.
loadingState
,
dashboard
:
state
.
dashboard
.
model
as
DashboardModel
,
dashboard
:
state
.
dashboard
.
model
as
DashboardModel
,
});
});
...
...
public/app/features/dashboard/state/actions.ts
View file @
dd0afd0a
...
@@ -8,20 +8,36 @@ import { loadPluginDashboards } from '../../plugins/state/actions';
...
@@ -8,20 +8,36 @@ import { loadPluginDashboards } from '../../plugins/state/actions';
import
{
notifyApp
}
from
'app/core/actions'
;
import
{
notifyApp
}
from
'app/core/actions'
;
// Types
// Types
import
{
ThunkResult
}
from
'app/types'
;
import
{
import
{
ThunkResult
,
DashboardAcl
,
DashboardAcl
,
DashboardAclDTO
,
DashboardAclDTO
,
PermissionLevel
,
PermissionLevel
,
DashboardAclUpdateDTO
,
DashboardAclUpdateDTO
,
NewDashboardAclItem
,
NewDashboardAclItem
,
}
from
'app/types/acl'
;
MutableDashboard
,
import
{
DashboardLoadingState
,
MutableDashboard
}
from
'app/types/dashboard'
;
DashboardInitError
,
}
from
'app/types'
;
export
const
loadDashboardPermissions
=
actionCreatorFactory
<
DashboardAclDTO
[]
>
(
'LOAD_DASHBOARD_PERMISSIONS'
).
create
();
export
const
loadDashboardPermissions
=
actionCreatorFactory
<
DashboardAclDTO
[]
>
(
'LOAD_DASHBOARD_PERMISSIONS'
).
create
();
export
const
setDashboardLoadingState
=
actionCreatorFactory
<
DashboardLoadingState
>
(
'SET_DASHBOARD_LOADING_STATE'
).
create
();
export
const
setDashboardModel
=
actionCreatorFactory
<
MutableDashboard
>
(
'SET_DASHBOARD_MODEL'
).
create
();
export
const
dashboardInitFetching
=
noPayloadActionCreatorFactory
(
'DASHBOARD_INIT_FETCHING'
).
create
();
export
const
setDashboardLoadingSlow
=
noPayloadActionCreatorFactory
(
'SET_DASHBOARD_LOADING_SLOW'
).
create
();
export
const
dashboardInitServices
=
noPayloadActionCreatorFactory
(
'DASHBOARD_INIT_SERVICES'
).
create
();
export
const
dashboardInitSlow
=
noPayloadActionCreatorFactory
(
'SET_DASHBOARD_INIT_SLOW'
).
create
();
export
const
dashboardInitCompleted
=
actionCreatorFactory
<
MutableDashboard
>
(
'DASHBOARD_INIT_COMLETED'
).
create
();
/*
* Unrecoverable init failure (fetch or model creation failed)
*/
export
const
dashboardInitFailed
=
actionCreatorFactory
<
DashboardInitError
>
(
'DASHBOARD_INIT_FAILED'
).
create
();
/*
* When leaving dashboard, resets state
* */
export
const
cleanUpDashboard
=
noPayloadActionCreatorFactory
(
'DASHBOARD_CLEAN_UP'
).
create
();
export
function
getDashboardPermissions
(
id
:
number
):
ThunkResult
<
void
>
{
export
function
getDashboardPermissions
(
id
:
number
):
ThunkResult
<
void
>
{
return
async
dispatch
=>
{
return
async
dispatch
=>
{
...
...
public/app/features/dashboard/state/initDashboard.test.ts
View file @
dd0afd0a
import
configureMockStore
from
'redux-mock-store'
;
import
configureMockStore
from
'redux-mock-store'
;
import
thunk
from
'redux-thunk'
;
import
thunk
from
'redux-thunk'
;
import
{
initDashboard
,
InitDashboardArgs
}
from
'./initDashboard'
;
import
{
initDashboard
,
InitDashboardArgs
}
from
'./initDashboard'
;
import
{
DashboardRouteInfo
,
DashboardLoadingState
}
from
'app/types'
;
import
{
DashboardRouteInfo
}
from
'app/types'
;
const
mockStore
=
configureMockStore
([
thunk
]);
const
mockStore
=
configureMockStore
([
thunk
]);
...
@@ -98,13 +98,11 @@ describeInitScenario('Initializing new dashboard', ctx => {
...
@@ -98,13 +98,11 @@ describeInitScenario('Initializing new dashboard', ctx => {
});
});
it
(
'Should send action to set loading state to fetching'
,
()
=>
{
it
(
'Should send action to set loading state to fetching'
,
()
=>
{
expect
(
ctx
.
actions
[
0
].
type
).
toBe
(
'SET_DASHBOARD_LOADING_STATE'
);
expect
(
ctx
.
actions
[
0
].
type
).
toBe
(
'DASHBOARD_INIT_FETCHING'
);
expect
(
ctx
.
actions
[
0
].
payload
).
toBe
(
DashboardLoadingState
.
Fetching
);
});
});
it
(
'Should send action to set loading state to Initializing'
,
()
=>
{
it
(
'Should send action to set loading state to Initializing'
,
()
=>
{
expect
(
ctx
.
actions
[
1
].
type
).
toBe
(
'SET_DASHBOARD_LOADING_STATE'
);
expect
(
ctx
.
actions
[
1
].
type
).
toBe
(
'DASHBOARD_INIT_SERVICES'
);
expect
(
ctx
.
actions
[
1
].
payload
).
toBe
(
DashboardLoadingState
.
Initializing
);
});
});
it
(
'Should update location with orgId query param'
,
()
=>
{
it
(
'Should update location with orgId query param'
,
()
=>
{
...
@@ -113,7 +111,7 @@ describeInitScenario('Initializing new dashboard', ctx => {
...
@@ -113,7 +111,7 @@ describeInitScenario('Initializing new dashboard', ctx => {
});
});
it
(
'Should send action to set dashboard model'
,
()
=>
{
it
(
'Should send action to set dashboard model'
,
()
=>
{
expect
(
ctx
.
actions
[
3
].
type
).
toBe
(
'
SET_DASHBOARD_MODEL
'
);
expect
(
ctx
.
actions
[
3
].
type
).
toBe
(
'
DASHBOARD_INIT_COMLETED
'
);
expect
(
ctx
.
actions
[
3
].
payload
.
title
).
toBe
(
'New dashboard'
);
expect
(
ctx
.
actions
[
3
].
payload
.
title
).
toBe
(
'New dashboard'
);
});
});
...
...
public/app/features/dashboard/state/initDashboard.ts
View file @
dd0afd0a
...
@@ -12,17 +12,16 @@ import { KeybindingSrv } from 'app/core/services/keybindingSrv';
...
@@ -12,17 +12,16 @@ import { KeybindingSrv } from 'app/core/services/keybindingSrv';
import
{
updateLocation
}
from
'app/core/actions'
;
import
{
updateLocation
}
from
'app/core/actions'
;
import
{
notifyApp
}
from
'app/core/actions'
;
import
{
notifyApp
}
from
'app/core/actions'
;
import
locationUtil
from
'app/core/utils/location_util'
;
import
locationUtil
from
'app/core/utils/location_util'
;
import
{
setDashboardLoadingState
,
setDashboardModel
,
setDashboardLoadingSlow
}
from
'./actions'
;
import
{
dashboardInitFetching
,
dashboardInitCompleted
,
dashboardInitFailed
,
dashboardInitSlow
,
dashboardInitServices
,
}
from
'./actions'
;
// Types
// Types
import
{
import
{
DashboardRouteInfo
,
StoreState
,
ThunkDispatch
,
ThunkResult
,
DashboardDTO
}
from
'app/types'
;
DashboardLoadingState
,
DashboardRouteInfo
,
StoreState
,
ThunkDispatch
,
ThunkResult
,
DashboardDTO
,
}
from
'app/types'
;
import
{
DashboardModel
}
from
'./DashboardModel'
;
import
{
DashboardModel
}
from
'./DashboardModel'
;
export
interface
InitDashboardArgs
{
export
interface
InitDashboardArgs
{
...
@@ -106,8 +105,7 @@ async function fetchDashboard(
...
@@ -106,8 +105,7 @@ async function fetchDashboard(
throw
{
message
:
'Unknown route '
+
args
.
routeInfo
};
throw
{
message
:
'Unknown route '
+
args
.
routeInfo
};
}
}
}
catch
(
err
)
{
}
catch
(
err
)
{
dispatch
(
setDashboardLoadingState
(
DashboardLoadingState
.
Error
));
dispatch
(
dashboardInitFailed
({
message
:
'Failed to fetch dashboard'
,
error
:
err
}));
dispatch
(
notifyApp
(
createErrorNotification
(
'Dashboard fetch failed'
,
err
)));
console
.
log
(
err
);
console
.
log
(
err
);
return
null
;
return
null
;
}
}
...
@@ -125,13 +123,13 @@ async function fetchDashboard(
...
@@ -125,13 +123,13 @@ async function fetchDashboard(
export
function
initDashboard
(
args
:
InitDashboardArgs
):
ThunkResult
<
void
>
{
export
function
initDashboard
(
args
:
InitDashboardArgs
):
ThunkResult
<
void
>
{
return
async
(
dispatch
,
getState
)
=>
{
return
async
(
dispatch
,
getState
)
=>
{
// set fetching state
// set fetching state
dispatch
(
setDashboardLoadingState
(
DashboardLoadingState
.
Fetching
));
dispatch
(
dashboardInitFetching
(
));
// Detect slow loading / initializing and set state flag
// Detect slow loading / initializing and set state flag
// This is in order to not show loading indication for fast loading dashboards as it creates blinking/flashing
// This is in order to not show loading indication for fast loading dashboards as it creates blinking/flashing
setTimeout
(()
=>
{
setTimeout
(()
=>
{
if
(
getState
().
dashboard
.
model
===
null
)
{
if
(
getState
().
dashboard
.
model
===
null
)
{
dispatch
(
setDashboardLoading
Slow
());
dispatch
(
dashboardInit
Slow
());
}
}
},
500
);
},
500
);
...
@@ -144,15 +142,14 @@ export function initDashboard(args: InitDashboardArgs): ThunkResult<void> {
...
@@ -144,15 +142,14 @@ export function initDashboard(args: InitDashboardArgs): ThunkResult<void> {
}
}
// set initializing state
// set initializing state
dispatch
(
setDashboardLoadingState
(
DashboardLoadingState
.
Initializing
));
dispatch
(
dashboardInitServices
(
));
// create model
// create model
let
dashboard
:
DashboardModel
;
let
dashboard
:
DashboardModel
;
try
{
try
{
dashboard
=
new
DashboardModel
(
dashDTO
.
dashboard
,
dashDTO
.
meta
);
dashboard
=
new
DashboardModel
(
dashDTO
.
dashboard
,
dashDTO
.
meta
);
}
catch
(
err
)
{
}
catch
(
err
)
{
dispatch
(
setDashboardLoadingState
(
DashboardLoadingState
.
Error
));
dispatch
(
dashboardInitFailed
({
message
:
'Failed create dashboard model'
,
error
:
err
}));
dispatch
(
notifyApp
(
createErrorNotification
(
'Dashboard model initializing failure'
,
err
)));
console
.
log
(
err
);
console
.
log
(
err
);
return
;
return
;
}
}
...
@@ -203,8 +200,8 @@ export function initDashboard(args: InitDashboardArgs): ThunkResult<void> {
...
@@ -203,8 +200,8 @@ export function initDashboard(args: InitDashboardArgs): ThunkResult<void> {
// legacy srv state
// legacy srv state
dashboardSrv
.
setCurrent
(
dashboard
);
dashboardSrv
.
setCurrent
(
dashboard
);
//
set model in redux (even though it's mutable)
//
yay we are done
dispatch
(
setDashboardModel
(
dashboard
));
dispatch
(
dashboardInitCompleted
(
dashboard
));
};
};
}
}
...
...
public/app/features/dashboard/state/reducers.ts
View file @
dd0afd0a
import
{
DashboardState
,
DashboardLoadingState
}
from
'app/types/dashboard'
;
import
{
DashboardState
,
DashboardInitPhase
}
from
'app/types'
;
import
{
loadDashboardPermissions
,
setDashboardLoadingState
,
setDashboardModel
,
setDashboardLoadingSlow
}
from
'./actions'
;
import
{
loadDashboardPermissions
,
dashboardInitFetching
,
dashboardInitSlow
,
dashboardInitServices
,
dashboardInitFailed
,
dashboardInitCompleted
,
cleanUpDashboard
,
}
from
'./actions'
;
import
{
reducerFactory
}
from
'app/core/redux'
;
import
{
reducerFactory
}
from
'app/core/redux'
;
import
{
processAclItems
}
from
'app/core/utils/acl'
;
import
{
processAclItems
}
from
'app/core/utils/acl'
;
import
{
DashboardModel
}
from
'./DashboardModel'
;
export
const
initialState
:
DashboardState
=
{
export
const
initialState
:
DashboardState
=
{
loadingState
:
DashboardLoadingStat
e
.
NotStarted
,
initPhase
:
DashboardInitPhas
e
.
NotStarted
,
is
Loading
Slow
:
false
,
is
Init
Slow
:
false
,
model
:
null
,
model
:
null
,
permissions
:
[],
permissions
:
[],
};
};
...
@@ -19,27 +28,60 @@ export const dashboardReducer = reducerFactory(initialState)
...
@@ -19,27 +28,60 @@ export const dashboardReducer = reducerFactory(initialState)
}),
}),
})
})
.
addMapper
({
.
addMapper
({
filter
:
setDashboardLoadingState
,
filter
:
dashboardInitFetching
,
mapper
:
(
state
,
action
)
=>
({
mapper
:
state
=>
({
...
state
,
initPhase
:
DashboardInitPhase
.
Fetching
,
}),
})
.
addMapper
({
filter
:
dashboardInitServices
,
mapper
:
state
=>
({
...
state
,
initPhase
:
DashboardInitPhase
.
Services
,
}),
})
.
addMapper
({
filter
:
dashboardInitSlow
,
mapper
:
state
=>
({
...
state
,
...
state
,
loadingState
:
action
.
payload
isInitSlow
:
true
,
}),
}),
})
})
.
addMapper
({
.
addMapper
({
filter
:
setDashboardModel
,
filter
:
dashboardInitFailed
,
mapper
:
(
state
,
action
)
=>
({
mapper
:
(
state
,
action
)
=>
({
...
state
,
...
state
,
model
:
action
.
payload
,
initPhase
:
DashboardInitPhase
.
Failed
,
isLoadingSlow
:
false
,
isInitSlow
:
false
,
initError
:
action
.
payload
,
model
:
new
DashboardModel
({
title
:
'Dashboard init failed'
},
{
canSave
:
false
,
canEdit
:
false
}),
}),
}),
})
})
.
addMapper
({
.
addMapper
({
filter
:
setDashboardLoadingSlow
,
filter
:
dashboardInitCompleted
,
mapper
:
(
state
,
action
)
=>
({
mapper
:
(
state
,
action
)
=>
({
...
state
,
...
state
,
isLoadingSlow
:
true
,
initPhase
:
DashboardInitPhase
.
Completed
,
model
:
action
.
payload
,
isInitSlow
:
false
,
}),
}),
})
})
.
addMapper
({
filter
:
cleanUpDashboard
,
mapper
:
(
state
,
action
)
=>
{
// tear down current dashboard
state
.
model
.
destroy
();
return
{
...
state
,
initPhase
:
DashboardInitPhase
.
NotStarted
,
model
:
null
,
isInitSlow
:
false
,
initError
:
null
,
};
},
})
.
create
();
.
create
();
export
default
{
export
default
{
...
...
public/app/types/dashboard.ts
View file @
dd0afd0a
...
@@ -2,6 +2,7 @@ import { DashboardAcl } from './acl';
...
@@ -2,6 +2,7 @@ import { DashboardAcl } from './acl';
export
interface
MutableDashboard
{
export
interface
MutableDashboard
{
meta
:
DashboardMeta
;
meta
:
DashboardMeta
;
destroy
:
()
=>
void
;
}
}
export
interface
DashboardDTO
{
export
interface
DashboardDTO
{
...
@@ -44,12 +45,17 @@ export enum DashboardRouteInfo {
...
@@ -44,12 +45,17 @@ export enum DashboardRouteInfo {
Scripted
=
'scripted-dashboard'
,
Scripted
=
'scripted-dashboard'
,
}
}
export
enum
Dashboard
LoadingStat
e
{
export
enum
Dashboard
InitPhas
e
{
NotStarted
=
'Not started'
,
NotStarted
=
'Not started'
,
Fetching
=
'Fetching'
,
Fetching
=
'Fetching'
,
Initializing
=
'Initializing'
,
Services
=
'Services'
,
Error
=
'Error'
,
Failed
=
'Failed'
,
Done
=
'Done'
,
Completed
=
'Completed'
,
}
export
interface
DashboardInitError
{
message
:
string
;
error
:
any
;
}
}
export
const
KIOSK_MODE_TV
=
'tv'
;
export
const
KIOSK_MODE_TV
=
'tv'
;
...
@@ -57,7 +63,8 @@ export type KioskUrlValue = 'tv' | '1' | true;
...
@@ -57,7 +63,8 @@ export type KioskUrlValue = 'tv' | '1' | true;
export
interface
DashboardState
{
export
interface
DashboardState
{
model
:
MutableDashboard
|
null
;
model
:
MutableDashboard
|
null
;
loadingState
:
DashboardLoadingState
;
initPhase
:
DashboardInitPhase
;
isLoadingSlow
:
boolean
;
isInitSlow
:
boolean
;
initError
?:
DashboardInitError
;
permissions
:
DashboardAcl
[]
|
null
;
permissions
:
DashboardAcl
[]
|
null
;
}
}
public/sass/pages/_dashboard.scss
View file @
dd0afd0a
...
@@ -282,6 +282,11 @@ div.flot-text {
...
@@ -282,6 +282,11 @@ div.flot-text {
display
:
flex
;
display
:
flex
;
align-items
:
center
;
align-items
:
center
;
justify-content
:
center
;
justify-content
:
center
;
.alert
{
max-width
:
600px
;
min-width
:
600px
;
}
}
}
.dashboard-loading__text
{
.dashboard-loading__text
{
...
...
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