Skip to content

Commit

Permalink
Merge pull request #108 from far-fetched/set-return-focus-on-clicked-…
Browse files Browse the repository at this point in the history
…element

✨ <Dialog/> Set return focus on clicked element
  • Loading branch information
GavinJoyce authored Nov 9, 2021
2 parents 1d1018f + 12f048f commit f8f6de5
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 21 deletions.
11 changes: 7 additions & 4 deletions addon/components/dialog.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,26 @@
aria-modal='true'
...attributes
{{headlessui-focus-trap
focusTrapOptions=(hash initialFocus=@initialFocus)
focusTrapOptions=(hash
initialFocus=@initialFocus
allowOutsideClick=this.allowOutsideClick
setReturnFocus=this.setReturnFocus
)
}}
{{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
isOpen=@isOpen
onClose=@onClose
onClose=this.onClose
Overlay=(component
'dialog/-overlay'
guid=this.overlayGuid
dialogGuid=this.guid
isOpen=@isOpen
onClose=@onClose
onClose=this.onClose
)
Title=(component
'dialog/-title'
Expand Down
19 changes: 19 additions & 0 deletions addon/components/dialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export default class DialogComponent extends Component<Args> {

guid = `${guidFor(this)}-headlessui-dialog`;
$portalRoot = getPortalRoot();
outsideClickedElement: HTMLElement | null = null;

handleEscapeKey = modifier(
(_element, [isOpen, onClose]: [boolean, () => void]) => {
Expand Down Expand Up @@ -103,6 +104,24 @@ export default class DialogComponent extends Component<Args> {
return `${this.guid}-description`;
}

@action
setReturnFocus(trigger: HTMLElement) {
return this.outsideClickedElement ? this.outsideClickedElement : trigger;
}

@action
allowOutsideClick(e: MouseEvent) {
let target = e.target as HTMLElement;

if (target && target.tagName !== 'BODY') {
this.outsideClickedElement = target;
}

this.onClose();

return true;
}

@action
onClose() {
if (this.dialogStackProvider.hasOpenChild(this)) return;
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
"ember-cli-babel": "^7.26.3",
"ember-cli-htmlbars": "^5.7.1",
"ember-cli-typescript": "^4.2.1",
"ember-click-outside-modifier": "^2.0.0",
"ember-concurrency": "^2.1.2",
"ember-element-helper": "^0.5.5",
"ember-set-helper": "^2.0.1",
Expand Down
38 changes: 31 additions & 7 deletions tests/integration/components/dialog-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -429,13 +429,33 @@ module('Integration | Component | <Dialog>', function (hooks) {

assertDialog({ state: DialogState.InvisibleUnmounted });

assertActiveElement(getByText('Trigger'));
await assertActiveElement(getByText('Trigger'));
});

todo(
'it should be possible to close the dialog, and keep focus on the focusable element',
async function () {}
);
test('it should be possible to close the dialog, and keep focus on the focusable element', async function () {
this.set('isOpen', false);

await render(hbs`
<button>Hello</button>
<button type="button" {{on "click" (set this "isOpen" true)}}>
Trigger
</button>
<Dialog @isOpen={{this.isOpen}} @onClose={{set this "isOpen" false}}>
Contents
<div tabindex="0"></div>
</Dialog>
`);

await click(getByText('Trigger'));

assertDialog({ state: DialogState.Visible });

await click(getByText('Hello'));

assertDialog({ state: DialogState.InvisibleUnmounted });

await assertActiveElement(getByText('Hello'));
});

test('it should stop propagating click events when clicking on the Dialog.Overlay', async function (assert) {
this.set('isOpen', true);
Expand Down Expand Up @@ -792,11 +812,15 @@ module('Integration | Component | <Dialog>', function (hooks) {
//close the top most dialog with ${strategy}
await action();

assert.equal(getDialogs(), 0, 'Verify that we have 0 open dialogs');
assert.equal(
getDialogs().length,
0,
'Verify that we have 0 open dialogs'
);

await assertActiveElement(
getByText('Open 1'),
'Verify that we have 1 open dialog'
'Verify that the `Open 1` got focused again'
);
}
);
Expand Down
9 changes: 0 additions & 9 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6218,15 +6218,6 @@ ember-cli@~3.27.0:
workerpool "^6.0.3"
yam "^1.0.0"

ember-click-outside-modifier@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ember-click-outside-modifier/-/ember-click-outside-modifier-2.0.0.tgz#8417a44db8bafef87e08f3614ad809ad1a6020e1"
integrity sha512-5FkTsSiSoDh4wL0mqJ2niOZH3ykevcRdLxA2pgZ9JqCMpvMqAQon5valAA4mvW6HiP9lnwm+GR/WijtL0dsdcg==
dependencies:
ember-cli-babel "^7.26.6"
ember-cli-htmlbars "^5.7.1"
ember-modifier "^2.1.2"

ember-compatibility-helpers@^1.1.2, ember-compatibility-helpers@^1.2.0, ember-compatibility-helpers@^1.2.1, ember-compatibility-helpers@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/ember-compatibility-helpers/-/ember-compatibility-helpers-1.2.4.tgz#70e0fef7048969141626eed6006f3880df612cd1"
Expand Down

0 comments on commit f8f6de5

Please sign in to comment.