feat: inline actions on dashboards
This commit is contained in:
parent
2e82a3d1ed
commit
e58677e12c
|
|
@ -0,0 +1,21 @@
|
|||
#
|
||||
# Integration Test Config: inline dashboard actions
|
||||
#
|
||||
|
||||
listenAddressSingleHTTPFrontend: 0.0.0.0:1337
|
||||
|
||||
logLevel: "DEBUG"
|
||||
checkForUpdates: false
|
||||
|
||||
# No top-level actions – actions will be defined inline on dashboard components.
|
||||
actions: []
|
||||
|
||||
dashboards:
|
||||
- title: Inline Dashboard
|
||||
contents:
|
||||
- title: Inline Dashboard Action
|
||||
inlineAction:
|
||||
shell: date
|
||||
icon: clock
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
import { describe, it, before, after } from 'mocha'
|
||||
import { assert } from 'chai'
|
||||
import {
|
||||
getRootAndWait,
|
||||
getActionButtons,
|
||||
takeScreenshotOnFailure,
|
||||
} from '../../lib/elements.js'
|
||||
|
||||
describe('config: inlineActions', function () {
|
||||
before(async function () {
|
||||
await runner.start('inlineActions')
|
||||
})
|
||||
|
||||
after(async () => {
|
||||
await runner.stop()
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
takeScreenshotOnFailure(this.currentTest, webdriver);
|
||||
});
|
||||
|
||||
it('Inline dashboard actions are rendered as clickable buttons', async function () {
|
||||
await getRootAndWait()
|
||||
|
||||
const buttons = await getActionButtons()
|
||||
assert.isArray(buttons, 'Action buttons should be an array')
|
||||
assert.isAtLeast(buttons.length, 1, 'There should be at least one action button')
|
||||
|
||||
const texts = await Promise.all(buttons.map(b => b.getText()))
|
||||
const combinedText = texts.join(' ')
|
||||
|
||||
assert.include(
|
||||
combinedText,
|
||||
'Inline Dashboard Action',
|
||||
'Inline dashboard action should be rendered as a button'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
|
@ -207,12 +207,13 @@ type LogDebugOptions struct {
|
|||
}
|
||||
|
||||
type DashboardComponent struct {
|
||||
Title string `koanf:"title"`
|
||||
Type string `koanf:"type"`
|
||||
Entity string `koanf:"entity"`
|
||||
Icon string `koanf:"icon"`
|
||||
CssClass string `koanf:"cssClass"`
|
||||
Contents []*DashboardComponent `koanf:"contents"`
|
||||
Title string `koanf:"title"`
|
||||
Type string `koanf:"type"`
|
||||
Entity string `koanf:"entity"`
|
||||
Icon string `koanf:"icon"`
|
||||
CssClass string `koanf:"cssClass"`
|
||||
InlineAction *Action `koanf:"inlineAction"`
|
||||
Contents []*DashboardComponent `koanf:"contents"`
|
||||
}
|
||||
|
||||
func DefaultConfig() *Config {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,82 @@ func (cfg *Config) Sanitize() {
|
|||
for idx := range cfg.Actions {
|
||||
cfg.Actions[idx].sanitize(cfg)
|
||||
}
|
||||
|
||||
cfg.sanitizeDashboardsForInlineActions()
|
||||
}
|
||||
|
||||
func (cfg *Config) sanitizeDashboardsForInlineActions() {
|
||||
for _, dashboard := range cfg.Dashboards {
|
||||
cfg.sanitizeDashboardComponentForInlineActions(dashboard)
|
||||
}
|
||||
}
|
||||
func (cfg *Config) sanitizeDashboardComponentForInlineActions(component *DashboardComponent) {
|
||||
if component == nil {
|
||||
return
|
||||
}
|
||||
|
||||
cfg.sanitizeInlineAction(component)
|
||||
cfg.sanitizeChildDashboardComponents(component)
|
||||
}
|
||||
|
||||
func (cfg *Config) sanitizeInlineAction(component *DashboardComponent) {
|
||||
if component.InlineAction == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if component.InlineAction.Title == "" {
|
||||
component.InlineAction.Title = component.Title
|
||||
}
|
||||
|
||||
component.InlineAction.sanitize(cfg)
|
||||
|
||||
if cfg.inlineActionExists(component.InlineAction) {
|
||||
return
|
||||
}
|
||||
|
||||
cfg.Actions = append(cfg.Actions, component.InlineAction)
|
||||
}
|
||||
|
||||
func (cfg *Config) inlineActionExists(action *Action) bool {
|
||||
if cfg.inlineActionPointerExists(action) {
|
||||
return true
|
||||
}
|
||||
|
||||
if cfg.inlineActionIDExists(action) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (cfg *Config) inlineActionPointerExists(action *Action) bool {
|
||||
for _, existingAction := range cfg.Actions {
|
||||
if existingAction == action {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (cfg *Config) inlineActionIDExists(action *Action) bool {
|
||||
if action.ID == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, existingAction := range cfg.Actions {
|
||||
if existingAction.ID == action.ID {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (cfg *Config) sanitizeChildDashboardComponents(component *DashboardComponent) {
|
||||
for _, child := range component.Contents {
|
||||
cfg.sanitizeDashboardComponentForInlineActions(child)
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg *Config) sanitizeLogLevel() {
|
||||
|
|
|
|||
|
|
@ -36,3 +36,39 @@ func TestSanitizeConfig(t *testing.T) {
|
|||
assert.Equal(t, "Carrots", a2.Arguments[0].Title, "Arg title is set to name")
|
||||
assert.Equal(t, "Waffle", a2.Arguments[0].Choices[0].Title, "Choice title is set to name")
|
||||
}
|
||||
|
||||
func TestSanitizeConfigInlineDashboardActions(t *testing.T) {
|
||||
c := DefaultConfig()
|
||||
|
||||
inline := &Action{
|
||||
Shell: "date",
|
||||
}
|
||||
|
||||
dashboardActionTitle := "Inline Dashboard Action"
|
||||
|
||||
c.Dashboards = []*DashboardComponent{
|
||||
{
|
||||
Title: "My Dashboard",
|
||||
Contents: []*DashboardComponent{
|
||||
{
|
||||
Title: dashboardActionTitle,
|
||||
InlineAction: inline,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
c.Sanitize()
|
||||
|
||||
// Inline action should have been appended to the global Actions slice.
|
||||
assert.GreaterOrEqual(t, len(c.Actions), 1, "At least one action should exist after sanitization")
|
||||
|
||||
// It should be discoverable by the dashboard component title when no explicit title was set.
|
||||
found := c.findAction(dashboardActionTitle)
|
||||
if assert.NotNil(t, found, "Inline dashboard action should be discoverable by title") {
|
||||
assert.Equal(t, dashboardActionTitle, found.Title, "Inline action title should default from dashboard component title")
|
||||
assert.Equal(t, 3, found.Timeout, "Inline action should have default timeout applied")
|
||||
assert.NotEmpty(t, found.Icon, "Inline action should have default icon applied")
|
||||
assert.NotEmpty(t, found.ID, "Inline action should have a generated ID")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue