Merge pull request #319 from hashicorp/feature-dialog

feat: 🎸 add dialog component with modal capabilities
This commit is contained in:
Susmitha Girumala authored and GitHub committed 2020-10-15 11:30:00 -04:00
commit 068bfb53e3
12 files changed
+361

No files matched your search

@@ -0,0 +1,3 @@
<div class="rose-dialog-body" tabindex="0">
{{yield}}
</div>
@@ -0,0 +1,75 @@
{{#if @modal}}
<div class="rose-dialog-modal-overlay"></div>
<section
...attributes
role="dialog"
class="rose-dialog{{if @modal ' modal'}}"
{{focus-trap}}
>
{{#if @heading}}
<header class="rose-dialog-header">
{{#if @icon}}
<Rose::Icon @name={{@icon}} @size="medium" />
{{/if}}
<h2 class="rose-dialog-heading">{{@heading}}</h2>
<button
type="button"
class="rose-dialog-dismiss"
title="Dismiss"
{{on "click" (fn this.dismiss @dismiss)}}
>
<Rose::Icon @name="cancel-plain" @size="small" />
</button>
</header>
{{/if}}
{{yield (hash
body=(component 'rose/dialog/body')
footer=(component 'rose/anonymous' tagName="footer" class="rose-dialog-footer")
)}}
</section>
{{else}}
<section
...attributes
role="dialog"
class="rose-dialog{{if @modal ' modal'}}"
>
{{#if @heading}}
<header class="rose-dialog-header">
{{#if @icon}}
<Rose::Icon @name={{@icon}} @size="medium" />
{{/if}}
<h2 class="rose-dialog-heading">{{@heading}}</h2>
<button
type="button"
class="rose-dialog-dismiss"
title="Dismiss"
{{on "click" (fn this.dismiss @dismiss)}}
>
<Rose::Icon @name="cancel-plain" @size="small" />
</button>
</header>
{{/if}}
{{yield (hash
body=(component 'rose/dialog/body')
footer=(component 'rose/anonymous' tagName="footer" class="rose-dialog-footer")
)}}
</section>
{{/if}}
@@ -0,0 +1,19 @@
import Component from '@glimmer/component';
import { action } from '@ember/object';
export default class RoseDialogComponent extends Component {
// =methods
/**
* Calls the passed dismiss function. This approach is convenient
* in case an external dismiss function is not passed into the component,
* a template error will not be thrown.
* @param {Function} fn
*/
@action
dismiss(fn) {
fn();
}
}
@@ -0,0 +1,63 @@
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|Dialog" component="Dialog" />
# Dialog
A dialog component with optional modal capabilities.
<Preview>
<Story name="Dialog" height="15rem">{{
template: hbs`
<Rose::Dialog
@modal={{modal}}
@dismiss={{dismiss}}
@icon={{icon}}
@heading={{headingText}}
as |dialog|>
<dialog.body>
Dialog Body
</dialog.body>
<dialog.footer>
Footer
</dialog.footer>
</Rose::Dialog>
`,
context: {
modal: boolean('modal', false),
headingText: text('headingText', 'Dialog Heading Text'),
icon: select(
'icon',
{
'check-circle-outline': 'check-circle-outline',
'copy-action': 'copy-action',
'unfold-more': 'unfold-more',
'unfold-less': 'unfold-less',
},
'check-circle-outline'
),
dismiss: action('dismiss'),
},
}}</Story>
</Preview>
```htmlbars
<Rose::Dialog
@modal={{true}}
@dismiss={{@dismissFn}}
@icon="check-circle-outline"
@heading="Heading"
as |dialog|>
<dialog.body>
Dialog Body
</dialog.body>
<dialog.footer>
Footer
</dialog.footer>
</Rose::Dialog>
```
@@ -0,0 +1,120 @@
@use '../variables/sizing';
@use '../utilities/type';
@use '../variables/media';
$m: sizing.rems(m); // 16
$s: sizing.rems(s); // 12
$xs: sizing.rems(xs); // 8
$xxs: sizing.rems(xxs); // 4
$xxxs: sizing.rems(xxxs); // 2
$xxxxs: sizing.rems(xxxxs); // 1
.rose-dialog {
border: $xxxxs solid var(--ui-gray-subtler-3);
border-radius: 2px;
background-color: var(--subtle);
box-shadow: 0 $xs $s rgba(var(--stark-components), var(--opacity-2)),
0 $xxs $xxs rgba(var(--stark-components), var(--opacity-3));
.rose-dialog-header {
display: flex;
align-items: center;
padding: ($xs + $xxxs) $m;
min-height: sizing.rems(l);
color: var(--ui-gray-starker-3);
background-color: var(--ui-gray-subtler-5);
border-bottom: $xxxxs solid var(--ui-gray-subtler-2);
.rose-icon {
color: currentColor;
margin-right: $xxs + $xxxs;
svg {
transform: scale(1.25);
position: relative;
top: $xxxxs;
}
}
.rose-dialog-heading {
@include type.type(m, semibold);
flex-grow: 1;
margin: 0;
padding: 0;
color: currentColor;
}
.rose-dialog-dismiss {
border: $xxxxs solid var(--ui-gray-subtler-2);
border-radius: $xxxs;
color: var(--ui-gray);
background-color: transparent;
padding: 0;
margin: 0;
height: sizing.rems(l);
width: sizing.rems(l);
&:hover {
color: var(--ui-gray-starker-2);
border-color: currentColor;
}
.rose-icon {
margin: 0;
svg {
top: $xxxs;
}
}
}
}
.rose-dialog-body {
margin: sizing.rems(l);
}
.rose-dialog-footer {
padding: $m;
border-top: $xxxxs solid var(--ui-gray-subtler-2);
background-color: var(--ui-gray-subtler-5);
button {
margin-bottom: 0;
}
}
}
@media (min-width: media.width(large)) {
.rose-dialog {
&.modal {
display: flex;
flex-direction: column;
position: fixed;
left: calc(50% - 300px);
top: 5%;
max-height: 85%;
max-width: 675px;
width: 100%;
.rose-dialog-body {
overflow-y: auto;
}
}
}
// overlay
.rose-dialog-modal-overlay {
content: " ";
background-color: var(--black);
width: 100vw;
height: 100vh;
position: fixed;
left: 0;
top: 0;
bottom: 0;
right: 0;
opacity: 0.5;
}
}
@@ -1,4 +1,5 @@
@import 'button';
@import 'dialog';
@import 'dropdown';
@import 'form';
@import 'header';
@@ -0,0 +1 @@
export { default } from 'rose/components/rose/dialog';
@@ -0,0 +1 @@
export { default } from 'rose/components/rose/dialog/body';
+1
View File
@@ -53,6 +53,7 @@
"ember-cli-update": "^0.54.6",
"ember-disable-prototype-extensions": "^1.1.3",
"ember-export-application-global": "^2.0.1",
"ember-focus-trap": "^0.4.0",
"ember-inline-svg": "^1.0.0",
"ember-load-initializers": "^2.1.1",
"ember-maybe-import-regenerator": "^0.1.6",
@@ -0,0 +1,28 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, find, click } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
module('Integration | Component | rose/dialog', function(hooks) {
setupRenderingTest(hooks);
test('it renders and handles dismiss actions', async function (assert) {
assert.expect(5);
this.dismiss = () => assert.ok(true, 'dismissed');
await render(hbs`
<Rose::Dialog
@dismiss={{this.dismiss}}
@heading="Heading"
as |dialog|>
<dialog.body>Dialog</dialog.body>
<dialog.footer>Dialog</dialog.footer>
</Rose::Dialog>
`);
assert.ok(find('.rose-dialog'));
assert.ok(find('.rose-dialog-header'));
assert.ok(find('.rose-dialog-body'));
assert.ok(find('.rose-dialog-footer'));
await click('button');
});
});
@@ -0,0 +1,26 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
module('Integration | Component | rose/dialog/body', function(hooks) {
setupRenderingTest(hooks);
test('it renders', async function(assert) {
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.set('myAction', function(val) { ... });
await render(hbs`<Rose::Dialog::Body />`);
assert.equal(this.element.textContent.trim(), '');
// Template block usage:
await render(hbs`
<Rose::Dialog::Body>
template block text
</Rose::Dialog::Body>
`);
assert.equal(this.element.textContent.trim(), 'template block text');
});
});
+23
View File
@@ -10355,6 +10355,16 @@ ember-fetch@^8.0.1:
node-fetch "^2.6.0"
whatwg-fetch "^3.0.0"
ember-focus-trap@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/ember-focus-trap/-/ember-focus-trap-0.4.0.tgz#b69a3a65a49784e2b3a19082655a585cfdaad473"
integrity sha512-NHKjTS1xoSS+fURDhAp5oyR7ZTTBnC4KVGw/VlUbFolx7jdLCePHuQdhnrhT02B1VP1dlX2RxnNvNiT2IcoA1w==
dependencies:
ember-auto-import "^1.5.3"
ember-cli-babel "^7.18.0"
ember-modifier-manager-polyfill "^1.2.0"
focus-trap "^5.1.0"
ember-get-config@0.2.4, ember-get-config@^0.2.2, ember-get-config@^0.2.4:
version "0.2.4"
resolved "https://registry.yarnpkg.com/ember-get-config/-/ember-get-config-0.2.4.tgz#118492a2a03d73e46004ed777928942021fe1ecd"
@@ -12143,6 +12153,14 @@ flush-write-stream@^1.0.0:
inherits "^2.0.3"
readable-stream "^2.3.6"
focus-trap@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/focus-trap/-/focus-trap-5.1.0.tgz#64a0bfabd95c382103397dbc96bfef3a3cf8e5ad"
integrity sha512-CkB/nrO55069QAUjWFBpX6oc+9V90Qhgpe6fBWApzruMq5gnlh90Oo7iSSDK7pKiV5ugG6OY2AXM5mxcmL3lwQ==
dependencies:
tabbable "^4.0.0"
xtend "^4.0.1"
follow-redirects@^1.0.0:
version "1.11.0"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.11.0.tgz#afa14f08ba12a52963140fe43212658897bc0ecb"
@@ -21067,6 +21085,11 @@ sync-disk-cache@^2.0.0:
rimraf "^3.0.0"
username-sync "^1.0.2"
tabbable@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-4.0.0.tgz#5bff1d1135df1482cf0f0206434f15eadbeb9261"
integrity sha512-H1XoH1URcBOa/rZZWxLxHCtOdVUEev+9vo5YdYhC9tCY4wnybX+VQrCYuy9ubkg69fCBxCONJOSLGfw0DWMffQ==
table@^3.7.8:
version "3.8.3"
resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f"