From 46d24599c1d85ddf41a24af35e73fcdd5326c906 Mon Sep 17 00:00:00 2001 From: nguyenlongdang0412 Date: Thu, 17 Sep 2026 20:53:57 +0700 Subject: [PATCH] fix(date-picker): anchor the calendar popover to the whole field DatePicker and DateRangePicker positioned their calendar against the calendar icon button, which sits at the end of the field. With the default align="start" the popover opened at the icon's left edge, hundreds of pixels right of the field and over neighbouring content. The popover is now anchored to the field root, so it opens flush under the field's left edge, matching how Select places its list. align="center" now centres on the field and align="end" is unchanged. Closes #218 --- CHANGELOG.md | 4 ++++ src/lib/components/DatePicker/DatePicker.svelte | 1 + .../DatePicker/DatePicker.svelte.spec.ts | 17 +++++++++++++++++ .../DateRangePicker/DateRangePicker.svelte | 1 + .../DateRangePicker.svelte.spec.ts | 17 +++++++++++++++++ 5 files changed, 40 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5344d0e..53f4cb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- **DatePicker**, **DateRangePicker** — the calendar is now anchored to the whole field instead of the calendar icon at its end, so with the default `align="start"` it opens flush under the field's left edge rather than floating to the right over neighbouring content. `align="center"` now centres on the field; `align="end"` is unchanged. ([#218](https://github.com/ndlabdev/sv5ui/issues/218)) + ## [2.7.0] - 2026-09-09 ### Added diff --git a/src/lib/components/DatePicker/DatePicker.svelte b/src/lib/components/DatePicker/DatePicker.svelte index d8e45cf..a8ccc39 100644 --- a/src/lib/components/DatePicker/DatePicker.svelte +++ b/src/lib/components/DatePicker/DatePicker.svelte @@ -241,6 +241,7 @@ {align} {alignOffset} collisionPadding={8} + customAnchor={rootEl} bind:ref={contentEl} class={classes.content} > diff --git a/src/lib/components/DatePicker/DatePicker.svelte.spec.ts b/src/lib/components/DatePicker/DatePicker.svelte.spec.ts index 0282bc5..033abfd 100644 --- a/src/lib/components/DatePicker/DatePicker.svelte.spec.ts +++ b/src/lib/components/DatePicker/DatePicker.svelte.spec.ts @@ -1,3 +1,4 @@ +import '../../../routes/layout.css' import { describe, expect, it, vi } from 'vitest' import { render } from 'vitest-browser-svelte' import DatePicker from './DatePicker.svelte' @@ -865,4 +866,20 @@ describe('DatePicker', () => { }) }) }) + + describe('popover anchor', () => { + it('aligns the calendar with the start of the field instead of the trigger icon', async () => { + const { container } = render(DatePicker) + container.style.paddingLeft = '48px' + await vi.waitFor(() => expect(getTrigger()).not.toBeNull()) + getTrigger()!.click() + await vi.waitFor(() => { + const field = getTrigger()!.parentElement!.getBoundingClientRect() + const trigger = getTrigger()!.getBoundingClientRect() + const content = getContent()!.getBoundingClientRect() + expect(trigger.left - field.left).toBeGreaterThan(100) + expect(Math.abs(content.left - field.left)).toBeLessThanOrEqual(1) + }) + }) + }) }) diff --git a/src/lib/components/DateRangePicker/DateRangePicker.svelte b/src/lib/components/DateRangePicker/DateRangePicker.svelte index c1531a6..2cf48f7 100644 --- a/src/lib/components/DateRangePicker/DateRangePicker.svelte +++ b/src/lib/components/DateRangePicker/DateRangePicker.svelte @@ -318,6 +318,7 @@ {align} {alignOffset} collisionPadding={8} + customAnchor={rootEl} bind:ref={contentEl} class={classes.content} > diff --git a/src/lib/components/DateRangePicker/DateRangePicker.svelte.spec.ts b/src/lib/components/DateRangePicker/DateRangePicker.svelte.spec.ts index 1df0095..583e3f4 100644 --- a/src/lib/components/DateRangePicker/DateRangePicker.svelte.spec.ts +++ b/src/lib/components/DateRangePicker/DateRangePicker.svelte.spec.ts @@ -1,3 +1,4 @@ +import '../../../routes/layout.css' import { describe, expect, it, vi } from 'vitest' import { render } from 'vitest-browser-svelte' import DateRangePicker from './DateRangePicker.svelte' @@ -850,4 +851,20 @@ describe('DateRangePicker', () => { }) }) }) + + describe('popover anchor', () => { + it('aligns the calendar with the start of the field instead of the trigger icon', async () => { + const { container } = render(DateRangePicker) + container.style.paddingLeft = '48px' + await vi.waitFor(() => expect(getTrigger()).not.toBeNull()) + getTrigger()!.click() + await vi.waitFor(() => { + const field = getTrigger()!.parentElement!.getBoundingClientRect() + const trigger = getTrigger()!.getBoundingClientRect() + const content = getContent()!.getBoundingClientRect() + expect(trigger.left - field.left).toBeGreaterThan(100) + expect(Math.abs(content.left - field.left)).toBeLessThanOrEqual(1) + }) + }) + }) })