Repository: vmware-tanzu/octant
Pull request #1368 (2020-09-21 03:33:42)

Restore the collapsed left navigation functionality
Changes introduced in #1341 inadvertently broke the left navigation in collapsed state. Namely, the flyout menu was positioned incorrectly and in some cases was not accessible after the scrollbar showed up. This change brings the old functionality back. 

The Navigation component simply does not support scrolling and that will be addressed during the navigation redesign (see #1353), but for the short term (0.16 release) we should keep that functionality the way it was.

Signed-off-by: Milan Klanjsek <mklanjsek@pivotal.io>



mklanjsek: @GuessWhoSamFoo Left these in since I did not find any issues with them. Please let me know if I missed something. 
GuessWhoSamFoo: See the screeenshot in #1359 

I'm not sure of the browser/OS specifics

Patch:
diff --git a/changelogs/unreleased/1335-wwitzel3 b/changelogs/unreleased/1335-wwitzel3
deleted file mode 100644
index e151684e..00000000
--- a/changelogs/unreleased/1335-wwitzel3
+++ /dev/null
@@ -1,1 +0,0 @@
-use icons and colors for indicator to make it more accessible
diff --git a/changelogs/unreleased/838-wwitzel3 b/changelogs/unreleased/838-wwitzel3
deleted file mode 100644
index 802f2431..00000000
--- a/changelogs/unreleased/838-wwitzel3
+++ /dev/null
@@ -1,1 +0,0 @@
-default provided namespaces to initial namespace when empty
diff --git a/internal/cluster/namespace.go b/internal/cluster/namespace.go
index f61bfb25..f443abc6 100644
--- a/internal/cluster/namespace.go
+++ b/internal/cluster/namespace.go
@@ -91,16 +91,10 @@ func namespaces(dc dynamic.Interface) ([]corev1.Namespace, error) {
 	return nsList.Items, nil
 }
 
-// InitialNamespace returns the initial namespace for Octant
 func (n *namespaceClient) InitialNamespace() string {
 	return n.initialNamespace
 }
 
-// ProvidedNamespaces returns the list of namespaces provided.
-// If no namespaces  are provided, it will default to returning the InitialNamespace
 func (n *namespaceClient) ProvidedNamespaces() []string {
-	if len(n.providedNamespaces) == 0 {
-		n.providedNamespaces = []string{n.initialNamespace}
-	}
 	return n.providedNamespaces
 }
diff --git a/internal/modules/configuration/applyyaml_describer.go b/internal/modules/configuration/applyyaml_describer.go
new file mode 100644
index 00000000..d373cc8c
--- /dev/null
+++ b/internal/modules/configuration/applyyaml_describer.go
@@ -0,0 +1,46 @@
+/*
+Copyright (c) 2020 the Octant contributors. All Rights Reserved.
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package configuration
+
+import (
+	"context"
+
+	"github.com/vmware-tanzu/octant/internal/describer"
+	"github.com/vmware-tanzu/octant/internal/octant"
+	"github.com/vmware-tanzu/octant/pkg/view/component"
+)
+
+// ApplyYamlDescriber describes an apply
+type ApplyYamlDescriber struct {
+}
+
+var _ describer.Describer = (*ApplyYamlDescriber)(nil)
+
+// Describe describes the apply yaml interface
+func (d *ApplyYamlDescriber) Describe(ctx context.Context, namespace string, options describer.Options) (component.ContentResponse, error) {
+	title := append([]component.TitleComponent{}, component.NewText("Apply YAML"))
+	editor := component.NewEditor(component.TitleFromString("YAML"), "", false)
+	editor.Config.SubmitLabel = "Apply"
+	editor.Config.SubmitAction = octant.ActionApplyYaml
+	list := component.NewList(title, []component.Component{editor})
+
+	return component.ContentResponse{
+		Components: []component.Component{list},
+	}, nil
+}
+
+func (d *ApplyYamlDescriber) PathFilters() []describer.PathFilter {
+	filter := describer.NewPathFilter("/apply", d)
+	return []describer.PathFilter{*filter}
+}
+
+func (d *ApplyYamlDescriber) Reset(ctx context.Context) error {
+	return nil
+}
+
+func NewApplyYamlDescriber() *ApplyYamlDescriber {
+	return &ApplyYamlDescriber{}
+}
diff --git a/internal/modules/configuration/objects.go b/internal/modules/configuration/objects.go
index d97e0156..49cf0a6e 100644
--- a/internal/modules/configuration/objects.go
+++ b/internal/modules/configuration/objects.go
@@ -10,9 +10,12 @@ import "github.com/vmware-tanzu/octant/internal/describer"
 var (
 	pluginDescriber = NewPluginListDescriber()
 
+	applyYamlDescriber = NewApplyYamlDescriber()
+
 	rootDescriber = describer.NewSection(
 		"/",
 		"Configuration",
 		pluginDescriber,
+		applyYamlDescriber,
 	)
 )
diff --git a/internal/printer/container.go b/internal/printer/container.go
index 06fadbc7..2d955edc 100644
--- a/internal/printer/container.go
+++ b/internal/printer/container.go
@@ -406,16 +406,21 @@ func describeEnvRows(ctx context.Context, namespace string, vars []corev1.EnvVar
 			}
 
 			u, err := objectStore.Get(ctx, key)
-			if u == nil || err != nil {
-				row["Value"] = component.NewText("<none>")
-				row["Source"] = component.NewText(fmt.Sprintf("%s:%s", ref.Name, ref.Key))
-			} else {
+			if err != nil {
+				return nil, err
+			}
+
+			if u != nil {
 				configMap := &corev1.ConfigMap{}
 				if err := kubernetes.FromUnstructured(u, configMap); err != nil {
 					return nil, err
 				}
+
 				row["Value"] = component.NewText(configMap.Data[ref.Key])
 				row["Source"] = source
+			} else {
+				row["Value"] = component.NewText("<none>")
+				row["Source"] = component.NewText(fmt.Sprintf("%s:%s", ref.Name, ref.Key))
 			}
 		}
 	}
diff --git a/pkg/view/component/resource_viewer.go b/pkg/view/component/resource_viewer.go
index 35c78859..4c95b5a8 100644
--- a/pkg/view/component/resource_viewer.go
+++ b/pkg/view/component/resource_viewer.go
@@ -67,40 +67,6 @@ type Node struct {
 	Path       *Link       `json:"path,omitempty"`
 }
 
-func (n *Node) UnmarshalJSON(data []byte) error {
-	x := struct {
-		Name       string         `json:"name,omitempty"`
-		APIVersion string         `json:"apiVersion,omitempty"`
-		Kind       string         `json:"kind,omitempty"`
-		Status     NodeStatus     `json:"status,omitempty"`
-		Details    []*TypedObject `json:"details,omitempty"`
-		Path       *Link          `json:"path,omitempty"`
-	}{}
-
-	if err := json.Unmarshal(data, &x); err != nil {
-		return err
-	}
-
-	n.Name = x.Name
-	n.APIVersion = x.APIVersion
-	n.Kind = x.Kind
-	n.Status = x.Status
-	n.Path = x.Path
-
-	if x.Details != nil {
-		n.Details = make([]Component, len(x.Details))
-	}
-	for i, detail := range x.Details {
-		dc, err := detail.ToComponent()
-		if err != nil {
-			return errors.Wrap(err, "unmarshal-ing detail")
-		}
-		n.Details[i] = dc
-	}
-
-	return nil
-}
-
 // ResourceViewerConfig is configuration for a resource viewer.
 type ResourceViewerConfig struct {
 	Edges    AdjList `json:"edges,omitempty"`
diff --git a/web/package-lock.json b/web/package-lock.json
index e1689f15..948d809c 100644
--- a/web/package-lock.json
+++ b/web/package-lock.json
@@ -15299,9 +15299,9 @@
       "dev": true
     },
     "dayjs": {
-      "version": "1.8.36",
-      "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.36.tgz",
-      "integrity": "sha512-3VmRXEtw7RZKAf+4Tv1Ym9AGeo8r8+CjDi26x+7SYQil1UqtqdaokhzoEJohqlzt0m5kacJSDhJQkG/LWhpRBw==",
+      "version": "1.8.35",
+      "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.35.tgz",
+      "integrity": "sha512-isAbIEenO4ilm6f8cpqvgjZCsuerDAz2Kb7ri201AiNn58aqXuaLJEnCtfIMdCvERZHNGRY5lDMTr/jdAnKSWQ==",
       "dev": true
     },
     "debug": {
diff --git a/web/src/app/modules/shared/components/presentation/indicator/indicator.component.html b/web/src/app/modules/shared/components/presentation/indicator/indicator.component.html
index 5eb7dfb6..8db0d39a 100644
--- a/web/src/app/modules/shared/components/presentation/indicator/indicator.component.html
+++ b/web/src/app/modules/shared/components/presentation/indicator/indicator.component.html
@@ -1,11 +1,7 @@
 <ng-container *ngIf="currentStatus">
   <ng-container *ngIf="detail !== undefined; else showStatus">
     <clr-signpost>
-
-      <clr-icon *ngIf="currentStatus === 'error'" shape="exclamation-circle" class="is-error is-solid" size="16" clrSignpostTrigger></clr-icon>
-      <clr-icon *ngIf="currentStatus === 'warning'" shape="info-circle" class="is-warning is-solid" size="16" clrSignpostTrigger></clr-icon>
-      <clr-icon *ngIf="currentStatus === 'success'" shape="check-circle" class="is-success is-solid" size="16" clrSignpostTrigger></clr-icon>
-      <span style="margin-right: 4px;"></span>
+      <div [ngClass]="[currentStatus, 'indicator']" clrSignpostTrigger></div>
       <clr-signpost-content *clrIfOpen>
         <app-view-container [view]="detail"></app-view-container>
       </clr-signpost-content>
@@ -14,5 +10,5 @@
 </ng-container>
 
 <ng-template #showStatus>
-  <div clrSignpostTrigger></div>
+  <div [ngClass]="[currentStatus, 'indicator']" clrSignpostTrigger></div>
 </ng-template>
diff --git a/web/src/app/modules/shared/components/presentation/indicator/indicator.component.scss b/web/src/app/modules/shared/components/presentation/indicator/indicator.component.scss
new file mode 100644
index 00000000..5066dc18
--- /dev/null
+++ b/web/src/app/modules/shared/components/presentation/indicator/indicator.component.scss
@@ -0,0 +1,39 @@
+/*!
+ *  Copyright (c) 2020 the Octant contributors. All Rights Reserved.
+ *  SPDX-License-Identifier: Apache-2.0
+ */
+
+$circumference: 4px;
+
+.indicator {
+  :host-context(body.dark) {
+    --ok-background-color: #60b515;
+    --warning-background-color: #f57600;
+    --error-background-color: #e12200;
+  }
+
+  :host-context(body) {
+    --ok-background-color: #60b515;
+    --warning-background-color: #f57600;
+    --error-background-color: #e12200;
+  }
+
+  height: $circumference;
+  width: $circumference;
+  border-radius: 50%;
+  display: inline-block;
+  transform: translateY(-50%);
+  margin-right: 0.5em;
+
+  &.ok {
+    background-color: var(--ok-background-color);
+  }
+
+  &.warning {
+    background-color: var(--warning-background-color);
+  }
+
+  &.error {
+    background-color: var(--error-background-color);
+  }
+}
diff --git a/web/src/app/modules/shared/components/presentation/indicator/indicator.component.spec.ts b/web/src/app/modules/shared/components/presentation/indicator/indicator.component.spec.ts
index 0f70d19c..6cdcdd9b 100644
--- a/web/src/app/modules/shared/components/presentation/indicator/indicator.component.spec.ts
+++ b/web/src/app/modules/shared/components/presentation/indicator/indicator.component.spec.ts
@@ -51,7 +51,9 @@ describe('IndicatorComponent', () => {
 
       it(`shows ${name} indicator`, () => {
         expect(
-          element.querySelector(`app-indicator clr-icon.is-${name}`)
+          element
+            .querySelector(`app-indicator div.${name}`)
+            .classList.contains(name)
         ).toBeTruthy();
       });
     });
diff --git a/web/src/app/modules/shared/components/presentation/indicator/indicator.component.ts b/web/src/app/modules/shared/components/presentation/indicator/indicator.component.ts
index c4aa1c86..59b0b79d 100644
--- a/web/src/app/modules/shared/components/presentation/indicator/indicator.component.ts
+++ b/web/src/app/modules/shared/components/presentation/indicator/indicator.component.ts
@@ -11,7 +11,7 @@ export const Status = {
 };
 
 export const statusLookup = {
-  [Status.Ok]: 'success',
+  [Status.Ok]: 'ok',
   [Status.Warning]: 'warning',
   [Status.Error]: 'error',
 };
@@ -19,6 +19,7 @@ export const statusLookup = {
 @Component({
   selector: 'app-indicator',
   templateUrl: './indicator.component.html',
+  styleUrls: ['./indicator.component.scss'],
 })
 export class IndicatorComponent implements OnChanges {
   @Input()
diff --git a/web/src/app/modules/shared/components/smart/editor/editor.component.html b/web/src/app/modules/shared/components/smart/editor/editor.component.html
index 2e98393f..ea884f02 100644
--- a/web/src/app/modules/shared/components/smart/editor/editor.component.html
+++ b/web/src/app/modules/shared/components/smart/editor/editor.component.html
@@ -3,7 +3,7 @@
     <ng-monaco-editor
       [(ngModel)]="value"
       [options]="options"
-      [attr.modelUri]="uri"
+      modelUri="file:text.yaml"
     ></ng-monaco-editor>
   </div>
 
diff --git a/web/src/app/modules/shared/components/smart/editor/editor.component.ts b/web/src/app/modules/shared/components/smart/editor/editor.component.ts
index b578b781..70c174fa 100644
--- a/web/src/app/modules/shared/components/smart/editor/editor.component.ts
+++ b/web/src/app/modules/shared/components/smart/editor/editor.component.ts
@@ -33,7 +33,6 @@ export class EditorComponent extends AbstractViewComponent<EditorView> {
 
   private editorValue: string;
   private pristineValue: string;
-  uri: string;
   private metadata: { [p: string]: string };
 
   isModified = false;
@@ -59,8 +58,6 @@ export class EditorComponent extends AbstractViewComponent<EditorView> {
       this.pristineValue = view.config.value;
       this.options.readOnly = view.config.readOnly;
       this.options.language = view.config.language;
-      this.uri =
-        'file:text-' + Math.random().toString(36).substring(2, 15) + '.yaml';
     }
 
     this.submitAction = view.config.submitAction || this.submitAction;
diff --git a/web/src/app/modules/shared/components/smart/helper/helper.component.html b/web/src/app/modules/shared/components/smart/helper/helper.component.html
index 537ef016..f12b8165 100644
--- a/web/src/app/modules/shared/components/smart/helper/helper.component.html
+++ b/web/src/app/modules/shared/components/smart/helper/helper.component.html
@@ -92,16 +92,6 @@
             <span class="label">Ctrl<span class="badge">Enter</span></span>
           </clr-stack-content>
         </clr-stack-block>
-        <clr-stack-block
-        [clrStackViewLevel]="2"
-        [clrStackViewSetsize]="0"
-        [clrStackViewPosinset]="1"
-      >
-        <clr-stack-label>Apply YAML</clr-stack-label>
-        <clr-stack-content>
-          <span class="label">Ctrl<span class="badge">Y</span></span>
-        </clr-stack-content>
-      </clr-stack-block>
       </clr-stack-block>
       <clr-stack-block
         [clrSbExpandable]="false"
diff --git a/web/src/app/modules/shared/components/smart/helper/helper.component.ts b/web/src/app/modules/shared/components/smart/helper/helper.component.ts
index d8f530c0..795937f7 100644
--- a/web/src/app/modules/shared/components/smart/helper/helper.component.ts
+++ b/web/src/app/modules/shared/components/smart/helper/helper.component.ts
@@ -33,14 +33,12 @@ export class HelperComponent implements OnInit, OnDestroy {
     this.buildInfoSubscription.unsubscribe();
   }
 
-  @HostListener('window:keydown', ['$event'])
+  @HostListener('window:keyup', ['$event'])
   keyEvent(event: KeyboardEvent) {
     if (this.isShortcutModalOpen) {
       return;
     }
     if (event.ctrlKey && event.key === '/') {
-      event.preventDefault();
-      event.cancelBubble = true;
       this.isShortcutModalOpen = true;
     }
   }
diff --git a/web/src/app/modules/sugarloaf/components/smart/apply-yaml/apply-yaml.component.html b/web/src/app/modules/sugarloaf/components/smart/apply-yaml/apply-yaml.component.html
deleted file mode 100644
index 6bc92240..00000000
--- a/web/src/app/modules/sugarloaf/components/smart/apply-yaml/apply-yaml.component.html
+++ /dev/null
@@ -1,11 +0,0 @@
-<div (click)="toggleModal()" class="header-upload">
-  <clr-icon shape="upload"></clr-icon>
-  <span>Apply YAML</span>
-</div>
-
-<clr-modal [(clrModalOpen)]="isOpen" [clrModalSize]="'xl'" [clrModalClosable]="true" [clrModalStaticBackdrop]="false">
-  <h3 class="modal-title">Apply YAML</h3>
-  <div class="modal-body">
-      <app-view-editor [view]="editorView"></app-view-editor>
-  </div>
-</clr-modal>
\ No newline at end of file
diff --git a/web/src/app/modules/sugarloaf/components/smart/apply-yaml/apply-yaml.component.scss b/web/src/app/modules/sugarloaf/components/smart/apply-yaml/apply-yaml.component.scss
deleted file mode 100644
index b3feb706..00000000
--- a/web/src/app/modules/sugarloaf/components/smart/apply-yaml/apply-yaml.component.scss
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Copyright (c) 2020 the Octant contributors. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-.modal-body {
-  ::ng-deep ng-monaco-editor {
-    height: calc(60vh - 100px);
-  }
-}
-
-.header-upload {
-  margin: auto 0.8rem;
-  color: white;
-  line-height: 50px;
-  text-decoration: none;
-  outline: none;
-
-  cursor: pointer;
-
-  clr-icon {
-    margin-right: 0.2rem;
-  }
-}
diff --git a/web/src/app/modules/sugarloaf/components/smart/apply-yaml/apply-yaml.component.spec.ts b/web/src/app/modules/sugarloaf/components/smart/apply-yaml/apply-yaml.component.spec.ts
deleted file mode 100644
index da55c853..00000000
--- a/web/src/app/modules/sugarloaf/components/smart/apply-yaml/apply-yaml.component.spec.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (c) 2020 the Octant contributors. All Rights Reserved.
-// SPDX-License-Identifier: Apache-2.0
-//
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-import { ApplyYAMLComponent } from './apply-yaml.component';
-
-describe('ApplyYAMLComponent', () => {
-  let component: ApplyYAMLComponent;
-  let fixture: ComponentFixture<ApplyYAMLComponent>;
-
-  beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      declarations: [ApplyYAMLComponent],
-    }).compileComponents();
-  }));
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(ApplyYAMLComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/web/src/app/modules/sugarloaf/components/smart/apply-yaml/apply-yaml.component.ts b/web/src/app/modules/sugarloaf/components/smart/apply-yaml/apply-yaml.component.ts
deleted file mode 100644
index abb35f0c..00000000
--- a/web/src/app/modules/sugarloaf/components/smart/apply-yaml/apply-yaml.component.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
-Copyright (c) 2020 the Octant contributors. All Rights Reserved.
-SPDX-License-Identifier: Apache-2.0
-*/
-
-import { Component, HostListener, OnInit } from '@angular/core';
-import { EditorView } from 'src/app/modules/shared/models/content';
-
-@Component({
-  selector: 'app-apply-yaml',
-  templateUrl: './apply-yaml.component.html',
-  styleUrls: ['./apply-yaml.component.scss'],
-})
-export class ApplyYAMLComponent implements OnInit {
-  isOpen: boolean;
-  editorView: EditorView = {
-    config: {
-      value: '',
-      language: 'yaml',
-      readOnly: false,
-      metadata: null,
-      submitAction: 'action.octant.dev/apply',
-      submitLabel: 'Apply',
-    },
-    metadata: {
-      type: 'editor',
-      title: [],
-    },
-  };
-
-  constructor() {}
-
-  ngOnInit() {
-    this.isOpen = false;
-  }
-
-  @HostListener('window:keydown', ['$event'])
-  keyEvent(event: KeyboardEvent) {
-    if (event.ctrlKey && event.key === 'y') {
-      event.preventDefault();
-      event.cancelBubble = true;
-      this.isOpen = !this.isOpen;
-    }
-  }
-
-  toggleModal() {
-    this.isOpen = true;
-  }
-}
diff --git a/web/src/app/modules/sugarloaf/components/smart/container/container.component.html b/web/src/app/modules/sugarloaf/components/smart/container/container.component.html
index 04b31675..0262f23d 100644
--- a/web/src/app/modules/sugarloaf/components/smart/container/container.component.html
+++ b/web/src/app/modules/sugarloaf/components/smart/container/container.component.html
@@ -19,7 +19,14 @@
         </div>
       </div>
       <div class="header-actions">
-        <app-apply-yaml class="header-centered"></app-apply-yaml>
+        <a
+          [routerLink]="['/configuration/apply']"
+          class="header-link"
+          title="Create or update resources"
+        >
+          <clr-icon shape="upload"></clr-icon>
+          <span>Apply YAML</span>
+        </a>
         <div class="namespace-switcher header-centered">
           <app-namespace></app-namespace>
         </div>
diff --git a/web/src/app/modules/sugarloaf/components/smart/container/container.component.scss b/web/src/app/modules/sugarloaf/components/smart/container/container.component.scss
index 24b85af9..05e9b219 100644
--- a/web/src/app/modules/sugarloaf/components/smart/container/container.component.scss
+++ b/web/src/app/modules/sugarloaf/components/smart/container/container.component.scss
@@ -25,9 +25,6 @@
 }
 
 .main-container {
-  flex: 1 1 auto;
-  overflow-y: scroll;
-
   // Navigation's color variables for Light Theme.
 
   :host-context(body) {
@@ -90,6 +87,20 @@
         width: 300px;
       }
     }
+
+    .header-actions {
+      .header-link {
+        margin: auto 0.8rem;
+        color: white;
+        line-height: 50px;
+        text-decoration: none;
+        outline: none;
+
+        clr-icon {
+          margin-right: 0.2rem;
+        }
+      }
+    }
   }
 
   .content-container {
@@ -105,10 +116,3 @@
   }
 }
 
