From 6ffadf40125e583b5e8e1af496a07e3a2f3b4877 Mon Sep 17 00:00:00 2001 From: bryanl Date: Sun, 29 Mar 2020 13:19:05 -0400 Subject: [PATCH] Create grid action Create grid action that allows users of the table component add grid actions. It allows Octant to take advantage of https://clarity.design/documentation/datagrid/single-action Signed-off-by: bryanl --- changelogs/unreleased/801-bryanl | 1 + pkg/view/component/base.go | 1 + pkg/view/component/grid_actions.go | 71 +++++++++++++++++++ pkg/view/component/grid_actions_test.go | 31 ++++++++ .../testdata/config_grid_actions.json | 11 +++ pkg/view/component/unmarshal.go | 5 ++ pkg/view/component/unmarshal_test.go | 18 +++++ .../datagrid/datagrid.component.html | 11 ++- .../datagrid/datagrid.component.ts | 39 ++++++++-- web/src/app/modules/shared/models/content.ts | 12 ++++ 10 files changed, 193 insertions(+), 7 deletions(-) create mode 100644 changelogs/unreleased/801-bryanl create mode 100644 pkg/view/component/grid_actions.go create mode 100644 pkg/view/component/grid_actions_test.go create mode 100644 pkg/view/component/testdata/config_grid_actions.json diff --git a/changelogs/unreleased/801-bryanl b/changelogs/unreleased/801-bryanl new file mode 100644 index 0000000000..1fc2acdaac --- /dev/null +++ b/changelogs/unreleased/801-bryanl @@ -0,0 +1 @@ +Add support for Clarity's single action for data grid rows diff --git a/pkg/view/component/base.go b/pkg/view/component/base.go index ef948f874c..4c59abddda 100644 --- a/pkg/view/component/base.go +++ b/pkg/view/component/base.go @@ -19,6 +19,7 @@ const ( typeExpressionSelector = "expressionSelector" typeFlexLayout = "flexlayout" typeGraphviz = "graphviz" + typeGridActions = "gridActions" typeIFrame = "iframe" typeLabels = "labels" typeLabelSelector = "labelSelector" diff --git a/pkg/view/component/grid_actions.go b/pkg/view/component/grid_actions.go new file mode 100644 index 0000000000..bdbece5c1b --- /dev/null +++ b/pkg/view/component/grid_actions.go @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2020 the Octant contributors. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + * + */ + +package component + +import ( + "encoding/json" + + "github.com/vmware-tanzu/octant/pkg/action" +) + +type GridAction struct { + // Name is the name of action. It will be shown to the user. + Name string `json:"name"` + // ActionPath is the path of the action. + ActionPath string `json:"actionPath"` + // Payload is the payload that will be submitted with the action is invoked. + Payload action.Payload `json:"payload"` +} + +// GridActions add the ability to have specific actions for rows. This will allow for dynamic injection of actions +// that could be dependent on the content of a grid row. +type GridActions struct { + base + + Config GridActionsConfig `json:"config"` +} + +var _ Component = &GridActions{} + +// NewGridActions creates an instance of GridActions. +func NewGridActions() *GridActions { + a := GridActions{ + base: newBase(typeGridActions, nil), + } + + return &a +} + +// AddAction adds an action to GridAction. +func (a *GridActions) AddAction(name, actionPath string, payload action.Payload) { + ga := GridAction{ + Name: name, + ActionPath: actionPath, + Payload: payload, + } + + a.Config.Actions = append(a.Config.Actions, ga) +} + +type gridActionsMarshal GridActions + +// MarshalJSON converts the GridActions to a JSON. +func (a GridActions) MarshalJSON() ([]byte, error) { + m := gridActionsMarshal{ + base: a.base, + Config: a.Config, + } + + m.Metadata.Type = typeGridActions + return json.Marshal(&m) +} + +// GridActionsConfig is configuration items for GridActions. +type GridActionsConfig struct { + // Actions is a slice that contains actions that can be performed by the user. + Actions []GridAction `json:"actions"` +} diff --git a/pkg/view/component/grid_actions_test.go b/pkg/view/component/grid_actions_test.go new file mode 100644 index 0000000000..4f83f5a930 --- /dev/null +++ b/pkg/view/component/grid_actions_test.go @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2020 the Octant contributors. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + * + */ + +package component + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/vmware-tanzu/octant/pkg/action" +) + +func TestGridActions_AddAction(t *testing.T) { + ga := NewGridActions() + + payload := action.Payload{"foo": "bar"} + ga.AddAction("name", "/path", payload) + + expected := []GridAction{ + { + Name: "name", + ActionPath: "/path", + Payload: payload, + }, + } + require.Equal(t, expected, ga.Config.Actions) +} diff --git a/pkg/view/component/testdata/config_grid_actions.json b/pkg/view/component/testdata/config_grid_actions.json new file mode 100644 index 0000000000..15cdebe6c8 --- /dev/null +++ b/pkg/view/component/testdata/config_grid_actions.json @@ -0,0 +1,11 @@ +{ + "actions": [ + { + "name": "name", + "actionPath": "/path", + "payload": { + "foo": "bar" + } + } + ] +} diff --git a/pkg/view/component/unmarshal.go b/pkg/view/component/unmarshal.go index 1aea6b78a4..8b09dbf057 100644 --- a/pkg/view/component/unmarshal.go +++ b/pkg/view/component/unmarshal.go @@ -76,6 +76,11 @@ func unmarshal(to TypedObject) (Component, error) { err = errors.Wrapf(json.Unmarshal(to.Config, &t.Config), "unmarshal graphviz config") o = t + case typeGridActions: + t := &GridActions{base: base{Metadata: to.Metadata}} + err = errors.Wrapf(json.Unmarshal(to.Config, &t.Config), + "unmarshal gridActions config") + o = t case typeIFrame: t := &IFrame{base: base{Metadata: to.Metadata}} err = errors.Wrapf(json.Unmarshal(to.Config, &t.Config), diff --git a/pkg/view/component/unmarshal_test.go b/pkg/view/component/unmarshal_test.go index 07ffab1120..189fb32ae9 100644 --- a/pkg/view/component/unmarshal_test.go +++ b/pkg/view/component/unmarshal_test.go @@ -12,6 +12,8 @@ import ( "testing" "github.com/stretchr/testify/require" + + "github.com/vmware-tanzu/octant/pkg/action" ) func Test_unmarshal(t *testing.T) { @@ -161,6 +163,22 @@ func Test_unmarshal(t *testing.T) { base: newBase(typeFlexLayout, nil), }, }, + { + name: "grid actions", + configFile: "config_grid_actions.json", + objectType: typeGridActions, + expected: &GridActions{ + Config: GridActionsConfig{ + Actions: []GridAction{ + { + Name: "name", + ActionPath: "/path", + Payload: action.Payload{"foo": "bar"}, + }, + }, + }, + }, + }, { name: "labels", configFile: "config_labels.json", diff --git a/web/src/app/modules/shared/components/presentation/datagrid/datagrid.component.html b/web/src/app/modules/shared/components/presentation/datagrid/datagrid.component.html index f01651c68f..91fe1ff860 100644 --- a/web/src/app/modules/shared/components/presentation/datagrid/datagrid.component.html +++ b/web/src/app/modules/shared/components/presentation/datagrid/datagrid.component.html @@ -17,16 +17,21 @@

