Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

💜 Cc 7187/purple banner for linking existing clusters #20275

Merged
merged 13 commits into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/20275.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:feature
ui: Added a banner to let users link their clusters to HCP
```
Original file line number Diff line number Diff line change
Expand Up @@ -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}`;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: BUSL-1.1
}}
{{#if this.hcpLinkStatus.shouldDisplayBanner}}
<Hds::Alert @type="page" @color="highlight" @onDismiss={{this.onDismiss}} class="link-to-hcp-banner"
data-test-link-to-hcp-banner as |A|>
<A.Title data-test-link-to-hcp-banner-title>{{t "components.link-to-hcp-banner.title"}}</A.Title>
<A.Description data-test-link-to-hcp-banner-description>{{t "components.link-to-hcp-banner.description"
isEnterprise=this.env.isEnterprise}}
</A.Description>
<A.Button @text={{t "components.link-to-hcp-banner.clusterLinkButton"}} @color="secondary" {{on "click"
this.onClusterLink}}
data-test-link-to-hcp-banner-button/>
<A.Link::Standalone @color="secondary" @icon="docs-link" @iconPosition="trailing" @text={{t
"components.link-to-hcp-banner.viewDocumentation"}}
@href="https://developer.hashicorp.com/hcp/docs/consul/self-managed"
data-test-link-to-hcp-banner-view-documentation/>
<A.Link::Standalone @color="secondary" @icon="docs-link" @iconPosition="trailing"
@text={{t "components.link-to-hcp-banner.consulCentralDocumentation"}}
@href="https://developer.hashicorp.com/hcp/docs/consul/concepts/consul-central"
data-test-link-to-hcp-banner-consul-central-documentation/>
</Hds::Alert>
{{/if}}
Original file line number Diff line number Diff line change
@@ -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
}
}
3 changes: 3 additions & 0 deletions ui/packages/consul-ui/app/services/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
34 changes: 34 additions & 0 deletions ui/packages/consul-ui/app/services/hcp-link-status.js
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ as |route|>
(or route.params.nspace route.model.user.token.Namespace 'default')

as |sort filters items partition nspace|}}

<LinkToHcpBanner/>
<AppView>
<BlockSlot @name="header">
<h1>
Expand Down
Original file line number Diff line number Diff line change
@@ -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');
});
});
Original file line number Diff line number Diff line change
@@ -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`<LinkToHcpBanner />`);

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`<LinkToHcpBanner />`);
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`<LinkToHcpBanner />`);
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.'
);
});
});
Original file line number Diff line number Diff line change
@@ -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