mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-09 03:18:44 +00:00
fix(ui): keep an outlived filter selection clearable, and defer the customer list
Two loading/empty transitions the disabled empty state got wrong. A selection made in a range that had options survives a move to a range that has none, and it still scopes the data below, so disabling the combobox outright took away the only control that could clear it. Disable it only when there is nothing selected to clear. The customer list defaulted to an empty array while its query was in flight, so the filter announced a range with no customers before anything had been read. Leave it undefined until the query resolves, as the tag list now does.
This commit is contained in:
parent
beed32eb60
commit
ceb2fa61c4
4 changed files with 42 additions and 2 deletions
|
|
@ -754,6 +754,19 @@ describe("UsagePage", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("should withhold the customer list while it is still loading", async () => {
|
||||
mockUseCustomers.mockReturnValue({ data: undefined, isLoading: true, error: null } as any);
|
||||
|
||||
renderWithProviders(<UsagePage {...defaultProps} />);
|
||||
|
||||
act(() => {
|
||||
fireEvent.change(screen.getByTestId("usage-view-select"), { target: { value: "customer" } });
|
||||
});
|
||||
|
||||
const entityUsage = await screen.findByTestId("entity-usage");
|
||||
expect(entityUsage).toHaveAttribute("data-entity-list", "null");
|
||||
});
|
||||
|
||||
it("should show agent usage view for admins", async () => {
|
||||
mockUseAgents.mockReturnValue({
|
||||
data: { agents: mockAgents },
|
||||
|
|
|
|||
|
|
@ -97,7 +97,9 @@ const UsagePage: React.FC<UsagePageProps> = ({ teams, organizations }) => {
|
|||
});
|
||||
|
||||
const [fetchedTags, setFetchedTags] = useState<FetchedForRange<EntityList[]> | null>(null);
|
||||
const { data: customers = [] } = useCustomers();
|
||||
// No [] default: an unresolved query must stay undefined so the customer
|
||||
// filter reads as loading rather than as a range with no customers.
|
||||
const { data: customers } = useCustomers();
|
||||
const { data: agentsResponse } = useAgents();
|
||||
const { data: currentUser } = useCurrentUser();
|
||||
const isAdmin = all_admin_roles.includes(userRole || "");
|
||||
|
|
|
|||
|
|
@ -104,6 +104,28 @@ describe("UsageExportHeader", () => {
|
|||
expect(screen.queryByPlaceholderText("Select tag to filter...")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should stay usable when a carried-over selection outlives its options", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onFiltersChange = vi.fn();
|
||||
renderWithProviders(
|
||||
<UsageExportHeader
|
||||
{...defaultProps}
|
||||
entityType="tag"
|
||||
showFilters
|
||||
filterLabel="Filter by tag"
|
||||
filterPlaceholder="Select tag to filter..."
|
||||
filterOptions={[]}
|
||||
selectedFilters={["prod"]}
|
||||
onFiltersChange={onFiltersChange}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByPlaceholderText("No tags with usage in this range")).toBeEnabled();
|
||||
|
||||
await user.click(screen.getByRole("button", { name: "Clear Filter by tag" }));
|
||||
expect(onFiltersChange).toHaveBeenCalledWith([]);
|
||||
});
|
||||
|
||||
it("should leave the filter enabled with its normal placeholder when options exist", () => {
|
||||
renderWithProviders(
|
||||
<UsageExportHeader
|
||||
|
|
|
|||
|
|
@ -59,6 +59,9 @@ const UsageExportHeader: React.FC<UsageExportHeaderProps> = ({
|
|||
const labelOf = (value: string) => filterOptions.find((option) => option.value === value)?.label ?? value;
|
||||
const hasNoOptions = filterOptions.length === 0;
|
||||
const emptyPlaceholder = `No ${entityType}s with usage in this range`;
|
||||
// A selection carried over from a range that did have options still scopes
|
||||
// the data below, so the control has to stay usable long enough to clear it.
|
||||
const isFilterDisabled = hasNoOptions && selectedFilters.length === 0;
|
||||
|
||||
const filterList = (
|
||||
<ComboboxContent anchor={anchor}>
|
||||
|
|
@ -76,7 +79,7 @@ const UsageExportHeader: React.FC<UsageExportHeaderProps> = ({
|
|||
const builtInFilter = (
|
||||
<Combobox
|
||||
multiple
|
||||
disabled={hasNoOptions}
|
||||
disabled={isFilterDisabled}
|
||||
items={optionValues}
|
||||
value={selectedFilters}
|
||||
onValueChange={(next: string[]) => onFiltersChange?.(next)}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue