chore: 🤖 Introduce main content header rose component

Main content header component contains a title(basic & dropdown) with
actions.
This commit is contained in:
Susmitha Girumala committed 2020-06-05 14:45:04 -04:00
1 parent ff3522ea8c
commit 6b09b643ab
15 files changed
+345 -7

No files matched your search

@@ -0,0 +1,7 @@
import Component from '@ember/component';
import layout from '../../../../templates/components/rose/layout/main/header';
export default Component.extend({
layout,
tagName: '',
});
@@ -0,0 +1,66 @@
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|Main" component="Header" />
# Layout | Main Header
A header component for main layout component with ability to configure title and action buttons. Title can be configured to be a either a dropdown component or a simple heading title. The title dropdown is `Rose::Header::Dropdown` component where as all header actions are `Rose::Button` components.
```html
<Rose::Layout::Main::Header as |header|>
<header.title as |title|>
<title.dropdown @text="Page title" as |dropdown|>
<dropdown.link @route="index">Menu item</dropdown.link>
<dropdown.link @route="about">Menu item</dropdown.link>
<dropdown.button>Button menu item</dropdown.button>
</title.dropdown>
</header.title>
<header.action @style="secondary">Secondary</header.action>
<header.action @style="primary">Primary</header.action>
</Rose::Layout::Main::Header>
```
<Preview>
<Story name="Main Header">{{
template: hbs`
<Rose::Layout::Main::Header as |header|>
<header.title as |title|>
<title.dropdown
@text={{pagetitle}}
@icon={{dropdownIcon}} as |dropdown|>
<dropdown.link @route="index">Menu item</dropdown.link>
<dropdown.link @route="about">Menu item</dropdown.link>
<dropdown.button>Button menu item</dropdown.button>
</title.dropdown>
</header.title>
<header.action @style="secondary">Secondary</header.action>
<header.action @style="primary">Primary</header.action>
</Rose::Layout::Main::Header>
`,
context: {
pagetitle: text('page title', 'Page Section Title'),
dropdownIcon: select(
'dropdown icon',
{
'user-square-fill': 'user-square-fill',
'check-circle-outline': 'check-circle-outline',
'copy-action': 'copy-action',
'unfold-more': 'unfold-more',
'unfold-less': 'unfold-less',
},
'user-square-fill'
),
},
}}</Story>
</Preview>
Main page title can configured to be a basic title.
```html
<Rose::Layout::Main::Header as |header|>
<header.title @text="Page title" @icon="user-square-fill"/>
</Rose::Layout::Main::Header>
```
@@ -0,0 +1,7 @@
import Component from '@ember/component';
import layout from '../../../../../templates/components/rose/layout/main/header/title';
export default Component.extend({
layout,
tagName: '',
});
@@ -1,22 +1,32 @@
@use 'sass:map';
@use '../../variables/sizing';
@use '../../utilities/type';
@use '../../utilities/dropdown';
$dropdown-chevron: (
'default':
"data:image/svg+xml;utf-8,<svg viewBox='0 0 24 24' fill='%23ebeef2' xmlns='http://www.w3.org/2000/svg'><path fill-rule='evenodd' clip-rule='evenodd' d='M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6-1.41-1.41z'></path></svg>",
@include dropdown.dropdown-style(
'light',
(
color: var(--white),
chevron: 'light',
)
);
@include dropdown.dropdown-style(
'dark',
(
color: var(--black),
chevron: 'dark',
)
);
.rose-header-dropdown {
position: relative;
display: inline-block;
.rose-header-dropdown-trigger {
cursor: default;
overflow: hidden;
display: flex;
align-items: center;
color: var(--white);
background: url(map.get($dropdown-chevron, default)) no-repeat right center;
background-size: sizing.rems(m);
margin-right: sizing.rems(s);
padding-right: sizing.rems(l) - sizing.rems(xxs);
@@ -68,6 +78,7 @@ $dropdown-chevron: (
background: var(--subtle);
box-shadow: 0 $xxs $xxs rgba(var(--stark-components), var(--opacity-3)),
0 $xxs $xs rgba(var(--stark-components), var(--opacity-4));
z-index: 99;
}
&.dropdown-right {
@@ -1,6 +1,8 @@
@use '../../variables/sizing';
@use '../../variables/media';
@import 'main';
.rose-layout-page {
display: grid;
grid-template-areas:
@@ -0,0 +1 @@
@import 'header';
@@ -0,0 +1,75 @@
@use '../../../../utilities/type' as typography;
@use '../../../../variables/sizing';
@use '../../../../variables/media';
.rose-layout-main-header {
display: grid;
grid-template-areas: 'title title actions';
grid-template-columns: 1fr auto;
.rose-layout-main-header-title {
display: inline-block;
grid-area: title;
.rose-layout-main-header-title-text {
display: inline-block;
color: var(--stark);
}
.rose-layout-main-header-title-icon {
vertical-align: sub;
svg {
width: sizing.rems(l);
width: sizing.rems(l);
}
}
}
.rose-header-dropdown {
.rose-header-dropdown-trigger-text {
// Apply heading level 1 style to text to fit page section style spec
// Duplicate of h1 style defined in typography
@include typography.type(h1, bold);
margin-top: sizing.rems(m);
margin-bottom: sizing.rems(xs);
margin-right: sizing.rems(xs);
}
.rose-header-dropdown-trigger {
background-size: sizing.rems(l);
background-position: right (sizing.rems(m) + sizing.rems(s));
}
.rose-icon > svg {
vertical-align: sub;
}
}
.rose-layout-main-header-actions {
grid-area: actions;
display: flex;
align-items: center;
justify-content: flex-end;
// Remove margin to align action buttons with h1 text
button {
margin-bottom: 0;
}
}
}
@media (max-width: media.width(small)) {
.rose-layout-main-header {
grid-template-columns: 1fr;
grid-template-rows: 1fr auto;
grid-template-areas:
'title'
'actions';
.rose-layout-main-header-actions {
flex-wrap: wrap;
}
}
}
@@ -0,0 +1,18 @@
@use 'sass:map';
$dropdown-chevron: (
'light':
"data:image/svg+xml;utf-8,<svg viewBox='0 0 24 24' fill='%23ebeef2' xmlns='http://www.w3.org/2000/svg'><path fill-rule='evenodd' clip-rule='evenodd' d='M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6-1.41-1.41z'></path></svg>",
'dark':
"data:image/svg+xml;utf-8,<svg viewBox='0 0 24 24' fill='%231b1f26' xmlns='http://www.w3.org/2000/svg'><path fill-rule='evenodd' clip-rule='evenodd' d='M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6-1.41-1.41z'></path></svg>",
);
@mixin dropdown-style($name, $default) {
.rose-header-dropdown-#{$name} {
.rose-header-dropdown-trigger {
color: map.get($default, color);
background: url(map.get($dropdown-chevron, map.get($default, chevron)))
no-repeat right center;
}
}
}
@@ -1,6 +1,6 @@
<details
...attributes
class="rose-header-dropdown{{if @alignRight " dropdown-right"}}"
class="rose-header-dropdown{{if @alignRight " dropdown-right"}} {{if @style (concat "rose-header-dropdown-" @style) "rose-header-dropdown-light"}}"
{{on-click-outside this.close}}
>
@@ -0,0 +1,11 @@
<div ...attributes class="rose-layout-main-header" >
{{yield (hash
title=(component 'rose/layout/main/header/title')
)}}
<div class="rose-layout-main-header-actions">
{{yield (hash
action=(component 'rose/button')
)}}
</div>
</div>
@@ -0,0 +1,19 @@
<div ...attributes
class="rose-layout-main-header-title"
>
{{#if (has-block)}}
{{yield (hash
dropdown=(component 'rose/header/dropdown' style='dark')
)}}
{{else}}
<section class="rose-layout-main-header-title-section">
{{#if @icon}}
<span class="rose-layout-main-header-title-icon">{{inline-svg @icon}}</span>
{{/if}}
{{#if @text}}
<h1 class="rose-layout-main-header-title-text">{{@text}}</h1>
{{/if}}
</section>
{{/if}}
</div>
@@ -0,0 +1 @@
export { default } from 'rose/components/rose/layout/main/header';
@@ -0,0 +1 @@
export { default } from 'rose/components/rose/layout/main/header/title';
@@ -0,0 +1,60 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, find, findAll } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
module('Integration | Component | rose/layout/main/header', function (hooks) {
setupRenderingTest(hooks);
test('it renders', async function (assert) {
await render(hbs`<Rose::Layout::Main::Header />`);
assert.ok(find('.rose-layout-main-header'));
});
test('it renders with title', async function (assert) {
await render(hbs`<Rose::Layout::Main::Header as |header|>
<header.title @text="header title" />
</Rose::Layout::Main::Header>`);
assert.ok(find('.rose-layout-main-header-title'));
assert.equal(
find('.rose-layout-main-header-title').textContent.trim(),
'header title'
);
});
test('it renders with dropdown title', async function (assert) {
await render(hbs`<Rose::Layout::Main::Header as |header|>
<header.title as |title|>
<title.dropdown @text="Dropdown title" />
</header.title>
</Rose::Layout::Main::Header>`);
assert.ok(find('.rose-header-dropdown'));
assert.equal(
find('.rose-layout-main-header-title').textContent.trim(),
'Dropdown title'
);
});
test('it renders with dropdown title menu', async function (assert) {
await render(hbs`<Rose::Layout::Main::Header as |header|>
<header.title as |title|>
<title.dropdown as |dropdown|>
<dropdown.link @route="index">Menu item</dropdown.link>
<dropdown.button>Button menu item</dropdown.button>
</title.dropdown>
</header.title>
</Rose::Layout::Main::Header>`);
assert.ok(find('.rose-header-dropdown'));
assert.equal(findAll('a').length, 1);
assert.equal(findAll('button').length, 1);
});
test('it renders with actions', async function (assert) {
await render(hbs`<Rose::Layout::Main::Header as |header|>
<header.action />
<header.action />
</Rose::Layout::Main::Header>`);
assert.ok(find('.rose-layout-main-header-actions'));
assert.ok(findAll('button').length, 2);
});
});
@@ -0,0 +1,59 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, click, find, findAll } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
module('Integration | Component | rose/layout/main/header/title', function (
hooks
) {
setupRenderingTest(hooks);
test('it renders', async function (assert) {
await render(hbs`<Rose::Layout::Main::Header::Title />`);
assert.ok(find('.rose-layout-main-header-title'));
});
test('it renders with title', async function (assert) {
await render(
hbs`<Rose::Layout::Main::Header::Title @text="header title" />`
);
assert.equal(
find('.rose-layout-main-header-title-text').textContent.trim(),
'header title'
);
});
test('it renders with title as dropdown', async function (assert) {
await render(hbs`<Rose::Layout::Main::Header::Title as |title|>
<title.dropdown @text="header title" as |dropdown|>
<dropdown.link @route="index">Menu item</dropdown.link>
<dropdown.link @route="index">Menu item</dropdown.link>
<dropdown.button>Button menu item</dropdown.button>
<dropdown.button>Button menu item</dropdown.button>
<dropdown.button>Button menu item</dropdown.button>
</title.dropdown>
</Rose::Layout::Main::Header::Title>`);
assert.equal(
find('.rose-header-dropdown-trigger').textContent.trim(),
'header title'
);
assert.equal(findAll('a').length, 2);
assert.equal(findAll('button').length, 3);
});
test('it opens title dropdown on click', async function (assert) {
await render(hbs`<Rose::Layout::Main::Header::Title id="title" as |title|>
<title.dropdown id="dropdown" as |dropdown|>
<dropdown.link @route="index">Menu item</dropdown.link>
<dropdown.link @route="index">Menu item</dropdown.link>
<dropdown.button>Button menu item</dropdown.button>
<dropdown.button>Button menu item</dropdown.button>
<dropdown.button>Button menu item</dropdown.button>
</title.dropdown>
</Rose::Layout::Main::Header::Title>`);
await click('summary');
assert.ok(find('#dropdown').open);
await click('#title');
assert.notOk(find('#dropdown').open);
});
});