Fix Datepicker getting stuck after the plugin throws in show() or hide() (#3255)

This commit is contained in:
Paweł Kuna authored and GitHub committed 2026-10-02 13:03:40 +02:00
1 parent 5aaa774470
commit a1b46ae15e
4 files changed
+58 -14

No files matched your search

@@ -0,0 +1,5 @@
---
"@tabler/core": patch
---
Fixed `Datepicker` getting stuck closed after the calendar plugin throws in `show()` or `hide()`.
+19 -12
View File
@@ -245,18 +245,22 @@ class Datepicker extends BaseComponent {
this._isShowing = true
this._skipPluginShow = false
this._calendar.show()
// The first show builds the popup and opens it a tick later
if (!this._isShown) {
await new Promise<void>((resolve) => {
this._resolveShown = resolve
afterPluginTimers(resolve)
})
// Reset even if the plugin throws, or every later `show()` returns early
try {
this._calendar.show()
// The first show builds the popup and opens it a tick later
if (!this._isShown) {
await new Promise<void>((resolve) => {
this._resolveShown = resolve
afterPluginTimers(resolve)
})
}
} finally {
this._resolveShown = null
this._isShowing = false
}
this._resolveShown = null
this._isShowing = false
}
// `async` like `show()`, so a plugin error reaches the caller as a rejection
@@ -283,8 +287,11 @@ class Datepicker extends BaseComponent {
})
this._isHiding = true
this._calendar.hide()
this._isHiding = false
try {
this._calendar.hide()
} finally {
this._isHiding = false
}
}
dispose(): void {
+32
View File
@@ -235,6 +235,38 @@ describe('Datepicker', () => {
expect(instance._isShown).toBe(false)
})
it('should show again after the plugin throws while showing', async () => {
fixtureEl.innerHTML = '<input type="text" data-bs-toggle="datepicker">'
const instance = new Datepicker(input())
const pluginShow = vi.spyOn(instance.calendar!, 'show').mockImplementationOnce(() => {
throw new Error('plugin failed')
})
await expect(instance.show()).rejects.toThrow('plugin failed')
expect(instance._isShowing).toBe(false)
await instance.show()
expect(pluginShow).toHaveBeenCalledTimes(2)
expect(instance._isShown).toBe(true)
})
it('should hide again after the plugin throws while hiding', async () => {
fixtureEl.innerHTML = '<input type="text" data-bs-toggle="datepicker">'
const instance = new Datepicker(input())
await instance.show()
vi.spyOn(instance.calendar!, 'hide').mockImplementationOnce(() => {
throw new Error('plugin failed')
})
await expect(instance.hide()).rejects.toThrow('plugin failed')
expect(instance._isHiding).toBe(false)
await instance.hide()
expect(instance._isShown).toBe(false)
})
})
describe('setSelectedDates', () => {
+2 -2
View File
@@ -212,7 +212,7 @@
},
{
"path": "./dist/js/tabler.js",
"maxSize": "58 kB"
"maxSize": "59 kB"
},
{
"path": "./dist/js/tabler.min.js",
@@ -220,7 +220,7 @@
},
{
"path": "./dist/js/tabler.esm.js",
"maxSize": "58 kB"
"maxSize": "59 kB"
},
{
"path": "./dist/js/tabler.esm.min.js",