Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

<Dialog/> feature/support for nesting dialogs #64

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What made this required? The @testing-library packages?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, then I updated it to last LTS version.


- 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