diff --git a/.changeset/quiet-pugs-clap.md b/.changeset/quiet-pugs-clap.md new file mode 100644 index 0000000000..733a6f7d9b --- /dev/null +++ b/.changeset/quiet-pugs-clap.md @@ -0,0 +1,11 @@ +--- +'@hyperdx/app': patch +--- + +fix: keep the LLM dashboard scope filters clearable when their options fail to +load + +The session and user dropdowns were disabled whenever their distinct-value query +was loading or had failed. With a filter applied that left the user looking at a +scope they could see but could not remove — permanently, if the query kept +failing. They now stay interactive whenever a value is applied. diff --git a/packages/app/src/llm/__tests__/DistinctValueSelect.test.tsx b/packages/app/src/llm/__tests__/DistinctValueSelect.test.tsx index 8565aaf639..57647a9028 100644 --- a/packages/app/src/llm/__tests__/DistinctValueSelect.test.tsx +++ b/packages/app/src/llm/__tests__/DistinctValueSelect.test.tsx @@ -5,11 +5,12 @@ import { DistinctValueSelect } from '@/llm/dashboard/DistinctValueSelect'; let mockRows: Record[] | undefined = []; let mockLoading = false; +let mockError = false; jest.mock('@/hooks/useChartConfig', () => ({ useQueriedChartConfig: () => ({ data: mockRows == null ? undefined : { data: mockRows }, isLoading: mockLoading, - isError: false, + isError: mockError, }), })); @@ -28,6 +29,7 @@ describe('DistinctValueSelect', () => { beforeEach(() => { mockRows = []; mockLoading = false; + mockError = false; }); it('shows the applied value when it is present in the fetched options', () => { @@ -83,6 +85,34 @@ describe('DistinctValueSelect', () => { expect(onChange).not.toHaveBeenCalled(); }); + // Otherwise a failing distinct query strands the user with a filter they + // can see but cannot remove. + it('stays interactive while a value is applied even if the query fails', () => { + mockRows = undefined; + mockError = true; + renderWithMantine( + , + ); + + const input = screen.getByRole('combobox'); + expect(input).toHaveValue('alice@x.com'); + expect(input).not.toBeDisabled(); + }); + + it('disables the control while loading with nothing applied', () => { + mockRows = undefined; + mockLoading = true; + renderWithMantine( + , + ); + + expect(screen.getByRole('combobox')).toBeDisabled(); + }); + it('falls back to the placeholder when no value is applied', () => { mockRows = [{ value: 'bob@x.com' }]; renderWithMantine( diff --git a/packages/app/src/llm/dashboard/DistinctValueSelect.tsx b/packages/app/src/llm/dashboard/DistinctValueSelect.tsx index fe947d79b0..c760aa608e 100644 --- a/packages/app/src/llm/dashboard/DistinctValueSelect.tsx +++ b/packages/app/src/llm/dashboard/DistinctValueSelect.tsx @@ -89,7 +89,10 @@ export function DistinctValueSelect({ data={values} value={value || null} onChange={v => onChange(v ?? '')} - disabled={isLoading || isError} + // Never lock the control while a scope is applied: the options are of no + // use yet, but clearing the filter must stay possible. On a failing + // distinct query that is otherwise permanent. + disabled={(isLoading || isError) && !value} comboboxProps={{ withinPortal: false }} searchable clearable