Merge pull request #249 from hashicorp/populate-global-footer
Add global footer
This commit is contained in:
29 files changed
+328
-10
No files matched your search
@@ -0,0 +1,12 @@
|
||||
<LinkTo
|
||||
...attributes
|
||||
class="rose-footer-brand"
|
||||
title={{@text}}
|
||||
@route={{if @route @route "index"}}
|
||||
>
|
||||
|
||||
{{#if @logo}}
|
||||
{{inline-svg @logo}}
|
||||
{{/if}}
|
||||
|
||||
</LinkTo>
|
||||
@@ -0,0 +1,11 @@
|
||||
<span class="rose-footer-copyright">
|
||||
<span class="rose-footer-copyright-symbol">
|
||||
©
|
||||
</span>
|
||||
<span class="rose-footer-copyright-year">
|
||||
{{this.currentYear}}
|
||||
</span>
|
||||
<span class="rose-footer-copyright-text">
|
||||
{{@text}}
|
||||
</span>
|
||||
</span>
|
||||
@@ -0,0 +1,8 @@
|
||||
import Component from '@glimmer/component';
|
||||
|
||||
export default class RoseFooterCopyrightComponent extends Component {
|
||||
|
||||
// =attributes
|
||||
currentYear = new Date().getFullYear();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<footer
|
||||
...attributes
|
||||
class="rose-footer"
|
||||
>
|
||||
{{yield (hash
|
||||
nav=(component 'rose/footer/nav')
|
||||
brand=(component 'rose/footer/brand')
|
||||
text=(component "rose/anonymous" class="rose-footer-text")
|
||||
)}}
|
||||
</footer>
|
||||
@@ -0,0 +1,40 @@
|
||||
import { Meta, Story, Preview } from '@storybook/addon-docs/blocks';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { text } from '@storybook/addon-knobs';
|
||||
|
||||
<Meta title="Rose|Footer" component="Footer" />
|
||||
|
||||
# Footer
|
||||
|
||||
A footer component that allows four distinct sections to be configured: brand, nav, and text.
|
||||
|
||||
```html
|
||||
<Rose::Footer as |footer|>
|
||||
<footer.brand @logo="logo" @text="Product Name" />
|
||||
<footer.text>Text</footer.text>
|
||||
<footer.nav as |nav|>
|
||||
<nav.link @route="index">Section</nav.link>
|
||||
<nav.link @href="index">Section</nav.link>
|
||||
</footer.nav>
|
||||
</Rose::Footer>
|
||||
```
|
||||
|
||||
<Preview>
|
||||
<Story name="Basic">{{
|
||||
template: hbs`
|
||||
<Rose::Footer as |footer|>
|
||||
<footer.brand @logo="logo" @text={{brandName}} />
|
||||
<footer.text>{{footerText}}</footer.text>
|
||||
<footer.nav as |nav|>
|
||||
<nav.link @route="index">Section</nav.link>
|
||||
<nav.link @href="index">Section</nav.link>
|
||||
</footer.nav>
|
||||
</Rose::Footer>
|
||||
`,
|
||||
context: {
|
||||
brandName: text('brandName', 'Brand Name'),
|
||||
footerText: text('footerText', 'Text'),
|
||||
},
|
||||
}}</Story>
|
||||
</Preview>
|
||||
@@ -0,0 +1,5 @@
|
||||
<nav class="rose-footer-nav" aria-label="footer-nav">
|
||||
{{yield (hash
|
||||
link=(component 'rose/footer/nav/link')
|
||||
)}}
|
||||
</nav>
|
||||
@@ -0,0 +1,20 @@
|
||||
{{#if @href}}
|
||||
<a target="_blank"
|
||||
href={{@href}}
|
||||
class="rose-footer-nav-link"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{{yield}}
|
||||
</a>
|
||||
{{/if}}
|
||||
|
||||
{{#if @route}}
|
||||
<LinkTo
|
||||
...attributes
|
||||
@route={{@route}}
|
||||
@model={{@model}}
|
||||
class="rose-footer-nav-link"
|
||||
>
|
||||
{{yield}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
@@ -0,0 +1,11 @@
|
||||
<span class="rose-footer-product">
|
||||
<span class="rose-footer-product-text">
|
||||
{{@text}}
|
||||
</span>
|
||||
|
||||
{{#if @version}}
|
||||
<span class="rose-footer-product-version">
|
||||
v{{@version}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</span>
|
||||
@@ -2,5 +2,6 @@
|
||||
{{yield (hash
|
||||
header=(component "rose/anonymous" tagName="header" class="rose-layout-global-header")
|
||||
body=(component "rose/anonymous" class="rose-layout-global-body")
|
||||
footer=(component "rose/anonymous" class="rose-layout-global-footer")
|
||||
)}}
|
||||
</section>
|
||||
@@ -1,29 +1,29 @@
|
||||
import { Meta, Story, Preview } from '@storybook/addon-docs/blocks';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { text, boolean, select } from '@storybook/addon-knobs';
|
||||
|
||||
<Meta title="Rose|Layout|Global" component="Global" />
|
||||
|
||||
# Layout|Global
|
||||
|
||||
A top-level layout/positioning component that allows two distinct sections to
|
||||
be configured: header and body.
|
||||
A top-level layout/positioning component that allows three distinct sections to
|
||||
be configured: header, body, and footer.
|
||||
|
||||
```htmlbars
|
||||
```html
|
||||
<Rose::Layout::Global as |layout|>
|
||||
<layout.header />
|
||||
<layout.body />
|
||||
<layout.footer />
|
||||
</Rose::Layout::Global>
|
||||
```
|
||||
|
||||
<Preview>
|
||||
<Story name="Global">{{
|
||||
template: hbs`
|
||||
<Rose::Layout::Global as |layout|>
|
||||
<layout.header style="background: var(--brand-gray-5);">Header</layout.header>
|
||||
<layout.body style="background: var(--brand-gray-7);">Body</layout.body>
|
||||
</Rose::Layout::Global>
|
||||
<Rose::Layout::Global as |layout|>
|
||||
<layout.header style="background: var(--brand-gray-5);">Header</layout.header>
|
||||
<layout.body style="background: var(--brand-gray-7);">Body</layout.body>
|
||||
<layout.footer style="background: var(--brand-gray-5);">Footer</layout.footer>
|
||||
</Rose::Layout::Global>
|
||||
`,
|
||||
}}</Story>
|
||||
</Preview>
|
||||
@@ -2,6 +2,7 @@
|
||||
@import 'dropdown';
|
||||
@import 'form';
|
||||
@import 'header';
|
||||
@import 'footer';
|
||||
@import 'layout';
|
||||
@import 'icon';
|
||||
@import 'list';
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
@use '../../variables/sizing';
|
||||
|
||||
.rose-footer-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--ui-gray-starker-1);
|
||||
margin: sizing.rems(xs);
|
||||
|
||||
svg {
|
||||
$logoSize: sizing.rems(m);
|
||||
display: inline-block;
|
||||
width: $logoSize;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
@use '../../variables/sizing';
|
||||
@use '../../variables/media';
|
||||
@use '../../utilities/type';
|
||||
|
||||
@import 'brand';
|
||||
@import 'nav';
|
||||
@import 'text';
|
||||
@import 'link';
|
||||
|
||||
.rose-footer {
|
||||
@include type.type(xs);
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
color: var(--ui-gray-starker-1);
|
||||
height: sizing.rems(xxl) + sizing.rems(xxs);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
@use '../../variables/sizing';
|
||||
|
||||
.rose-footer-nav-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
text-decoration: underline;
|
||||
color: var(--ui-gray-starker-1);
|
||||
margin: 0 sizing.rems(xs);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
@use '../../variables/sizing';
|
||||
|
||||
.rose-footer-nav {
|
||||
grid-area: nav;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
margin-right: sizing.rems(s);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
@use '../../variables/sizing';
|
||||
|
||||
.rose-footer-text {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin: 0 sizing.rems(xs);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'rose/components/rose/footer';
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'rose/components/rose/footer/brand';
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'rose/components/rose/footer/copyright';
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'rose/components/rose/footer/nav';
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'rose/components/rose/footer/nav/link';
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'rose/components/rose/footer/product';
|
||||
@@ -0,0 +1,42 @@
|
||||
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 | rose/footer', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders', async function (assert) {
|
||||
assert.expect(4);
|
||||
await render(hbs`<Rose::Footer />`);
|
||||
assert.ok(find('.rose-footer'));
|
||||
assert.notOk(find('.rose-footer-nav'));
|
||||
assert.notOk(find('.rose-footer-text'));
|
||||
assert.notOk(find('.rose-footer-brand'));
|
||||
});
|
||||
|
||||
test('it renders with brand', async function (assert) {
|
||||
assert.expect(1);
|
||||
await render(hbs`<Rose::Footer as |footer| >
|
||||
<footer.brand />
|
||||
</Rose::Footer>`);
|
||||
assert.ok(find('.rose-footer-brand'));
|
||||
});
|
||||
|
||||
test('it renders with nav', async function (assert) {
|
||||
assert.expect(1);
|
||||
await render(hbs`<Rose::Footer as |footer| >
|
||||
<footer.nav />
|
||||
</Rose::Footer>`);
|
||||
assert.ok(find('.rose-footer-nav'));
|
||||
});
|
||||
|
||||
test('it renders with text', async function (assert) {
|
||||
assert.expect(1);
|
||||
await render(hbs`<Rose::Footer as |footer| >
|
||||
<footer.text />
|
||||
</Rose::Footer>`);
|
||||
assert.ok(find('.rose-footer-text'));
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
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 | rose/footer/nav', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders', async function (assert) {
|
||||
assert.expect(1);
|
||||
await render(hbs`<Rose::Footer::nav />`);
|
||||
assert.ok(find('.rose-footer-nav'));
|
||||
});
|
||||
|
||||
test('it renders nav external link elements', async function (assert) {
|
||||
assert.expect(1);
|
||||
await render(hbs`<Rose::Footer::nav as |nav| >
|
||||
<nav.link @href="index"/>
|
||||
<nav.link @route="about"/>
|
||||
</Rose::Footer::nav>`);
|
||||
assert.equal(findAll('a').length, 2);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
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 | rose/footer/nav/link', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders with @href', async function (assert) {
|
||||
assert.expect(2);
|
||||
await render(hbs`<Rose::Footer::Nav::Link @href="localhost" />`);
|
||||
assert.ok(find('a'));
|
||||
assert.ok(find('.rose-footer-nav-link'));
|
||||
});
|
||||
|
||||
test('it renders with @route', async function (assert) {
|
||||
assert.expect(2);
|
||||
await render(hbs`<Rose::Footer::Nav::Link @route="index" />`);
|
||||
assert.ok(find('a'));
|
||||
assert.ok(find('.rose-footer-nav-link'));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
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 | rose/footer/text', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders', async function (assert) {
|
||||
assert.expect(1);
|
||||
await render(hbs`<Rose::Footer as |footer|>
|
||||
<footer.text />
|
||||
</Rose::Footer>`);
|
||||
assert.ok(find('.rose-footer-text'));
|
||||
});
|
||||
|
||||
test('it renders content', async function (assert) {
|
||||
assert.expect(1);
|
||||
await render(hbs`<Rose::Footer as |footer|>
|
||||
<footer.text>content</footer.text>
|
||||
</Rose::Footer>`);
|
||||
assert.equal(find('.rose-footer-text').textContent.trim(), 'content');
|
||||
});
|
||||
});
|
||||
@@ -30,4 +30,11 @@ module('Integration | Component | rose/layout/global', function (hooks) {
|
||||
</Rose::Layout::Global>`);
|
||||
assert.ok(find('.rose-layout-global-body'));
|
||||
});
|
||||
|
||||
test('it renders with footer', async function (assert) {
|
||||
await render(hbs`<Rose::Layout::Global as |layout|>
|
||||
<layout.footer />
|
||||
</Rose::Layout::Global>`);
|
||||
assert.ok(find('.rose-layout-global-footer'));
|
||||
});
|
||||
});
|
||||
@@ -37,6 +37,17 @@
|
||||
{{outlet}}
|
||||
</layout.body>
|
||||
|
||||
<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.nav as |nav|>
|
||||
<nav.link @href={{t "hashicorp.documentation.link"}}>{{t "hashicorp.documentation.title"}}</nav.link>
|
||||
</footer.nav>
|
||||
</Rose::Footer>
|
||||
</layout.footer>
|
||||
|
||||
</Rose::Layout::Global>
|
||||
|
||||
<EmberNotify @closeAfter={{this.notifyTimeout}} as |message close|>
|
||||
|
||||
@@ -17,4 +17,11 @@ titles:
|
||||
global: Global
|
||||
actions: Actions
|
||||
descriptions:
|
||||
empty-set: There are no items to display yet. You may be able to add items or try back later.
|
||||
empty-set: There are no items to display yet. You may be able to add items or try back later.
|
||||
hashicorp:
|
||||
title: HashiCorp
|
||||
copyright: © 2020 HashiCorp
|
||||
product: Boundary v0.1
|
||||
documentation:
|
||||
title: Documentation
|
||||
link: https://boundaryproject.io
|
||||
Reference in new issue
Block a user