chore: 🤖 add primary nav component for sidebar

This commit is contained in:
Randall Morey committed 2020-05-30 12:42:10 -04:00
1 parent 76fefca1c7
commit 5e3b3ae922
13 files changed
+347 -177

No files matched your search

@@ -0,0 +1,11 @@
import Component from '@ember/component';
import layout from '../../../templates/components/rose/nav/primary';
import ComponentAutoId from '../../../mixins/component-auto-id';
/**
* A simple navigation component intended for the sidebar layout region.
*/
export default Component.extend(ComponentAutoId, {
layout,
tagName: '',
});
@@ -0,0 +1,34 @@
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|Nav/Primary" component="Nav/Primary" />
# Primary Navigation
A simple navigation component intended for use in the sidebar layout region.
<Preview>
<Story name="Basic Primary Nav" height="8rem">{{
template: hbs`
<Rose::Nav::Primary @title={{title}} as |nav|>
<nav.link @route="index" class="active">Item Name</nav.link>
<nav.link @route="index">Item Name</nav.link>
</Rose::Nav::Primary>
`,
context: {
title: text('title', 'Title'),
},
}}</Story>
</Preview>
_Note_: do not assign the `active` class directly. This is done by the link
component automatically. We use it in this story for illustrative purposes.
```htmlbars
<Rose::Nav::Primary @title="Title" as |nav|>
<nav.link @route="index">Item Name</nav.link>
<nav.link @route="about">Item Name</nav.link>
</Rose::Nav::Primary>
```
@@ -4,3 +4,4 @@
@import 'layout';
@import 'notification';
@import 'card';
@import 'nav';
@@ -0,0 +1 @@
@import "primary";
@@ -0,0 +1,40 @@
@use '../../variables/sizing';
@use '../../utilities/type';
.rose-nav-primary {
$threePX: sizing.rems(xxxs) + sizing.rems(xxxxs);
.rose-nav-title {
@include type.type(xs, semibold);
display: block;
color: var(--ui-gray);
text-transform: uppercase;
$lineHeight: 20 / 12;
line-height: $lineHeight;
padding: 0 sizing.rems(m);
margin-bottom: $threePX;
border-bottom: sizing.rems(xxxxs) solid var(--ui-gray-subtler-2);
}
.rose-nav-link {
@include type.type(s);
display: block;
color: var(--stark);
$lineHeight: 36 / 14;
line-height: $lineHeight;
text-decoration: none;
padding: 0 sizing.rems(m);
border-right: $threePX solid transparent;
&.active {
color: var(--action);
border-color: currentColor;
}
&:hover {
text-decoration: underline;
color: var(--action);
background-color: var(--action-subtler-1h);
}
}
}
@@ -56,6 +56,7 @@
--success-starker-1: #{color(green, 800)};
--action-subtler-2: #{color(blue, 010)};
--action-subtler-1h: #{color(blue, 050)};
--action-subtler-1: #{color(blue, 100)};
--action: #{color(blue)};
--action-starker-1: #{color(blue, 700)};
@@ -115,6 +116,7 @@
--success-starker-1: #{color(green, 200)};
--action-subtler-2: #{color(blue, 900)};
--action-subtler-1h: #{color(blue, 800)};
--action-subtler-1: #{color(blue, 800)};
--action: #{color(blue)};
--action-starker-1: #{color(blue, 300)};
@@ -0,0 +1,17 @@
<nav
...attributes
class="rose-nav-primary"
id={{this.id}}
aria-labelledby={{if @title (concat "title-" this.id)}}>
{{#if @title}}
<span class="rose-nav-title" id="title-{{this.id}}">
{{@title}}
</span>
{{/if}}
{{yield (hash
link=(component "link-to" class="rose-nav-link")
)}}
</nav>
@@ -0,0 +1 @@
export { default } from 'rose/components/rose/nav/primary';
+191 -176
View File
@@ -1,187 +1,202 @@
<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>
<Rose::Nav::Primary @title="Title" as |nav|>
<nav.link @route="index">Item Name</nav.link>
<nav.link @route="about">Item Name</nav.link>
<nav.link @route="about">Item Name</nav.link>
<nav.link @route="about">Item Name</nav.link>
</Rose::Nav::Primary>
</page.sidebar>
<page.main>
Dummy app index template
<Rose::Notification
@style="error"
@heading="Error"
@dismiss={{this.actionHandler}}
@dismissText="Dismiss"
>
<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>
An error occurred.
</Rose::Notification>
</Rose::Header>
<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
@onSubmit={{this.actionHandler}}
@cancel={{this.actionHandler}}
@disabled={{false}}
as |form|
>
<h3>Inputs</h3>
<form.input
@label="Label"
@value="Read only"
readonly={{true}}
/>
<form.input
@label="Label"
@helperText="Helper text"
@value="Text"
/>
<form.input
@label="Label"
@helperText="Helper text"
@value="Text"
@error={{true}}
/>
<form.input
@label="Label"
@helperText="Helper text"
@value="Text"
disabled={{true}}
/>
<h3>Textareas</h3>
<form.textarea
@label="Label"
@value="Read only"
readonly={{true}}
/>
<form.textarea
@label="Label"
@helperText="Helper text"
@value="Text"
/>
<form.textarea
@label="Label"
@helperText="Helper text"
@value="Text"
@error={{true}}
/>
<form.textarea
@label="Label"
@helperText="Helper text"
@value="Text"
disabled={{true}}
/>
<h3>Selects</h3>
<form.select
@label="Label"
@helperText="Helper text"
@onChange={{fn this.actionHandler}}
as |field|
>
<field.field 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>
</field.field>
</form.select>
<form.select
@label="Label"
@helperText="Helper text"
@onChange={{fn this.actionHandler}}
as |field|
>
<field.field as |select|>
<select.option>Choose an option</select.option>
</field.field>
</form.select>
Dummy app index template
<h3>Radio Group</h3>
<form.radioGroup @name="color" @selectedValue={{this.color}} as |radioGroup|>
<radioGroup.radio
@label="Red"
@value="red"
/>
<radioGroup.radio
@label="Green"
@value="green"
/>
<radioGroup.radio
@label="Blue"
@value="blue"
@disabled={{true}}
/>
</form.radioGroup>
<Rose::Notification
@style="error"
@heading="Error"
@dismiss={{this.actionHandler}}
@dismissText="Dismiss"
>
An error occurred.
</Rose::Notification>
<h3>Checkboxes</h3>
<form.checkbox
@label="Label"
/>
<form.checkbox
@error={{true}}
@label="Error"
/>
<form.checkbox
@checked={{true}}
@label="Checked"
/>
<form.checkbox
@label="Disabled"
disabled={{true}}
/>
<form.checkbox
@checked={{true}}
@label="Disabled + Checked"
disabled={{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" />
<form.actions
@submitText="Save"
@cancelText="Cancel" />
</Rose::Form>
<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
@onSubmit={{this.actionHandler}}
@cancel={{this.actionHandler}}
@disabled={{false}}
as |form|
>
<h3>Inputs</h3>
<form.input
@label="Label"
@value="Read only"
readonly={{true}}
/>
<form.input
@label="Label"
@helperText="Helper text"
@value="Text"
/>
<form.input
@label="Label"
@helperText="Helper text"
@value="Text"
@error={{true}}
/>
<form.input
@label="Label"
@helperText="Helper text"
@value="Text"
disabled={{true}}
/>
<h3>Textareas</h3>
<form.textarea
@label="Label"
@value="Read only"
readonly={{true}}
/>
<form.textarea
@label="Label"
@helperText="Helper text"
@value="Text"
/>
<form.textarea
@label="Label"
@helperText="Helper text"
@value="Text"
@error={{true}}
/>
<form.textarea
@label="Label"
@helperText="Helper text"
@value="Text"
disabled={{true}}
/>
<h3>Selects</h3>
<form.select
@label="Label"
@helperText="Helper text"
@onChange={{fn this.actionHandler}}
as |field|
>
<field.field 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>
</field.field>
</form.select>
<form.select
@label="Label"
@helperText="Helper text"
@onChange={{fn this.actionHandler}}
as |field|
>
<field.field as |select|>
<select.option>Choose an option</select.option>
</field.field>
</form.select>
<h3>Radio Group</h3>
<form.radioGroup @name="color" @selectedValue={{this.color}} as |radioGroup|>
<radioGroup.radio
@label="Red"
@value="red"
/>
<radioGroup.radio
@label="Green"
@value="green"
/>
<radioGroup.radio
@label="Blue"
@value="blue"
@disabled={{true}}
/>
</form.radioGroup>
<h3>Checkboxes</h3>
<form.checkbox
@label="Label"
/>
<form.checkbox
@error={{true}}
@label="Error"
/>
<form.checkbox
@checked={{true}}
@label="Checked"
/>
<form.checkbox
@label="Disabled"
disabled={{true}}
/>
<form.checkbox
@checked={{true}}
@label="Disabled + Checked"
disabled={{true}}
/>
<form.actions
@submitText="Save"
@cancelText="Cancel" />
</Rose::Form>
<Rose::Card::Link @title="I'm a card" />
<Rose::Card::Link @title="I'm a card" @description="I'm a description" />
<Rose::Card::Link @title="I'm a card" @description="I'm a description" @subtitle="I'm a subtitle"/>
<Rose::Card::Link @title="I'm a card" />
<Rose::Card::Link @title="I'm a card" @description="I'm a description" />
<Rose::Card::Link @title="I'm a card" @description="I'm a description" @subtitle="I'm a subtitle"/>
</page.main>
</Rose::Layout::Page>
@@ -0,0 +1,34 @@
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/nav/primary', function(hooks) {
setupRenderingTest(hooks);
test('it renders', async function(assert) {
assert.expect(2)
await render(hbs`
<Rose::Nav::Primary @title="Title" as |nav|>
<nav.link @route="index">Item Name</nav.link>
</Rose::Nav::Primary>
`);
assert.equal(find('.rose-nav-title').textContent.trim(), 'Title');
assert.equal(find('.rose-nav-link').textContent.trim(), 'Item Name');
});
test('it associates title with nav via aria-labelledby', async function (assert) {
assert.expect(2)
await render(hbs`
<Rose::Nav::Primary @title="Title" as |nav|>
<nav.link @route="index">Item Name</nav.link>
</Rose::Nav::Primary>
`);
const el = find('.rose-nav-primary');
const id = el.id;
const titleId = `title-${id}`;
const titleEl = find('.rose-nav-title');
assert.equal(titleEl.id, titleId);
assert.equal(el.getAttribute('aria-labelledby').trim(), titleId);
});
});
+4
View File
@@ -3,6 +3,10 @@
@import 'rose';
@import 'notify';
.rose-nav-primary {
margin-top: sizing.rems(xxl);
}
.projects-overview {
margin: sizing.rems(l);
+9 -1
View File
@@ -29,7 +29,15 @@
{{outlet}}
</page.main>
<page.sidebar />
<page.sidebar>
<Rose::Nav::Primary @title={{t 'dev.title'}} as |nav|>
<nav.link @route="orgs" class="active">{{t 'dev.item-name'}}</nav.link>
<nav.link @route="index">{{t 'dev.item-name'}}</nav.link>
<nav.link @route="index">{{t 'dev.item-name'}}</nav.link>
<nav.link @route="index">{{t 'dev.item-name'}}</nav.link>
</Rose::Nav::Primary>
</page.sidebar>
</Rose::Layout::Page>
<EmberNotify @closeAfter={{4000}} as |message close|>
+2
View File
@@ -21,3 +21,5 @@ dev:
menu-item: Menu item
button-menu-item: Button menu item
section: Section
title: Title
item-name: Item Name