Repository: vmware-tanzu/octant
Pull request #2406 (2021-05-03 13:47:07)

Bump core-js from 3.11.0 to 3.11.2 in /web
Bumps [core-js](https://github.com/zloirock/core-js/tree/HEAD/packages/core-js) from 3.11.0 to 3.11.2.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/zloirock/core-js/blob/master/CHANGELOG.md">core-js's changelog</a>.</em></p>
<blockquote>
<h5>3.11.2 - 2021.05.03</h5>
<ul>
<li>Added a workaround of WebKit ~ iOS 10.3 Safari <code>Promise</code> bug, <a href="https://github-redirect.dependabot.com/zloirock/core-js/issues/932">#932</a></li>
<li><code>Promise#then</code> of incorrect native <code>Promise</code> implementations with correct subclassing no longer wrapped</li>
<li>Changed the order of <code>Promise</code> feature detection, removed unhandled rejection tracking check in non-browser non-node platforms</li>
</ul>
<h5>3.11.1 - 2021.04.28</h5>
<ul>
<li>Made <code>instanceof Promise</code> and <code>.constructor === Promise</code> work with polyfilled <code>Promise</code> for all native promise-based APIs</li>
<li>Added a workaround for some buggy V8 versions ~4.5 related to fixing of <code>%TypedArray%</code> static methods, <a href="https://github-redirect.dependabot.com/zloirock/core-js/issues/564">#564</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/zloirock/core-js/commit/040f7836ad467dae5a0b849b02442a3ed2607b18"><code>040f783</code></a> 3.11.2</li>
<li><a href="https://github.com/zloirock/core-js/commit/e5b874c5542529f0a9d0e284fb341dd27edf83bd"><code>e5b874c</code></a> remove unhandled rejection tracking check in non-browser non-node platforms</li>
<li><a href="https://github.com/zloirock/core-js/commit/afc66bcc886c63d72132d9fb0027869271e53735"><code>afc66bc</code></a> avoid <code>Promise.resolve</code> in the main <code>Promise</code> feature detection</li>
<li><a href="https://github.com/zloirock/core-js/commit/5623fa34498be98bbbb720fe49020963deb706a0"><code>5623fa3</code></a> change the order of <code>Promise</code> feature detection</li>
<li><a href="https://github.com/zloirock/core-js/commit/e280a0a036a52359d281a7c9b4da95d3d0e8b513"><code>e280a0a</code></a> don't wrap <code>Promise#then</code> of incorrect native <code>Promise</code> implementations with ...</li>
<li><a href="https://github.com/zloirock/core-js/commit/c78f69d921600bfb6caf14b592d8370487d88274"><code>c78f69d</code></a> add a workaround of WebKit ~ iOS 10.3 Safari <code>Promise</code> instantiation bug, clo...</li>
<li><a href="https://github.com/zloirock/core-js/commit/1e9c4fbb22c7954d50a4db09d40d5c7648bead88"><code>1e9c4fb</code></a> 3.11.1</li>
<li><a href="https://github.com/zloirock/core-js/commit/fd344a81c276305737fd3b0356be8526f092c8db"><code>fd344a8</code></a> make sure that wrappers doesn't use original methods which can't work with no...</li>
<li><a href="https://github.com/zloirock/core-js/commit/38aaa133165e803bcde686ab7b29437092dc214f"><code>38aaa13</code></a> fix <a href="https://github.com/zloirock/core-js/tree/HEAD/packages/core-js/issues/564">#564</a></li>
<li><a href="https://github.com/zloirock/core-js/commit/a09e802f51baa7f6dac58cbb98f8c6a285f4a487"><code>a09e802</code></a> make <code>.constructor === Promise</code> work with polyfilled <code>Promise</code> for all native...</li>
<li>Additional commits viewable in <a href="https://github.com/zloirock/core-js/commits/v3.11.2/packages/core-js">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=core-js&package-manager=npm_and_yarn&previous-version=3.11.0&new-version=3.11.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Patch:
diff --git a/changelogs/unreleased/1565-GuessWhoSamFoo b/changelogs/unreleased/1565-GuessWhoSamFoo
new file mode 100644
index 00000000..9f8005d5
--- /dev/null
+++ b/changelogs/unreleased/1565-GuessWhoSamFoo
@@ -0,0 +1,1 @@
+Fixed Daemonset incorrectly showing status when pod is unavailable
diff --git a/changelogs/unreleased/512-GuessWhoSamFoo b/changelogs/unreleased/512-GuessWhoSamFoo
new file mode 100644
index 00000000..4adfc15e
--- /dev/null
+++ b/changelogs/unreleased/512-GuessWhoSamFoo
@@ -0,0 +1,1 @@
+Added clipboard option to text component
diff --git a/internal/objectstatus/daemonset.go b/internal/objectstatus/daemonset.go
index f78398e7..105aeb3e 100644
--- a/internal/objectstatus/daemonset.go
+++ b/internal/objectstatus/daemonset.go
@@ -36,7 +36,7 @@ func daemonSet(_ context.Context, object runtime.Object, _ store.Store) (ObjectS
 			nodeStatus: component.NodeStatusWarning,
 			Details:    []component.Component{component.NewText("Daemon Set pods are running on nodes that aren't supposed to run Daemon Set pods")},
 		}, nil
-	case status.NumberAvailable != status.NumberReady:
+	case status.DesiredNumberScheduled != status.NumberReady:
 		return ObjectStatus{
 			nodeStatus: component.NodeStatusWarning,
 			Details:    []component.Component{component.NewText("Daemon Set pods are not ready")},
diff --git a/internal/printer/secret.go b/internal/printer/secret.go
index c3b10294..556cbbd6 100644
--- a/internal/printer/secret.go
+++ b/internal/printer/secret.go
@@ -103,10 +103,11 @@ func (s *SecretConfiguration) Create(options Options) (*component.Summary, error
 func describeSecretData(secret corev1.Secret) (*component.Table, error) {
 	table := component.NewTable("Data", "This secret has no data!", secretDataCols)
 
-	for key := range secret.Data {
+	for key, value := range secret.Data {
 		row := component.TableRow{}
-		row["Key"] = component.NewText(key)
-
+		keyText := component.NewText(key)
+		keyText.AddClipboard(string(value))
+		row["Key"] = keyText
 		table.Add(row)
 	}
 
diff --git a/pkg/view/component/button.go b/pkg/view/component/button.go
index 9eedafda..cd07de78 100644
--- a/pkg/view/component/button.go
+++ b/pkg/view/component/button.go
@@ -29,7 +29,7 @@ type ButtonStatus string
 const (
 	// ButtonSizeBlock is a full width button
 	ButtonSizeBlock ButtonSize = "block"
-	// ButtonSizeLarge is a small button
+	// ButtonSizeMedium is a clarity small button
 	ButtonSizeMedium ButtonSize = "md"
 )
 
diff --git a/pkg/view/component/text.go b/pkg/view/component/text.go
index 53331604..bc50df96 100644
--- a/pkg/view/component/text.go
+++ b/pkg/view/component/text.go
@@ -43,6 +43,8 @@ type TextConfig struct {
 	TrustedContent bool `json:"trustedContent,omitempty"`
 	// Status sets the status of the component.
 	Status TextStatus `json:"status,omitempty"`
+	// ClipboardValue adds a copy button with text to be added to clipboard
+	ClipboardValue string `json:"clipboardValue,omitempty"`
 }
 
 // NewText creates a text component
@@ -104,6 +106,11 @@ func (t *Text) DisableMarkdown() {
 	t.Config.IsMarkdown = false
 }
 
+// AddClipboard adds a clipboard button next to text
+func (t *Text) AddClipboard(value string) {
+	t.Config.ClipboardValue = value
+}
+
 // SetStatus sets the status of the text component.
 func (t *Text) SetStatus(status TextStatus) {
 	t.Config.Status = status
diff --git a/web/package-lock.json b/web/package-lock.json
index 9683cd85..b20fd1e3 100644
--- a/web/package-lock.json
+++ b/web/package-lock.json
@@ -12099,9 +12099,9 @@
       }
     },
     "core-js": {
-      "version": "3.11.0",
-      "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.11.0.tgz",
-      "integrity": "sha512-bd79DPpx+1Ilh9+30aT5O1sgpQd4Ttg8oqkqi51ZzhedMM1omD2e6IOF48Z/DzDCZ2svp49tN/3vneTK6ZBkXw=="
+      "version": "3.11.2",
+      "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.11.2.tgz",
+      "integrity": "sha512-3tfrrO1JpJSYGKnd9LKTBPqgUES/UYiCzMKeqwR1+jF16q4kD1BY2NvqkfuzXwQ6+CIWm55V9cjD7PQd+hijdw=="
     },
     "core-js-compat": {
       "version": "3.9.0",
diff --git a/web/src/app/modules/shared/components/presentation/text/text.component.html b/web/src/app/modules/shared/components/presentation/text/text.component.html
index 750a3006..5453b09d 100644
--- a/web/src/app/modules/shared/components/presentation/text/text.component.html
+++ b/web/src/app/modules/shared/components/presentation/text/text.component.html
@@ -3,6 +3,14 @@
     <app-indicator [status]="view.config.status"></app-indicator>
   </ng-container>
   {{ value }}
+  <div *ngIf="clipboardValue" class="button-container">
+    <div cds-layout="horizonal gap:sm">
+      <cds-icon-button action="flat" ariaLabel="copy-button" (click)="copyToClipboard()">
+        <cds-icon shape="clipboard"></cds-icon>
+        <ng-container *ngIf="copied">Copied</ng-container>
+      </cds-icon-button>
+    </div>
+  </div>
 </ng-container>
 
 <ng-template #markdown>
diff --git a/web/src/app/modules/shared/components/presentation/text/text.component.scss b/web/src/app/modules/shared/components/presentation/text/text.component.scss
index e78526e0..0f47acd0 100644
--- a/web/src/app/modules/shared/components/presentation/text/text.component.scss
+++ b/web/src/app/modules/shared/components/presentation/text/text.component.scss
@@ -1,3 +1,7 @@
 /* Copyright (c) 2019 the Octant contributors. All Rights Reserved.
  * SPDX-License-Identifier: Apache-2.0
  */
+
+.button-container {
+  float: right;
+}
diff --git a/web/src/app/modules/shared/components/presentation/text/text.component.ts b/web/src/app/modules/shared/components/presentation/text/text.component.ts
index 0f1bc61d..8eb3884d 100644
--- a/web/src/app/modules/shared/components/presentation/text/text.component.ts
+++ b/web/src/app/modules/shared/components/presentation/text/text.component.ts
@@ -1,7 +1,14 @@
 // Copyright (c) 2019 the Octant contributors. All Rights Reserved.
 // SPDX-License-Identifier: Apache-2.0
 //
-import { Component, OnInit, SecurityContext } from '@angular/core';
+import {
+  ChangeDetectorRef,
+  Component,
+  OnInit,
+  SecurityContext,
+} from '@angular/core';
+import '@cds/core/button/register';
+import { ClarityIcons, clipboardIcon } from '@cds/core/icon';
 import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
 import { TextView } from 'src/app/modules/shared/models/content';
 import { AbstractViewComponent } from '../../abstract-view/abstract-view.component';
@@ -16,13 +23,19 @@ export class TextComponent
   extends AbstractViewComponent<TextView>
   implements OnInit {
   value: string | SafeHtml;
+  clipboardValue: string;
+  copied: boolean;
 
   isMarkdown: boolean;
 
   hasStatus = false;
 
-  constructor(private readonly sanitizer: DomSanitizer) {
+  constructor(
+    private readonly sanitizer: DomSanitizer,
+    private cdr: ChangeDetectorRef
+  ) {
     super();
+    ClarityIcons.addIcons(clipboardIcon);
   }
 
   update() {
@@ -42,5 +55,23 @@ export class TextComponent
     if (view.config.status) {
       this.hasStatus = true;
     }
+
+    if (view.config.clipboardValue) {
+      this.clipboardValue = view.config.clipboardValue;
+    }
+  }
+
+  copyToClipboard(): void {
+    document.addEventListener('copy', (e: ClipboardEvent) => {
+      e.clipboardData.setData('text/plain', this.clipboardValue);
+      e.preventDefault();
+      document.removeEventListener('copy', null);
+    });
+    document.execCommand('copy');
+    this.copied = !this.copied;
+    setTimeout(() => {
+      this.copied = false;
+      this.cdr.detectChanges();
+    }, 1500);
   }
 }
diff --git a/web/src/app/modules/shared/models/content.ts b/web/src/app/modules/shared/models/content.ts
index c804ed62..487c11b1 100644
--- a/web/src/app/modules/shared/models/content.ts
+++ b/web/src/app/modules/shared/models/content.ts
@@ -379,6 +379,7 @@ export interface TextView extends View {
     isMarkdown?: boolean;
     trustedContent?: boolean;
     status?: number;
+    clipboardValue?: string;
   };
 }
 


Test patch:
diff --git a/internal/printer/secret_test.go b/internal/printer/secret_test.go
index e2ebb147..3e78859f 100644
--- a/internal/printer/secret_test.go
+++ b/internal/printer/secret_test.go
@@ -136,9 +136,13 @@ func Test_describeSecretData(t *testing.T) {
 
 	cols := component.NewTableCols("Key")
 	expected := component.NewTable("Data", "This secret has no data!", cols)
+	barText := component.NewText("bar")
+	barText.AddClipboard(string([]byte{0, 1, 2, 3}))
+	fooText := component.NewText("foo")
+	fooText.AddClipboard(string([]byte{0, 1, 2, 3}))
 	expected.Add([]component.TableRow{
-		{"Key": component.NewText("bar")},
-		{"Key": component.NewText("foo")},
+		{"Key": barText},
+		{"Key": fooText},
 	}...)
 
 	component.AssertEqual(t, expected, got)
diff --git a/pkg/view/component/text_test.go b/pkg/view/component/text_test.go
index f814fa8b..7e7ad12d 100644
--- a/pkg/view/component/text_test.go
+++ b/pkg/view/component/text_test.go
@@ -83,6 +83,25 @@ func Test_Text_Marshal(t *testing.T) {
             }
 `,
 		},
+		{
+			name: "with clipboard",
+			input: &Text{
+				Config: TextConfig{
+					Text:           "secret",
+					ClipboardValue: "my-secret",
+				},
+			},
+			expected: `
+            {
+                "metadata": {
+                  "type": "text"
+                },
+                "config": {
+                  "value": "secret",
+				  "clipboardValue": "my-secret"
+                }
+            }
+`},
 	}
 
 	for _, tc := range tests {