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
83d933d0
Unverified
Commit
83d933d0
authored
May 25, 2020
by
Hugo Häggmark
Committed by
GitHub
May 25, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
DashboardLinks: variables are resolved and limits to 100 (#25076)
parent
6a7cbd96
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
190 additions
and
58 deletions
+190
-58
public/app/features/dashboard/components/SubMenu/DashboardLinksDashboard.test.tsx
+114
-0
public/app/features/dashboard/components/SubMenu/DashboardLinksDashboard.tsx
+72
-54
public/app/features/dashboard/state/DashboardModel.ts
+4
-4
No files found.
public/app/features/dashboard/components/SubMenu/DashboardLinksDashboard.test.tsx
0 → 100644
View file @
83d933d0
import
{
DashboardLink
}
from
'../../state/DashboardModel'
;
import
{
DashboardSearchHit
,
DashboardSearchItemType
}
from
'../../../search/types'
;
import
{
resolveLinks
,
searchForTags
}
from
'./DashboardLinksDashboard'
;
import
{
describe
,
expect
}
from
'../../../../../test/lib/common'
;
describe
(
'searchForTags'
,
()
=>
{
const
setupTestContext
=
()
=>
{
const
tags
=
[
'A'
,
'B'
];
const
link
:
DashboardLink
=
{
targetBlank
:
false
,
asDropdown
:
false
,
icon
:
'some icon'
,
tags
,
title
:
'some title'
,
tooltip
:
'some tooltip'
,
type
:
'dashboards'
,
url
:
undefined
,
};
const
backendSrv
:
any
=
{
search
:
jest
.
fn
(
args
=>
[]),
};
return
{
link
,
backendSrv
};
};
describe
(
'when called'
,
()
=>
{
it
(
'then tags from link should be used in search and limit should be 100'
,
async
()
=>
{
const
{
link
,
backendSrv
}
=
setupTestContext
();
const
results
=
await
searchForTags
(
link
,
{
getBackendSrv
:
()
=>
backendSrv
});
expect
(
results
.
length
).
toEqual
(
0
);
expect
(
backendSrv
.
search
).
toHaveBeenCalledWith
({
tag
:
[
'A'
,
'B'
],
limit
:
100
});
expect
(
backendSrv
.
search
).
toHaveBeenCalledTimes
(
1
);
});
});
});
describe
(
'resolveLinks'
,
()
=>
{
const
setupTestContext
=
(
dashboardId
:
number
,
searchHitId
:
number
)
=>
{
const
link
:
DashboardLink
=
{
targetBlank
:
false
,
asDropdown
:
false
,
icon
:
'some icon'
,
tags
:
[],
title
:
'some title'
,
tooltip
:
'some tooltip'
,
type
:
'dashboards'
,
url
:
undefined
,
};
const
searchHits
:
DashboardSearchHit
[]
=
[
{
id
:
searchHitId
,
title
:
'DashLinks'
,
url
:
'/d/6ieouugGk/DashLinks'
,
isStarred
:
false
,
items
:
[],
tags
:
[],
uri
:
'db/DashLinks'
,
type
:
DashboardSearchItemType
.
DashDB
,
},
];
const
linkSrv
:
any
=
{
getLinkUrl
:
jest
.
fn
(
args
=>
args
.
url
),
};
const
sanitize
=
jest
.
fn
(
args
=>
args
);
const
sanitizeUrl
=
jest
.
fn
(
args
=>
args
);
return
{
dashboardId
,
link
,
searchHits
,
linkSrv
,
sanitize
,
sanitizeUrl
};
};
describe
(
'when called'
,
()
=>
{
it
(
'should filter out the calling dashboardId'
,
()
=>
{
const
{
dashboardId
,
link
,
searchHits
,
linkSrv
,
sanitize
,
sanitizeUrl
}
=
setupTestContext
(
1
,
1
);
const
results
=
resolveLinks
(
dashboardId
,
link
,
searchHits
,
{
getLinkSrv
:
()
=>
linkSrv
,
sanitize
,
sanitizeUrl
});
expect
(
results
.
length
).
toEqual
(
0
);
expect
(
linkSrv
.
getLinkUrl
).
toHaveBeenCalledTimes
(
0
);
expect
(
sanitize
).
toHaveBeenCalledTimes
(
0
);
expect
(
sanitizeUrl
).
toHaveBeenCalledTimes
(
0
);
});
it
(
'should resolve link url'
,
()
=>
{
const
{
dashboardId
,
link
,
searchHits
,
linkSrv
,
sanitize
,
sanitizeUrl
}
=
setupTestContext
(
1
,
2
);
const
results
=
resolveLinks
(
dashboardId
,
link
,
searchHits
,
{
getLinkSrv
:
()
=>
linkSrv
,
sanitize
,
sanitizeUrl
});
expect
(
results
.
length
).
toEqual
(
1
);
expect
(
linkSrv
.
getLinkUrl
).
toHaveBeenCalledTimes
(
1
);
expect
(
linkSrv
.
getLinkUrl
).
toHaveBeenCalledWith
({
...
link
,
url
:
searchHits
[
0
].
url
});
});
it
(
'should sanitize title'
,
()
=>
{
const
{
dashboardId
,
link
,
searchHits
,
linkSrv
,
sanitize
,
sanitizeUrl
}
=
setupTestContext
(
1
,
2
);
const
results
=
resolveLinks
(
dashboardId
,
link
,
searchHits
,
{
getLinkSrv
:
()
=>
linkSrv
,
sanitize
,
sanitizeUrl
});
expect
(
results
.
length
).
toEqual
(
1
);
expect
(
sanitize
).
toHaveBeenCalledTimes
(
1
);
expect
(
sanitize
).
toHaveBeenCalledWith
(
searchHits
[
0
].
title
);
});
it
(
'should sanitize url'
,
()
=>
{
const
{
dashboardId
,
link
,
searchHits
,
linkSrv
,
sanitize
,
sanitizeUrl
}
=
setupTestContext
(
1
,
2
);
const
results
=
resolveLinks
(
dashboardId
,
link
,
searchHits
,
{
getLinkSrv
:
()
=>
linkSrv
,
sanitize
,
sanitizeUrl
});
expect
(
results
.
length
).
toEqual
(
1
);
expect
(
sanitizeUrl
).
toHaveBeenCalledTimes
(
1
);
expect
(
sanitizeUrl
).
toHaveBeenCalledWith
(
searchHits
[
0
].
url
);
});
});
});
public/app/features/dashboard/components/SubMenu/DashboardLinksDashboard.tsx
View file @
83d933d0
...
@@ -13,73 +13,53 @@ interface Props {
...
@@ -13,73 +13,53 @@ interface Props {
}
}
interface
State
{
interface
State
{
searchHits
:
DashboardSearchHit
[];
resolvedLinks
:
ResolvedLinkDTO
[];
}
}
export
class
DashboardLinksDashboard
extends
PureComponent
<
Props
,
State
>
{
export
class
DashboardLinksDashboard
extends
PureComponent
<
Props
,
State
>
{
state
=
{
searchHits
:
[]
as
DashboardSearchHit
[]
};
state
:
State
=
{
resolvedLinks
:
[]
};
componentDidMount
()
{
if
(
!
this
.
props
.
link
.
asDropdown
)
{
componentDidUpdate
(
prevProps
:
Readonly
<
Props
>
)
{
this
.
onDropDownClick
();
if
(
!
this
.
props
.
link
.
asDropdown
&&
prevProps
.
linkInfo
!==
this
.
props
.
linkInfo
)
{
this
.
onResolveLinks
();
}
}
}
}
on
DropDownClick
=
()
=>
{
on
ResolveLinks
=
async
()
=>
{
const
{
dashboardId
,
link
}
=
this
.
props
;
const
{
dashboardId
,
link
}
=
this
.
props
;
const
limit
=
7
;
const
searchHits
=
await
searchForTags
(
link
);
getBackendSrv
()
const
resolvedLinks
=
resolveLinks
(
dashboardId
,
link
,
searchHits
);
.
search
({
tag
:
link
.
tags
,
limit
})
.
then
((
dashboards
:
DashboardSearchHit
[])
=>
{
this
.
setState
({
resolvedLinks
});
const
processed
=
dashboards
.
filter
(
dash
=>
dash
.
id
!==
dashboardId
)
.
map
(
dash
=>
{
return
{
...
dash
,
url
:
getLinkSrv
().
getLinkUrl
(
dash
),
};
});
this
.
setState
({
searchHits
:
processed
,
});
});
};
};
renderElement
=
(
linkElement
:
JSX
.
Element
)
=>
{
renderElement
=
(
linkElement
:
JSX
.
Element
,
key
:
string
)
=>
{
const
{
link
}
=
this
.
props
;
const
{
link
}
=
this
.
props
;
if
(
link
.
tooltip
)
{
return
(
return
(
<
div
className=
"gf-form"
key=
{
key
}
>
<
div
className=
"gf-form"
>
{
link
.
tooltip
&&
<
Tooltip
content=
{
link
.
tooltip
}
>
{
linkElement
}
</
Tooltip
>
}
<
Tooltip
content=
{
link
.
tooltip
}
>
{
linkElement
}
</
Tooltip
>
;
{
!
link
.
tooltip
&&
<>
{
linkElement
}
</>
}
</
div
>
</
div
>
);
);
}
else
{
return
<
div
className=
"gf-form"
>
{
linkElement
}
</
div
>;
}
};
};
renderList
=
()
=>
{
renderList
=
()
=>
{
const
{
link
}
=
this
.
props
;
const
{
link
}
=
this
.
props
;
const
{
searchHit
s
}
=
this
.
state
;
const
{
resolvedLink
s
}
=
this
.
state
;
return
(
return
(
<>
<>
{
searchHit
s
.
length
>
0
&&
{
resolvedLink
s
.
length
>
0
&&
searchHits
.
map
((
dashboard
:
any
,
index
:
number
)
=>
{
resolvedLinks
.
map
((
resolvedLink
,
index
)
=>
{
const
linkElement
=
(
const
linkElement
=
(
<
a
<
a
className=
"gf-form-label"
href=
{
resolvedLink
.
url
}
target=
{
link
.
targetBlank
?
'_blank'
:
'_self'
}
>
key=
{
`${dashboard.id}-${index}`
}
className=
"gf-form-label"
href=
{
sanitizeUrl
(
dashboard
.
url
)
}
target=
{
link
.
targetBlank
?
'_blank'
:
'_self'
}
>
<
Icon
name=
"apps"
style=
{
{
marginRight
:
'4px'
}
}
/>
<
Icon
name=
"apps"
style=
{
{
marginRight
:
'4px'
}
}
/>
<
span
>
{
sanitize
(
dashboard
.
title
)
}
</
span
>
<
span
>
{
resolvedLink
.
title
}
</
span
>
</
a
>
</
a
>
);
);
return
this
.
renderElement
(
linkElement
);
return
this
.
renderElement
(
linkElement
,
`dashlinks-list-item-${resolvedLink.id}-${index}`
);
})
}
})
}
</>
</>
);
);
...
@@ -87,13 +67,13 @@ export class DashboardLinksDashboard extends PureComponent<Props, State> {
...
@@ -87,13 +67,13 @@ export class DashboardLinksDashboard extends PureComponent<Props, State> {
renderDropdown
=
()
=>
{
renderDropdown
=
()
=>
{
const
{
link
,
linkInfo
}
=
this
.
props
;
const
{
link
,
linkInfo
}
=
this
.
props
;
const
{
searchHit
s
}
=
this
.
state
;
const
{
resolvedLink
s
}
=
this
.
state
;
const
linkElement
=
(
const
linkElement
=
(
<>
<>
<
a
<
a
className=
"gf-form-label pointer"
className=
"gf-form-label pointer"
onClick=
{
this
.
on
DropDownClick
}
onClick=
{
this
.
on
ResolveLinks
}
data
-
placement=
"bottom"
data
-
placement=
"bottom"
data
-
toggle=
"dropdown"
data
-
toggle=
"dropdown"
>
>
...
@@ -101,12 +81,12 @@ export class DashboardLinksDashboard extends PureComponent<Props, State> {
...
@@ -101,12 +81,12 @@ export class DashboardLinksDashboard extends PureComponent<Props, State> {
<
span
>
{
linkInfo
.
title
}
</
span
>
<
span
>
{
linkInfo
.
title
}
</
span
>
</
a
>
</
a
>
<
ul
className=
"dropdown-menu pull-right"
role=
"menu"
>
<
ul
className=
"dropdown-menu pull-right"
role=
"menu"
>
{
searchHit
s
.
length
>
0
&&
{
resolvedLink
s
.
length
>
0
&&
searchHits
.
map
((
dashboard
:
any
,
index
:
number
)
=>
{
resolvedLinks
.
map
((
resolvedLink
,
index
)
=>
{
return
(
return
(
<
li
key=
{
`
${dashboard
.id}-${index}`
}
>
<
li
key=
{
`
dashlinks-dropdown-item-${resolvedLink
.id}-${index}`
}
>
<
a
href=
{
sanitizeUrl
(
dashboard
.
url
)
}
target=
{
link
.
targetBlank
?
'_blank'
:
'_self'
}
>
<
a
href=
{
resolvedLink
.
url
}
target=
{
link
.
targetBlank
?
'_blank'
:
'_self'
}
>
{
sanitize
(
dashboard
.
title
)
}
{
resolvedLink
.
title
}
</
a
>
</
a
>
</
li
>
</
li
>
);
);
...
@@ -115,14 +95,52 @@ export class DashboardLinksDashboard extends PureComponent<Props, State> {
...
@@ -115,14 +95,52 @@ export class DashboardLinksDashboard extends PureComponent<Props, State> {
</>
</>
);
);
return
this
.
renderElement
(
linkElement
);
return
this
.
renderElement
(
linkElement
,
'dashlinks-dropdown'
);
};
};
render
()
{
render
()
{
if
(
this
.
props
.
link
.
asDropdown
)
{
if
(
this
.
props
.
link
.
asDropdown
)
{
return
this
.
renderDropdown
();
return
this
.
renderDropdown
();
}
else
{
return
this
.
renderList
();
}
}
return
this
.
renderList
();
}
}
}
}
interface
ResolvedLinkDTO
{
id
:
any
;
url
:
string
;
title
:
string
;
}
export
async
function
searchForTags
(
link
:
DashboardLink
,
dependencies
:
{
getBackendSrv
:
typeof
getBackendSrv
}
=
{
getBackendSrv
}
):
Promise
<
DashboardSearchHit
[]
>
{
const
limit
=
100
;
const
searchHits
:
DashboardSearchHit
[]
=
await
dependencies
.
getBackendSrv
().
search
({
tag
:
link
.
tags
,
limit
});
return
searchHits
;
}
export
function
resolveLinks
(
dashboardId
:
any
,
link
:
DashboardLink
,
searchHits
:
DashboardSearchHit
[],
dependencies
:
{
getLinkSrv
:
typeof
getLinkSrv
;
sanitize
:
typeof
sanitize
;
sanitizeUrl
:
typeof
sanitizeUrl
}
=
{
getLinkSrv
,
sanitize
,
sanitizeUrl
,
}
):
ResolvedLinkDTO
[]
{
return
searchHits
.
filter
(
searchHit
=>
searchHit
.
id
!==
dashboardId
)
.
map
(
searchHit
=>
{
const
id
=
searchHit
.
id
;
const
title
=
dependencies
.
sanitize
(
searchHit
.
title
);
const
resolvedLink
=
dependencies
.
getLinkSrv
().
getLinkUrl
({
...
link
,
url
:
searchHit
.
url
});
const
url
=
dependencies
.
sanitizeUrl
(
resolvedLink
);
return
{
id
,
title
,
url
};
});
}
public/app/features/dashboard/state/DashboardModel.ts
View file @
83d933d0
...
@@ -12,13 +12,13 @@ import { GridPos, panelAdded, PanelModel, panelRemoved } from './PanelModel';
...
@@ -12,13 +12,13 @@ import { GridPos, panelAdded, PanelModel, panelRemoved } from './PanelModel';
import
{
DashboardMigrator
}
from
'./DashboardMigrator'
;
import
{
DashboardMigrator
}
from
'./DashboardMigrator'
;
import
{
import
{
AppEvent
,
AppEvent
,
dateTimeFormat
,
dateTimeFormatTimeAgo
,
DateTimeInput
,
DateTimeInput
,
PanelEvents
,
PanelEvents
,
TimeRange
,
TimeRange
,
TimeZone
,
TimeZone
,
UrlQueryValue
,
UrlQueryValue
,
dateTimeFormat
,
dateTimeFormatTimeAgo
,
}
from
'@grafana/data'
;
}
from
'@grafana/data'
;
import
{
CoreEvents
,
DashboardMeta
,
KIOSK_MODE_TV
}
from
'app/types'
;
import
{
CoreEvents
,
DashboardMeta
,
KIOSK_MODE_TV
}
from
'app/types'
;
import
{
getConfig
}
from
'../../../core/config'
;
import
{
getConfig
}
from
'../../../core/config'
;
...
@@ -42,8 +42,8 @@ export interface DashboardLink {
...
@@ -42,8 +42,8 @@ export interface DashboardLink {
type
:
DashboardLinkType
;
type
:
DashboardLinkType
;
url
:
string
;
url
:
string
;
asDropdown
:
boolean
;
asDropdown
:
boolean
;
tags
:
[];
tags
:
any
[];
searchHits
?:
[];
searchHits
?:
any
[];
targetBlank
:
boolean
;
targetBlank
:
boolean
;
}
}
...
...
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