Skip to content
This repository has been archived by the owner on Jan 19, 2023. It is now read-only.

Commit

Permalink
Add Tabs Component
Browse files Browse the repository at this point in the history
Signed-off-by: ftovaro <[email protected]>
  • Loading branch information
ftovaro committed Jun 3, 2021
1 parent 75bb9e6 commit 44de7c1
Show file tree
Hide file tree
Showing 18 changed files with 695 additions and 2 deletions.
1 change: 1 addition & 0 deletions changelogs/unreleased/2482-ftovaro-lenriquez
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added tabs as components
4 changes: 3 additions & 1 deletion pkg/view/component/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const (
TypeAccordion = "accordion"
// TypeAnnotations is an annotations component.
TypeAnnotations = "annotations"
// ButtonGroup is a button group component.
// TypeButtonGroup is a button group component.
TypeButtonGroup = "buttonGroup"
// TypeCard is a card component.
TypeCard = "card"
Expand Down Expand Up @@ -96,6 +96,8 @@ const (
TypeSignpost = "signpost"
// TypeButton is a Button component.
TypeButton = "button"
// TypeTabsView is a Tab component.
TypeTabsView = "tabsView"
)

// Base is an abstract base for components..
Expand Down
2 changes: 1 addition & 1 deletion pkg/view/component/button_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func (bg *ButtonGroupConfig) UnmarshalJSON(data []byte) error {

button, ok := component.(*Button)
if !ok {
return fmt.Errorf("item was not a card")
return fmt.Errorf("item was not a button group")
}

bg.Buttons = append(bg.Buttons, *button)
Expand Down
94 changes: 94 additions & 0 deletions pkg/view/component/tabs_view.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
Copyright (c) 2021 the Octant contributors. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package component

import (
"fmt"

"github.com/vmware-tanzu/octant/internal/util/json"
)

type TabsView struct {
Base
Config TabsViewConfig `json:"config"`
}

// TabsOrientation is the direction of the Tabs
type TabsOrientation string

const (
// VerticalTabs are tabs organized vertically
VerticalTabs TabsOrientation = "vertical"
// HorizontalTabs are tabs organized horizontally
HorizontalTabs TabsOrientation = "horizontal"
)

type TabsViewConfig struct {
// Tabs are an array of Tab structs
Tabs []SingleTab `json:"tabs"`
// Orientation is the direction of the tabs
Orientation TabsOrientation `json:"orientation,omitempty"`
}

func NewTabs(orientation TabsOrientation, tabs []SingleTab) *TabsView {
return &TabsView{
Base: newBase(TypeTabsView, nil),
Config: TabsViewConfig{
Tabs: tabs,
Orientation: orientation,
},
}
}

type tabsMarshal TabsView

// MarshalJSON marshals a button group.
func (t *TabsView) MarshalJSON() ([]byte, error) {
m := tabsMarshal(*t)
m.Metadata.Type = TypeTabsView
return json.Marshal(&m)
}

var _ Component = (*TabsView)(nil)

type SingleTab struct {
Name string `json:"name"`
Contents FlexLayout `json:"contents"`
}

func (t *TabsViewConfig) UnmarshalJSON(data []byte) error {
x := struct {
Orientation TabsOrientation `json:"orientation,omitempty"`
Tabs []struct {
Name string `json:"name"`
Contents TypedObject `json:"contents"`
} `json:"tabs"`
}{}

if err := json.Unmarshal(data, &x); err != nil {
return err
}

for _, tab := range x.Tabs {
c, err := tab.Contents.ToComponent()
if err != nil {
return err
}
fl, ok := c.(*FlexLayout)
if !ok {
return fmt.Errorf("item was not a FlexLayout")
}
st := SingleTab{
Name: tab.Name,
Contents: *fl,
}
t.Tabs = append(t.Tabs, st)
}

t.Orientation = x.Orientation

return nil
}
74 changes: 74 additions & 0 deletions pkg/view/component/tabs_view_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
Copyright (c) 2021 the Octant contributors. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package component

import (
"io/ioutil"
"path"
"testing"

"github.com/vmware-tanzu/octant/pkg/action"

"github.com/stretchr/testify/assert"

"github.com/stretchr/testify/require"

"github.com/vmware-tanzu/octant/internal/util/json"
)

func Test_Tabs_Marshal(t *testing.T) {
fl := NewFlexLayout("title")
button := NewButton("test", action.Payload{"foo": "bar"})
section := FlexLayoutSection{
{
Width: WidthFull,
View: button,
},
}
fl.AddSections(section)

test := []struct {
name string
input Component
expectedPath string
isErr bool
}{
{
name: "in general",
input: &TabsView{
Base: newBase(TypeTabsView, nil),
Config: TabsViewConfig{
Tabs: []SingleTab{
{
Name: "title",
Contents: *fl,
},
{
Name: "title 2",
Contents: *fl,
},
},
},
},
expectedPath: "tabs.json",
isErr: false,
},
}

for _, tc := range test {
t.Run(tc.name, func(t *testing.T) {
actual, err := json.Marshal(tc.input)
isErr := err != nil
if isErr != tc.isErr {
t.Fatalf("UnExpected error: %v", err)
}

expected, err := ioutil.ReadFile(path.Join("testdata", tc.expectedPath))
require.NoError(t, err)
assert.JSONEq(t, string(expected), string(actual))
})
}
}
34 changes: 34 additions & 0 deletions pkg/view/component/testdata/config_tabs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"tabs":
[
{
"name":"Tab 1",
"contents":{
"config":{
"sections":
[
[
{
"width": 24,
"view":{
"config":{
"name":"test",
"payload":{
"foo":"bar"
}
},
"metadata":{
"type":"button"
}
}
}
]
]
},
"metadata":{
"type":"flexlayout"
}
}
}
]
}
104 changes: 104 additions & 0 deletions pkg/view/component/testdata/tabs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
{
"metadata":{"type":"tabsView"},
"config":{
"tabs":
[
{
"name":"title",
"contents":{
"config":{
"buttonGroup":{
"config":{
"buttons":null
},
"metadata":{
"type":"buttonGroup"
}
},
"sections":
[
[
{
"width": 24,
"view":{
"config":{
"name":"test",
"payload":{
"foo":"bar"
}
},
"metadata":{
"type":"button"
}
}
}
]
]
},
"metadata":{
"title":
[
{
"config":{
"value":"title"
},
"metadata":{
"type":"text"
}
}
],
"type":"flexlayout"
}
}
},
{
"name":"title 2",
"contents":{
"config":{
"buttonGroup":{
"config":{
"buttons":null
},
"metadata":{
"type":"buttonGroup"
}
},
"sections":
[
[
{
"width": 24,
"view":{
"config":{
"name":"test",
"payload":{
"foo":"bar"
}
},
"metadata":{
"type":"button"
}
}
}
]
]
},
"metadata":{
"title":
[
{
"config":{
"value":"title"
},
"metadata":{
"type":"text"
}
}
],
"type":"flexlayout"
}
}
}
]
}
}
5 changes: 5 additions & 0 deletions pkg/view/component/unmarshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,11 @@ func unmarshal(to TypedObject) (Component, error) {
err = errors.Wrapf(json.Unmarshal(to.Config, &t.Config),
"unmarshal signpost config")
o = t
case TypeTabsView:
t := &TabsView{Base: Base{Metadata: to.Metadata}}
err = errors.Wrapf(json.Unmarshal(to.Config, &t.Config),
"unmarshal tabs config")
o = t
default:
return nil, errors.Errorf("unknown view component %q", to.Metadata.Type)
}
Expand Down
23 changes: 23 additions & 0 deletions pkg/view/component/unmarshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,29 @@ func Test_unmarshal(t *testing.T) {
Base: newBase(TypeStepper, nil),
},
},
{
name: "tabsView",
configFile: "config_tabs.json",
objectType: "tabsView",
expected: &TabsView{
Config: TabsViewConfig{
Tabs: []SingleTab{{
Name: "Tab 1",
Contents: FlexLayout{
Base: newBase(TypeFlexLayout, nil),
Config: FlexLayoutConfig{
Sections: []FlexLayoutSection{{{
Width: WidthFull,
View: NewButton("test", action.Payload{"foo": "bar"}),
}}},
ButtonGroup: nil,
},
},
}},
},
Base: newBase(TypeTabsView, nil),
},
},
{
name: "summary",
configFile: "config_summary.json",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<clr-tabs *ngIf="tabs.length > 0" clrLayout="{{ orientation }}">
<clr-tab *ngFor="let tab of tabs; trackBy: identifyTab">
<button clrTabLink class="tab-button" (click)="clickTab(tab.name)">
{{ tab.name }}
</button>
<ng-template [clrIfActive]="activeTab === tab.name">
<clr-tab-content>
<div [ngClass]="{'tabs-container': orientation === 'horizontal'}">
<app-view-container [view]="tab.contents"></app-view-container>
</div>
</clr-tab-content>
</ng-template>
</clr-tab>
</clr-tabs>
Loading

0 comments on commit 44de7c1

Please sign in to comment.