Inline ListChangedIndicator with titles, normalize screen gaps, and rename ResultPanel to ToolResultPanel
- Place ListChangedIndicator on same line as section titles using Group with space-between in ToolControls, ResourceControls, and PromptControls - Add ListChanged stories for ResourceControls and show indicators in ConnectedView stories - Normalize ScreenLayout gap from "xl" to "md" across all screens for consistent spacing - Rename ResultPanel to ToolResultPanel (component, interface, files, and stories) - Replace Grid layout in ToolsScreen with equal-width flex children for detail and result panels Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ed6d79c5ed
commit
5594e940c9
16 changed files with 69 additions and 56 deletions
|
|
@ -32,7 +32,7 @@ export function ListChangedIndicator({
|
||||||
<Group gap="xs">
|
<Group gap="xs">
|
||||||
<Dot />
|
<Dot />
|
||||||
<UpdateLabel>List updated</UpdateLabel>
|
<UpdateLabel>List updated</UpdateLabel>
|
||||||
<RefreshButton onClick={onRefresh}>↻ Refresh</RefreshButton>
|
<RefreshButton onClick={onRefresh}>Refresh</RefreshButton>
|
||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ function formatPlaceholder(name: string): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PromptArgumentsForm({
|
export function PromptArgumentsForm({
|
||||||
name,
|
|
||||||
description,
|
description,
|
||||||
arguments: promptArguments,
|
arguments: promptArguments,
|
||||||
argumentValues,
|
argumentValues,
|
||||||
|
|
@ -34,7 +33,7 @@ export function PromptArgumentsForm({
|
||||||
}: PromptArgumentsFormProps) {
|
}: PromptArgumentsFormProps) {
|
||||||
return (
|
return (
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
<Title order={4}>{name}</Title>
|
<Title order={4}>Prompt Arguments</Title>
|
||||||
{description && <DescriptionText>{description}</DescriptionText>}
|
{description && <DescriptionText>{description}</DescriptionText>}
|
||||||
{promptArguments.length > 0 && (
|
{promptArguments.length > 0 && (
|
||||||
<Stack gap="sm">
|
<Stack gap="sm">
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { ScrollArea, Stack, TextInput, Title } from "@mantine/core";
|
import { Group, ScrollArea, Stack, TextInput, Title } from "@mantine/core";
|
||||||
import { ListChangedIndicator } from "../../elements/ListChangedIndicator/ListChangedIndicator";
|
import { ListChangedIndicator } from "../../elements/ListChangedIndicator/ListChangedIndicator";
|
||||||
import { PromptListItem } from "../PromptListItem/PromptListItem";
|
import { PromptListItem } from "../PromptListItem/PromptListItem";
|
||||||
import type { PromptItem } from "../../screens/PromptsScreen/PromptsScreen";
|
import type { PromptItem } from "../../screens/PromptsScreen/PromptsScreen";
|
||||||
|
|
@ -30,8 +30,10 @@ export function PromptControls({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack gap="sm">
|
<Stack gap="sm">
|
||||||
<Title order={4}>Prompts</Title>
|
<Group justify="space-between">
|
||||||
<ListChangedIndicator visible={listChanged} onRefresh={onRefreshList} />
|
<Title order={4}>Prompts</Title>
|
||||||
|
<ListChangedIndicator visible={listChanged} onRefresh={onRefreshList} />
|
||||||
|
</Group>
|
||||||
<TextInput
|
<TextInput
|
||||||
placeholder="Search prompts..."
|
placeholder="Search prompts..."
|
||||||
value={searchText}
|
value={searchText}
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,13 @@ export const WithSearch: Story = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const ListChanged: Story = {
|
||||||
|
args: {
|
||||||
|
...Default.args,
|
||||||
|
listChanged: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export const Empty: Story = {
|
export const Empty: Story = {
|
||||||
args: {
|
args: {
|
||||||
resources: [],
|
resources: [],
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,12 @@
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Accordion, ScrollArea, Stack, TextInput, Title } from "@mantine/core";
|
import {
|
||||||
|
Accordion,
|
||||||
|
Group,
|
||||||
|
ScrollArea,
|
||||||
|
Stack,
|
||||||
|
TextInput,
|
||||||
|
Title,
|
||||||
|
} from "@mantine/core";
|
||||||
import { ListChangedIndicator } from "../../elements/ListChangedIndicator/ListChangedIndicator";
|
import { ListChangedIndicator } from "../../elements/ListChangedIndicator/ListChangedIndicator";
|
||||||
import { ResourceListItem } from "../ResourceListItem/ResourceListItem";
|
import { ResourceListItem } from "../ResourceListItem/ResourceListItem";
|
||||||
import { ResourceSubscribedItem } from "../ResourceSubscribedItem/ResourceSubscribedItem";
|
import { ResourceSubscribedItem } from "../ResourceSubscribedItem/ResourceSubscribedItem";
|
||||||
|
|
@ -68,8 +75,10 @@ export function ResourceControls({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack gap="sm">
|
<Stack gap="sm">
|
||||||
<Title order={4}>Resources</Title>
|
<Group justify="space-between">
|
||||||
<ListChangedIndicator visible={listChanged} onRefresh={onRefreshList} />
|
<Title order={4}>Resources</Title>
|
||||||
|
<ListChangedIndicator visible={listChanged} onRefresh={onRefreshList} />
|
||||||
|
</Group>
|
||||||
<TextInput
|
<TextInput
|
||||||
placeholder="Search..."
|
placeholder="Search..."
|
||||||
value={searchText}
|
value={searchText}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { ScrollArea, Stack, TextInput, Title } from "@mantine/core";
|
import { Group, ScrollArea, Stack, TextInput, Title } from "@mantine/core";
|
||||||
import { ListChangedIndicator } from "../../elements/ListChangedIndicator/ListChangedIndicator";
|
import { ListChangedIndicator } from "../../elements/ListChangedIndicator/ListChangedIndicator";
|
||||||
import { ToolListItem } from "../ToolListItem/ToolListItem";
|
import { ToolListItem } from "../ToolListItem/ToolListItem";
|
||||||
import type { ToolListItemProps } from "../ToolListItem/ToolListItem";
|
import type { ToolListItemProps } from "../ToolListItem/ToolListItem";
|
||||||
|
|
@ -32,8 +32,10 @@ export function ToolControls({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack gap="sm">
|
<Stack gap="sm">
|
||||||
<Title order={4}>Tools</Title>
|
<Group justify="space-between">
|
||||||
<ListChangedIndicator visible={listChanged} onRefresh={onRefreshList} />
|
<Title order={4}>Tools</Title>
|
||||||
|
<ListChangedIndicator visible={listChanged} onRefresh={onRefreshList} />
|
||||||
|
</Group>
|
||||||
<TextInput
|
<TextInput
|
||||||
placeholder="Search tools..."
|
placeholder="Search tools..."
|
||||||
value={searchText}
|
value={searchText}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||||
|
import { fn } from "storybook/test";
|
||||||
|
import { ToolResultPanel } from "./ToolResultPanel";
|
||||||
|
|
||||||
|
const meta: Meta<typeof ToolResultPanel> = {
|
||||||
|
title: "Groups/ToolResultPanel",
|
||||||
|
component: ToolResultPanel,
|
||||||
|
args: {
|
||||||
|
onClear: fn(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<typeof ToolResultPanel>;
|
||||||
|
|
||||||
|
export const Empty: Story = {
|
||||||
|
args: {
|
||||||
|
content: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TextResult: Story = {
|
||||||
|
args: {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text: "The current weather in San Francisco is 65°F and sunny.",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const JsonResult: Story = {
|
||||||
|
args: {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "json",
|
||||||
|
text: '{"temperature":65,"unit":"fahrenheit","condition":"sunny","city":"San Francisco"}',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ImageResult: Story = {
|
||||||
|
args: {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "image",
|
||||||
|
data: "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==",
|
||||||
|
mimeType: "image/png",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MixedContent: Story = {
|
||||||
|
args: {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text: "Here is the generated image based on your description:",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "image",
|
||||||
|
data: "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==",
|
||||||
|
mimeType: "image/png",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
import { Button, Group, Stack, Text, Title } from "@mantine/core";
|
||||||
|
import { ContentViewer } from "../../elements/ContentViewer/ContentViewer";
|
||||||
|
|
||||||
|
export interface ResultContentItem {
|
||||||
|
type: string;
|
||||||
|
text?: string;
|
||||||
|
data?: string;
|
||||||
|
mimeType?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ToolResultPanelProps {
|
||||||
|
content: ResultContentItem[];
|
||||||
|
onClear: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ClearButton = Button.withProps({
|
||||||
|
variant: "light",
|
||||||
|
size: "sm",
|
||||||
|
});
|
||||||
|
|
||||||
|
function resolveTextType(type: string): "text" | "json" {
|
||||||
|
return type === "text" ? "text" : "json";
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveMediaType(mimeType?: string): "image" | "audio" {
|
||||||
|
return mimeType?.startsWith("image") ? "image" : "audio";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ToolResultPanel({ content, onClear }: ToolResultPanelProps) {
|
||||||
|
return (
|
||||||
|
<Stack>
|
||||||
|
<Title order={4}>Results</Title>
|
||||||
|
{content.length === 0 ? (
|
||||||
|
<Text c="dimmed">No results yet</Text>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{content.map((item, index) => {
|
||||||
|
if (item.text !== undefined) {
|
||||||
|
return (
|
||||||
|
<ContentViewer
|
||||||
|
key={index}
|
||||||
|
type={resolveTextType(item.type)}
|
||||||
|
content={item.text}
|
||||||
|
copyable
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (item.data !== undefined) {
|
||||||
|
return (
|
||||||
|
<ContentViewer
|
||||||
|
key={index}
|
||||||
|
type={resolveMediaType(item.mimeType)}
|
||||||
|
content={item.data}
|
||||||
|
mimeType={item.mimeType}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<Group>
|
||||||
|
<ClearButton onClick={onClear}>Clear</ClearButton>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -14,7 +14,7 @@ export interface HistoryScreenProps {
|
||||||
const ScreenLayout = Flex.withProps({
|
const ScreenLayout = Flex.withProps({
|
||||||
variant: "screen",
|
variant: "screen",
|
||||||
h: "calc(100vh - var(--app-shell-header-height, 0px))",
|
h: "calc(100vh - var(--app-shell-header-height, 0px))",
|
||||||
gap: "xl",
|
gap: "md",
|
||||||
p: "xl",
|
p: "xl",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ const ALL_LEVELS_VISIBLE: Record<string, boolean> = {
|
||||||
const ScreenLayout = Flex.withProps({
|
const ScreenLayout = Flex.withProps({
|
||||||
variant: "screen",
|
variant: "screen",
|
||||||
h: "calc(100vh - var(--app-shell-header-height, 0px))",
|
h: "calc(100vh - var(--app-shell-header-height, 0px))",
|
||||||
gap: "xl",
|
gap: "md",
|
||||||
p: "xl",
|
p: "xl",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ export interface PromptsScreenProps {
|
||||||
const ScreenLayout = Flex.withProps({
|
const ScreenLayout = Flex.withProps({
|
||||||
variant: "screen",
|
variant: "screen",
|
||||||
h: "calc(100vh - var(--app-shell-header-height, 0px))",
|
h: "calc(100vh - var(--app-shell-header-height, 0px))",
|
||||||
gap: "xl",
|
gap: "md",
|
||||||
p: "xl",
|
p: "xl",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ export interface ResourcesScreenProps {
|
||||||
const ScreenLayout = Flex.withProps({
|
const ScreenLayout = Flex.withProps({
|
||||||
variant: "screen",
|
variant: "screen",
|
||||||
h: "calc(100vh - var(--app-shell-header-height, 0px))",
|
h: "calc(100vh - var(--app-shell-header-height, 0px))",
|
||||||
gap: "xl",
|
gap: "md",
|
||||||
p: "xl",
|
p: "xl",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ export interface TasksScreenProps {
|
||||||
const ScreenLayout = Flex.withProps({
|
const ScreenLayout = Flex.withProps({
|
||||||
variant: "screen",
|
variant: "screen",
|
||||||
h: "calc(100vh - var(--app-shell-header-height, 0px))",
|
h: "calc(100vh - var(--app-shell-header-height, 0px))",
|
||||||
gap: "xl",
|
gap: "md",
|
||||||
p: "xl",
|
p: "xl",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { fn } from "storybook/test";
|
||||||
import { ToolsScreen } from "./ToolsScreen";
|
import { ToolsScreen } from "./ToolsScreen";
|
||||||
import type { ToolListItemProps } from "../../groups/ToolListItem/ToolListItem";
|
import type { ToolListItemProps } from "../../groups/ToolListItem/ToolListItem";
|
||||||
import type { ToolDetailPanelProps } from "../../groups/ToolDetailPanel/ToolDetailPanel";
|
import type { ToolDetailPanelProps } from "../../groups/ToolDetailPanel/ToolDetailPanel";
|
||||||
import type { ResultPanelProps } from "../../groups/ResultPanel/ResultPanel";
|
import type { ToolResultPanelProps } from "../../groups/ToolResultPanel/ToolResultPanel";
|
||||||
|
|
||||||
const meta: Meta<typeof ToolsScreen> = {
|
const meta: Meta<typeof ToolsScreen> = {
|
||||||
title: "Screens/ToolsScreen",
|
title: "Screens/ToolsScreen",
|
||||||
|
|
@ -67,7 +67,7 @@ const selectedToolData: ToolDetailPanelProps = {
|
||||||
onCancel: fn(),
|
onCancel: fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const resultData: ResultPanelProps = {
|
const resultData: ToolResultPanelProps = {
|
||||||
content: [
|
content: [
|
||||||
{
|
{
|
||||||
type: "text",
|
type: "text",
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
import { Card, Flex, Grid, Stack, Text } from "@mantine/core";
|
import { Card, Flex, Stack, Text } from "@mantine/core";
|
||||||
import { ToolControls } from "../../groups/ToolControls/ToolControls";
|
import { ToolControls } from "../../groups/ToolControls/ToolControls";
|
||||||
import { ToolDetailPanel } from "../../groups/ToolDetailPanel/ToolDetailPanel";
|
import { ToolDetailPanel } from "../../groups/ToolDetailPanel/ToolDetailPanel";
|
||||||
import { ResultPanel } from "../../groups/ResultPanel/ResultPanel";
|
import { ToolResultPanel } from "../../groups/ToolResultPanel/ToolResultPanel";
|
||||||
import type { ToolListItemProps } from "../../groups/ToolListItem/ToolListItem";
|
import type { ToolListItemProps } from "../../groups/ToolListItem/ToolListItem";
|
||||||
import type { ToolDetailPanelProps } from "../../groups/ToolDetailPanel/ToolDetailPanel";
|
import type { ToolDetailPanelProps } from "../../groups/ToolDetailPanel/ToolDetailPanel";
|
||||||
import type { ResultPanelProps } from "../../groups/ResultPanel/ResultPanel";
|
import type { ToolResultPanelProps } from "../../groups/ToolResultPanel/ToolResultPanel";
|
||||||
|
|
||||||
export interface ToolsScreenProps {
|
export interface ToolsScreenProps {
|
||||||
tools: ToolListItemProps[];
|
tools: ToolListItemProps[];
|
||||||
selectedTool?: ToolDetailPanelProps;
|
selectedTool?: ToolDetailPanelProps;
|
||||||
result?: ResultPanelProps;
|
result?: ToolResultPanelProps;
|
||||||
listChanged: boolean;
|
listChanged: boolean;
|
||||||
searchText: string;
|
searchText: string;
|
||||||
onSearchChange: (text: string) => void;
|
onSearchChange: (text: string) => void;
|
||||||
|
|
@ -20,8 +20,9 @@ export interface ToolsScreenProps {
|
||||||
const ScreenLayout = Flex.withProps({
|
const ScreenLayout = Flex.withProps({
|
||||||
variant: "screen",
|
variant: "screen",
|
||||||
h: "calc(100vh - var(--app-shell-header-height, 0px))",
|
h: "calc(100vh - var(--app-shell-header-height, 0px))",
|
||||||
gap: "xl",
|
gap: "md",
|
||||||
p: "xl",
|
p: "xl",
|
||||||
|
align: "flex-start",
|
||||||
});
|
});
|
||||||
|
|
||||||
const Sidebar = Stack.withProps({
|
const Sidebar = Stack.withProps({
|
||||||
|
|
@ -34,10 +35,9 @@ const SidebarCard = Card.withProps({
|
||||||
padding: "lg",
|
padding: "lg",
|
||||||
});
|
});
|
||||||
|
|
||||||
const FullHeightCard = Card.withProps({
|
const ContentCard = Card.withProps({
|
||||||
withBorder: true,
|
withBorder: true,
|
||||||
padding: "lg",
|
padding: "lg",
|
||||||
h: "100%",
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const EmptyState = Text.withProps({
|
const EmptyState = Text.withProps({
|
||||||
|
|
@ -71,27 +71,21 @@ export function ToolsScreen({
|
||||||
</SidebarCard>
|
</SidebarCard>
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
|
|
||||||
<Grid align="stretch" flex={1}>
|
<ContentCard flex={1}>
|
||||||
<Grid.Col span={7}>
|
{selectedTool ? (
|
||||||
<FullHeightCard>
|
<ToolDetailPanel {...selectedTool} />
|
||||||
{selectedTool ? (
|
) : (
|
||||||
<ToolDetailPanel {...selectedTool} />
|
<EmptyState>Select a tool to view details</EmptyState>
|
||||||
) : (
|
)}
|
||||||
<EmptyState>Select a tool to view details</EmptyState>
|
</ContentCard>
|
||||||
)}
|
|
||||||
</FullHeightCard>
|
|
||||||
</Grid.Col>
|
|
||||||
|
|
||||||
<Grid.Col span={5}>
|
<ContentCard flex={1}>
|
||||||
<FullHeightCard>
|
{result ? (
|
||||||
{result ? (
|
<ToolResultPanel {...result} />
|
||||||
<ResultPanel {...result} />
|
) : (
|
||||||
) : (
|
<EmptyState>Results will appear here</EmptyState>
|
||||||
<EmptyState>Results will appear here</EmptyState>
|
)}
|
||||||
)}
|
</ContentCard>
|
||||||
</FullHeightCard>
|
|
||||||
</Grid.Col>
|
|
||||||
</Grid>
|
|
||||||
</ScreenLayout>
|
</ScreenLayout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -230,7 +230,7 @@ export const ToolsActive: Story = {
|
||||||
onClear: fn(),
|
onClear: fn(),
|
||||||
}}
|
}}
|
||||||
searchText=""
|
searchText=""
|
||||||
listChanged={false}
|
listChanged={true}
|
||||||
onSearchChange={fn()}
|
onSearchChange={fn()}
|
||||||
onRefreshList={fn()}
|
onRefreshList={fn()}
|
||||||
onSelectTool={fn()}
|
onSelectTool={fn()}
|
||||||
|
|
@ -446,7 +446,7 @@ export const ResourcesActive: Story = {
|
||||||
lastUpdated: "2026-03-17T11:15:00Z",
|
lastUpdated: "2026-03-17T11:15:00Z",
|
||||||
isSubscribed: false,
|
isSubscribed: false,
|
||||||
}}
|
}}
|
||||||
listChanged={false}
|
listChanged={true}
|
||||||
searchText=""
|
searchText=""
|
||||||
onSearchChange={fn()}
|
onSearchChange={fn()}
|
||||||
onRefreshList={fn()}
|
onRefreshList={fn()}
|
||||||
|
|
@ -513,7 +513,7 @@ export const PromptsActive: Story = {
|
||||||
{ role: "assistant", content: "Hola, como estas?" },
|
{ role: "assistant", content: "Hola, como estas?" },
|
||||||
],
|
],
|
||||||
}}
|
}}
|
||||||
listChanged={false}
|
listChanged={true}
|
||||||
searchText=""
|
searchText=""
|
||||||
onSearchChange={fn()}
|
onSearchChange={fn()}
|
||||||
onRefreshList={fn()}
|
onRefreshList={fn()}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue