Skip to content
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
50 changes: 50 additions & 0 deletions __tests__/controllers/popup_controller_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,56 @@ describe('PopupController', () => {
expect(Hellotext.eventEmitter.dispatch).toHaveBeenNthCalledWith(3, 'popup:closed')
})

describe('closing with Escape', () => {
const escape = (init = {}) => new KeyboardEvent('keydown', { key: 'Escape', cancelable: true, ...init })

it('dismisses the popup while the dialog is showing', () => {
const { element, dialog } = buildController({ hasBubble: false })

controller.connect()
controller.closeOnEscape(escape())

expect(element.hidden).toBe(true)
expect(dialog.hidden).toBe(true)
expect(Hellotext.eventEmitter.dispatch).toHaveBeenLastCalledWith('popup:closed')
})

it('leaves a bubble popup alone until its dialog is open', () => {
const { element, bubble } = buildController({ hasBubble: true })

controller.connect()
controller.closeOnEscape(escape())

expect(element.hidden).toBe(false)
expect(bubble.hidden).toBe(false)
expect(Hellotext.eventEmitter.dispatch).not.toHaveBeenCalledWith('popup:closed')
})

it('ignores Escape while the whole popup is hidden', () => {
const { element } = buildController({ hasBubble: false })

controller.connect()
element.hidden = true
Hellotext.eventEmitter.dispatch.mockClear()
controller.closeOnEscape(escape())

expect(Hellotext.eventEmitter.dispatch).not.toHaveBeenCalledWith('popup:closed')
})

it('ignores Escape already handled by another control or an input method', () => {
const { element } = buildController({ hasBubble: false })

controller.connect()

const handled = escape()
handled.preventDefault()
controller.closeOnEscape(handled)
controller.closeOnEscape(escape({ isComposing: true }))

expect(element.hidden).toBe(false)
})
})

it('validates the current step before moving to the next one', async () => {
const { stepOne, stepTwo, emailInput } = buildController({ hasBubble: false })

Expand Down
2 changes: 1 addition & 1 deletion dist/hellotext.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/popups.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ Use `Hellotext.on(event, callback)` to listen for popup events. Register listene

- `popup:mounted` - Emitted when the popup is mounted, before any automatic `popup:opened` event. The dialog may remain hidden because of device targeting or bubble mode.
- `popup:opened` - Emitted when the dialog becomes visible, either automatically or after a bubble click. Showing the bubble alone does not emit this event.
- `popup:closed` - Emitted when the visitor dismisses the popup.
- `popup:closed` - Emitted when the visitor dismisses the popup, with the close button or the Escape key.

These events do not include a payload.

Expand Down
12 changes: 12 additions & 0 deletions lib/controllers/popup_controller.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,18 @@ class _default extends _stimulus.Controller {
_hellotext.default.eventEmitter.dispatch('popup:closed');
}

/**
* Dismiss the popup with the Escape key, only while the popup and its dialog are showing, so Escape on the
* page never hides the launcher bubble or reacts to a key another control already handled.
*
* @param {KeyboardEvent} event - Escape keydown reaching the window.
* @returns {void}
*/
closeOnEscape(event) {
if (this.element.hidden || this.dialogTarget.hidden || event.defaultPrevented || event.isComposing) return;
this.close(event);
}

/**
* Validate the current step before advancing, or submit if this is the final step.
* Clear previous server validity errors first so corrected values can be checked.
Expand Down
12 changes: 12 additions & 0 deletions lib/controllers/popup_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,18 @@ export default class extends Controller {
Hellotext.eventEmitter.dispatch('popup:closed');
}

/**
* Dismiss the popup with the Escape key, only while the popup and its dialog are showing, so Escape on the
* page never hides the launcher bubble or reacts to a key another control already handled.
*
* @param {KeyboardEvent} event - Escape keydown reaching the window.
* @returns {void}
*/
closeOnEscape(event) {
if (this.element.hidden || this.dialogTarget.hidden || event.defaultPrevented || event.isComposing) return;
this.close(event);
}

/**
* Validate the current step before advancing, or submit if this is the final step.
* Clear previous server validity errors first so corrected values can be checked.
Expand Down
19 changes: 19 additions & 0 deletions src/controllers/popup_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,25 @@ export default class extends Controller {
Hellotext.eventEmitter.dispatch('popup:closed')
}

/**
* Dismiss the popup with the Escape key, only while the popup and its dialog are showing, so Escape on the
* page never hides the launcher bubble or reacts to a key another control already handled.
*
* @param {KeyboardEvent} event - Escape keydown reaching the window.
* @returns {void}
*/
closeOnEscape(event) {
if (
this.element.hidden ||
this.dialogTarget.hidden ||
event.defaultPrevented ||
event.isComposing
)
return

this.close(event)
}

/**
* Validate the current step before advancing, or submit if this is the final step.
* Clear previous server validity errors first so corrected values can be checked.
Expand Down
Loading