Skip to content

Commit

Permalink
Merge pull request #64 from far-fetched/feature/support-for-nesting-d…
Browse files Browse the repository at this point in the history
…ialogs

✨ `<Dialog/>` feature/support for nesting dialogs
  • Loading branch information
alexlafroscia authored Jul 6, 2021
2 parents 9b1c513 + 0bed280 commit d1b192c
Show file tree
Hide file tree
Showing 15 changed files with 538 additions and 20 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 10.x
node-version: 14.17.2

- uses: actions/cache@v2
with:
Expand Down
6 changes: 4 additions & 2 deletions addon/components/dialog.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
aria-modal='true'
...attributes
{{focus-trap focusTrapOptions=(hash initialFocus=@initialFocus)}}
{{click-outside @onClose event='mouseup'}}
{{this.handleEscapeKey @isOpen @onClose}}
{{click-outside this.onClose event='mouseup'}}
{{this.handleEscapeKey @isOpen this.onClose}}
{{did-insert (fn this.dialogStackProvider.push this)}}
{{will-destroy (fn this.dialogStackProvider.remove this)}}
>
{{yield
(hash
Expand Down
11 changes: 11 additions & 0 deletions addon/components/dialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { getOwnConfig } from '@embroider/macros';
import { modifier } from 'ember-modifier';

import { Keys } from 'ember-headlessui/utils/keyboard';
import { action } from '@ember/object';
import { inject as service } from '@ember/service';

function getPortalRoot() {
const { rootElement } = getOwnConfig();
Expand All @@ -15,6 +17,9 @@ function getPortalRoot() {
}

export default class DialogComponent extends Component {
@service
dialogStackProvider;

DEFAULT_TAG_NAME = 'div';

guid = `${guidFor(this)}-headlessui-dialog`;
Expand Down Expand Up @@ -88,4 +93,10 @@ export default class DialogComponent extends Component {
get descriptionGuid() {
return `${this.guid}-description`;
}

@action
onClose() {
if (this.dialogStackProvider.hasOpenChild(this)) return;
this.args.onClose();
}
}
22 changes: 22 additions & 0 deletions addon/services/dialog-stack-provider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import Service from '@ember/service';
import { action } from '@ember/object';

export default class DialogStackProviderService extends Service {
stack = [];

@action
hasOpenChild(dialog) {
return this.stack[this.stack.length - 1] !== dialog.guid;
}

@action
remove(dialog) {
let ix = this.stack.findIndex((guid) => guid === dialog.guid);
this.stack.splice(ix, 1);
}

@action
push(dialog) {
this.stack.push(dialog.guid);
}
}
1 change: 1 addition & 0 deletions app/services/dialog-stack-provider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from 'ember-headlessui/services/dialog-stack-provider';
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
"@glimmer/component": "^1.0.4",
"@glimmer/tracking": "^1.0.4",
"@tailwindcss/ui": "^0.6.0",
"@testing-library/dom": "^8.1.0",
"@testing-library/user-event": "^13.1.9",
"babel-eslint": "^10.1.0",
"broccoli-asset-rev": "^3.0.0",
"dotenv-cli": "^4.0.0",
Expand Down
19 changes: 16 additions & 3 deletions tests/accessibility-assertions.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ function getDialog() {
return document.querySelector('[role="dialog"]');
}

function getDialogs() {
return Array.from(document.querySelectorAll('[role="dialog"]'));
}

function getDialogOverlay() {
return document.querySelector('[id$="-headlessui-dialog-overlay"]');
}
Expand All @@ -16,6 +20,12 @@ function getDialogDescription() {
return document.querySelector('[id$="-headlessui-dialog-description"]');
}

function getDialogOverlays() {
return Array.from(
document.querySelectorAll('[id$="-headlessui-dialog-overlay"]')
);
}

const DialogState = {
/** The dialog is visible to the user. */
Visible: 'Visible',
Expand All @@ -31,14 +41,15 @@ function assertNever(x) {
throw new Error('Unexpected object: ' + x);
}

function assertActiveElement(element) {
function assertActiveElement(element, comment) {
try {
if (element === null) {
Qunit.assert.notEqual(element, null);
return;
}

Qunit.assert.equal(document.activeElement, element);
return Qunit.assert.waitFor(() => {
Qunit.assert.equal(document.activeElement, element, comment);
});
} catch (err) {
Error.captureStackTrace(err, assertActiveElement);
throw err;
Expand Down Expand Up @@ -523,7 +534,9 @@ export {
assertDialogOverlay,
assertDialogTitle,
getDialog,
getDialogs,
getDialogOverlay,
getDialogOverlays,
assertActiveElement,
getByText,
};
49 changes: 49 additions & 0 deletions tests/dummy/app/components/dialogs/nested.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{{#let (hash value=false) as |childState|}}
<Dialog
class='fixed inset-0 z-10 overflow-y-auto'
@as='div'
@isOpen={{@isOpen}}
@onClose={{@onClose}}
as |dialog|
>
<div
class='flex items-end justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:block sm:p-0'
>
<dialog.Overlay class='fixed inset-0' />

<div
class='inline-block px-4 pt-5 pb-4 overflow-hidden text-left align-bottom bg-white rounded-lg shadow-xl transform transition-all sm:my-{{mult
8
@level
}}
sm:align-middle sm:max-w-sm sm:w-full sm:p-6 sm:ml-{{mult 8 @level}}'
>

<div class='mt-3 text-center sm:mt-5'>
<p>Level: {{@level}}</p>
<button
type='button'
class='inline-flex items-center px-4 py-2 text-base font-medium text-white bg-indigo-600 border border-transparent rounded-md shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500'
{{on 'click' (set childState.value true)}}
>Open {{inc @level}} a</button>
<button
type='button'
class='inline-flex items-center px-4 py-2 text-base font-medium text-white bg-indigo-600 border border-transparent rounded-md shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500'
{{on 'click' (set childState.value true)}}
>Open {{inc @level}} b</button>
<button
type='button'
class='inline-flex items-center px-4 py-2 text-base font-medium text-white bg-indigo-600 border border-transparent rounded-md shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500'
{{on 'click' (set childState.value true)}}
>Open {{inc @level}} c</button>
</div>

</div>
</div>
<Dialogs::Nested
@isOpen={{childState.value}}
@onClose={{set childState.value false}}
@level={{inc @level}}
/>
</Dialog>
{{/let}}
5 changes: 5 additions & 0 deletions tests/dummy/app/helpers/inc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { helper } from '@ember/component/helper';

export default helper(function inc([value] /*, hash*/) {
return value + 1;
});
5 changes: 5 additions & 0 deletions tests/dummy/app/helpers/mult.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { helper } from '@ember/component/helper';

export default helper(function mult([first, second] /*, hash*/) {
return first * second;
});
1 change: 1 addition & 0 deletions tests/dummy/app/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@ Router.map(function () {
this.route('dialog', function () {
this.route('dialog-modal');
this.route('dialog-slide-over');
this.route('dialog-nested');
});
});
16 changes: 16 additions & 0 deletions tests/dummy/app/templates/dialog/dialog-nested.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<div class='flex items-start justify-center w-screen h-full p-12 bg-gray-50'>
{{#let (hash value=false) as |state|}}
<button
type='button'
class='inline-flex items-center px-4 py-2 text-base font-medium text-white bg-indigo-600 border border-transparent rounded-md shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500'
{{on 'click' (set state.value true)}}
>
open 1
</button>
<Dialogs::Nested
@isOpen={{state.value}}
@onClose={{set state.value false}}
@level={{1}}
/>
{{/let}}
</div>
3 changes: 3 additions & 0 deletions tests/dummy/app/templates/index.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
<li>
<LinkTo @route='dialog.dialog-slide-over'>Dialog (slide over)</LinkTo>
</li>
<li>
<LinkTo @route='dialog.dialog-nested'>Dialog (nested)</LinkTo>
</li>
</ul>
</li>
</ul>
Expand Down
Loading

0 comments on commit d1b192c

Please sign in to comment.