Merge pull request #284 from hashicorp/feature-doc-links
feat: 🎸 add doc-url helper and doc-link component
This commit is contained in:
41 files changed
+232
-41
No files matched your search
@@ -4,7 +4,7 @@ module.exports = {
|
||||
extends: 'octane',
|
||||
rules: {
|
||||
'no-bare-strings': true,
|
||||
'no-curly-component-invocation': { allow: ['app-name'] },
|
||||
'no-implicit-this': { allow: ['app-name'] }
|
||||
'no-curly-component-invocation': { allow: ['app-name', 'company-name', 'company-copyright'] },
|
||||
'no-implicit-this': { allow: ['app-name', 'company-name', 'company-copyright', 'doc-url'] }
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
<a
|
||||
class="doc-link"
|
||||
href={{doc-url @doc}}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
title={{t "actions.get-topic-help"}}>
|
||||
<Rose::Icon @name="help-circle-outline" @size={{@iconSize}} />
|
||||
</a>
|
||||
@@ -0,0 +1,7 @@
|
||||
import { helper } from '@ember/component/helper';
|
||||
import config from '../config/environment';
|
||||
|
||||
export default helper(function companyCopyright() {
|
||||
const currentYear = new Date().getFullYear();
|
||||
return `© ${currentYear} ${config.companyName}`;
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
import { helper } from '@ember/component/helper';
|
||||
import config from '../config/environment';
|
||||
|
||||
export default helper(function companyName() {
|
||||
return config.companyName;
|
||||
});
|
||||
@@ -0,0 +1,18 @@
|
||||
import { helper } from '@ember/component/helper';
|
||||
import { get } from '@ember/object';
|
||||
import { assert } from '@ember/debug';
|
||||
import config from '../config/environment';
|
||||
|
||||
export default helper(function docUrl(params/*, hash*/) {
|
||||
const baseURL = config.documentation.baseURL;
|
||||
const docKey = params[0] || '';
|
||||
const configuredPath = get(config.documentation, docKey);
|
||||
if (docKey) {
|
||||
assert(`
|
||||
Documentation for "${docKey}" could not be found. Please ensure that
|
||||
this key exists under "documentation" in your app config.
|
||||
`, configuredPath);
|
||||
}
|
||||
const path = configuredPath || '';
|
||||
return `${baseURL}${path}`;
|
||||
});
|
||||
+3
-3
@@ -23,7 +23,7 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostSetsHo
|
||||
super.renderTemplate(...arguments);
|
||||
|
||||
this.render(
|
||||
'scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/-header',
|
||||
'scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/host-set/-header',
|
||||
{
|
||||
into: 'scopes/scope/projects/project/host-catalogs/host-catalog',
|
||||
outlet: 'header',
|
||||
@@ -32,7 +32,7 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostSetsHo
|
||||
);
|
||||
|
||||
this.render(
|
||||
'scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/-navigation',
|
||||
'scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/host-set/-navigation',
|
||||
{
|
||||
into: 'scopes/scope/projects/project/host-catalogs/host-catalog',
|
||||
outlet: 'navigation',
|
||||
@@ -41,7 +41,7 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostSetsHo
|
||||
);
|
||||
|
||||
this.render(
|
||||
'scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/-actions',
|
||||
'scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/host-set/-actions',
|
||||
{
|
||||
into: 'scopes/scope/projects/project/host-catalogs/host-catalog',
|
||||
outlet: 'actions',
|
||||
|
||||
+3
-3
@@ -23,7 +23,7 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostsHostR
|
||||
super.renderTemplate(...arguments);
|
||||
|
||||
this.render(
|
||||
'scopes/scope/projects/project/host-catalogs/host-catalog/hosts/-header',
|
||||
'scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host/-header',
|
||||
{
|
||||
into: 'scopes/scope/projects/project/host-catalogs/host-catalog',
|
||||
outlet: 'header',
|
||||
@@ -32,7 +32,7 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostsHostR
|
||||
);
|
||||
|
||||
this.render(
|
||||
'scopes/scope/projects/project/host-catalogs/host-catalog/hosts/-navigation',
|
||||
'scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host/-navigation',
|
||||
{
|
||||
into: 'scopes/scope/projects/project/host-catalogs/host-catalog',
|
||||
outlet: 'navigation',
|
||||
@@ -41,7 +41,7 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostsHostR
|
||||
);
|
||||
|
||||
this.render(
|
||||
'scopes/scope/projects/project/host-catalogs/host-catalog/hosts/-actions',
|
||||
'scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host/-actions',
|
||||
{
|
||||
into: 'scopes/scope/projects/project/host-catalogs/host-catalog',
|
||||
outlet: 'actions',
|
||||
|
||||
@@ -59,11 +59,13 @@
|
||||
|
||||
<layout.footer>
|
||||
<Rose::Footer as |footer|>
|
||||
<footer.brand @logo="logo-hashi" @text={{t "hashicorp.title"}} />
|
||||
<footer.text>{{t "hashicorp.copyright"}}</footer.text>
|
||||
<footer.text>{{t "hashicorp.product"}}</footer.text>
|
||||
<footer.brand @logo="logo-hashi" @text={{company-name}} />
|
||||
<footer.text>{{company-copyright}}</footer.text>
|
||||
<footer.text>{{app-name}}</footer.text>
|
||||
<footer.nav as |nav|>
|
||||
<nav.link @href={{t "hashicorp.documentation.link"}}>{{t "hashicorp.documentation.title"}}</nav.link>
|
||||
<nav.link @href={{doc-url}}>
|
||||
{{t "titles.documentation"}}
|
||||
</nav.link>
|
||||
</footer.nav>
|
||||
</Rose::Footer>
|
||||
</layout.footer>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<h2>{{t "resources.auth-method.title"}}</h2>
|
||||
<h2>
|
||||
{{t "resources.auth-method.title"}}
|
||||
<DocLink @doc="topics.auth-method" @iconSize="large" />
|
||||
</h2>
|
||||
<p>{{t "resources.auth-method.description"}}</p>
|
||||
<Copyable
|
||||
@text={{@model.id}}
|
||||
|
||||
+4
-1
@@ -1,4 +1,7 @@
|
||||
<h2>{{t "resources.account.title"}}</h2>
|
||||
<h2>
|
||||
{{t "resources.account.title"}}
|
||||
<DocLink @doc="topics.account" @iconSize="large" />
|
||||
</h2>
|
||||
<Copyable
|
||||
@text={{@model.id}}
|
||||
@buttonText={{t "actions.copy-to-clipboard"}}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<Rose::Layout::Page as |page|>
|
||||
<page.header>
|
||||
<h2>{{t "resources.auth-method.title_plural"}}</h2>
|
||||
<h2>
|
||||
{{t "resources.auth-method.title_plural"}}
|
||||
<DocLink @doc="topics.auth-method" @iconSize="large" />
|
||||
</h2>
|
||||
<p>{{t "resources.auth-method.description"}}</p>
|
||||
</page.header>
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<h2>{{t "resources.group.title"}}</h2>
|
||||
<h2>
|
||||
{{t "resources.group.title"}}
|
||||
<DocLink @doc="topics.group" @iconSize="large" />
|
||||
</h2>
|
||||
<p>{{t "resources.group.description"}}</p>
|
||||
<Copyable
|
||||
@text={{@model.id}}
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
</page.breadcrumbs>
|
||||
|
||||
<page.header>
|
||||
<h2>{{t "resources.group.title_plural"}}</h2>
|
||||
<h2>
|
||||
{{t "resources.group.title_plural"}}
|
||||
<DocLink @doc="topics.group" @iconSize="large" />
|
||||
</h2>
|
||||
<p>{{t "resources.group.description"}}</p>
|
||||
</page.header>
|
||||
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
</page.breadcrumbs>
|
||||
|
||||
<page.header>
|
||||
<h2>{{t "resources.org.title_plural"}}</h2>
|
||||
<h2>
|
||||
{{t "resources.org.title_plural"}}
|
||||
<DocLink @doc="topics.org" @iconSize="large" />
|
||||
</h2>
|
||||
{{#if @model}}
|
||||
<p>{{t "resources.org.description"}}</p>
|
||||
{{/if}}
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
</page.breadcrumbs>
|
||||
|
||||
<page.header>
|
||||
<h2>{{t "resources.project.title_plural"}}</h2>
|
||||
<h2>
|
||||
{{t "resources.project.title_plural"}}
|
||||
<DocLink @doc="topics.project" @iconSize="large" />
|
||||
</h2>
|
||||
{{#if @model}}
|
||||
<p>{{t "resources.project.description"}}</p>
|
||||
{{/if}}
|
||||
|
||||
+4
-1
@@ -1,4 +1,7 @@
|
||||
<h2>{{t "resources.host-catalog.title"}}</h2>
|
||||
<h2>
|
||||
{{t "resources.host-catalog.title"}}
|
||||
<DocLink @doc="topics.host-catalog" @iconSize="large" />
|
||||
</h2>
|
||||
<p>{{t "resources.host-catalog.description"}}</p>
|
||||
<Copyable
|
||||
@text={{@model.id}}
|
||||
|
||||
File renamed without changes.
+4
-1
@@ -1,4 +1,7 @@
|
||||
<h2>{{t "resources.host-set.title"}}</h2>
|
||||
<h2>
|
||||
{{t "resources.host-set.title"}}
|
||||
<DocLink @doc="topics.host-set" @iconSize="large" />
|
||||
</h2>
|
||||
<p>{{t "resources.host-set.description"}}</p>
|
||||
<Copyable
|
||||
@text={{@model.id}}
|
||||
File renamed without changes.
File renamed without changes.
+4
-1
@@ -1,4 +1,7 @@
|
||||
<h2>{{t "resources.host.title"}}</h2>
|
||||
<h2>
|
||||
{{t "resources.host.title"}}
|
||||
<DocLink @doc="topics.host" @iconSize="large" />
|
||||
</h2>
|
||||
<p>{{t "resources.host.description"}}</p>
|
||||
<Copyable
|
||||
@text={{@model.id}}
|
||||
File renamed without changes.
@@ -5,7 +5,10 @@
|
||||
</page.breadcrumbs>
|
||||
|
||||
<page.header>
|
||||
<h2>{{t "resources.host-catalog.title_plural"}}</h2>
|
||||
<h2>
|
||||
{{t "resources.host-catalog.title_plural"}}
|
||||
<DocLink @doc="topics.host-catalog" @iconSize="large" />
|
||||
</h2>
|
||||
<p>{{t "resources.host-catalog.description"}}</p>
|
||||
</page.header>
|
||||
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
</page.breadcrumbs>
|
||||
|
||||
<page.header>
|
||||
<h2>{{t "resources.project.title"}}</h2>
|
||||
<h2>
|
||||
{{t "resources.project.title"}}
|
||||
<DocLink @doc="topics.project" @iconSize="large" />
|
||||
</h2>
|
||||
<p>{{t "resources.project.description"}}</p>
|
||||
<Copyable
|
||||
@text={{@model.id}}
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
</page.breadcrumbs>
|
||||
|
||||
<page.header>
|
||||
<h2>{{t "resources.session.title_plural"}}</h2>
|
||||
<h2>
|
||||
{{t "resources.session.title_plural"}}
|
||||
<DocLink @doc="topics.session" @iconSize="large" />
|
||||
</h2>
|
||||
<p>{{t "resources.session.description"}}</p>
|
||||
</page.header>
|
||||
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
</page.breadcrumbs>
|
||||
|
||||
<page.header>
|
||||
<h2>{{t "resources.target.title_plural"}}</h2>
|
||||
<h2>
|
||||
{{t "resources.target.title_plural"}}
|
||||
<DocLink @doc="topics.target" @iconSize="large" />
|
||||
</h2>
|
||||
{{#if @model}}
|
||||
<p>{{t "resources.target.description"}}</p>
|
||||
{{/if}}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<h2>{{t "resources.target.title"}}</h2>
|
||||
<h2>
|
||||
{{t "resources.target.title"}}
|
||||
<DocLink @doc="topics.target" @iconSize="large" />
|
||||
</h2>
|
||||
<p>{{t "resources.target.description"}}</p>
|
||||
<Copyable
|
||||
@text={{@model.id}}
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
</page.breadcrumbs>
|
||||
|
||||
<page.header>
|
||||
<h2>{{t "resources.role.title_plural"}}</h2>
|
||||
<h2>
|
||||
{{t "resources.role.title_plural"}}
|
||||
<DocLink @doc="topics.role" @iconSize="large" />
|
||||
</h2>
|
||||
{{#if @model}}
|
||||
<p>{{t "resources.role.description"}}</p>
|
||||
{{/if}}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<h2>{{t "resources.role.title"}}</h2>
|
||||
<h2>
|
||||
{{t "resources.role.title"}}
|
||||
<DocLink @doc="topics.role" @iconSize="large" />
|
||||
</h2>
|
||||
<p>{{t "resources.role.description"}}</p>
|
||||
<Copyable
|
||||
@text={{@model.id}}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<h2>{{t "resources.role.title"}}</h2>
|
||||
<h2>
|
||||
{{t "resources.role.title"}}
|
||||
<DocLink @doc="topics.role" @iconSize="large" />
|
||||
</h2>
|
||||
<p>{{t "resources.role.grant.description"}}</p>
|
||||
<Copyable
|
||||
@text={{@model.id}}
|
||||
@@ -6,4 +9,4 @@
|
||||
@acknowledgeText={{t "states.copied"}}
|
||||
>
|
||||
<code>{{@model.id}}</code>
|
||||
</Copyable>
|
||||
</Copyable>
|
||||
@@ -1,4 +1,7 @@
|
||||
<h2>{{t "resources.role.title"}}</h2>
|
||||
<h2>
|
||||
{{t "resources.role.title"}}
|
||||
<DocLink @doc="topics.role" @iconSize="large" />
|
||||
</h2>
|
||||
<p>{{t "resources.role.principal.description"}}</p>
|
||||
<Copyable
|
||||
@text={{@model.id}}
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
</page.breadcrumbs>
|
||||
|
||||
<page.header>
|
||||
<h2>{{t "resources.user.title_plural"}}</h2>
|
||||
<h2>
|
||||
{{t "resources.user.title_plural"}}
|
||||
<DocLink @doc="topics.user" @iconSize="large" />
|
||||
</h2>
|
||||
{{#if @model}}
|
||||
<p>{{t "resources.user.description"}}</p>
|
||||
{{/if}}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<h2>{{t "resources.user.title"}}</h2>
|
||||
<h2>
|
||||
{{t "resources.user.title"}}
|
||||
<DocLink @doc="topics.user" @iconSize="large" />
|
||||
</h2>
|
||||
<p>{{t "resources.user.description"}}</p>
|
||||
<Copyable
|
||||
@text={{@model.id}}
|
||||
|
||||
@@ -32,11 +32,30 @@ module.exports = function (environment) {
|
||||
},
|
||||
|
||||
appName: APP_NAME,
|
||||
companyName: 'HashiCorp',
|
||||
|
||||
notifyTimeout: 4000,
|
||||
sessionPollingTimeoutSeconds: 2.5,
|
||||
enableConfirmService: true,
|
||||
|
||||
documentation: {
|
||||
baseURL: 'https://boundaryproject.io/help/admin-ui',
|
||||
topics: {
|
||||
org: '/org',
|
||||
project: '/project',
|
||||
account: '/account',
|
||||
'auth-method': '/auth-method',
|
||||
group: '/group',
|
||||
'host-catalog': '/host-catalog',
|
||||
'host-set': '/host-set',
|
||||
host: '/host',
|
||||
role: '/role',
|
||||
session: '/session',
|
||||
target: '/target',
|
||||
user: '/user'
|
||||
}
|
||||
},
|
||||
|
||||
contentSecurityPolicyHeader: 'Content-Security-Policy',
|
||||
contentSecurityPolicyMeta: true,
|
||||
contentSecurityPolicy: {
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render, find } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
|
||||
module('Integration | Component | doc-link', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders', async function(assert) {
|
||||
assert.expect(1);
|
||||
await render(hbs`<DocLink @doc="topics.account" @iconSize="large" />`);
|
||||
assert.ok(find('.doc-link .rose-icon.large'));
|
||||
});
|
||||
});
|
||||
@@ -7,6 +7,7 @@ module('Integration | Helper | app-name', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders', async function (assert) {
|
||||
assert.expect(1);
|
||||
await render(hbs`{{app-name}}`);
|
||||
assert.equal(this.element.textContent.trim(), 'Boundary');
|
||||
});
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
|
||||
module('Integration | Helper | company-copyright', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders', async function (assert) {
|
||||
assert.expect(1);
|
||||
const currentYear = new Date().getFullYear();
|
||||
await render(hbs`{{company-copyright}}`);
|
||||
assert.equal(this.element.textContent.trim(), `© ${currentYear} HashiCorp`);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
|
||||
module('Integration | Helper | company-name', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders', async function (assert) {
|
||||
assert.expect(1);
|
||||
await render(hbs`{{company-name}}`);
|
||||
assert.equal(this.element.textContent.trim(), 'HashiCorp');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
import config from 'ember-get-config';
|
||||
|
||||
module('Integration | Helper | doc-url', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders a URL generated from the documentation config', async function(assert) {
|
||||
assert.expect(3);
|
||||
const baseURL = config.documentation.baseURL;
|
||||
const path = config.documentation.topics.account;
|
||||
await render(hbs`{{doc-url 'topics.account'}}`);
|
||||
assert.ok(baseURL);
|
||||
assert.ok(path);
|
||||
assert.equal(this.element.textContent.trim(), `${baseURL}${path}`);
|
||||
});
|
||||
|
||||
test('it throws an error if the specified documentation path cannot be found', async function(assert) {
|
||||
assert.expect(2);
|
||||
const helper = this.owner.lookup('helper:doc-url');
|
||||
assert.ok(helper.compute(['topics.account']), 'Specified document exists.')
|
||||
assert.throws(() => {
|
||||
helper.compute(['no.such.doc']);
|
||||
}, 'Specified documentation was not found, so the helper threw an error.');
|
||||
});
|
||||
|
||||
});
|
||||
@@ -7,6 +7,7 @@ save: Save
|
||||
manage: Manage
|
||||
help: Need help?
|
||||
back: Back
|
||||
get-topic-help: Get help for this topic
|
||||
copy-to-clipboard: Copy to Clipboard
|
||||
authenticate: Authenticate
|
||||
deauthenticate: Deauthenticate
|
||||
|
||||
@@ -16,15 +16,9 @@ titles:
|
||||
empty-set: No Items Yet
|
||||
global: Global
|
||||
actions: Actions
|
||||
documentation: Documentation
|
||||
descriptions:
|
||||
empty-set: There are no items to display yet. You may be able to add items or try back later.
|
||||
questions:
|
||||
delete-confirm: Are you sure you want to delete this resource?
|
||||
remove-confirm: Are you sure you want to remove this association?
|
||||
hashicorp:
|
||||
title: HashiCorp
|
||||
copyright: © 2020 HashiCorp
|
||||
product: Boundary v0.1
|
||||
documentation:
|
||||
title: Documentation
|
||||
link: https://boundaryproject.io
|
||||
Reference in new issue
Block a user