Skip to content
Closed
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
35 changes: 35 additions & 0 deletions libs/widget-lib/src/widgetIframeLoading.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,41 @@ describe('widgetIframeLoading error UI styling', () => {

expect(document.head.querySelectorAll(`style[data-${ERROR_CLASS}]`).length).toBeLessThanOrEqual(1)
})

it('shows a loading state on the reload button while the retry is in progress', () => {
const { container, iframe } = setup()
failIframe(iframe)

const reloadBtn = container.querySelector<HTMLButtonElement>(`.${ERROR_CLASS} button`)
expect(reloadBtn?.disabled).toBe(false)

reloadBtn?.click()

// The panel stays mounted and the button is disabled (cursor:progress) while the retry runs.
expect(container.querySelector(`.${ERROR_CLASS}`)).not.toBeNull()
expect(reloadBtn?.disabled).toBe(true)
})

it('removes the error panel and reveals the iframe once the widget is ready', () => {
const { container, iframe, state } = setup()
failIframe(iframe)
expect(container.querySelector(`.${ERROR_CLASS}`)).not.toBeNull()
expect(iframe.style.display).toBe('none')

state.onWidgetReady()

expect(container.querySelector(`.${ERROR_CLASS}`)).toBeNull()
expect(iframe.style.display).toBe('')
})

it('keeps a single error panel across repeated failures on the same widget', () => {
const { container, iframe } = setup()

failIframe(iframe)
failIframe(iframe)

expect(container.querySelectorAll(`.${ERROR_CLASS}`).length).toBe(1)
})
})

function setup(): { container: HTMLElement; iframe: HTMLIFrameElement; state: LoadingState } {
Expand Down
21 changes: 17 additions & 4 deletions libs/widget-lib/src/widgetIframeLoading.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,34 @@ export function widgetIframeLoading(
let cancelled = false
let timeout: number | null = null

function removeLoadingErrorContainer(): void {
const existing = container.getElementsByClassName(WIDGET_LOADING_ERROR_CLASS)[0]
if (existing) container.removeChild(existing)
}

function onLoadingError(): void {
onWidgetLoadingError?.()

iframe.style.display = 'none'
ensureLoadingErrorStyles()

// Replace any previous error/loading panel so a repeated failure doesn't stack panels.
removeLoadingErrorContainer()

const loadingContainer = document.createElement('div')
const reloadBtn = document.createElement('button')
const errorText = document.createElement('p')

reloadBtn.innerText = 'Reload'
reloadBtn.addEventListener('click', () => {
container.removeChild(loadingContainer)
// Keep the panel mounted and switch it to a loading state so the retry is visibly in
// progress; it's cleared by onWidgetReady on success or replaced by onLoadingError on
// another failure.
reloadBtn.disabled = true
errorText.innerText = 'Loading…'

destroy(true)
iframe.src = 'about:blank'
iframe.style.display = ''

setTimeout(() => {
iframe.src = originalSrc
Expand Down Expand Up @@ -69,8 +81,9 @@ export function widgetIframeLoading(
onWidgetReady() {
if (timeout) clearTimeout(timeout)

const loadingContainer = document.getElementsByClassName(WIDGET_LOADING_ERROR_CLASS)[0]
if (loadingContainer) container.removeChild(loadingContainer)
// Reveal the iframe again — it is hidden while an error/retry panel is shown.
iframe.style.display = ''
removeLoadingErrorContainer()
},
cancelWidgetLoading() {
cancelled = true
Expand Down
Loading