mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-06 08:16:43 +00:00
fix(ui): keep the usage filter visible when the caller's scope is empty
The tag usage filter was removed from the DOM whenever the tag list came back empty, so an internal user whose traffic all runs through team keys saw a blank Tag Usage panel with no filter and no explanation. Their scope is legitimately empty, but the page gave them no way to tell that apart from a broken or gated feature. Render the filter whenever the entity list has resolved, disabling it and swapping in an entity-specific empty message when there are no options. A still-unresolved list keeps hiding the control as before.
This commit is contained in:
parent
3746ba58d7
commit
cbac167a4a
4 changed files with 76 additions and 4 deletions
|
|
@ -65,10 +65,19 @@ vi.mock("@/components/EntityUsageExport/EntityUsageExportModal", () => ({
|
|||
}));
|
||||
|
||||
vi.mock("@/components/EntityUsageExport", () => ({
|
||||
UsageExportHeader: ({ filterLabel, filterSlot }: { filterLabel?: string; filterSlot?: ReactNode }) => (
|
||||
UsageExportHeader: ({
|
||||
filterLabel,
|
||||
filterSlot,
|
||||
showFilters,
|
||||
}: {
|
||||
filterLabel?: string;
|
||||
filterSlot?: ReactNode;
|
||||
showFilters?: boolean;
|
||||
}) => (
|
||||
<div>
|
||||
<span>Usage Export Header</span>
|
||||
<span>{filterLabel}</span>
|
||||
<span>{`show-filters:${showFilters === true}`}</span>
|
||||
{filterSlot}
|
||||
</div>
|
||||
),
|
||||
|
|
@ -739,6 +748,26 @@ describe("EntityUsage", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("should still request the filter when the caller's tag scope is empty", async () => {
|
||||
render(<EntityUsage {...defaultProps} entityList={[]} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockTagDailyActivityCall).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
expect(screen.getByText("show-filters:true")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should not request the filter while the entity list is still unresolved", async () => {
|
||||
render(<EntityUsage {...defaultProps} entityList={null} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockTagDailyActivityCall).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
expect(screen.getByText("show-filters:false")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should display Agent Activity tab for team entity type", async () => {
|
||||
render(<EntityUsage {...defaultProps} entityType="team" />);
|
||||
|
||||
|
|
|
|||
|
|
@ -661,7 +661,7 @@ const EntityUsage: React.FC<EntityUsageProps> = ({
|
|||
dateValue={dateValue}
|
||||
entityType={entityType}
|
||||
spendData={spendData}
|
||||
showFilters={filterSlot === undefined && entityList !== null && entityList.length > 0}
|
||||
showFilters={filterSlot === undefined && entityList !== null}
|
||||
filterSlot={filterSlot}
|
||||
filterLabel={getFilterLabel(entityType)}
|
||||
filterPlaceholder={getFilterPlaceholder(entityType)}
|
||||
|
|
|
|||
|
|
@ -84,4 +84,41 @@ describe("UsageExportHeader", () => {
|
|||
expect(screen.getByTestId("custom-filter")).toBeInTheDocument();
|
||||
expect(screen.queryByRole("combobox")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should keep the filter visible and disabled with an explanation when the caller has no options", () => {
|
||||
renderWithProviders(
|
||||
<UsageExportHeader
|
||||
{...defaultProps}
|
||||
entityType="tag"
|
||||
showFilters
|
||||
filterLabel="Filter by tag"
|
||||
filterPlaceholder="Select tag to filter..."
|
||||
filterOptions={[]}
|
||||
onFiltersChange={vi.fn()}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByText("Filter by tag")).toBeInTheDocument();
|
||||
const input = screen.getByPlaceholderText("No tags with usage in this range");
|
||||
expect(input).toBeDisabled();
|
||||
expect(screen.queryByPlaceholderText("Select tag to filter...")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should leave the filter enabled with its normal placeholder when options exist", () => {
|
||||
renderWithProviders(
|
||||
<UsageExportHeader
|
||||
{...defaultProps}
|
||||
entityType="tag"
|
||||
showFilters
|
||||
filterLabel="Filter by tag"
|
||||
filterPlaceholder="Select tag to filter..."
|
||||
filterOptions={[{ label: "prod", value: "prod" }]}
|
||||
onFiltersChange={vi.fn()}
|
||||
/>,
|
||||
);
|
||||
|
||||
const input = screen.getByPlaceholderText("Select tag to filter...");
|
||||
expect(input).toBeEnabled();
|
||||
expect(screen.queryByPlaceholderText("No tags with usage in this range")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -54,9 +54,11 @@ const UsageExportHeader: React.FC<UsageExportHeaderProps> = ({
|
|||
const anchor = useComboboxAnchor();
|
||||
const [isExportModalOpen, setIsExportModalOpen] = useState(false);
|
||||
|
||||
const hasFilters = filterSlot != null || (showFilters && filterOptions.length > 0);
|
||||
const hasFilters = filterSlot != null || showFilters;
|
||||
const optionValues = filterOptions.map((option) => option.value);
|
||||
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`;
|
||||
|
||||
const filterList = (
|
||||
<ComboboxContent anchor={anchor}>
|
||||
|
|
@ -74,6 +76,7 @@ const UsageExportHeader: React.FC<UsageExportHeaderProps> = ({
|
|||
const builtInFilter = (
|
||||
<Combobox
|
||||
multiple
|
||||
disabled={hasNoOptions}
|
||||
items={optionValues}
|
||||
value={selectedFilters}
|
||||
onValueChange={(next: string[]) => onFiltersChange?.(next)}
|
||||
|
|
@ -88,7 +91,10 @@ const UsageExportHeader: React.FC<UsageExportHeaderProps> = ({
|
|||
))
|
||||
}
|
||||
</ComboboxValue>
|
||||
<ComboboxChipsInput placeholder={filterPlaceholder} aria-label={filterPlaceholder} />
|
||||
<ComboboxChipsInput
|
||||
placeholder={hasNoOptions ? emptyPlaceholder : filterPlaceholder}
|
||||
aria-label={hasNoOptions ? emptyPlaceholder : filterPlaceholder}
|
||||
/>
|
||||
{selectedFilters.length > 0 && <ComboboxClear aria-label={`Clear ${filterLabel ?? "filters"}`} />}
|
||||
</ComboboxChips>
|
||||
{filterList}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue