Skip to content
Open
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
11 changes: 5 additions & 6 deletions assets/js/live_toast/live_toast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -446,13 +446,13 @@ export function createLiveToastHook(duration = 6000, maxItems = 3) {
}
}

window.addEventListener('phx:clear-flash', e => {
this.handleEvent('clear-flash', payload => {
this.pushEvent('lv:clear-flash', {
key: (e as CustomEvent<{ key: string }>).detail.key
key: (payload as { key: string }).key
})
})

window.addEventListener('flash-leave', async event => {
this.el.addEventListener('flash-leave', async event => {
if (event.target === this.el) {
// animate this flash sliding out
doAnimations.bind(this, duration, maxItems, this.el)()
Expand All @@ -466,9 +466,8 @@ export function createLiveToastHook(duration = 6000, maxItems = 3) {
await dismissToast.bind(this)(duration, maxItems)
})

window.addEventListener(`phx:${dismissEvent}`, async event => {
const detail = (event as CustomEvent<{ id?: string; uuid?: string }>)
.detail
this.handleEvent(dismissEvent, async payload => {
const detail = payload as { id?: string; uuid?: string }
const id = detail.id || `toast-${detail.uuid}`

if (id === this.el.id) {
Expand Down
57 changes: 56 additions & 1 deletion assets/test/live_toast.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ function mountToast(duration: number | 'Infinity' = 1000, countdown = false) {
const el = document.getElementById('toast-one') as HTMLElement
const group = document.getElementById('toast-group') as HTMLElement
const pushes: Array<[Element | string, string, Record<string, string>]> = []
const flashPushes: Array<[string, Record<string, string>]> = []
const serverListeners: Array<[string, EventListener]> = []
let hovered = false
let focused = false

Expand All @@ -87,7 +89,19 @@ function mountToast(duration: number | 'Infinity' = 1000, countdown = false) {
const callbacks = createLiveToastHook(1000, 3)
const hook = {
el,
pushEvent: () => undefined,
handleEvent(
event: string,
callback: (payload: Record<string, string>) => void
) {
const listener: EventListener = event => {
callback((event as CustomEvent<Record<string, string>>).detail)
}
serverListeners.push([`phx:${event}`, listener])
window.addEventListener(`phx:${event}`, listener)
},
pushEvent: (event: string, payload: Record<string, string>) => {
flashPushes.push([event, payload])
},
pushEventTo: (
target: Element | string,
event: string,
Expand All @@ -106,6 +120,15 @@ function mountToast(duration: number | 'Infinity' = 1000, countdown = false) {
group,
hook,
pushes,
flashPushes,
destroy() {
callbacks.destroyed.call(hook as never)
// LiveView removes listeners registered through handleEvent after destroyed.
for (const [event, listener] of serverListeners) {
window.removeEventListener(event, listener)
}
el.remove()
},
setFocused(value: boolean) {
focused = value
el.dispatchEvent(new Event(value ? 'focusin' : 'focusout'))
Expand Down Expand Up @@ -285,6 +308,38 @@ describe('LiveToast timed dismissal', () => {
expect(toast.pushes).toHaveLength(1)
})

test('stops handling server events after the hook is destroyed', async () => {
const toast = mountToast(0)

window.dispatchEvent(
new CustomEvent('phx:clear-flash', { detail: { key: 'info' } })
)
expect(toast.flashPushes).toEqual([['lv:clear-flash', { key: 'info' }]])

toast.destroy()
window.dispatchEvent(
new CustomEvent('phx:clear-flash', { detail: { key: 'info' } })
)
window.dispatchEvent(
new CustomEvent('phx:live-toast-dismiss', { detail: { id: toast.el.id } })
)
await settle()

expect(toast.flashPushes).toHaveLength(1)
expect(toast.pushes).toHaveLength(0)
})

test('handles flash-leave on the toast element', async () => {
const toast = mountToast(0)
toast.el.dataset.component = 'flash'
animationEvents.length = 0

toast.el.dispatchEvent(new Event('flash-leave'))
await settle()

expect(animationEvents).toContain('animate')
})

})

describe('LiveToast client API', () => {
Expand Down
10 changes: 5 additions & 5 deletions priv/static/live_toast.cjs.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions priv/static/live_toast.cjs.js.map

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions priv/static/live_toast.esm.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions priv/static/live_toast.esm.js.map

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions priv/static/live_toast.js
Original file line number Diff line number Diff line change
Expand Up @@ -1051,12 +1051,12 @@ var LiveMotion = (() => {
return;
}
}
window.addEventListener("phx:clear-flash", (e) => {
this.handleEvent("clear-flash", (payload) => {
this.pushEvent("lv:clear-flash", {
key: e.detail.key
key: payload.key
});
});
window.addEventListener("flash-leave", (event) => __async(this, null, function* () {
this.el.addEventListener("flash-leave", (event) => __async(this, null, function* () {
if (event.target === this.el) {
doAnimations.bind(this, duration, maxItems, this.el)();
yield animateOut.bind(this)();
Expand All @@ -1066,8 +1066,8 @@ var LiveMotion = (() => {
event.stopPropagation();
yield dismissToast.bind(this)(duration, maxItems);
}));
window.addEventListener(`phx:${dismissEvent}`, (event) => __async(this, null, function* () {
const detail = event.detail;
this.handleEvent(dismissEvent, (payload) => __async(this, null, function* () {
const detail = payload;
const id = detail.id || `toast-${detail.uuid}`;
if (id === this.el.id) {
yield dismissToast.bind(this)(duration, maxItems);
Expand Down
Loading