Merge pull request #107 from hashicorp/add-manage-button-to-edit-form

Update edit actions into a new dropdown for IAM resources
This commit is contained in:
Randall Morey authored and GitHub committed 2020-08-19 14:06:43 -04:00
commit 8b66634e84
45 files changed
+431 -383

No files matched your search

@@ -2,6 +2,9 @@ import Component from '@ember/component';
import layout from '../../templates/components/rose/dropdown';
import { action } from '@ember/object';
/**
* A dropdown component composed of HTML disclosure elements: details and summary
*/
export default Component.extend({
// =attributes
@@ -13,7 +16,7 @@ export default Component.extend({
/**
* Closes the details element by setting its `open` attribute to `false`.
* While the details element handles open/close functionality when clicking
* directly on the summar elemenet without JavaScript, we include this helper
* directly on the summary element without JavaScript, we include this helper
* action to ensure that when the user clicks _outside_ of the element,
* or _inside_ the content, it still closes.
* @param {HTMLElement} element
@@ -1,7 +1,5 @@
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|Dropdown" component="Dropdown" />
@@ -15,10 +13,10 @@ A simple dropdown component with support for links and buttons.
<Rose::Dropdown @text="Choose One&hellip;" @icon="user-organization" as |dropdown|>
<dropdown.link @route="index">Menu item</dropdown.link>
<dropdown.link @route="about">Menu item</dropdown.link>
<dropdown.separator />
<dropdown.button>Button menu item</dropdown.button>
</Rose::Dropdown>
`,
context: {}
}}</Story>
</Preview>
@@ -28,10 +26,10 @@ A simple dropdown component with support for links and buttons.
<Rose::Dropdown @text="Choose One&hellip;" @icon="user-organization" @iconOnly={{true}} as |dropdown|>
<dropdown.link @route="index">Menu item</dropdown.link>
<dropdown.link @route="about">Menu item</dropdown.link>
<dropdown.separator />
<dropdown.button>Button menu item</dropdown.button>
</Rose::Dropdown>
`,
context: {}
}}</Story>
</Preview>
@@ -43,3 +41,23 @@ A simple dropdown component with support for links and buttons.
<dropdown.button>Button menu item</dropdown.button>
</Rose::Dropdown>
```
Dropdown content can be aligned to right edge of the component instead of the default left edge, which is the default setting. The following example floats the component to right edge of the page inorder to provide enough space to display right aligned dropdown content.
```htmlbars
<Rose::Dropdown @text="Choose One&hellip;" style="float: right" @dropdownRight={{true}} as |dropdown|>
<dropdown.link @route="index">Menu item</dropdown.link>
<dropdown.link @route="about">Menu item</dropdown.link>
<dropdown.separator />
<dropdown.button>Button menu item</dropdown.button>
</Rose::Dropdown>
```
Dropdown supports `danger` styling through `style` attribute.
```htmlbars
<Rose::Dropdown @text="Dropdown" as |dropdown|>
<dropdown.link @style="danger">Link - danger</dropdown.link>
<dropdown.button @style="danger">Button - danger</dropdown.button>
</Rose::Dropdown>
```
@@ -0,0 +1,10 @@
import Component from '@ember/component';
import layout from '../../../templates/components/rose/dropdown/button';
/**
* A dropdown button component based on simplified rose/button
*/
export default Component.extend({
layout,
tagName: '',
});
@@ -0,0 +1,10 @@
import Component from '@ember/component';
import layout from '../../../templates/components/rose/dropdown/link';
/**
* A dropdown link wrapper for LinkTo component.
*/
export default Component.extend({
layout,
tagName: '',
});
@@ -1,16 +0,0 @@
import Component from '@ember/component';
import layout from '../../../templates/components/rose/header/dropdown';
import { action } from '@ember/object';
export default Component.extend({
layout,
tagName: '',
// Uses details widget state to close dropdown
@action
close(element) {
if (element.open) {
element.open = false;
}
},
});
@@ -0,0 +1,36 @@
@use '../../variables/sizing';
@use '../../utilities/type';
.rose-dropdown-button {
@include type.type(s);
display: block;
box-sizing: border-box;
width: 100%;
// Targeting min-width of ~200
min-width: (sizing.rems(m) + sizing.rems(xs)) * 10;
border: 0;
padding: 0 sizing.rems(s);
text-decoration: none;
text-align: left;
$line-height: 36 / 14;
line-height: $line-height;
--background-color: transparent;
--color: var(--stark);
color: var(--color);
background-color: var(--background-color);
&:hover {
--background-color: var(--ui-gray-subtler-5);
}
&.rose-dropdown-button-danger {
--background-color: var(--white);
--color: var(--failure);
&:hover {
--color: var(--white);
--background-color: var(--failure);
}
}
}
@@ -1,6 +1,9 @@
@use '../variables/sizing';
@use '../utilities/type';
@use '../utilities/dropdown';
@use '../../variables/sizing';
@use '../../utilities/type';
$xs: sizing.rems(xs); // 8
$xxs: sizing.rems(xxs); // 4
$chevron: "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>";
.rose-dropdown {
position: relative;
@@ -12,18 +15,27 @@
overflow: hidden;
display: flex;
align-items: center;
margin-right: sizing.rems(s);
padding: 0 sizing.rems(xl) 0 sizing.rems(l);
$lineHeight: 36 / 13;
line-height: $lineHeight;
$yPadding: sizing.rems(xxs) + sizing.rems(xxxs);
padding: $yPadding sizing.rems(xl) $yPadding sizing.rems(l);
color: var(--black);
background: url($chevron) no-repeat right sizing.rems(m) center;
border-radius: sizing.rems(xxxs);
// Hide default twistie triangle
// Webkit / Chromium
&::-webkit-details-marker {
display: none;
}
// Firefox
list-style-type: none;
&::marker {
display: none;
}
//color: var(--subtle);
background: url("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>")
no-repeat right sizing.rems(s) center;
background-size: sizing.rems(m);
&:hover {
background-color: var(--ui-gray-subtler-5);
background-color: var(--ui-gray-subtler-4);
}
&:not(.has-icon-only) .rose-icon:first-child {
@@ -58,14 +70,11 @@
}
}
.rose-dropdown-trigger:active,
&[open] > .rose-dropdown-trigger {
background-color: var(--ui-gray-subtler-4);
}
.rose-dropdown-content {
$xs: sizing.rems(xs); // 8
$xxs: sizing.rems(xxs); // 4
position: absolute;
padding: $xxs 0;
border: 1px solid var(--ui-gray-subtler-2);
@@ -76,36 +85,16 @@
z-index: 99;
}
&.dropdown-right {
&.rose-dropdown-right {
.rose-dropdown-content {
right: 0;
}
}
.rose-dropdown-link,
.rose-button-dropdown {
@include type.type(m, normal);
display: block;
// Targeting min-width of 200
min-width: (sizing.rems(m) + sizing.rems(xxs)) * 10;
padding: 0 sizing.rems(s);
margin: 0;
border: 0;
color: var(--stark);
background-color: transparent;
// target line height is 36, which is not a standard line height for this
// font size
$lineHeight: 36 / 16;
line-height: $lineHeight;
text-align: left;
text-decoration: none;
&:hover {
background-color: var(--ui-gray-subtler-4);
}
}
.rose-button-dropdown {
width: 100%;
.rose-dropdown-separator {
background-color: var(--ui-gray-subtler-3);
height: sizing.rems(xxxxs);
border: none;
margin: $xxs 0;
}
}
@@ -0,0 +1,3 @@
@import 'dropdown';
@import 'button';
@import 'link';
@@ -0,0 +1,36 @@
@use '../../variables/sizing';
@use '../../utilities/type';
.rose-dropdown-link {
@include type.type(s);
display: block;
box-sizing: border-box;
width: 100%;
// Targeting min-width of ~200
min-width: (sizing.rems(m) + sizing.rems(xs)) * 10;
border: 0;
padding: 0 sizing.rems(s);
text-decoration: none;
text-align: left;
$line-height: 36 / 14;
line-height: $line-height;
--background-color: transparent;
--color: var(--stark);
color: var(--color);
background-color: var(--background-color);
&:hover {
--background-color: var(--ui-gray-subtler-5);
}
&.rose-dropdown-link-danger {
--background-color: var(--white);
--color: var(--failure);
&:hover {
--color: var(--white);
--background-color: var(--failure);
}
}
}
@@ -1,116 +1,42 @@
@use '../../variables/sizing';
@use '../../utilities/type';
@use '../../utilities/dropdown';
@include dropdown.dropdown-style(
'light',
(
color: var(--white),
chevron: 'light',
)
);
$chevron: "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(
'dark',
(
color: var(--black),
chevron: 'dark',
)
);
.rose-header {
.rose-dropdown {
&,
&[open] {
.rose-dropdown-trigger {
color: var(--white);
background: url($chevron) no-repeat right sizing.rems(s) center;
background-size: sizing.rems(m);
.rose-header-dropdown {
position: relative;
display: inline-block;
$yPadding: sizing.rems(xxxs);
$rightPadding: sizing.rems(xs) + sizing.rems(l);
padding: $yPadding $rightPadding $yPadding sizing.rems(s);
.rose-header-dropdown-trigger {
cursor: default;
overflow: hidden;
display: flex;
align-items: center;
background-size: sizing.rems(m);
margin-right: sizing.rems(s);
padding-right: sizing.rems(l) - sizing.rems(xxs);
$lineHeight: 48 / 16;
line-height: $lineHeight;
&.has-icon-only {
padding-left: sizing.rems(xs);
}
&.icon-only {
padding-right: sizing.rems(m);
.rose-icon {
--size: #{sizing.rems(l)};
}
.rose-header-dropdown-trigger-text {
// TODO: factor out into visually-hidden mixin utility
position: absolute;
left: -1000px;
top: -1000px;
width: 1px;
height: 1px;
opacity: 0;
.rose-icon {
display: inline-block;
height: var(--size);
> svg {
vertical-align: top;
}
}
&:hover, &:focus {
background-color: transparent;
}
}
}
// Hide default twistie triangle
// Webkit / Chromium
&::-webkit-details-marker {
display: none;
}
// Firefox
list-style-type: none;
&::marker {
display: none;
}
.rose-icon {
display: inline-block;
height: var(--size);
> svg {
vertical-align: top;
}
}
}
.rose-header-dropdown-content {
$xs: sizing.rems(xs); // 8
$xxs: sizing.rems(xxs); // 4
position: absolute;
padding: $xxs 0;
border: 1px solid var(--ui-gray-subtler-2);
border-radius: sizing.rems(xxxs);
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 {
.rose-header-dropdown-content {
right: 0;
}
}
.rose-header-dropdown-link,
.rose-button-header-dropdown {
@include type.type(m, normal);
display: block;
// Targeting min-width of 200
min-width: (sizing.rems(m) + sizing.rems(xxs)) * 10;
padding: 0 sizing.rems(s);
margin: 0;
border: 0;
color: var(--stark);
background-color: transparent;
// target line height is 36, which is not a standard line height for this
// font size
$lineHeight: 36 / 16;
line-height: $lineHeight;
text-align: left;
text-decoration: none;
&:hover {
background-color: var(--ui-gray-subtler-4);
}
}
.rose-button-header-dropdown {
width: 100%;
}
}
@@ -6,6 +6,7 @@
display: flex;
flex-wrap: wrap;
align-items: center;
margin-right: sizing.rems(s);
@media (max-width: media.width(small)) {
padding: 0 20px;
@@ -6,4 +6,5 @@
flex-wrap: wrap;
flex-direction: row-reverse;
align-items: center;
margin-right: sizing.rems(s);
}
@@ -1,18 +0,0 @@
@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;
}
}
}
@@ -2,7 +2,7 @@
...attributes
type={{if @submit "submit" "button"}}
disabled={{if @disabled "disabled"}}
class="{{if @style (concat "rose-button-" @style)}} {{if @iconLeft "has-icon-left"}} {{if @iconRight "has-icon-right"}} {{if @iconOnly "has-icon-only"}}"
class="{{@classNames}} {{if @style (concat "rose-button-" @style)}} {{if @iconLeft "has-icon-left"}} {{if @iconRight "has-icon-right"}} {{if @iconOnly "has-icon-only"}}"
>
<span class="rose-button-wrapper">
@@ -1,11 +1,11 @@
<details
...attributes
class="rose-dropdown"
class="rose-dropdown {{if @dropdownRight "rose-dropdown-right"}}"
{{on-click-outside this.close}}
>
<summary
class="rose-dropdown-trigger{{if @iconOnly " has-icon-only"}}"
class="rose-dropdown-trigger {{if @iconOnly "has-icon-only"}}"
title={{@text}}
>
@@ -24,8 +24,9 @@
<div class="rose-dropdown-content" {{on-click-inside this.close}}>
{{yield (hash
link=(component "link-to" class="rose-dropdown-link")
button=(component "rose/button" style="dropdown")
link=(component "rose/dropdown/link" style=this.style)
button=(component "rose/dropdown/button" style=this.style)
separator=(component "rose/anonymous" tagName="hr" class="rose-dropdown-separator")
)}}
</div>
@@ -0,0 +1,19 @@
<button
...attributes
type={{if @submit "submit" "button"}}
disabled={{if @disabled "disabled"}}
class="rose-dropdown-button {{if @style (concat "rose-dropdown-button-" @style)}} {{if @iconOnly "has-icon-only"}}"
>
<span class="rose-dropdown-button-wrapper">
<span class="rose-dropdown-button-body">
{{yield}}
</span>
{{#if @iconOnly}}
<Rose::Icon
@name={{@iconOnly}}
@size="medium"
/>
{{/if}}
</span>
</button>
@@ -0,0 +1,8 @@
<LinkTo
...attributes
@route={{if @route @route "index"}}
@model={{@model}}
class="rose-dropdown-link {{if @style (concat "rose-dropdown-link-" @style)}}"
>
{{yield}}
</LinkTo>
@@ -1,32 +0,0 @@
<details
...attributes
class="rose-header-dropdown{{if @alignRight " dropdown-right"}} {{if @style (concat "rose-header-dropdown-" @style) "rose-header-dropdown-light"}}"
{{on-click-outside this.close}}
>
<summary
class="rose-header-dropdown-trigger{{if @iconOnly " icon-only"}}"
title={{@text}}
>
{{#if @icon}}
<Rose::Icon
@name={{@icon}}
@size="large"
/>
{{/if}}
<span class="rose-header-dropdown-trigger-text">
{{@text}}
</span>
</summary>
<div class="rose-header-dropdown-content">
{{yield (hash
link=(component "link-to" class="rose-header-dropdown-link")
button=(component "rose/button" style="header-dropdown")
)}}
</div>
</details>
@@ -1,6 +1,6 @@
<nav class="rose-header-nav">
{{yield (hash
link=(component 'link-to' class="rose-header-nav-link")
dropdown=(component 'rose/header/dropdown')
dropdown=(component 'rose/dropdown')
)}}
</nav>
@@ -1,5 +1,5 @@
<div class="rose-header-utilities">
{{yield (hash
dropdown=(component 'rose/header/dropdown' alignRight=true)
dropdown=(component 'rose/dropdown' dropdownRight=true)
)}}
</div>
@@ -0,0 +1 @@
export { default } from 'rose/components/rose/dropdown/button';
@@ -0,0 +1 @@
export { default } from 'rose/components/rose/dropdown/link';
@@ -1 +0,0 @@
export { default } from 'rose/components/rose/header/dropdown';
+38 -23
View File
@@ -1,23 +1,38 @@
<Rose::Table as |table|>
<table.header as |header|>
<header.row as |row|>
<row.headerCell>Address</row.headerCell>
<row.headerCell>Host Set</row.headerCell>
<row.headerCell>Name</row.headerCell>
<row.headerCell>Description</row.headerCell>
</header.row>
</table.header>
<table.body as |body|>
<body.row as |row|>
<row.headerCell>1.1.1.1</row.headerCell>
<row.cell>host-set-1, host-set-2</row.cell>
<row.cell>optional name</row.cell>
<row.cell>description</row.cell>
</body.row>
</table.body>
<table.footer as |footer|>
<footer.row as |row|>
<row.cell>Footer</row.cell>
</footer.row>
</table.footer>
</Rose::Table>
<Rose::Header as |header|>
<header.brand @logo="logo" @text="Product Name" />
<header.nav as |nav|>
<nav.dropdown @text="org-name" @icon="user-organization" as |dropdown|>
<dropdown.link @route="index">Menu item</dropdown.link>
<dropdown.link @route="about" @style="danger">Menu item - danger</dropdown.link>
<dropdown.separator />
<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|
>
<dropdown.link @route="index">Menu item</dropdown.link>
<dropdown.link @route="about" @style="danger">Menu item - danger</dropdown.link>
<dropdown.button>Button - default</dropdown.button>
<dropdown.separator />
<dropdown.button @style="danger">Button - danger</dropdown.button>
</utilities.dropdown>
</header.utilities>
</Rose::Header>
<Rose::Dropdown @text="Dropdown" as |dropdown|>
<dropdown.link @route="index">Menu item</dropdown.link>
<dropdown.link @route="about" @style="danger">Menu item - danger</dropdown.link>
<dropdown.separator></dropdown.separator>
<dropdown.button>Button menu item</dropdown.button>
<dropdown.button @style="danger">Button - danger</dropdown.button>
</Rose::Dropdown>
@@ -9,6 +9,7 @@ module('Integration | Component | rose/dropdown', function (hooks) {
test('it renders', async function (assert) {
await render(hbs`<Rose::Dropdown />`);
assert.ok(find('.rose-dropdown'));
assert.notOk(find('.rose-dropdown-right'));
});
test('it renders with html attributes', async function (assert) {
@@ -32,6 +33,13 @@ module('Integration | Component | rose/dropdown', function (hooks) {
assert.ok(find('.has-icon-only'));
});
test('it supports right aligned content', async function (assert) {
await render(hbs`
<Rose::Dropdown @dropdownRight={{true}} />
`);
assert.ok(find('.rose-dropdown-right'));
});
test('it renders with content', async function (assert) {
await render(hbs`<Rose::Dropdown @text="Click me" as |dropdown|>
<dropdown.link @route="about"/>
@@ -0,0 +1,38 @@
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/dropdown/button', function(hooks) {
setupRenderingTest(hooks);
test('it renders', async function (assert) {
await render(hbs`<Rose::Dropdown::Button />`);
assert.ok(find('.rose-dropdown-button'));
});
test('it is type="button" by default', async function (assert) {
await render(hbs`<Rose::Dropdown::Button />`);
assert.equal(find('button').type, 'button');
});
test('it is type="submit" when @submit={{true}}', async function (assert) {
await render(hbs`<Rose::Dropdown::Button @submit={{true}} />`);
assert.equal(find('button').type, 'submit');
});
test('it is disabled when @disabled={{true}}', async function (assert) {
await render(hbs`<Rose::Dropdown::Button @disabled={{true}} />`);
assert.equal(find('button').disabled, true);
});
test('it adds a style class', async function (assert) {
await render(hbs`<Rose::Dropdown::Button @style="warning" />`);
assert.ok(find('.rose-dropdown-button-warning'));
});
test('it supports an icon-only type', async function (assert) {
await render(hbs`<Rose::Dropdown::Button @iconOnly="chevron-left" />`);
assert.ok(find('.has-icon-only .rose-icon'));
});
});
@@ -0,0 +1,31 @@
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/dropdown/link', function(hooks) {
setupRenderingTest(hooks);
test('it renders', async function (assert) {
await render(hbs`<Rose::Dropdown::Link />`);
assert.ok(find('a'));
assert.ok(find('.rose-dropdown-link'));
});
test('it renders with attributes', async function (assert) {
await render(hbs`<Rose::Dropdown::Link id="dropdown" />`);
assert.ok(find('#dropdown'));
});
test('it renders with content', async function (assert) {
await render(hbs`<Rose::Dropdown::Link>
content
</Rose::Dropdown::Link>`);
assert.equal(find('.rose-dropdown-link').textContent.trim(), 'content');
});
test('it supports style', async function (assert) {
await render(hbs`<Rose::Dropdown::Link @style='warning' />`);
assert.ok('.rose-dropdown-link-warning');
});
});
@@ -53,6 +53,6 @@ module('Integration | Component | rose/header', function (hooks) {
<utility.dropdown />
</header.utilities>
</Rose::Header>`);
assert.ok(find('.rose-header-dropdown'));
assert.ok(find('.rose-dropdown'));
});
});
@@ -1,80 +0,0 @@
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/header/dropdown', function (hooks) {
setupRenderingTest(hooks);
test('it renders', async function (assert) {
await render(hbs`<Rose::Header::Dropdown />`);
assert.ok(find('.rose-header-dropdown'));
});
test('it renders with html attributes', async function (assert) {
await render(
hbs`<Rose::Header::Dropdown id="custom-id" class="custom-class"/>`
);
assert.ok(find('#custom-id'));
assert.ok(find('.custom-class'));
});
test('it renders with trigger', async function (assert) {
await render(hbs`<Rose::Header::Dropdown @text="Click me" />`);
assert.equal(
find('.rose-header-dropdown-trigger').textContent.trim(),
'Click me'
);
});
test('it supports an icon', async function (assert) {
await render(hbs`<Rose::Header::Dropdown @icon="user-square-fill" />`);
assert.ok(find('svg'));
});
test('it supports icon-only display', async function (assert) {
await render(hbs`<Rose::Header::Dropdown @iconOnly={{true}} />`);
assert.ok(find('.icon-only'));
});
test('it supports right-aligned content', async function (assert) {
await render(hbs`<Rose::Header::Dropdown @alignRight={{true}} />`);
assert.ok(find('.dropdown-right'));
});
test('it renders with content', async function (assert) {
await render(hbs`<Rose::Header::Dropdown @text="Click me" as |dropdown|>
<dropdown.link @route="about"/>
<dropdown.link @route="about"/>
<dropdown.button />
<dropdown.button />
<dropdown.button />
</Rose::Header::Dropdown>`);
assert.ok(find('.rose-header-dropdown-content'));
assert.equal(findAll('a').length, 2);
assert.equal(findAll('button').length, 3);
});
test('it is toggled on click on trigger content', async function (assert) {
await render(hbs`<Rose::Header::Dropdown id="dropdown" />`);
assert.notOk(find('#dropdown').open);
await click('summary');
assert.ok(find('#dropdown').open);
await click('summary');
assert.notOk(find('#dropdown').open);
});
test('it is closed with outside click is triggered', async function (assert) {
await render(hbs`
<div id="wrapper">
<Rose::Header::Dropdown id="dropdown" />
</div>
`);
await click('summary');
assert.ok(find('#dropdown').open);
await click('#wrapper');
assert.notOk(find('#dropdown').open);
});
});
@@ -24,6 +24,6 @@ module('Integration | Component | rose/header/nav', function (hooks) {
<nav.dropdown />
<nav.dropdown />
</Rose::Header::nav>`);
assert.equal(findAll('.rose-header-dropdown').length, 2);
assert.equal(findAll('.rose-dropdown').length, 2);
});
});
@@ -16,6 +16,13 @@ module('Integration | Component | rose/header/utilities', function (hooks) {
<nav.dropdown />
<nav.dropdown />
</Rose::Header::Utilities>`);
assert.equal(findAll('.rose-header-dropdown').length, 2);
assert.equal(findAll('.rose-dropdown').length, 2);
});
test('it renders nav dropdown elements as right aligned', async function (assert) {
await render(hbs`<Rose::Header::Utilities as |nav| >
<nav.dropdown />
</Rose::Header::Utilities>`);
assert.ok(find('.rose-dropdown.rose-dropdown-right'));
});
});
+3 -1
View File
@@ -14,7 +14,9 @@
@iconOnly={{true}}
@icon="user-square-fill" as |dropdown|
>
<dropdown.button {{on "click" (route-action "invalidateSession")}}>
<dropdown.button
{{on "click" (route-action "invalidateSession")}}
>
{{t 'actions.deauthenticate'}}
</dropdown.button>
</utilities.dropdown>
@@ -16,20 +16,24 @@
</Rose::Nav::Tabs>
</page.navigation>
<page.actions>
<Rose::Dropdown @text="{{t 'form.label.manage'}}" @dropdownRight={{true}} as |dropdown|>
<dropdown.button
@style="danger"
@disabled={{@model.canSave}}
{{on "click" (route-action "delete" @model)}}
>
{{t 'actions.delete-group'}}
</dropdown.button>
</Rose::Dropdown>
</page.actions>
<page.body>
<Form::Project
@model={{@model}}
@submit={{route-action "save" @model}}
@cancel={{route-action "cancel" @model}}
/>
<Rose::Separator />
<Rose::Button
@style="warning"
@disabled={{@model.canSave}}
{{on "click" (route-action "delete" @model)}}
>
{{t 'actions.delete'}}
</Rose::Button>
</page.body>
</Rose::Layout::Page>
@@ -16,20 +16,24 @@
</Rose::Nav::Tabs>
</page.navigation>
<page.actions>
<Rose::Dropdown @text="{{t 'form.label.manage'}}" @dropdownRight={{true}} as |dropdown|>
<dropdown.button
@style="danger"
@disabled={{@model.canSave}}
{{on "click" (route-action "delete" @model)}}
>
{{t 'actions.delete-host-catalog'}}
</dropdown.button>
</Rose::Dropdown>
</page.actions>
<page.body>
<Form::HostCatalog
@model={{@model}}
@submit={{route-action "save" @model}}
@cancel={{route-action "cancel" @model}}
/>
<Rose::Separator />
<Rose::Button
@style="warning"
@disabled={{@model.canSave}}
{{on "click" (route-action "delete" @model)}}
>
{{t 'actions.delete'}}
</Rose::Button>
</page.body>
</Rose::Layout::Page>
@@ -16,20 +16,24 @@
</Rose::Nav::Tabs>
</page.navigation>
<page.actions>
<Rose::Dropdown @text="{{t 'form.label.manage'}}" @dropdownRight={{true}} as |dropdown|>
<dropdown.button
@style="danger"
@disabled={{@model.canSave}}
{{on "click" (route-action "delete" @model)}}
>
{{t 'actions.delete-project'}}
</dropdown.button>
</Rose::Dropdown>
</page.actions>
<page.body>
<Form::Project
@model={{@model}}
@submit={{route-action "save" @model}}
@cancel={{route-action "cancel" @model}}
/>
<Rose::Separator />
<Rose::Button
@style="warning"
@disabled={{@model.canSave}}
{{on "click" (route-action "delete" @model)}}
>
{{t 'actions.delete'}}
</Rose::Button>
</page.body>
</Rose::Layout::Page>
@@ -10,6 +10,18 @@
<h2>{{t "resources.role"}}</h2>
</page.header>
<page.actions>
<Rose::Dropdown @text="{{t 'form.label.manage'}}" @dropdownRight={{true}} as |dropdown|>
<dropdown.button
@style="danger"
@disabled={{@model.canSave}}
{{on "click" (route-action "delete" @model)}}
>
{{t 'actions.delete-role'}}
</dropdown.button>
</Rose::Dropdown>
</page.actions>
<page.navigation>
<Rose::Nav::Tabs as |nav|>
<nav.link @route='scopes.scope.roles.role.index'>
@@ -3,11 +3,4 @@
@submit={{route-action "save" @model}}
@cancel={{route-action "cancel" @model}}
/>
<Rose::Separator />
<Rose::Button
@style="warning"
@disabled={{@model.canSave}}
{{on "click" (route-action "delete" @model)}}
>
{{t 'actions.delete'}}
</Rose::Button>
@@ -8,6 +8,18 @@
<h2>{{t "resources.user"}}</h2>
</page.header>
<page.actions>
<Rose::Dropdown @text="{{t 'form.label.manage'}}" @dropdownRight={{true}} as |dropdown|>
<dropdown.button
@style="danger"
@disabled={{@model.canSave}}
{{on "click" (route-action "delete" @model)}}
>
{{t 'actions.delete-user'}}
</dropdown.button>
</Rose::Dropdown>
</page.actions>
<page.navigation>
<Rose::Nav::Tabs as |nav|>
<nav.link @route='scopes.scope.users.user'>
@@ -17,19 +29,11 @@
</page.navigation>
<page.body>
<Form::Project
<Form::User
@model={{@model}}
@submit={{route-action "save" @model}}
@cancel={{route-action "cancel" @model}}
/>
<Rose::Separator />
<Rose::Button
@style="warning"
@disabled={{@model.canSave}}
{{on "click" (route-action "delete" @model)}}
>
{{t 'actions.delete'}}
</Rose::Button>
</page.body>
</Rose::Layout::Page>
@@ -1,5 +1,5 @@
import { module, test } from 'qunit';
import { visit, currentURL, fillIn, click, find } from '@ember/test-helpers';
import { visit, currentURL, fillIn, click, find, findAll } from '@ember/test-helpers';
import { setupApplicationTest } from 'ember-qunit';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
import { Response } from 'miragejs';
@@ -172,7 +172,11 @@ module('Acceptance | authentication', function (hooks) {
await fillIn('[name="password"]', 'test');
await click('[type="submit"]');
assert.ok(currentSession().isAuthenticated);
await click('.rose-button-header-dropdown');
// Open header utilities dropdown
await click('.rose-header-utilities .rose-dropdown summary');
// Find and click on last element in dropdown - should be deauthenticate button
const menu = findAll('.rose-header-utilities .rose-dropdown .rose-dropdown-content button');
await click(menu[menu.length - 1]);
assert.notOk(currentSession().isAuthenticated);
assert.equal(currentURL(), authMethodGlobalAuthenticateURL);
});
+2 -2
View File
@@ -133,7 +133,7 @@ module('Acceptance | groups', function (hooks) {
assert.expect(1);
const groupsCount = this.server.db.groups.length;
await visit(groupURL);
await click('.rose-button-warning');
await click('.rose-layout-page-actions .rose-dropdown-button-danger');
assert.equal(this.server.db.groups.length, groupsCount - 1);
});
@@ -210,7 +210,7 @@ module('Acceptance | groups', function (hooks) {
);
});
await visit(groupURL);
await click('.rose-button-warning');
await click('.rose-layout-page-actions .rose-dropdown-button-danger');
assert.ok(
find('[role="alert"]').textContent.trim(),
'Oops.',
@@ -80,7 +80,7 @@ module('Acceptance | host catalogs', function (hooks) {
authenticateSession({});
await visit(urls.hostCatalog);
assert.equal(currentURL(), urls.hostCatalog);
await click('.rose-button-warning');
await click('.rose-layout-page-actions .rose-dropdown-button-danger');
assert.equal(this.server.db.hostCatalogs.length, hostCatalogsCount - 1);
});
@@ -162,7 +162,7 @@ module('Acceptance | host catalogs', function (hooks) {
});
authenticateSession({});
await visit(urls.hostCatalog);
await click('.rose-button-warning');
await click('.rose-layout-page-actions .rose-dropdown-button-danger');
await a11yAudit();
assert.ok(
find('[role="alert"]').textContent.trim(),
+2 -2
View File
@@ -135,7 +135,7 @@ module('Acceptance | projects', function (hooks) {
test('can delete project', async function (assert) {
assert.expect(1);
await visit(existingProjectURL);
await click('.rose-button-warning');
await click('.rose-layout-page-actions .rose-dropdown-button-danger');
assert.equal(getProjectScopesCount(), initialProjectScopesCount - 1);
});
@@ -212,7 +212,7 @@ module('Acceptance | projects', function (hooks) {
);
});
await visit(existingProjectURL);
await click('.rose-button-warning');
await click('.rose-layout-page-actions .rose-dropdown-button-danger');
assert.ok(
find('[role="alert"]').textContent.trim(),
'Oops.',
+2 -2
View File
@@ -133,7 +133,7 @@ module('Acceptance | roles', function (hooks) {
assert.expect(1);
const rolesCount = this.server.db.roles.length;
await visit(roleURL);
await click('.rose-button-warning');
await click('.rose-layout-page-actions .rose-dropdown-button-danger');
assert.equal(this.server.db.roles.length, rolesCount - 1);
});
@@ -210,7 +210,7 @@ module('Acceptance | roles', function (hooks) {
);
});
await visit(roleURL);
await click('.rose-button-warning');
await click('.rose-layout-page-actions .rose-dropdown-button-danger');
assert.ok(
find('[role="alert"]').textContent.trim(),
'Oops.',
+2 -2
View File
@@ -133,7 +133,7 @@ module('Acceptance | users', function (hooks) {
assert.expect(1);
const usersCount = this.server.db.users.length;
await visit(userURL);
await click('.rose-button-warning');
await click('.rose-layout-page-actions .rose-dropdown-button-danger');
assert.equal(this.server.db.users.length, usersCount - 1);
});
@@ -210,7 +210,7 @@ module('Acceptance | users', function (hooks) {
);
});
await visit(userURL);
await click('.rose-button-warning');
await click('.rose-layout-page-actions .rose-dropdown-button-danger');
assert.ok(
find('[role="alert"]').textContent.trim(),
'Oops.',
+6
View File
@@ -9,6 +9,11 @@ actions:
deauthenticate: Deauthenticate
choose-org: Choose Scope
choose-different-org: Choose a Different Scope
delete-user: Delete user
delete-group: Delete group
delete-role: Delete role
delete-project: Delete project
delete-host-catalog: Delete host catalog
auth:
titles:
no-scopes: No Scopes Available
@@ -76,6 +81,7 @@ form:
type: Type
identification: Username
password: Password
manage: Manage
grant: Grant
principal: Principal
errors: