chore: 🤖 Introduce page layout/positioning component
This commit is contained in:
20 files changed
+428
-185
No files matched your search
@@ -0,0 +1,7 @@
|
||||
import Component from '@ember/component';
|
||||
import layout from '../../../templates/components/rose/layout/page';
|
||||
|
||||
export default Component.extend({
|
||||
layout,
|
||||
tagName: '',
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
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|Page" component="Page" />
|
||||
|
||||
# Layout|Page
|
||||
|
||||
A page layout/positioning component that allows three distinct sections to be configured: header, sidebar, and main.
|
||||
|
||||
```htmlbars
|
||||
<Rose::Layout::Page as |page|>
|
||||
<page.header />
|
||||
<page.sidebar />
|
||||
<page.main />
|
||||
</Rose::Layout::Page>
|
||||
```
|
||||
|
||||
<Preview>
|
||||
<Story name="Basic">{{
|
||||
template: hbs`
|
||||
<Rose::Layout::Page as |page|>
|
||||
<page.header style="background: var(--success);">Header</page.header>
|
||||
<page.sidebar style="background: var(--alert);">Sidebar</page.sidebar>
|
||||
<page.main style="background: var(--warn);">Main</page.main>
|
||||
</Rose::Layout::Page>
|
||||
`,
|
||||
}}</Story>
|
||||
</Preview>
|
||||
@@ -0,0 +1,7 @@
|
||||
import Component from '@ember/component';
|
||||
import layout from '../../../../templates/components/rose/layout/page/header';
|
||||
|
||||
export default Component.extend({
|
||||
layout,
|
||||
tagName: '',
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
import Component from '@ember/component';
|
||||
import layout from '../../../../templates/components/rose/layout/page/main';
|
||||
|
||||
export default Component.extend({
|
||||
layout,
|
||||
tagName: '',
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
import Component from '@ember/component';
|
||||
import layout from '../../../../templates/components/rose/layout/page/sidebar';
|
||||
|
||||
export default Component.extend({
|
||||
layout,
|
||||
tagName: '',
|
||||
});
|
||||
@@ -1,3 +1,4 @@
|
||||
@import 'button';
|
||||
@import 'form';
|
||||
@import 'header';
|
||||
@import 'layout';
|
||||
@@ -0,0 +1,34 @@
|
||||
@use '../../variables/media';
|
||||
|
||||
.rose-layout-page {
|
||||
display: grid;
|
||||
grid-template-areas:
|
||||
'header header header header'
|
||||
'sidebar main main main';
|
||||
grid-template-columns: 0.5fr repeat(3, 1fr);
|
||||
grid-template-rows: 0.1fr auto;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.rose-layout-header {
|
||||
grid-area: header;
|
||||
}
|
||||
|
||||
.rose-layout-main {
|
||||
grid-area: main;
|
||||
}
|
||||
|
||||
.rose-layout-sidebar {
|
||||
grid-area: sidebar;
|
||||
}
|
||||
|
||||
@media (max-width: media.width(small)) {
|
||||
.rose-layout-page {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-areas:
|
||||
'header'
|
||||
'sidebar'
|
||||
'main';
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<div ...attributes class="rose-layout-page">
|
||||
{{yield (hash
|
||||
header=(component 'rose/layout/page/header')
|
||||
main=(component 'rose/layout/page/main')
|
||||
sidebar=(component 'rose/layout/page/sidebar')
|
||||
)}}
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
<header ...attributes class="rose-layout-header" >
|
||||
{{yield}}
|
||||
</header>
|
||||
@@ -0,0 +1,3 @@
|
||||
<main ...attributes class="rose-layout-main">
|
||||
{{yield}}
|
||||
</main>
|
||||
@@ -0,0 +1,3 @@
|
||||
<aside ...attributes class="rose-layout-sidebar">
|
||||
{{yield}}
|
||||
</aside>
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'rose/components/rose/layout/page';
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'rose/components/rose/layout/page/header';
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'rose/components/rose/layout/page/main';
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'rose/components/rose/layout/page/sidebar';
|
||||
@@ -1,200 +1,205 @@
|
||||
<Rose::Header as |header|>
|
||||
<Rose::Layout::Page as |page|>
|
||||
<page.header>
|
||||
<Rose::Header as |header|>
|
||||
<header.brand @logo="logo" @text="Product Name" />
|
||||
|
||||
<header.brand @logo="logo" @text="Product Name" />
|
||||
|
||||
<header.nav as |nav|>
|
||||
<nav.dropdown @text="org-name" 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>
|
||||
</nav.dropdown>
|
||||
<nav.link @route="index">Section</nav.link>
|
||||
<nav.link @route="about">Section</nav.link>
|
||||
</header.nav>
|
||||
<header.nav as |nav|>
|
||||
<nav.dropdown @text="org-name" 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>
|
||||
</nav.dropdown>
|
||||
<nav.link @route="index">Section</nav.link>
|
||||
<nav.link @route="about">Section</nav.link>
|
||||
</header.nav>
|
||||
|
||||
|
||||
<header.utilities as |utilities|>
|
||||
<utilities.dropdown
|
||||
@text="user@example.net"
|
||||
@iconOnly={{true}}
|
||||
@icon="user-square-fill" as |dropdown|
|
||||
<header.utilities as |utilities|>
|
||||
<utilities.dropdown
|
||||
@text="user@example.net"
|
||||
@iconOnly={{true}}
|
||||
@icon="user-square-fill" 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>
|
||||
</utilities.dropdown>
|
||||
</header.utilities>
|
||||
|
||||
</Rose::Header>
|
||||
</page.header>
|
||||
<page.sidebar />
|
||||
<page.main>
|
||||
Dummy app index template
|
||||
|
||||
<h2>Icon</h2>
|
||||
<Rose::Icon @name="alert-circle-fill" />
|
||||
<Rose::Icon @name="alert-circle-fill" @size="medium" />
|
||||
<Rose::Icon @name="alert-circle-fill" @size="small" />
|
||||
|
||||
<h2>Button</h2>
|
||||
<Rose::Button @style="primary">Primary</Rose::Button>
|
||||
<br>
|
||||
<Rose::Button @style="primary" @disabled={{true}}>Primary</Rose::Button>
|
||||
<br>
|
||||
|
||||
<Rose::Button @style="secondary">Secondary</Rose::Button>
|
||||
<br>
|
||||
<Rose::Button @style="secondary" @disabled={{true}}>Secondary</Rose::Button>
|
||||
<br>
|
||||
|
||||
<h2>Form</h2>
|
||||
<Rose::Form::Select
|
||||
@label="Label"
|
||||
@helperText="Helper text"
|
||||
@onChange={{fn this.selectChange}}
|
||||
as |select|
|
||||
>
|
||||
<dropdown.link @route="index">Menu item</dropdown.link>
|
||||
<dropdown.link @route="about">Menu item</dropdown.link>
|
||||
<dropdown.button>Button menu item</dropdown.button>
|
||||
</utilities.dropdown>
|
||||
</header.utilities>
|
||||
<select.option>Choose an option</select.option>
|
||||
<select.option @value="value-1">Value 1</select.option>
|
||||
<select.option @value="value-2">Value 2</select.option>
|
||||
<select.option @value="value-3">Value 3</select.option>
|
||||
</Rose::Form::Select>
|
||||
|
||||
</Rose::Header>
|
||||
<Rose::Form::Select
|
||||
disabled={{true}}
|
||||
@label="Label"
|
||||
@helperText="Helper text"
|
||||
@onChange={{fn this.selectChange}}
|
||||
as |select|
|
||||
>
|
||||
<select.option>Choose an option</select.option>
|
||||
</Rose::Form::Select>
|
||||
|
||||
<Rose::Form::Input
|
||||
@label="Label"
|
||||
@value="Read only"
|
||||
readonly={{true}}
|
||||
/>
|
||||
|
||||
<Rose::Form::Input
|
||||
@label="Label"
|
||||
@helperText="Helper text"
|
||||
@value="Text"
|
||||
/>
|
||||
|
||||
<Rose::Form::Input
|
||||
@label="Label"
|
||||
@helperText="Helper text"
|
||||
@value="Text"
|
||||
@error={{true}}
|
||||
/>
|
||||
|
||||
<Rose::Form::Input
|
||||
@label="Label"
|
||||
@helperText="Helper text"
|
||||
@value="Text"
|
||||
disabled={{true}}
|
||||
/>
|
||||
|
||||
|
||||
Dummy app index template
|
||||
<Rose::Form::Textarea
|
||||
@label="Label"
|
||||
@value="Read only"
|
||||
readonly={{true}}
|
||||
/>
|
||||
|
||||
<h2>Icon</h2>
|
||||
<Rose::Icon @name="alert-circle-fill" />
|
||||
<Rose::Icon @name="alert-circle-fill" @size="medium" />
|
||||
<Rose::Icon @name="alert-circle-fill" @size="small" />
|
||||
<Rose::Form::Textarea
|
||||
@label="Label"
|
||||
@helperText="Helper text"
|
||||
@value="Text"
|
||||
/>
|
||||
|
||||
<h2>Button</h2>
|
||||
<Rose::Button @style="primary">Primary</Rose::Button>
|
||||
<br>
|
||||
<Rose::Button @style="primary" @disabled={{true}}>Primary</Rose::Button>
|
||||
<br>
|
||||
<Rose::Form::Textarea
|
||||
@label="Label"
|
||||
@helperText="Helper text"
|
||||
@value="Text"
|
||||
@error={{true}}
|
||||
/>
|
||||
|
||||
<Rose::Button @style="secondary">Secondary</Rose::Button>
|
||||
<br>
|
||||
<Rose::Button @style="secondary" @disabled={{true}}>Secondary</Rose::Button>
|
||||
<br>
|
||||
<Rose::Form::Textarea
|
||||
@label="Label"
|
||||
@helperText="Helper text"
|
||||
@value="Text"
|
||||
disabled={{true}}
|
||||
/>
|
||||
|
||||
<h2>Form</h2>
|
||||
<Rose::Form::Select
|
||||
@label="Label"
|
||||
@helperText="Helper text"
|
||||
@onChange={{fn this.selectChange}}
|
||||
as |select|
|
||||
>
|
||||
<select.option>Choose an option</select.option>
|
||||
<select.option @value="value-1">Value 1</select.option>
|
||||
<select.option @value="value-2">Value 2</select.option>
|
||||
<select.option @value="value-3">Value 3</select.option>
|
||||
</Rose::Form::Select>
|
||||
<h2>Checkbox</h2>
|
||||
<Rose::Form::Checkbox
|
||||
@id="checkbox-1"
|
||||
@label="Label"
|
||||
/>
|
||||
<Rose::Form::Checkbox
|
||||
@id="checkbox-2"
|
||||
@error={{true}}
|
||||
@label="Error"
|
||||
/>
|
||||
<Rose::Form::Checkbox
|
||||
@id="checkbox-3"
|
||||
@checked={{true}}
|
||||
@label="Checked"
|
||||
/>
|
||||
<Rose::Form::Checkbox
|
||||
@id="checkbox-4"
|
||||
@label="Disabled"
|
||||
disabled={{true}}
|
||||
/>
|
||||
<Rose::Form::Checkbox
|
||||
@id="checkbox-5"
|
||||
@checked={{true}}
|
||||
@label="Disabled + Checked"
|
||||
disabled={{true}}
|
||||
/>
|
||||
|
||||
<Rose::Form::Select
|
||||
disabled={{true}}
|
||||
@label="Label"
|
||||
@helperText="Helper text"
|
||||
@onChange={{fn this.selectChange}}
|
||||
as |select|
|
||||
>
|
||||
<select.option>Choose an option</select.option>
|
||||
</Rose::Form::Select>
|
||||
<h2>Radio field</h2>
|
||||
<Rose::Form::Radio::Radio
|
||||
@id="radio-1"
|
||||
@label="Label"
|
||||
@value="radio-1"
|
||||
/>
|
||||
<Rose::Form::Radio::Radio
|
||||
@id="radio-2"
|
||||
@error={{true}}
|
||||
@label="Error"
|
||||
@value="radio-2"
|
||||
/>
|
||||
<Rose::Form::Radio::Radio
|
||||
@id="radio-3"
|
||||
@label="Checked"
|
||||
/>
|
||||
|
||||
<Rose::Form::Input
|
||||
@label="Label"
|
||||
@value="Read only"
|
||||
readonly={{true}}
|
||||
/>
|
||||
<Rose::Form::Radio::Radio
|
||||
@id="radio-4"
|
||||
@label="Disabled"
|
||||
@disabled={{true}}
|
||||
@value="radio-4"
|
||||
/>
|
||||
<Rose::Form::Radio::Radio
|
||||
@id="radio-5"
|
||||
@label="Disabled + Checked"
|
||||
@value="radio-5"
|
||||
@variable="radio-5"
|
||||
@disabled={{true}}
|
||||
/>
|
||||
|
||||
<Rose::Form::Input
|
||||
@label="Label"
|
||||
@helperText="Helper text"
|
||||
@value="Text"
|
||||
/>
|
||||
<h2>Radio field group</h2>
|
||||
<Rose::Form::Radio::Group @name="color" @variable="green" as |radioGroup|>
|
||||
<radioGroup.radio
|
||||
@id="red-radio"
|
||||
@label="Red"
|
||||
@value="red"
|
||||
/>
|
||||
<radioGroup.radio
|
||||
@id="green-radio"
|
||||
@label="Green"
|
||||
@value="green"
|
||||
/>
|
||||
<radioGroup.radio
|
||||
@id="blue-radio"
|
||||
@label="Blue"
|
||||
@value="blue"
|
||||
/>
|
||||
</Rose::Form::Radio::Group>
|
||||
|
||||
<Rose::Form::Input
|
||||
@label="Label"
|
||||
@helperText="Helper text"
|
||||
@value="Text"
|
||||
@error={{true}}
|
||||
/>
|
||||
|
||||
<Rose::Form::Input
|
||||
@label="Label"
|
||||
@helperText="Helper text"
|
||||
@value="Text"
|
||||
disabled={{true}}
|
||||
/>
|
||||
|
||||
|
||||
<Rose::Form::Textarea
|
||||
@label="Label"
|
||||
@value="Read only"
|
||||
readonly={{true}}
|
||||
/>
|
||||
|
||||
<Rose::Form::Textarea
|
||||
@label="Label"
|
||||
@helperText="Helper text"
|
||||
@value="Text"
|
||||
/>
|
||||
|
||||
<Rose::Form::Textarea
|
||||
@label="Label"
|
||||
@helperText="Helper text"
|
||||
@value="Text"
|
||||
@error={{true}}
|
||||
/>
|
||||
|
||||
<Rose::Form::Textarea
|
||||
@label="Label"
|
||||
@helperText="Helper text"
|
||||
@value="Text"
|
||||
disabled={{true}}
|
||||
/>
|
||||
|
||||
<h2>Checkbox</h2>
|
||||
<Rose::Form::Checkbox
|
||||
@id="checkbox-1"
|
||||
@label="Label"
|
||||
/>
|
||||
<Rose::Form::Checkbox
|
||||
@id="checkbox-2"
|
||||
@error={{true}}
|
||||
@label="Error"
|
||||
/>
|
||||
<Rose::Form::Checkbox
|
||||
@id="checkbox-3"
|
||||
@checked={{true}}
|
||||
@label="Checked"
|
||||
/>
|
||||
<Rose::Form::Checkbox
|
||||
@id="checkbox-4"
|
||||
@label="Disabled"
|
||||
disabled={{true}}
|
||||
/>
|
||||
<Rose::Form::Checkbox
|
||||
@id="checkbox-5"
|
||||
@checked={{true}}
|
||||
@label="Disabled + Checked"
|
||||
disabled={{true}}
|
||||
/>
|
||||
|
||||
<h2>Radio field</h2>
|
||||
<Rose::Form::Radio::Radio
|
||||
@id="radio-1"
|
||||
@label="Label"
|
||||
@value="radio-1"
|
||||
/>
|
||||
<Rose::Form::Radio::Radio
|
||||
@id="radio-2"
|
||||
@error={{true}}
|
||||
@label="Error"
|
||||
@value="radio-2"
|
||||
/>
|
||||
<Rose::Form::Radio::Radio
|
||||
@id="radio-3"
|
||||
@label="Checked"
|
||||
/>
|
||||
|
||||
<Rose::Form::Radio::Radio
|
||||
@id="radio-4"
|
||||
@label="Disabled"
|
||||
@disabled={{true}}
|
||||
@value="radio-4"
|
||||
/>
|
||||
<Rose::Form::Radio::Radio
|
||||
@id="radio-5"
|
||||
@label="Disabled + Checked"
|
||||
@value="radio-5"
|
||||
@variable="radio-5"
|
||||
@disabled={{true}}
|
||||
/>
|
||||
|
||||
<h2>Radio field group</h2>
|
||||
<Rose::Form::Radio::Group @name="color" @variable="green" as |radioGroup|>
|
||||
<radioGroup.radio
|
||||
@id="red-radio"
|
||||
@label="Red"
|
||||
@value="red"
|
||||
/>
|
||||
<radioGroup.radio
|
||||
@id="green-radio"
|
||||
@label="Green"
|
||||
@value="green"
|
||||
/>
|
||||
<radioGroup.radio
|
||||
@id="blue-radio"
|
||||
@label="Blue"
|
||||
@value="blue"
|
||||
/>
|
||||
</Rose::Form::Radio::Group>
|
||||
</page.main>
|
||||
</Rose::Layout::Page>
|
||||
@@ -0,0 +1,47 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render, find } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
module('Integration | Component | rose/layout/page', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders', async function (assert) {
|
||||
await render(hbs`<Rose::Layout::Page />`);
|
||||
assert.ok(find('div'));
|
||||
assert.ok(find('.rose-layout-page'));
|
||||
});
|
||||
|
||||
test('it renders with attributes', async function (assert) {
|
||||
await render(hbs`<Rose::Layout::Page id="page"/>`);
|
||||
assert.ok(find('#page'));
|
||||
});
|
||||
|
||||
test('it renders with content', async function (assert) {
|
||||
await render(hbs`<Rose::Layout::Page as |page|>
|
||||
<button id="content" />
|
||||
</Rose::Layout::Page>`);
|
||||
assert.ok(find('#content'));
|
||||
});
|
||||
|
||||
test('it renders with header', async function (assert) {
|
||||
await render(hbs`<Rose::Layout::Page as |page|>
|
||||
<page.header />
|
||||
</Rose::Layout::Page>`);
|
||||
assert.ok(find('header'));
|
||||
});
|
||||
|
||||
test('it renders with main', async function (assert) {
|
||||
await render(hbs`<Rose::Layout::Page as |page|>
|
||||
<page.main />
|
||||
</Rose::Layout::Page>`);
|
||||
assert.ok(find('main'));
|
||||
});
|
||||
|
||||
test('it renders with sidebar', async function (assert) {
|
||||
await render(hbs`<Rose::Layout::Page as |page|>
|
||||
<page.sidebar />
|
||||
</Rose::Layout::Page>`);
|
||||
assert.ok(find('aside'));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,26 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render, find } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
module('Integration | Component | rose/layout/page/header', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders', async function (assert) {
|
||||
await render(hbs`<Rose::Layout::Page::Header />`);
|
||||
assert.ok(find('header'));
|
||||
assert.ok(find('.rose-layout-header'));
|
||||
});
|
||||
|
||||
test('it renders with attributes', async function (assert) {
|
||||
await render(hbs`<Rose::Layout::Page::Header id="header"/>`);
|
||||
assert.ok(find('#header'));
|
||||
});
|
||||
|
||||
test('it renders with content', async function (assert) {
|
||||
await render(hbs`<Rose::Layout::Page::Header>
|
||||
<button id="content" />
|
||||
</Rose::Layout::Page::Header>`);
|
||||
assert.ok(find('#content'));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,26 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render, find } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
module('Integration | Component | rose/layout/page/main', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders', async function (assert) {
|
||||
await render(hbs`<Rose::Layout::Page::Main />`);
|
||||
assert.ok(find('main'));
|
||||
assert.ok(find('.rose-layout-main'));
|
||||
});
|
||||
|
||||
test('it renders with attributes', async function (assert) {
|
||||
await render(hbs`<Rose::Layout::Page::Main id="main"/>`);
|
||||
assert.ok(find('#main'));
|
||||
});
|
||||
|
||||
test('it renders with content', async function (assert) {
|
||||
await render(hbs`<Rose::Layout::Page::Main>
|
||||
<button id="content" />
|
||||
</Rose::Layout::Page::Main>`);
|
||||
assert.ok(find('#content'));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,26 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render, find } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
module('Integration | Component | rose/layout/page/sidebar', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders', async function (assert) {
|
||||
await render(hbs`<Rose::Layout::Page::Sidebar />`);
|
||||
assert.ok(find('aside'));
|
||||
assert.ok(find('.rose-layout-sidebar'));
|
||||
});
|
||||
|
||||
test('it renders with attributes', async function (assert) {
|
||||
await render(hbs`<Rose::Layout::Page::Sidebar id="sidebar"/>`);
|
||||
assert.ok(find('#sidebar'));
|
||||
});
|
||||
|
||||
test('it renders with content', async function (assert) {
|
||||
await render(hbs`<Rose::Layout::Page::Sidebar>
|
||||
<button id="content" />
|
||||
</Rose::Layout::Page::Sidebar>`);
|
||||
assert.ok(find('#content'));
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user