-.main-container {
-  .content-container {
-    flex: 1;
-    overflow-y: scroll;
-  }
-}
-
diff --git a/web/src/app/modules/sugarloaf/components/smart/navigation/navigation.component.scss b/web/src/app/modules/sugarloaf/components/smart/navigation/navigation.component.scss
index 5624d392..43408496 100644
--- a/web/src/app/modules/sugarloaf/components/smart/navigation/navigation.component.scss
+++ b/web/src/app/modules/sugarloaf/components/smart/navigation/navigation.component.scss
@@ -91,7 +91,7 @@
 
   .navigation-items-collapsed {
     flex-grow: 1;
-    overflow-y: scroll;
+    overflow-y: hidden;
   }
 
   .flyout-header {
diff --git a/web/src/app/modules/sugarloaf/sugarloaf.module.ts b/web/src/app/modules/sugarloaf/sugarloaf.module.ts
index 9c3d6d1c..2919dbc5 100644
--- a/web/src/app/modules/sugarloaf/sugarloaf.module.ts
+++ b/web/src/app/modules/sugarloaf/sugarloaf.module.ts
@@ -7,7 +7,6 @@ import { InputFilterComponent } from './components/smart/input-filter/input-filt
 import { NotifierComponent } from './components/smart/notifier/notifier.component';
 import { NavigationComponent } from './components/smart/navigation/navigation.component';
 import { QuickSwitcherComponent } from './components/smart/quick-switcher/quick-switcher.component';
-import { ApplyYAMLComponent } from './components/smart/apply-yaml/apply-yaml.component';
 import { ThemeSwitchButtonComponent } from './components/smart/theme-switch/theme-switch-button.component';
 import { UploaderComponent } from './components/smart/uploader/uploader.component';
 import { ClarityModule } from '@clr/angular';
@@ -37,7 +36,6 @@ export class UnstripTrailingSlashLocation extends Location {
 
 @NgModule({
   declarations: [
-    ApplyYAMLComponent,
     ContainerComponent,
     NamespaceComponent,
     PageNotFoundComponent,


Test patch:
diff --git a/internal/cluster/namespace_test.go b/internal/cluster/namespace_test.go
index 46428925..e3a59dfe 100644
--- a/internal/cluster/namespace_test.go
+++ b/internal/cluster/namespace_test.go
@@ -18,6 +18,8 @@ import (
 func Test_namespaceClient_Names(t *testing.T) {
 	scheme := runtime.NewScheme()
 
+	// NOTE: this should be reverted to the k8s.io/client-go/dynamic/fake when bug fix is
+	// merged upstream
 	dc := dynamicfake.NewSimpleDynamicClient(scheme,
 		newUnstructured("v1", "Namespace", "", "default"),
 		newUnstructured("v1", "Namespace", "", "app-1"),
@@ -32,19 +34,6 @@ func Test_namespaceClient_Names(t *testing.T) {
 	assert.Equal(t, expected, got)
 }
 
-func Test_namespaceClient_providedNamespaces(t *testing.T) {
-	providedNamespaces := []string{"default", "user-1"}
-
-	scheme := runtime.NewScheme()
-	dc := dynamicfake.NewSimpleDynamicClient(scheme)
-	nc := newNamespaceClient(dc, nil, "default", providedNamespaces)
-
-	assert.Equal(t, providedNamespaces, nc.ProvidedNamespaces())
-
-	nc = newNamespaceClient(dc, nil, "default", []string{})
-	assert.Equal(t, nc.ProvidedNamespaces(), []string{"default"})
-}
-
 func Test_namespaceClient_InitialNamespace(t *testing.T) {
 	expected := "inital-namespace"
 	nc := newNamespaceClient(nil, nil, expected, []string{})
diff --git a/internal/modules/configuration/applyyaml_describer_test.go b/internal/modules/configuration/applyyaml_describer_test.go
new file mode 100644
index 00000000..33c3be7b
--- /dev/null
+++ b/internal/modules/configuration/applyyaml_describer_test.go
@@ -0,0 +1,52 @@
+/*
+Copyright (c) 2020 the Octant contributors. All Rights Reserved.
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package configuration
+
+import (
+	"context"
+	"testing"
+
+	"github.com/golang/mock/gomock"
+	"github.com/stretchr/testify/require"
+
+	configFake "github.com/vmware-tanzu/octant/internal/config/fake"
+	"github.com/vmware-tanzu/octant/internal/describer"
+	"github.com/vmware-tanzu/octant/pkg/view/component"
+)
+
+func TestApplyYamlDescriber(t *testing.T) {
+	controller := gomock.NewController(t)
+	defer controller.Finish()
+
+	namespace := "default"
+
+	dashConfig := configFake.NewMockDash(controller)
+
+	p := NewApplyYamlDescriber()
+
+	options := describer.Options{
+		Dash: dashConfig,
+	}
+
+	cResponse, err := p.Describe(context.TODO(), namespace, options)
+	require.NoError(t, err)
+
+	list := component.NewList(append([]component.TitleComponent{}, component.NewText("Apply YAML")), nil)
+
+	editor := component.NewEditor(component.TitleFromString("YAML"), "", false)
+	editor.Config.SubmitAction = "action.octant.dev/apply"
+	editor.Config.SubmitLabel = "Apply"
+	list.Add(editor)
+
+	require.Len(t, cResponse.Components, 1)
+	component.AssertEqual(t, list, cResponse.Components[0])
+
+	pf := p.PathFilters()
+	require.Equal(t, "/apply", pf[0].String())
+
+	err = p.Reset(context.TODO())
+	require.NoError(t, err)
+}
diff --git a/pkg/view/component/testdata/config_resource_viewer.json b/pkg/view/component/testdata/config_resource_viewer.json
index 80acbb36..708b8ef1 100644
--- a/pkg/view/component/testdata/config_resource_viewer.json
+++ b/pkg/view/component/testdata/config_resource_viewer.json
@@ -24,13 +24,7 @@
       "name": "my-nginx",
       "apiVersion": "v1",
       "kind": "Service",
-      "status": "ok",
-      "details": [
-        {
-          "metadata": { "type": "text" },
-          "config": { "value": "my-nginx" }
-        }
-      ]
+      "status": "ok"
     },
     "71c2b4eb-2949-11e9-b356-42010a8000e5": {
       "name": "nginx-deployment",
diff --git a/pkg/view/component/unmarshal_test.go b/pkg/view/component/unmarshal_test.go
index aebbbb16..f00ce6fa 100644
--- a/pkg/view/component/unmarshal_test.go
+++ b/pkg/view/component/unmarshal_test.go
@@ -307,9 +307,6 @@ func Test_unmarshal(t *testing.T) {
 							APIVersion: "v1",
 							Kind:       "Service",
 							Status:     "ok",
-							Details: []Component{
-								NewText("my-nginx"),
-							},
 						},
 						"71c2b4eb-2949-11e9-b356-42010a8000e5": Node{
 							Name:       "nginx-deployment",