{{ title }}

> - + + + + - + Items per page - + {{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} of {{pagination.totalItems}} items diff --git a/web/src/app/modules/shared/components/presentation/datagrid/datagrid.component.ts b/web/src/app/modules/shared/components/presentation/datagrid/datagrid.component.ts index 8f57eac11c..7a40b35fb5 100644 --- a/web/src/app/modules/shared/components/presentation/datagrid/datagrid.component.ts +++ b/web/src/app/modules/shared/components/presentation/datagrid/datagrid.component.ts @@ -4,6 +4,8 @@ import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; import { + GridAction, + GridActionsView, TableFilters, TableRow, TableView, @@ -12,6 +14,7 @@ import { import trackByIndex from 'src/app/util/trackBy/trackByIndex'; import trackByIdentity from 'src/app/util/trackBy/trackByIdentity'; import { ViewService } from '../../../services/view/view.service'; +import { ActionService } from '../../../services/action/action.service'; @Component({ selector: 'app-view-datagrid', @@ -29,7 +32,7 @@ export class DatagridComponent implements OnChanges { } columns: string[]; - rows: TableRow[]; + rowsWithMetadata: TableRowWithMetadata[]; title: string; placeholder: string; lastUpdated: Date; @@ -41,7 +44,10 @@ export class DatagridComponent implements OnChanges { identifyColumn = trackByIdentity; loading: boolean; - constructor(private viewService: ViewService) {} + constructor( + private viewService: ViewService, + private actionService: ActionService + ) {} ngOnChanges(changes: SimpleChanges): void { if (changes.view) { @@ -51,9 +57,10 @@ export class DatagridComponent implements OnChanges { ) { this.title = this.viewService.viewTitleAsText(this.view); - const current = changes.view.currentValue; + const current = changes.view.currentValue as TableView; this.columns = current.config.columns.map(column => column.name); - this.rows = current.config.rows; + this.rowsWithMetadata = this.getRowsWithMetadata(current.config.rows); + this.placeholder = current.config.emptyContent; this.lastUpdated = new Date(); this.loading = current.config.loading; @@ -63,4 +70,28 @@ export class DatagridComponent implements OnChanges { } } } + + private getRowsWithMetadata(rows: TableRow[]) { + return rows.map(row => { + let actions: GridAction[] = []; + + if (row.hasOwnProperty('_action')) { + actions = (row._action as GridActionsView).config.actions; + } + return { + data: row, + actions, + }; + }); + } + + runAction(actionPath: string, payload: {}) { + const update = { ...payload, action: actionPath }; + this.actionService.perform(update); + } +} + +interface TableRowWithMetadata { + data: TableRow; + actions?: GridAction[]; } diff --git a/web/src/app/modules/shared/models/content.ts b/web/src/app/modules/shared/models/content.ts index 01fe78fe03..88220f9d45 100644 --- a/web/src/app/modules/shared/models/content.ts +++ b/web/src/app/modules/shared/models/content.ts @@ -120,6 +120,18 @@ export interface FlexLayoutView extends View { }; } +export interface GridAction { + name: string; + actionPath: string; + payload: {}; +} + +export interface GridActionsView extends View { + config: { + actions: GridAction[]; + }; +} + export interface LabelsView extends View { config: { labels: { [key: string]: string };