From 5119667cd16c527af111c339594a08354b7a5cb0 Mon Sep 17 00:00:00 2001 From: Chris Hut Date: Tue, 23 Jan 2024 14:29:53 -0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=92=9C=20Cc=207187/purple=20banner=20for?= =?UTF-8?q?=20linking=20existing=20clusters=20(#20275)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Adding banner on services page * Simplified version of setting/unsetting banner * Translating the text based off of enterprise or not * Add an integration test * Adding an acceptance test * Enable config dismissal as well * Adding changelog * Adding some copyrights to the other files * Revert "Enable config dismissal as well" This reverts commit e6784c4335bdff99d9183d28571aa6ab4b852cbd. We'll be doing this in CC-7347 --- .changelog/20275.txt | 3 + .../app/components/hashicorp-consul/index.js | 2 +- .../components/link-to-hcp-banner/index.hbs | 24 +++++ .../components/link-to-hcp-banner/index.js | 22 +++++ ui/packages/consul-ui/app/services/env.js | 3 + .../consul-ui/app/services/hcp-link-status.js | 34 +++++++ .../app/styles/routes/dc/services/index.scss | 3 + .../app/templates/dc/services/index.hbs | 2 +- .../acceptance/link-to-hcp-banner-test.js | 35 ++++++++ .../components/link-to-hcp-banner-test.js | 89 +++++++++++++++++++ .../components/link-to-hcp-banner/en-us.yaml | 10 +++ 11 files changed, 225 insertions(+), 2 deletions(-) create mode 100644 .changelog/20275.txt create mode 100644 ui/packages/consul-ui/app/components/link-to-hcp-banner/index.hbs create mode 100644 ui/packages/consul-ui/app/components/link-to-hcp-banner/index.js create mode 100644 ui/packages/consul-ui/app/services/hcp-link-status.js create mode 100644 ui/packages/consul-ui/tests/acceptance/link-to-hcp-banner-test.js create mode 100644 ui/packages/consul-ui/tests/integration/components/link-to-hcp-banner-test.js create mode 100644 ui/packages/consul-ui/translations/components/link-to-hcp-banner/en-us.yaml diff --git a/.changelog/20275.txt b/.changelog/20275.txt new file mode 100644 index 000000000000..e82fc542bcac --- /dev/null +++ b/.changelog/20275.txt @@ -0,0 +1,3 @@ +```release-note:feature +ui: Added a banner to let users link their clusters to HCP +``` \ No newline at end of file diff --git a/ui/packages/consul-ui/app/components/hashicorp-consul/index.js b/ui/packages/consul-ui/app/components/hashicorp-consul/index.js index e47ca22c6262..f8f4a0663d35 100644 --- a/ui/packages/consul-ui/app/components/hashicorp-consul/index.js +++ b/ui/packages/consul-ui/app/components/hashicorp-consul/index.js @@ -11,7 +11,7 @@ export default class HashiCorpConsul extends Component { @service('env') env; get consulVersion() { - const suffix = !['', 'oss'].includes(this.env.var('CONSUL_BINARY_TYPE')) ? '+ent' : ''; + const suffix = this.env.isEnterprise ? '+ent' : ''; return `${this.env.var('CONSUL_VERSION')}${suffix}`; } } diff --git a/ui/packages/consul-ui/app/components/link-to-hcp-banner/index.hbs b/ui/packages/consul-ui/app/components/link-to-hcp-banner/index.hbs new file mode 100644 index 000000000000..950c30c945f9 --- /dev/null +++ b/ui/packages/consul-ui/app/components/link-to-hcp-banner/index.hbs @@ -0,0 +1,24 @@ +{{! + Copyright (c) HashiCorp, Inc. + SPDX-License-Identifier: BUSL-1.1 +}} +{{#if this.hcpLinkStatus.shouldDisplayBanner}} + + {{t "components.link-to-hcp-banner.title"}} + {{t "components.link-to-hcp-banner.description" + isEnterprise=this.env.isEnterprise}} + + + + + +{{/if}} \ No newline at end of file diff --git a/ui/packages/consul-ui/app/components/link-to-hcp-banner/index.js b/ui/packages/consul-ui/app/components/link-to-hcp-banner/index.js new file mode 100644 index 000000000000..3d0c96f2fa37 --- /dev/null +++ b/ui/packages/consul-ui/app/components/link-to-hcp-banner/index.js @@ -0,0 +1,22 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { inject as service } from '@ember/service'; + +export default class LinkToHcpBannerComponent extends Component { + @service('hcp-link-status') hcpLinkStatus; + @service('env') env; + + @action + onDismiss() { + this.hcpLinkStatus.dismissHcpLinkBanner(); + } + @action + onClusterLink() { + // TODO: CC-7147: Open simplified modal + } +} diff --git a/ui/packages/consul-ui/app/services/env.js b/ui/packages/consul-ui/app/services/env.js index 450206b15fed..39f2a740e9d4 100644 --- a/ui/packages/consul-ui/app/services/env.js +++ b/ui/packages/consul-ui/app/services/env.js @@ -7,6 +7,9 @@ import Service from '@ember/service'; import { env } from 'consul-ui/env'; export default class EnvService extends Service { + get isEnterprise() { + return !['', 'oss'].includes(this.var('CONSUL_BINARY_TYPE')); + } // deprecated // TODO: Remove this elsewhere in the app and use var instead env(key) { diff --git a/ui/packages/consul-ui/app/services/hcp-link-status.js b/ui/packages/consul-ui/app/services/hcp-link-status.js new file mode 100644 index 000000000000..4fec7f5babfc --- /dev/null +++ b/ui/packages/consul-ui/app/services/hcp-link-status.js @@ -0,0 +1,34 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +import Service from '@ember/service'; +import { tracked } from '@glimmer/tracking'; + +const LOCAL_STORAGE_KEY = 'consul:hideHcpLinkBanner'; + +export default class HcpLinkStatus extends Service { + @tracked + alreadyLinked = false; + @tracked + userDismissedBanner = false; + + get shouldDisplayBanner() { + return !this.alreadyLinked && !this.userDismissedBanner; + } + + constructor() { + super(...arguments); + this.userDismissedBanner = !!localStorage.getItem(LOCAL_STORAGE_KEY); + } + + userHasLinked() { + // TODO: CC-7145 - once can fetch the link status from the backend, fetch it and set it here + } + + dismissHcpLinkBanner() { + localStorage.setItem(LOCAL_STORAGE_KEY, true); + this.userDismissedBanner = true; + } +} diff --git a/ui/packages/consul-ui/app/styles/routes/dc/services/index.scss b/ui/packages/consul-ui/app/styles/routes/dc/services/index.scss index 3065579e1107..c7c6732734d9 100644 --- a/ui/packages/consul-ui/app/styles/routes/dc/services/index.scss +++ b/ui/packages/consul-ui/app/styles/routes/dc/services/index.scss @@ -18,6 +18,9 @@ html[data-route^='dc.services.instance'] .app-view > header dl { margin-bottom: 23px; margin-right: 50px; } +html[data-route^='dc.services.index'] .link-to-hcp-banner { + margin: 0 -48px; +} html[data-route^='dc.services.instance'] .app-view > header dt { font-weight: var(--token-typography-font-weight-bold); } diff --git a/ui/packages/consul-ui/app/templates/dc/services/index.hbs b/ui/packages/consul-ui/app/templates/dc/services/index.hbs index 100e7c2d9761..7f33e16c6a57 100644 --- a/ui/packages/consul-ui/app/templates/dc/services/index.hbs +++ b/ui/packages/consul-ui/app/templates/dc/services/index.hbs @@ -62,7 +62,7 @@ as |route|> (or route.params.nspace route.model.user.token.Namespace 'default') as |sort filters items partition nspace|}} - +

diff --git a/ui/packages/consul-ui/tests/acceptance/link-to-hcp-banner-test.js b/ui/packages/consul-ui/tests/acceptance/link-to-hcp-banner-test.js new file mode 100644 index 000000000000..aa501a78e3f5 --- /dev/null +++ b/ui/packages/consul-ui/tests/acceptance/link-to-hcp-banner-test.js @@ -0,0 +1,35 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +import { module, test } from 'qunit'; +import { click, visit } from '@ember/test-helpers'; +import { setupApplicationTest } from 'ember-qunit'; + +const bannerSelector = '[data-test-link-to-hcp-banner]'; +module('Acceptance | link to hcp banner', function (hooks) { + setupApplicationTest(hooks); + + hooks.beforeEach(function () { + // clear local storage so we don't have any settings + window.localStorage.clear(); + // setupTestEnv(this.owner, { + // CONSUL_ACLS_ENABLED: true, + // }); + }); + + test('the banner is initially displayed on services page', async function (assert) { + assert.expect(3); + // default route is services page so we're good here + await visit('/'); + // Expect the banner to be visible by default + assert.dom(bannerSelector).exists({ count: 1 }); + // Click on the dismiss button + await click(`${bannerSelector} button[aria-label="Dismiss"]`); + assert.dom(bannerSelector).doesNotExist('Banner is gone after dismissing'); + // Refresh the page + await visit('/'); + assert.dom(bannerSelector).doesNotExist('Banner is still gone after refresh'); + }); +}); diff --git a/ui/packages/consul-ui/tests/integration/components/link-to-hcp-banner-test.js b/ui/packages/consul-ui/tests/integration/components/link-to-hcp-banner-test.js new file mode 100644 index 000000000000..d20646bf34ba --- /dev/null +++ b/ui/packages/consul-ui/tests/integration/components/link-to-hcp-banner-test.js @@ -0,0 +1,89 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { click, render } from '@ember/test-helpers'; +import hbs from 'htmlbars-inline-precompile'; +import Service from '@ember/service'; +import sinon from 'sinon'; + +const userDismissedBannerStub = sinon.stub(); +const userHasLinkedStub = sinon.stub(); +const dismissHcpLinkBannerStub = sinon.stub(); +const bannerSelector = '[data-test-link-to-hcp-banner]'; +module('Integration | Component | link-to-hcp-banner', function (hooks) { + setupRenderingTest(hooks); + + class HcpLinkStatusStub extends Service { + get shouldDisplayBanner() { + return true; + } + userDismissedBanner = userDismissedBannerStub; + userHasLinked = userHasLinkedStub; + dismissHcpLinkBanner = dismissHcpLinkBannerStub; + } + + class EnvStub extends Service { + isEnterprise = false; + var(key) { + return key; + } + } + + hooks.beforeEach(function () { + this.owner.register('service:hcp-link-status', HcpLinkStatusStub); + this.owner.register('service:env', EnvStub); + }); + + test('it renders banner when hcp-link-status says it should', async function (assert) { + await render(hbs``); + + assert.dom(bannerSelector).exists({ count: 1 }); + await click(`${bannerSelector} button[aria-label="Dismiss"]`); + assert.ok(dismissHcpLinkBannerStub.calledOnce, 'userDismissedBanner was called'); + // Can't test that banner is no longer visible since service isn't hooked up + assert + .dom('[data-test-link-to-hcp-banner-title]') + .hasText( + 'Link this cluster to HCP Consul Central in a few steps to start managing your clusters in one place' + ); + assert + .dom('[data-test-link-to-hcp-banner-description]') + .hasText( + 'By linking your clusters to HCP Consul Central, you’ll get global, cross-cluster metrics, visual service maps, and a global API. Link to access a free 90 day trial for full feature access in your HCP organization.' + ); + }); + + test('banner does not render when hcp-link-status says it should NOT', async function (assert) { + class HcpLinkStatusStub extends Service { + get shouldDisplayBanner() { + return false; + } + userDismissedBanner = sinon.stub(); + userHasLinked = sinon.stub(); + dismissHcpLinkBanner = sinon.stub(); + } + this.owner.register('service:hcp-link-status', HcpLinkStatusStub); + await render(hbs``); + assert.dom(bannerSelector).doesNotExist(); + }); + + test('it displays different banner text when consul is enterprise', async function (assert) { + class EnvStub extends Service { + isEnterprise = true; + var(key) { + return key; + } + } + this.owner.register('service:env', EnvStub); + await render(hbs``); + assert + .dom('[data-test-link-to-hcp-banner-description]') + .hasText( + 'By linking your clusters to HCP Consul Central, you’ll get global, cross-cluster metrics, visual service maps, and a global API. HCP Consul Central’s full feature set is included with an Enterprise license.' + ); + }); +}); diff --git a/ui/packages/consul-ui/translations/components/link-to-hcp-banner/en-us.yaml b/ui/packages/consul-ui/translations/components/link-to-hcp-banner/en-us.yaml new file mode 100644 index 000000000000..e2b8149b3acb --- /dev/null +++ b/ui/packages/consul-ui/translations/components/link-to-hcp-banner/en-us.yaml @@ -0,0 +1,10 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +title: Link this cluster to HCP Consul Central in a few steps to start managing your clusters in one place +description: By linking your clusters to HCP Consul Central, you’ll get global, cross-cluster metrics, visual service maps, and a global API. {isEnterprise, select, + true {HCP Consul Central’s full feature set is included with an Enterprise license.} + other {Link to access a free 90 day trial for full feature access in your HCP organization.}} +clusterLinkButton: Link this cluster +viewDocumentation: View documentation +consulCentralDocumentation: Consul Central documentation