Merge pull request #47 from hashicorp/chore-breadcrumbs
Integrate breadcrumbs with core UI
This commit is contained in:
28 files changed
+402
-81
No files matched your search
@@ -13,7 +13,7 @@
|
||||
|
||||
.rose-layout-global-body {
|
||||
grid-area: body;
|
||||
margin: 0 sizing.rems(m);
|
||||
padding: sizing.rems(l);
|
||||
}
|
||||
|
||||
.rose-layout-global-header {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
color: var(--color);
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
|
||||
&:hover {
|
||||
--color: var(--action);
|
||||
|
||||
Whitespace-only changes.
@@ -0,0 +1,15 @@
|
||||
{{#if this.breadCrumbs}}
|
||||
<Rose::Nav::Breadcrumbs as |crumbs|>
|
||||
{{#each this.breadCrumbs as |crumb|}}
|
||||
{{#if crumb.model}}
|
||||
<crumbs.link @route={{crumb.path}} @model={{crumb.model}}>
|
||||
{{crumb.label}}
|
||||
</crumbs.link>
|
||||
{{else}}
|
||||
<crumbs.link @route={{crumb.path}}>
|
||||
{{crumb.label}}
|
||||
</crumbs.link>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</Rose::Nav::Breadcrumbs>
|
||||
{{/if}}
|
||||
Whitespace-only changes.
@@ -0,0 +1,19 @@
|
||||
import Controller from '@ember/controller';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class OrgsOrgProjectsController extends Controller {
|
||||
|
||||
// =services
|
||||
|
||||
@service intl;
|
||||
|
||||
// =attributes
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
get breadCrumb() {
|
||||
return this.intl.t('resources.projects');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import Controller from '@ember/controller';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class OrgsOrgProjectsNewController extends Controller {
|
||||
|
||||
// =services
|
||||
|
||||
@service intl;
|
||||
|
||||
// =attributes
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
get breadCrumb() {
|
||||
return this.intl.t('titles.new');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import Controller from '@ember/controller';
|
||||
import { alias } from '@ember/object/computed';
|
||||
|
||||
export default class OrgsOrgProjectsProjectController extends Controller {
|
||||
|
||||
// =attributes
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
@alias('model.name') breadCrumb;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import Controller from '@ember/controller';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class OrgsOrgProjectsProjectHostCatalogsController extends Controller {
|
||||
|
||||
// =services
|
||||
|
||||
@service intl;
|
||||
|
||||
// =attributes
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
get breadCrumb() {
|
||||
return this.intl.t('resources.host-catalogs');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import Controller from '@ember/controller';
|
||||
import { alias } from '@ember/object/computed';
|
||||
|
||||
export default class OrgsOrgProjectsProjectHostCatalogsHostCatalogController extends Controller {
|
||||
|
||||
// =attributes
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
@alias('model.name') breadCrumb;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import Controller from '@ember/controller';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class OrgsOrgProjectsProjectHostCatalogsNewController extends Controller {
|
||||
|
||||
// =services
|
||||
|
||||
@service intl;
|
||||
|
||||
// =attributes
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
get breadCrumb() {
|
||||
return this.intl.t('titles.new');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,31 +18,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.project-overview,
|
||||
.project-new {
|
||||
.breadcrumbs,
|
||||
.breadcrumbs a {
|
||||
color: #6f7682;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.project-new-form {
|
||||
.form-actions {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
position: relative;
|
||||
float: right;
|
||||
|
||||
button {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
input,
|
||||
textarea {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
<Rose::Layout::Page class="projects-overview" as |page|>
|
||||
<Rose::Layout::Page as |page|>
|
||||
|
||||
<page.breadcrumbs>
|
||||
<BreadCrumbs />
|
||||
</page.breadcrumbs>
|
||||
|
||||
<page.header>
|
||||
<h2>{{t "resources.projects"}}</h2>
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
<Rose::Layout::Page class="project-new" as |page|>
|
||||
<Rose::Layout::Page as |page|>
|
||||
<page.breadcrumbs>
|
||||
<div class="breadcrumbs">
|
||||
<LinkTo @route='orgs.org.projects'>{{t "resources.projects"}}</LinkTo>
|
||||
>
|
||||
<LinkTo @route='orgs.org.projects.new'>{{t "titles.new"}}</LinkTo>
|
||||
</div>
|
||||
<BreadCrumbs />
|
||||
</page.breadcrumbs>
|
||||
|
||||
<page.header>
|
||||
@@ -16,7 +12,6 @@
|
||||
@model={{@model}}
|
||||
@submit={{route-action "save" @model}}
|
||||
@cancel={{route-action "cancel" @model}}
|
||||
class="project-new-form"
|
||||
/>
|
||||
</page.body>
|
||||
</Rose::Layout::Page>
|
||||
+24
-15
@@ -1,18 +1,27 @@
|
||||
<div class="project-overview">
|
||||
<Rose::Layout::Page as |page|>
|
||||
|
||||
<h2>{{t "resources.host-catalog"}}</h2>
|
||||
<page.breadcrumbs>
|
||||
<BreadCrumbs />
|
||||
</page.breadcrumbs>
|
||||
|
||||
<Form::HostCatalog
|
||||
@model={{@model}}
|
||||
@submit={{route-action "save" @model}}
|
||||
@cancel={{route-action "cancel" @model}}
|
||||
/>
|
||||
<page.header>
|
||||
<h2>{{t "resources.host-catalog"}}</h2>
|
||||
</page.header>
|
||||
|
||||
<Rose::Button
|
||||
@style="warning"
|
||||
@disabled={{@model.canSave}}
|
||||
{{on "click" (route-action "delete" @model)}}
|
||||
>
|
||||
{{t 'actions.delete'}}
|
||||
</Rose::Button>
|
||||
</div>
|
||||
<page.body>
|
||||
<Form::HostCatalog
|
||||
@model={{@model}}
|
||||
@submit={{route-action "save" @model}}
|
||||
@cancel={{route-action "cancel" @model}}
|
||||
/>
|
||||
|
||||
<Rose::Button
|
||||
@style="warning"
|
||||
@disabled={{@model.canSave}}
|
||||
{{on "click" (route-action "delete" @model)}}
|
||||
>
|
||||
{{t 'actions.delete'}}
|
||||
</Rose::Button>
|
||||
</page.body>
|
||||
|
||||
</Rose::Layout::Page>
|
||||
@@ -1,4 +1,9 @@
|
||||
<Rose::Layout::Page class="host-catalogs" as |page|>
|
||||
<Rose::Layout::Page as |page|>
|
||||
|
||||
<page.breadcrumbs>
|
||||
<BreadCrumbs />
|
||||
</page.breadcrumbs>
|
||||
|
||||
<page.header>
|
||||
<h2>{{t 'resources.host-catalogs'}}</h2>
|
||||
</page.header>
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
<Rose::Layout::Page as |page|>
|
||||
|
||||
<page.breadcrumbs>
|
||||
<BreadCrumbs />
|
||||
</page.breadcrumbs>
|
||||
|
||||
<page.header>
|
||||
<h2>{{t "titles.new"}}</h2>
|
||||
</page.header>
|
||||
|
||||
@@ -1,30 +1,35 @@
|
||||
<div class="project-overview">
|
||||
<div class="breadcrumbs">
|
||||
<LinkTo @route='orgs.org.projects'>{{t "resources.projects"}}</LinkTo>
|
||||
<Rose::Layout::Page as |page|>
|
||||
|
||||
<page.breadcrumbs>
|
||||
<BreadCrumbs />
|
||||
</page.breadcrumbs>
|
||||
|
||||
<page.header>
|
||||
<h2>{{t "resources.project"}}</h2>
|
||||
</page.header>
|
||||
|
||||
<page.navigation>
|
||||
<Rose::Nav::Tabs as |nav|>
|
||||
<nav.link @route='orgs.org.projects.project'>
|
||||
{{t 'titles.settings'}}
|
||||
</nav.link>
|
||||
</Rose::Nav::Tabs>
|
||||
</page.navigation>
|
||||
|
||||
<page.body>
|
||||
<Form::Project
|
||||
@model={{@model}}
|
||||
@submit={{route-action "save" @model}}
|
||||
@cancel={{route-action "cancel" @model}}
|
||||
/>
|
||||
|
||||
<Rose::Button
|
||||
@style="warning"
|
||||
@disabled={{@model.canSave}}
|
||||
{{on "click" (route-action "delete" @model)}}
|
||||
>
|
||||
<LinkTo @route='orgs.org.projects.project'>{{@model.name}}</LinkTo>
|
||||
</div>
|
||||
{{t 'actions.delete'}}
|
||||
</Rose::Button>
|
||||
</page.body>
|
||||
|
||||
<h2>{{t "resources.project"}}</h2>
|
||||
|
||||
<Rose::Nav::Tabs as |nav|>
|
||||
<nav.link @route='orgs.org.projects.project'>
|
||||
{{t 'titles.settings'}}
|
||||
</nav.link>
|
||||
</Rose::Nav::Tabs>
|
||||
|
||||
<Form::Project
|
||||
@model={{@model}}
|
||||
@submit={{route-action "save" @model}}
|
||||
@cancel={{route-action "cancel" @model}}
|
||||
class="project-new-form"
|
||||
/>
|
||||
|
||||
<Rose::Button
|
||||
@style="warning"
|
||||
@disabled={{@model.canSave}}
|
||||
{{on "click" (route-action "delete" @model)}}
|
||||
>
|
||||
{{t 'actions.delete'}}
|
||||
</Rose::Button>
|
||||
</div>
|
||||
</Rose::Layout::Page>
|
||||
@@ -38,6 +38,7 @@
|
||||
"doctoc": "^1.4.0",
|
||||
"ember-a11y-testing": "^3.0.0",
|
||||
"ember-auto-import": "^1.5.3",
|
||||
"ember-breadcrumbs": "^0.2.3",
|
||||
"ember-cli": "~3.16.1",
|
||||
"ember-cli-app-version": "^3.2.0",
|
||||
"ember-cli-babel": "^7.17.2",
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { visit, currentURL, click, find, findAll } from '@ember/test-helpers';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
|
||||
module('Acceptance | breadcrumbs', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
setupMirage(hooks);
|
||||
|
||||
let project;
|
||||
let hostCatalog;
|
||||
let projectsURL;
|
||||
let newProjectURL;
|
||||
let projectURL;
|
||||
let hostCatalogsURL;
|
||||
let newHostCatalogURL;
|
||||
let hostCatalogURL;
|
||||
|
||||
let crumbPath;
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
project = this.server.create('project');
|
||||
hostCatalog = this.server.create('host-catalog');
|
||||
projectsURL = `/orgs/1/projects`;
|
||||
newProjectURL = `${projectsURL}/new`;
|
||||
projectURL = `${projectsURL}/${project.id}`;
|
||||
hostCatalogsURL = `${projectURL}/host-catalogs`;
|
||||
newHostCatalogURL = `${hostCatalogsURL}/new`;
|
||||
hostCatalogURL = `${hostCatalogsURL}/${hostCatalog.id}`;
|
||||
});
|
||||
|
||||
test('can navigate via breadcrumbs to projects from project', async function (assert) {
|
||||
assert.expect(3);
|
||||
await visit(projectURL);
|
||||
assert.equal(currentURL(), projectURL, 'We begin on th expected page.');
|
||||
crumbPath = new URL(find('.rose-nav-breadcrumbs-link:last-child').href).pathname;
|
||||
assert.equal(crumbPath, projectURL, 'Last crumb has expected URL.');
|
||||
await click(find('.rose-nav-breadcrumbs-link:first-child'));
|
||||
assert.equal(currentURL(), '/orgs/1/projects', 'After navigating via breadcrumbs, we are one level up.');
|
||||
});
|
||||
|
||||
test('can navigate via breadcrumbs to projects from new project', async function (assert) {
|
||||
assert.expect(3);
|
||||
await visit(newProjectURL);
|
||||
assert.equal(currentURL(), newProjectURL, 'We begin on th expected page.');
|
||||
crumbPath = new URL(find('.rose-nav-breadcrumbs-link:last-child').href).pathname;
|
||||
assert.equal(crumbPath, newProjectURL, 'Last crumb has expected URL.');
|
||||
await click(find('.rose-nav-breadcrumbs-link:first-child'));
|
||||
assert.equal(currentURL(), '/orgs/1/projects', 'After navigating via breadcrumbs, we are one level up.');
|
||||
});
|
||||
|
||||
test('can navigate via breadcrumbs to project from host catalogs', async function (assert) {
|
||||
assert.expect(3);
|
||||
await visit(hostCatalogsURL);
|
||||
assert.equal(currentURL(), hostCatalogsURL, 'We begin on th expected page.');
|
||||
crumbPath = new URL(find('.rose-nav-breadcrumbs-link:last-child').href).pathname;
|
||||
assert.equal(crumbPath, hostCatalogsURL, 'Last crumb has expected URL.');
|
||||
await click(findAll('.rose-nav-breadcrumbs-link')[1]);
|
||||
assert.equal(currentURL(), projectURL, 'After navigating via breadcrumbs, we are one level up.');
|
||||
});
|
||||
|
||||
test('can navigate via breadcrumbs to host catalogs from host catalog', async function (assert) {
|
||||
assert.expect(3);
|
||||
await visit(hostCatalogURL);
|
||||
assert.equal(currentURL(), hostCatalogURL, 'We begin on th expected page.');
|
||||
crumbPath = new URL(find('.rose-nav-breadcrumbs-link:last-child').href).pathname;
|
||||
assert.equal(crumbPath, hostCatalogURL, 'Last crumb has expected URL.');
|
||||
await click(findAll('.rose-nav-breadcrumbs-link')[2]);
|
||||
assert.equal(currentURL(), hostCatalogsURL, 'After navigating via breadcrumbs, we are one level up.');
|
||||
});
|
||||
|
||||
test('can navigate via breadcrumbs to host catalogs from new host catalog', async function (assert) {
|
||||
assert.expect(3);
|
||||
await visit(newHostCatalogURL);
|
||||
assert.equal(currentURL(), newHostCatalogURL, 'We begin on th expected page.');
|
||||
crumbPath = new URL(find('.rose-nav-breadcrumbs-link:last-child').href).pathname;
|
||||
assert.equal(crumbPath, newHostCatalogURL, 'Last crumb has expected URL.');
|
||||
await click(findAll('.rose-nav-breadcrumbs-link')[2]);
|
||||
assert.equal(currentURL(), hostCatalogsURL, 'After navigating via breadcrumbs, we are one level up.');
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,47 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render, find, findAll } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
|
||||
module('Integration | Component | bread-crumbs', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders', async function(assert) {
|
||||
this.set('breadCrumbs', [{label: 'Level 1'}]);
|
||||
|
||||
await render(hbs`<BreadCrumbs @breadCrumbs={{this.breadCrumbs}} />`);
|
||||
// Default index route
|
||||
assert.ok(find('.rose-nav-breadcrumbs a').href.match(/#$/));
|
||||
assert.equal(find('.rose-nav-breadcrumbs').textContent.trim(), 'Level 1');
|
||||
});
|
||||
|
||||
test('it renders with model', async function(assert) {
|
||||
let store = this.owner.lookup('service:store');
|
||||
let project = store.createRecord('project', {id: '1234'});
|
||||
|
||||
this.set('breadCrumbs', [{
|
||||
label: 'Level 2',
|
||||
path: 'orgs.org.projects.project',
|
||||
model: project
|
||||
}]);
|
||||
|
||||
await render(hbs`<BreadCrumbs @breadCrumbs={{this.breadCrumbs}} />`);
|
||||
assert.equal(find('.rose-nav-breadcrumbs').textContent.trim(), 'Level 2');
|
||||
});
|
||||
|
||||
test('it renders multiple breadCrumbs', async function(assert) {
|
||||
this.set('breadCrumbs', [{
|
||||
label: 'Level 1'
|
||||
},{
|
||||
label: 'Level 2'
|
||||
}, {
|
||||
label: 'Level 3'
|
||||
}]);
|
||||
|
||||
await render(hbs`<BreadCrumbs @breadCrumbs={{this.breadCrumbs}} />`);
|
||||
assert.equal(findAll('a').length, 3);
|
||||
assert.equal(findAll('a')[0].textContent.trim(), 'Level 1');
|
||||
assert.equal(findAll('a')[1].textContent.trim(), 'Level 2');
|
||||
assert.equal(findAll('a')[2].textContent.trim(), 'Level 3');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Controller | orgs/org/projects', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function(assert) {
|
||||
let controller = this.owner.lookup('controller:orgs/org/projects');
|
||||
assert.ok(controller);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Controller | orgs/org/projects/new', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function(assert) {
|
||||
let controller = this.owner.lookup('controller:orgs/org/projects/new');
|
||||
assert.ok(controller);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Controller | orgs/org/projects/project', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function(assert) {
|
||||
let controller = this.owner.lookup('controller:orgs/org/projects/project');
|
||||
assert.ok(controller);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Controller | orgs/org/projects/project/host-catalogs', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function(assert) {
|
||||
let controller = this.owner.lookup('controller:orgs/org/projects/project/host-catalogs');
|
||||
assert.ok(controller);
|
||||
});
|
||||
});
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Controller | orgs/org/projects/project/host-catalogs/host-catalog', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function(assert) {
|
||||
let controller = this.owner.lookup('controller:orgs/org/projects/project/host-catalogs/host-catalog');
|
||||
assert.ok(controller);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Controller | orgs/org/projects/project/host-catalogs/new', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function(assert) {
|
||||
let controller = this.owner.lookup('controller:orgs/org/projects/project/host-catalogs/new');
|
||||
assert.ok(controller);
|
||||
});
|
||||
});
|
||||
@@ -7058,6 +7058,13 @@ ember-auto-import@^1.2.15, ember-auto-import@^1.2.19, ember-auto-import@^1.5.3:
|
||||
walk-sync "^0.3.3"
|
||||
webpack "~4.28"
|
||||
|
||||
ember-breadcrumbs@^0.2.3:
|
||||
version "0.2.3"
|
||||
resolved "https://registry.yarnpkg.com/ember-breadcrumbs/-/ember-breadcrumbs-0.2.3.tgz#a95e02b73fdc2150f3d552e84a479d6dc96a8b31"
|
||||
integrity sha512-QUiPXwAlTeiXykF5RnyilEgcj9NSFGciAawktImv6V6Lq9sNfmJHZAYZXAg8U/wA1tw7CG0fQl991K+jtuBTKQ==
|
||||
dependencies:
|
||||
ember-cli-babel "^7.10.0"
|
||||
|
||||
ember-cli-addon-docs-yuidoc@^0.2.3:
|
||||
version "0.2.4"
|
||||
resolved "https://registry.yarnpkg.com/ember-cli-addon-docs-yuidoc/-/ember-cli-addon-docs-yuidoc-0.2.4.tgz#dd8f5cf4982e8492b255fb677e9d771925e27dce"
|
||||
|
||||
Reference in new issue
Block a user