From 59e05fffbe2fd8c12d6798f4218cf55870df3776 Mon Sep 17 00:00:00 2001 From: Blake Niemyjski Date: Thu, 6 Aug 2026 15:46:23 -0500 Subject: [PATCH 1/2] fix(web): preserve custom date range values after quick selection --- .../date-range-picker.svelte | 6 +++ .../date-range-picker.svelte.test.ts | 51 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 src/Exceptionless.Web/ClientApp/src/lib/features/shared/components/date-range-picker/date-range-picker.svelte.test.ts diff --git a/src/Exceptionless.Web/ClientApp/src/lib/features/shared/components/date-range-picker/date-range-picker.svelte b/src/Exceptionless.Web/ClientApp/src/lib/features/shared/components/date-range-picker/date-range-picker.svelte index 0ff5f89763..239c330764 100644 --- a/src/Exceptionless.Web/ClientApp/src/lib/features/shared/components/date-range-picker/date-range-picker.svelte +++ b/src/Exceptionless.Web/ClientApp/src/lib/features/shared/components/date-range-picker/date-range-picker.svelte @@ -52,6 +52,12 @@ const isCustomValid = $derived(startValidation.valid && endValidation.valid && !!startValue && !!endValue); function selectRange(rangeValue: string) { + const range = extractRangeExpressions(rangeValue); + if (range) { + startValue = range.start ?? ''; + endValue = range.end ?? ''; + } + value = rangeValue; onselect?.(rangeValue); } diff --git a/src/Exceptionless.Web/ClientApp/src/lib/features/shared/components/date-range-picker/date-range-picker.svelte.test.ts b/src/Exceptionless.Web/ClientApp/src/lib/features/shared/components/date-range-picker/date-range-picker.svelte.test.ts new file mode 100644 index 0000000000..39f186d940 --- /dev/null +++ b/src/Exceptionless.Web/ClientApp/src/lib/features/shared/components/date-range-picker/date-range-picker.svelte.test.ts @@ -0,0 +1,51 @@ +import { fireEvent, render, screen, waitFor } from '@testing-library/svelte'; +import { describe, expect, it, vi } from 'vitest'; + +import DateRangePicker from './date-range-picker.svelte'; + +describe('DateRangePicker', () => { + it('allows applying a custom range after selecting the last 90 days', async () => { + const onselect = vi.fn(); + render(DateRangePicker, { + onselect, + value: '[now-30d TO now]' + }); + + await fireEvent.click(screen.getByRole('button', { name: 'Last 90 days' })); + await fireEvent.click(screen.getByRole('button', { name: 'Custom range' })); + + const startInput = screen.getByPlaceholderText('Start: now-1h, 2024-01-01'); + const endInput = screen.getByPlaceholderText('End: now, 2024-12-31'); + await fireEvent.input(startInput, { target: { value: 'now-1y' } }); + await fireEvent.input(endInput, { target: { value: 'now' } }); + + const applyButton = screen.getByRole('button', { name: 'Apply' }); + await waitFor(() => expect((applyButton as HTMLButtonElement).disabled).toBe(false)); + await fireEvent.click(applyButton); + + expect(onselect).toHaveBeenLastCalledWith('[now-1y TO now]'); + }); + + it('allows editing a custom range after selecting the last 90 days while custom range is open', async () => { + const onselect = vi.fn(); + render(DateRangePicker, { + onselect, + value: '[now-30d TO now]' + }); + + await fireEvent.click(screen.getByRole('button', { name: 'Custom range' })); + await fireEvent.click(screen.getByRole('button', { name: 'Last 90 days' })); + + const startInput = screen.getByPlaceholderText('Start: now-1h, 2024-01-01'); + const endInput = screen.getByPlaceholderText('End: now, 2024-12-31'); + expect((startInput as HTMLInputElement).value).toBe('now-90d'); + expect((endInput as HTMLInputElement).value).toBe('now'); + await fireEvent.input(startInput, { target: { value: 'now-1y' } }); + + const applyButton = screen.getByRole('button', { name: 'Apply' }); + await waitFor(() => expect((applyButton as HTMLButtonElement).disabled).toBe(false)); + await fireEvent.click(applyButton); + + expect(onselect).toHaveBeenLastCalledWith('[now-1y TO now]'); + }); +}); From 026301f1ec8ae1006e40f4c09333523f8a979e4c Mon Sep 17 00:00:00 2001 From: Blake Niemyjski Date: Thu, 6 Aug 2026 16:52:05 -0500 Subject: [PATCH 2/2] fix(web): initialize date range fields after remount --- .../date-range-picker.stories.ts | 13 +++++++++ .../date-range-picker.svelte | 27 +++++++++---------- .../date-range-picker.svelte.test.ts | 13 ++++----- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/src/Exceptionless.Web/ClientApp/src/lib/features/shared/components/date-range-picker/date-range-picker.stories.ts b/src/Exceptionless.Web/ClientApp/src/lib/features/shared/components/date-range-picker/date-range-picker.stories.ts index 3ad712a0cc..f3e4dc5f98 100644 --- a/src/Exceptionless.Web/ClientApp/src/lib/features/shared/components/date-range-picker/date-range-picker.stories.ts +++ b/src/Exceptionless.Web/ClientApp/src/lib/features/shared/components/date-range-picker/date-range-picker.stories.ts @@ -74,6 +74,19 @@ export const AutoSelectQuickTab: Story = { } }; +export const QuickRangeCustomEditing: Story = { + args: { + value: '[now-90d TO now]' + }, + parameters: { + docs: { + description: { + story: 'A persisted quick range should populate the Custom range fields when the section is opened after the picker mounts.' + } + } + } +}; + export const CommandSearchFiltering: Story = { args: { value: '[now-5m TO now]' diff --git a/src/Exceptionless.Web/ClientApp/src/lib/features/shared/components/date-range-picker/date-range-picker.svelte b/src/Exceptionless.Web/ClientApp/src/lib/features/shared/components/date-range-picker/date-range-picker.svelte index 239c330764..5ba2753812 100644 --- a/src/Exceptionless.Web/ClientApp/src/lib/features/shared/components/date-range-picker/date-range-picker.svelte +++ b/src/Exceptionless.Web/ClientApp/src/lib/features/shared/components/date-range-picker/date-range-picker.svelte @@ -1,6 +1,4 @@