feat(ui): add isPending prop and deprecate loading
Add `isPending` prop to Alert, Button, ButtonIcon, Table, and TableRoot, aligning with React Aria naming conventions. The `loading` prop is deprecated but remains functional as an alias. CSS selectors now target `data-ispending` instead of `data-loading` for pending state styling. The `data-loading` attribute is still emitted for backward compatibility. Internal Table hooks (`PaginationResult`, `UsePageCacheResult`) renamed `loading` to `isPending`. The `useTable` hook returns both `isPending` and `loading` on `tableProps` to preserve backward compatibility. Updated docs-ui documentation and stories accordingly. Signed-off-by: Johan Persson <johanopersson@gmail.com>
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/ui': patch
|
||||
---
|
||||
|
||||
Added `isPending` prop to Alert, Button, ButtonIcon, Table, and TableRoot as a replacement for the `loading` prop, aligning with React Aria naming conventions. The `loading` prop is now deprecated but still supported as an alias. CSS selectors now use `data-ispending` instead of `data-loading` for styling pending states; `data-loading` is still emitted for backward compatibility but will be removed alongside the `loading` prop.
|
||||
|
||||
**Affected components:** Alert, Button, ButtonIcon, Table, TableRoot
|
||||
@@ -119,20 +119,20 @@ export const WithActionsAndDescriptions = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export const LoadingStates = () => {
|
||||
export const PendingStates = () => {
|
||||
return (
|
||||
<Flex direction="column" gap="4">
|
||||
<Alert
|
||||
status="info"
|
||||
icon={true}
|
||||
loading
|
||||
isPending
|
||||
title="Processing your request..."
|
||||
/>
|
||||
<Alert status="success" icon={true} loading title="Saving changes..." />
|
||||
<Alert status="success" icon={true} isPending title="Saving changes..." />
|
||||
<Alert
|
||||
status="info"
|
||||
icon={true}
|
||||
loading
|
||||
isPending
|
||||
title="Processing your request"
|
||||
description="This may take a few moments. Please do not close this window."
|
||||
/>
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
statusVariantsSnippet,
|
||||
withDescriptionSnippet,
|
||||
withActionsSnippet,
|
||||
loadingStatesSnippet,
|
||||
pendingStatesSnippet,
|
||||
withoutIconsSnippet,
|
||||
customIconSnippet,
|
||||
} from './snippets';
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
StatusVariants,
|
||||
WithDescription,
|
||||
WithActions,
|
||||
LoadingStates,
|
||||
PendingStates,
|
||||
WithoutIcons,
|
||||
CustomIcon,
|
||||
} from './components';
|
||||
@@ -79,16 +79,16 @@ Include custom actions like buttons for interactive alerts.
|
||||
code={withActionsSnippet}
|
||||
/>
|
||||
|
||||
### Loading States
|
||||
### Pending State
|
||||
|
||||
The loading spinner replaces the icon to indicate an ongoing process.
|
||||
The pending spinner replaces the icon to indicate an ongoing process.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<LoadingStates />}
|
||||
code={loadingStatesSnippet}
|
||||
preview={<PendingStates />}
|
||||
code={pendingStatesSnippet}
|
||||
/>
|
||||
|
||||
### Without Icons
|
||||
|
||||
@@ -10,31 +10,45 @@ export const alertPropDefs: Record<string, PropDef> = {
|
||||
values: ['info', 'success', 'warning', 'danger'],
|
||||
responsive: true,
|
||||
default: 'info',
|
||||
description:
|
||||
'Visual status of the alert, which determines color and default icon.',
|
||||
},
|
||||
icon: {
|
||||
type: 'enum',
|
||||
values: ['boolean', 'React.ReactElement'],
|
||||
responsive: false,
|
||||
description:
|
||||
'Set to true to show the default status icon, or pass a custom icon element. Set to false to hide the icon.',
|
||||
},
|
||||
isPending: {
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
description:
|
||||
'Replaces the icon with a spinner to indicate a pending operation.',
|
||||
},
|
||||
loading: {
|
||||
type: 'enum',
|
||||
values: ['boolean'],
|
||||
responsive: false,
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
description: 'Deprecated. Use `isPending` instead.',
|
||||
},
|
||||
title: {
|
||||
type: 'enum',
|
||||
values: ['React.ReactNode'],
|
||||
responsive: false,
|
||||
description: 'Primary message displayed in the alert.',
|
||||
},
|
||||
description: {
|
||||
type: 'enum',
|
||||
values: ['React.ReactNode'],
|
||||
responsive: false,
|
||||
description: 'Additional detail shown below the title.',
|
||||
},
|
||||
customActions: {
|
||||
type: 'enum',
|
||||
values: ['React.ReactNode'],
|
||||
responsive: false,
|
||||
description:
|
||||
'Custom action buttons displayed on the right side of the alert.',
|
||||
},
|
||||
m: {
|
||||
type: 'enum',
|
||||
|
||||
@@ -97,13 +97,13 @@ export const withActionsAndDescriptionsSnippet = `<Alert
|
||||
}
|
||||
/>`;
|
||||
|
||||
export const loadingStatesSnippet = `<Flex direction="column" gap="4">
|
||||
<Alert status="info" icon={true} loading title="Processing your request..." />
|
||||
<Alert status="success" icon={true} loading title="Saving changes..." />
|
||||
export const pendingStatesSnippet = `<Flex direction="column" gap="4">
|
||||
<Alert status="info" icon={true} isPending title="Processing your request..." />
|
||||
<Alert status="success" icon={true} isPending title="Saving changes..." />
|
||||
<Alert
|
||||
status="info"
|
||||
icon={true}
|
||||
loading
|
||||
isPending
|
||||
title="Processing your request"
|
||||
description="This may take a few moments. Please do not close this window."
|
||||
/>
|
||||
|
||||
@@ -56,12 +56,12 @@ export const Disabled = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export const Loading = () => {
|
||||
export const Pending = () => {
|
||||
return (
|
||||
<ButtonIcon
|
||||
icon={<RiCloudLine />}
|
||||
variant="primary"
|
||||
loading
|
||||
isPending
|
||||
aria-label="Cloud"
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -7,9 +7,9 @@ import {
|
||||
variantsSnippet,
|
||||
sizesSnippet,
|
||||
disabledSnippet,
|
||||
loadingSnippet,
|
||||
isPendingSnippet,
|
||||
} from './snippets';
|
||||
import { Variants, Sizes, Disabled, Loading } from './components';
|
||||
import { Variants, Sizes, Disabled, Pending } from './components';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { Theming } from '@/components/Theming';
|
||||
import { ButtonIconDefinition } from '../../../utils/definitions';
|
||||
@@ -63,7 +63,7 @@ export const reactAriaUrls = {
|
||||
code={disabledSnippet}
|
||||
/>
|
||||
|
||||
### Loading
|
||||
### Pending
|
||||
|
||||
Shows a spinner during async operations.
|
||||
|
||||
@@ -71,8 +71,8 @@ Shows a spinner during async operations.
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<Loading />}
|
||||
code={loadingSnippet}
|
||||
preview={<Pending />}
|
||||
code={isPendingSnippet}
|
||||
/>
|
||||
|
||||
<Theming definition={ButtonIconDefinition} />
|
||||
|
||||
@@ -42,11 +42,16 @@ export const buttonIconPropDefs: Record<string, PropDef> = {
|
||||
default: 'false',
|
||||
description: 'Prevents interaction and applies disabled styling.',
|
||||
},
|
||||
loading: {
|
||||
isPending: {
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
description: 'Shows a spinner and disables the button.',
|
||||
},
|
||||
loading: {
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
description: 'Deprecated. Use `isPending` instead.',
|
||||
},
|
||||
type: {
|
||||
type: 'enum',
|
||||
values: ['button', 'submit', 'reset'],
|
||||
|
||||
@@ -20,4 +20,4 @@ export const disabledSnippet = `<Flex direction="row" gap="2">
|
||||
<ButtonIcon isDisabled icon={<RiCloudLine />} variant="tertiary" aria-label="Cloud" />
|
||||
</Flex>`;
|
||||
|
||||
export const loadingSnippet = `<ButtonIcon icon={<RiCloudLine />} variant="primary" loading aria-label="Cloud" />`;
|
||||
export const isPendingSnippet = `<ButtonIcon icon={<RiCloudLine />} variant="primary" isPending aria-label="Cloud" />`;
|
||||
|
||||
@@ -75,9 +75,9 @@ export const Destructive = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export const Loading = () => {
|
||||
export const Pending = () => {
|
||||
return (
|
||||
<Button variant="primary" loading={true}>
|
||||
<Button variant="primary" isPending={true}>
|
||||
Load more items
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
withIconsSnippet,
|
||||
disabledSnippet,
|
||||
destructiveSnippet,
|
||||
loadingSnippet,
|
||||
isPendingSnippet,
|
||||
asLinkSnippet,
|
||||
buttonSnippetUsage,
|
||||
buttonResponsiveSnippet,
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
WithIcons,
|
||||
Disabled,
|
||||
Destructive,
|
||||
Loading,
|
||||
Pending,
|
||||
AsLink,
|
||||
} from './components';
|
||||
|
||||
@@ -34,7 +34,7 @@ export const reactAriaUrls = {
|
||||
|
||||
<PageTitle
|
||||
title="Button"
|
||||
description="A button with primary, secondary, and tertiary variants, destructive styling, and loading state."
|
||||
description="A button with primary, secondary, and tertiary variants, destructive styling, and pending state."
|
||||
/>
|
||||
|
||||
<Snippet align="center" py={4} preview={<Variants />} code={variantsSnippet} />
|
||||
@@ -110,7 +110,7 @@ Use the `destructive` prop for dangerous actions like delete or remove.
|
||||
layout="side-by-side"
|
||||
/>
|
||||
|
||||
### Loading
|
||||
### Pending
|
||||
|
||||
Shows a spinner and disables interaction during async operations.
|
||||
|
||||
@@ -118,8 +118,8 @@ Shows a spinner and disables interaction during async operations.
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<Loading />}
|
||||
code={loadingSnippet}
|
||||
preview={<Pending />}
|
||||
code={isPendingSnippet}
|
||||
layout="side-by-side"
|
||||
/>
|
||||
|
||||
|
||||
@@ -48,11 +48,16 @@ export const buttonPropDefs: Record<string, PropDef> = {
|
||||
default: 'false',
|
||||
description: 'Prevents interaction and applies disabled styling.',
|
||||
},
|
||||
loading: {
|
||||
isPending: {
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
description: 'Shows a spinner and disables the button.',
|
||||
},
|
||||
loading: {
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
description: 'Deprecated. Use `isPending` instead.',
|
||||
},
|
||||
children: {
|
||||
type: 'enum',
|
||||
values: ['ReactNode'],
|
||||
|
||||
@@ -55,7 +55,7 @@ export const destructiveSnippet = `<Flex gap="4">
|
||||
</Button>
|
||||
</Flex>`;
|
||||
|
||||
export const loadingSnippet = `<Button variant="primary" loading={true}>
|
||||
export const isPendingSnippet = `<Button variant="primary" isPending={true}>
|
||||
Load more items
|
||||
</Button>`;
|
||||
|
||||
|
||||
@@ -202,11 +202,11 @@ Use `mode: 'cursor'` when your API uses cursor-based pagination (common with Gra
|
||||
|
||||
<CodeBlock code={tableCursorPaginationSnippet} />
|
||||
|
||||
### Loading States
|
||||
### Pending States
|
||||
|
||||
When fetching data, the table shows a loading state. If the user triggers a new query (by paginating, sorting, or searching) while previous data is displayed, the table enters a "stale" state where it continues showing the previous data until new data arrives. This prevents jarring layout shifts.
|
||||
When fetching data, the table shows a pending state. If the user triggers a new query (by paginating, sorting, or searching) while previous data is displayed, the table enters a "stale" state where it continues showing the previous data until new data arrives. This prevents jarring layout shifts.
|
||||
|
||||
You can access these states via `tableProps.loading` and `tableProps.isStale` if you need to render additional loading indicators.
|
||||
You can access these states via `tableProps.isPending` and `tableProps.isStale` if you need to render additional pending indicators.
|
||||
|
||||
## Combining Features
|
||||
|
||||
|
||||
@@ -166,7 +166,7 @@ export const useTableReturnPropDefs: Record<string, PropDef> = {
|
||||
description: (
|
||||
<>
|
||||
Props to spread onto the <Chip>Table</Chip> component. Includes data,
|
||||
loading, error, pagination, and sort state.
|
||||
isPending, error, pagination, and sort state.
|
||||
</>
|
||||
),
|
||||
},
|
||||
@@ -207,10 +207,15 @@ export const tablePropDefs: Record<string, PropDef> = {
|
||||
values: ['T[]'],
|
||||
description: 'Array of data items to display in the table.',
|
||||
},
|
||||
isPending: {
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
description: 'Whether the table is in a pending state.',
|
||||
},
|
||||
loading: {
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
description: 'Whether the table is in a loading state.',
|
||||
description: 'Deprecated. Use `isPending` instead.',
|
||||
},
|
||||
isStale: {
|
||||
type: 'boolean',
|
||||
@@ -466,17 +471,22 @@ export const tableRootPropDefs: Record<string, PropDef> = {
|
||||
</>
|
||||
),
|
||||
},
|
||||
loading: {
|
||||
isPending: {
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
description: (
|
||||
<>
|
||||
Whether the table is in a loading state (e.g., initial data fetch). Adds{' '}
|
||||
<Chip>aria-busy</Chip> attribute and <Chip>data-loading</Chip> data
|
||||
Whether the table is in a pending state (e.g., initial data fetch). Adds{' '}
|
||||
<Chip>aria-busy</Chip> attribute and <Chip>data-ispending</Chip> data
|
||||
attribute for styling.
|
||||
</>
|
||||
),
|
||||
},
|
||||
loading: {
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
description: 'Deprecated. Use `isPending` instead.',
|
||||
},
|
||||
};
|
||||
|
||||
export const columnPropDefs: Record<string, PropDef> = {
|
||||
|
||||
@@ -222,6 +222,9 @@ export const AlertDefinition: {
|
||||
readonly dataAttribute: true;
|
||||
readonly default: 'info';
|
||||
};
|
||||
readonly isPending: {
|
||||
readonly dataAttribute: true;
|
||||
};
|
||||
readonly loading: {
|
||||
readonly dataAttribute: true;
|
||||
};
|
||||
@@ -239,6 +242,7 @@ export const AlertDefinition: {
|
||||
export type AlertOwnProps = {
|
||||
status?: Responsive<'info' | 'success' | 'warning' | 'danger'>;
|
||||
icon?: boolean | ReactElement;
|
||||
isPending?: boolean;
|
||||
loading?: boolean;
|
||||
customActions?: ReactNode;
|
||||
title?: ReactNode;
|
||||
@@ -514,6 +518,9 @@ export const ButtonDefinition: {
|
||||
readonly destructive: {
|
||||
readonly dataAttribute: true;
|
||||
};
|
||||
readonly isPending: {
|
||||
readonly dataAttribute: true;
|
||||
};
|
||||
readonly loading: {
|
||||
readonly dataAttribute: true;
|
||||
};
|
||||
@@ -549,6 +556,9 @@ export const ButtonIconDefinition: {
|
||||
readonly dataAttribute: true;
|
||||
readonly default: 'primary';
|
||||
};
|
||||
readonly isPending: {
|
||||
readonly dataAttribute: true;
|
||||
};
|
||||
readonly loading: {
|
||||
readonly dataAttribute: true;
|
||||
};
|
||||
@@ -562,6 +572,7 @@ export type ButtonIconOwnProps = {
|
||||
size?: Responsive<'small' | 'medium'>;
|
||||
variant?: Responsive<'primary' | 'secondary' | 'tertiary'>;
|
||||
icon?: ReactElement;
|
||||
isPending?: boolean;
|
||||
loading?: boolean;
|
||||
className?: string;
|
||||
};
|
||||
@@ -627,6 +638,7 @@ export type ButtonOwnProps = {
|
||||
destructive?: boolean;
|
||||
iconStart?: ReactElement;
|
||||
iconEnd?: ReactElement;
|
||||
isPending?: boolean;
|
||||
loading?: boolean;
|
||||
children?: ReactNode;
|
||||
className?: string;
|
||||
@@ -2741,6 +2753,9 @@ export const TableDefinition: {
|
||||
readonly stale: {
|
||||
readonly dataAttribute: true;
|
||||
};
|
||||
readonly isPending: {
|
||||
readonly dataAttribute: true;
|
||||
};
|
||||
readonly loading: {
|
||||
readonly dataAttribute: true;
|
||||
};
|
||||
@@ -2844,8 +2859,10 @@ export interface TableProps<T extends TableItem> {
|
||||
// (undocumented)
|
||||
error?: Error;
|
||||
// (undocumented)
|
||||
isStale?: boolean;
|
||||
isPending?: boolean;
|
||||
// (undocumented)
|
||||
isStale?: boolean;
|
||||
// @deprecated (undocumented)
|
||||
loading?: boolean;
|
||||
// (undocumented)
|
||||
pagination: TablePaginationType;
|
||||
@@ -2867,6 +2884,7 @@ export const TableRoot: (props: TableRootProps) => JSX_2.Element;
|
||||
// @public (undocumented)
|
||||
export type TableRootOwnProps = {
|
||||
stale?: boolean;
|
||||
isPending?: boolean;
|
||||
loading?: boolean;
|
||||
};
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ const meta = preview.meta({
|
||||
icon: {
|
||||
control: 'boolean',
|
||||
},
|
||||
loading: {
|
||||
isPending: {
|
||||
control: 'boolean',
|
||||
},
|
||||
},
|
||||
@@ -208,25 +208,25 @@ export const WithActionsAndDescriptions = WithActions.extend({
|
||||
},
|
||||
});
|
||||
|
||||
export const LoadingVariants = meta.story({
|
||||
export const PendingVariants = meta.story({
|
||||
render: () => (
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>Info</Text>
|
||||
<Alert
|
||||
status="info"
|
||||
icon={true}
|
||||
loading
|
||||
isPending
|
||||
title="Processing your request..."
|
||||
/>
|
||||
|
||||
<Text>Success</Text>
|
||||
<Alert status="success" icon={true} loading title="Saving changes..." />
|
||||
<Alert status="success" icon={true} isPending title="Saving changes..." />
|
||||
|
||||
<Text>Warning</Text>
|
||||
<Alert
|
||||
status="warning"
|
||||
icon={true}
|
||||
loading
|
||||
isPending
|
||||
title="Checking for issues..."
|
||||
/>
|
||||
|
||||
@@ -234,27 +234,27 @@ export const LoadingVariants = meta.story({
|
||||
<Alert
|
||||
status="danger"
|
||||
icon={true}
|
||||
loading
|
||||
isPending
|
||||
title="Attempting recovery..."
|
||||
/>
|
||||
</Flex>
|
||||
),
|
||||
});
|
||||
|
||||
export const LoadingWithDescription = meta.story({
|
||||
export const PendingWithDescription = meta.story({
|
||||
render: () => (
|
||||
<Flex direction="column" gap="4">
|
||||
<Alert
|
||||
status="info"
|
||||
icon={true}
|
||||
loading
|
||||
isPending
|
||||
title="Processing your request"
|
||||
description="This may take a few moments. Please do not close this window."
|
||||
/>
|
||||
<Alert
|
||||
status="success"
|
||||
icon={true}
|
||||
loading
|
||||
isPending
|
||||
title="Deployment in Progress"
|
||||
description="Your application is being deployed to production. You'll receive a notification when complete."
|
||||
/>
|
||||
|
||||
@@ -32,7 +32,7 @@ import { AlertDefinition } from './definition';
|
||||
*
|
||||
* @remarks
|
||||
* The Alert component supports multiple status variants (info, success, warning, danger)
|
||||
* and can display icons, loading states, and custom actions. It automatically handles
|
||||
* and can display icons, pending states, and custom actions. It automatically handles
|
||||
* icon selection based on status when the icon prop is set to true.
|
||||
*
|
||||
* @example
|
||||
@@ -53,14 +53,14 @@ import { AlertDefinition } from './definition';
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
* With custom actions and loading state:
|
||||
* With custom actions and pending state:
|
||||
* ```tsx
|
||||
* <Alert
|
||||
* status="success"
|
||||
* icon={true}
|
||||
* title="Operation completed"
|
||||
* description="Your changes have been saved successfully."
|
||||
* loading={isProcessing}
|
||||
* isPending={isProcessing}
|
||||
* customActions={
|
||||
* <>
|
||||
* <Button size="small" variant="tertiary">Dismiss</Button>
|
||||
@@ -76,13 +76,21 @@ export const Alert = forwardRef(
|
||||
(props: AlertProps, ref: Ref<HTMLDivElement>) => {
|
||||
const { ownProps, restProps, dataAttributes, utilityStyle } = useDefinition(
|
||||
AlertDefinition,
|
||||
props,
|
||||
// Merge deprecated `loading` into `isPending` so data attributes and
|
||||
// internal logic only need to check a single prop.
|
||||
{
|
||||
...props,
|
||||
isPending:
|
||||
props.isPending || props.loading
|
||||
? true
|
||||
: props.isPending ?? props.loading,
|
||||
},
|
||||
);
|
||||
const {
|
||||
classes,
|
||||
status,
|
||||
icon,
|
||||
loading,
|
||||
isPending,
|
||||
customActions,
|
||||
title,
|
||||
description,
|
||||
@@ -132,7 +140,7 @@ export const Alert = forwardRef(
|
||||
data-has-description={description ? 'true' : 'false'}
|
||||
>
|
||||
<div className={classes.contentWrapper}>
|
||||
{loading ? (
|
||||
{isPending ? (
|
||||
<div className={classes.icon}>
|
||||
<ProgressBar
|
||||
aria-label="Loading"
|
||||
|
||||
@@ -36,6 +36,7 @@ export const AlertDefinition = defineComponent<AlertOwnProps>()({
|
||||
},
|
||||
propDefs: {
|
||||
status: { dataAttribute: true, default: 'info' },
|
||||
isPending: { dataAttribute: true },
|
||||
loading: { dataAttribute: true },
|
||||
icon: {},
|
||||
customActions: {},
|
||||
|
||||
@@ -21,6 +21,8 @@ import type { Responsive, MarginProps } from '../../types';
|
||||
export type AlertOwnProps = {
|
||||
status?: Responsive<'info' | 'success' | 'warning' | 'danger'>;
|
||||
icon?: boolean | ReactElement;
|
||||
isPending?: boolean;
|
||||
/** @deprecated Use `isPending` instead. */
|
||||
loading?: boolean;
|
||||
customActions?: ReactNode;
|
||||
title?: ReactNode;
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
&[data-loading='true'] {
|
||||
&[data-ispending='true'] {
|
||||
cursor: wait;
|
||||
}
|
||||
}
|
||||
@@ -66,7 +66,7 @@
|
||||
--fg: var(--bui-fg-solid);
|
||||
|
||||
&[data-disabled='true'],
|
||||
&[data-loading='true'] {
|
||||
&[data-ispending='true'] {
|
||||
--bg: var(--bui-bg-solid-disabled);
|
||||
--bg-hover: var(--bui-bg-solid-disabled);
|
||||
--bg-active: var(--bui-bg-solid-disabled);
|
||||
@@ -100,7 +100,7 @@
|
||||
--fg: var(--fg-solid-danger);
|
||||
|
||||
&[data-disabled='true'],
|
||||
&[data-loading='true'] {
|
||||
&[data-ispending='true'] {
|
||||
--bg: var(--bg-solid-danger-disabled);
|
||||
--bg-hover: var(--bg-solid-danger-disabled);
|
||||
--bg-active: var(--bg-solid-danger-disabled);
|
||||
@@ -137,7 +137,7 @@
|
||||
}
|
||||
|
||||
&[data-disabled='true'],
|
||||
&[data-loading='true'] {
|
||||
&[data-ispending='true'] {
|
||||
--bg-hover: var(--bg);
|
||||
--bg-active: var(--bg);
|
||||
--fg: var(--bui-fg-disabled);
|
||||
@@ -166,7 +166,7 @@
|
||||
--fg: var(--bui-fg-danger);
|
||||
|
||||
&[data-disabled='true'],
|
||||
&[data-loading='true'] {
|
||||
&[data-ispending='true'] {
|
||||
--bg-hover: var(--bg);
|
||||
--bg-active: var(--bg);
|
||||
--fg: var(--bui-fg-disabled);
|
||||
@@ -198,7 +198,7 @@
|
||||
}
|
||||
|
||||
&[data-disabled='true'],
|
||||
&[data-loading='true'] {
|
||||
&[data-ispending='true'] {
|
||||
--bg-hover: var(--bg);
|
||||
--bg-active: var(--bg);
|
||||
--fg: var(--bui-fg-disabled);
|
||||
@@ -226,7 +226,7 @@
|
||||
--fg: var(--bui-fg-danger);
|
||||
|
||||
&[data-disabled='true'],
|
||||
&[data-loading='true'] {
|
||||
&[data-ispending='true'] {
|
||||
--bg-hover: var(--bg);
|
||||
--bg-active: var(--bg);
|
||||
--fg: var(--bui-fg-disabled);
|
||||
@@ -268,7 +268,7 @@
|
||||
width: 100%;
|
||||
transition: opacity var(--loading-duration) ease-out;
|
||||
|
||||
.bui-Button[data-loading='true'] & {
|
||||
.bui-Button[data-ispending='true'] & {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@@ -282,7 +282,7 @@
|
||||
opacity: 0;
|
||||
transition: opacity var(--loading-duration) ease-in;
|
||||
|
||||
.bui-Button[data-loading='true'] & {
|
||||
.bui-Button[data-ispending='true'] & {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ export const Destructive = meta.story({
|
||||
<Button variant="primary" destructive isDisabled>
|
||||
Disabled
|
||||
</Button>
|
||||
<Button variant="primary" destructive loading>
|
||||
<Button variant="primary" destructive isPending>
|
||||
Loading
|
||||
</Button>
|
||||
</Flex>
|
||||
@@ -124,7 +124,7 @@ export const Destructive = meta.story({
|
||||
<Button variant="secondary" destructive isDisabled>
|
||||
Disabled
|
||||
</Button>
|
||||
<Button variant="secondary" destructive loading>
|
||||
<Button variant="secondary" destructive isPending>
|
||||
Loading
|
||||
</Button>
|
||||
</Flex>
|
||||
@@ -141,7 +141,7 @@ export const Destructive = meta.story({
|
||||
<Button variant="tertiary" destructive isDisabled>
|
||||
Disabled
|
||||
</Button>
|
||||
<Button variant="tertiary" destructive loading>
|
||||
<Button variant="tertiary" destructive isPending>
|
||||
Loading
|
||||
</Button>
|
||||
</Flex>
|
||||
@@ -254,94 +254,94 @@ export const Responsive = meta.story({
|
||||
},
|
||||
});
|
||||
|
||||
export const Loading = meta.story({
|
||||
export const Pending = meta.story({
|
||||
render: () => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isPending, setIsPending] = useState(false);
|
||||
|
||||
const handleClick = () => {
|
||||
setIsLoading(true);
|
||||
setIsPending(true);
|
||||
setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
setIsPending(false);
|
||||
}, 3000);
|
||||
};
|
||||
|
||||
return (
|
||||
<Button variant="primary" loading={isLoading} onPress={handleClick}>
|
||||
<Button variant="primary" isPending={isPending} onPress={handleClick}>
|
||||
Load more items
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export const LoadingVariants = meta.story({
|
||||
export const PendingVariants = meta.story({
|
||||
render: () => (
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>Primary</Text>
|
||||
<Flex align="center" gap="4">
|
||||
<Button variant="primary" size="small" loading>
|
||||
<Button variant="primary" size="small" isPending>
|
||||
Small Loading
|
||||
</Button>
|
||||
<Button variant="primary" size="medium" loading>
|
||||
<Button variant="primary" size="medium" isPending>
|
||||
Medium Loading
|
||||
</Button>
|
||||
<Button variant="primary" loading iconStart={<RiCloudLine />}>
|
||||
<Button variant="primary" isPending iconStart={<RiCloudLine />}>
|
||||
With Icon
|
||||
</Button>
|
||||
</Flex>
|
||||
|
||||
<Text>Secondary</Text>
|
||||
<Flex align="center" gap="4">
|
||||
<Button variant="secondary" size="small" loading>
|
||||
<Button variant="secondary" size="small" isPending>
|
||||
Small Loading
|
||||
</Button>
|
||||
<Button variant="secondary" size="medium" loading>
|
||||
<Button variant="secondary" size="medium" isPending>
|
||||
Medium Loading
|
||||
</Button>
|
||||
<Button variant="secondary" loading iconStart={<RiCloudLine />}>
|
||||
<Button variant="secondary" isPending iconStart={<RiCloudLine />}>
|
||||
With Icon
|
||||
</Button>
|
||||
</Flex>
|
||||
|
||||
<Text>Tertiary</Text>
|
||||
<Flex align="center" gap="4">
|
||||
<Button variant="tertiary" size="small" loading>
|
||||
<Button variant="tertiary" size="small" isPending>
|
||||
Small Loading
|
||||
</Button>
|
||||
<Button variant="tertiary" size="medium" loading>
|
||||
<Button variant="tertiary" size="medium" isPending>
|
||||
Medium Loading
|
||||
</Button>
|
||||
<Button variant="tertiary" loading iconStart={<RiCloudLine />}>
|
||||
<Button variant="tertiary" isPending iconStart={<RiCloudLine />}>
|
||||
With Icon
|
||||
</Button>
|
||||
</Flex>
|
||||
|
||||
<Text>Primary Destructive</Text>
|
||||
<Flex align="center" gap="4">
|
||||
<Button variant="primary" destructive size="small" loading>
|
||||
<Button variant="primary" destructive size="small" isPending>
|
||||
Small Loading
|
||||
</Button>
|
||||
<Button variant="primary" destructive size="medium" loading>
|
||||
<Button variant="primary" destructive size="medium" isPending>
|
||||
Medium Loading
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
destructive
|
||||
loading
|
||||
isPending
|
||||
iconStart={<RiCloudLine />}
|
||||
>
|
||||
With Icon
|
||||
</Button>
|
||||
</Flex>
|
||||
|
||||
<Text>Loading vs Disabled</Text>
|
||||
<Text>Pending vs Disabled</Text>
|
||||
<Flex align="center" gap="4">
|
||||
<Button variant="primary" loading>
|
||||
<Button variant="primary" isPending>
|
||||
Loading
|
||||
</Button>
|
||||
<Button variant="primary" isDisabled>
|
||||
Disabled
|
||||
</Button>
|
||||
<Button variant="primary" loading isDisabled>
|
||||
<Button variant="primary" isPending isDisabled>
|
||||
Both (Disabled Wins)
|
||||
</Button>
|
||||
</Flex>
|
||||
|
||||
@@ -43,7 +43,7 @@ import { ButtonDefinition } from './definition';
|
||||
* variant="primary"
|
||||
* size="medium"
|
||||
* iconStart={<IconComponent />}
|
||||
* loading={isSubmitting}
|
||||
* isPending={isSubmitting}
|
||||
* >
|
||||
* Submit
|
||||
* </Button>
|
||||
@@ -55,15 +55,23 @@ export const Button = forwardRef(
|
||||
(props: ButtonProps, ref: Ref<HTMLButtonElement>) => {
|
||||
const { ownProps, restProps, dataAttributes } = useDefinition(
|
||||
ButtonDefinition,
|
||||
props,
|
||||
// Merge deprecated `loading` into `isPending` so data attributes and
|
||||
// internal logic only need to check a single prop.
|
||||
{
|
||||
...props,
|
||||
isPending:
|
||||
props.isPending || props.loading
|
||||
? true
|
||||
: props.isPending ?? props.loading,
|
||||
},
|
||||
);
|
||||
const { classes, iconStart, iconEnd, loading, children } = ownProps;
|
||||
const { classes, iconStart, iconEnd, isPending, children } = ownProps;
|
||||
|
||||
return (
|
||||
<RAButton
|
||||
className={classes.root}
|
||||
ref={ref}
|
||||
isPending={loading}
|
||||
isPending={isPending}
|
||||
{...dataAttributes}
|
||||
{...restProps}
|
||||
>
|
||||
|
||||
@@ -34,6 +34,7 @@ export const ButtonDefinition = defineComponent<ButtonOwnProps>()({
|
||||
size: { dataAttribute: true, default: 'small' },
|
||||
variant: { dataAttribute: true, default: 'primary' },
|
||||
destructive: { dataAttribute: true },
|
||||
isPending: { dataAttribute: true },
|
||||
loading: { dataAttribute: true },
|
||||
iconStart: {},
|
||||
iconEnd: {},
|
||||
|
||||
@@ -25,6 +25,8 @@ export type ButtonOwnProps = {
|
||||
destructive?: boolean;
|
||||
iconStart?: ReactElement;
|
||||
iconEnd?: ReactElement;
|
||||
isPending?: boolean;
|
||||
/** @deprecated Use `isPending` instead. */
|
||||
loading?: boolean;
|
||||
children?: ReactNode;
|
||||
className?: string;
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
&[data-loading='true'] {
|
||||
&[data-ispending='true'] {
|
||||
cursor: wait;
|
||||
}
|
||||
}
|
||||
@@ -66,7 +66,7 @@
|
||||
--fg: var(--bui-fg-solid);
|
||||
|
||||
&[data-disabled='true'],
|
||||
&[data-loading='true'] {
|
||||
&[data-ispending='true'] {
|
||||
--bg: var(--bui-bg-solid-disabled);
|
||||
--bg-hover: var(--bui-bg-solid-disabled);
|
||||
--bg-active: var(--bui-bg-solid-disabled);
|
||||
@@ -104,7 +104,7 @@
|
||||
}
|
||||
|
||||
&[data-disabled='true'],
|
||||
&[data-loading='true'] {
|
||||
&[data-ispending='true'] {
|
||||
--bg-hover: var(--bg);
|
||||
--bg-active: var(--bg);
|
||||
--fg: var(--bui-fg-disabled);
|
||||
@@ -138,7 +138,7 @@
|
||||
}
|
||||
|
||||
&[data-disabled='true'],
|
||||
&[data-loading='true'] {
|
||||
&[data-ispending='true'] {
|
||||
--bg-hover: var(--bg);
|
||||
--bg-active: var(--bg);
|
||||
--fg: var(--bui-fg-disabled);
|
||||
@@ -179,7 +179,7 @@
|
||||
width: 100%;
|
||||
transition: opacity var(--loading-duration) ease-out;
|
||||
|
||||
.bui-ButtonIcon[data-loading='true'] & {
|
||||
.bui-ButtonIcon[data-ispending='true'] & {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@@ -193,7 +193,7 @@
|
||||
opacity: 0;
|
||||
transition: opacity var(--loading-duration) ease-in;
|
||||
|
||||
.bui-ButtonIcon[data-loading='true'] & {
|
||||
.bui-ButtonIcon[data-ispending='true'] & {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -82,14 +82,14 @@ export const Responsive = meta.story({
|
||||
render: args => <ButtonIcon {...args} icon={<RiCloudLine />} />,
|
||||
});
|
||||
|
||||
export const Loading = meta.story({
|
||||
export const Pending = meta.story({
|
||||
render: () => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isPending, setIsPending] = useState(false);
|
||||
|
||||
const handleClick = () => {
|
||||
setIsLoading(true);
|
||||
setIsPending(true);
|
||||
setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
setIsPending(false);
|
||||
}, 3000);
|
||||
};
|
||||
|
||||
@@ -97,14 +97,14 @@ export const Loading = meta.story({
|
||||
<ButtonIcon
|
||||
variant="primary"
|
||||
icon={<RiCloudLine />}
|
||||
loading={isLoading}
|
||||
isPending={isPending}
|
||||
onPress={handleClick}
|
||||
/>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export const LoadingVariants = meta.story({
|
||||
export const PendingVariants = meta.story({
|
||||
render: () => (
|
||||
<Flex direction="column" gap="4">
|
||||
<Text>Primary</Text>
|
||||
@@ -113,13 +113,13 @@ export const LoadingVariants = meta.story({
|
||||
variant="primary"
|
||||
size="small"
|
||||
icon={<RiCloudLine />}
|
||||
loading
|
||||
isPending
|
||||
/>
|
||||
<ButtonIcon
|
||||
variant="primary"
|
||||
size="medium"
|
||||
icon={<RiCloudLine />}
|
||||
loading
|
||||
isPending
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
@@ -129,13 +129,13 @@ export const LoadingVariants = meta.story({
|
||||
variant="secondary"
|
||||
size="small"
|
||||
icon={<RiCloudLine />}
|
||||
loading
|
||||
isPending
|
||||
/>
|
||||
<ButtonIcon
|
||||
variant="secondary"
|
||||
size="medium"
|
||||
icon={<RiCloudLine />}
|
||||
loading
|
||||
isPending
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
@@ -145,24 +145,24 @@ export const LoadingVariants = meta.story({
|
||||
variant="tertiary"
|
||||
size="small"
|
||||
icon={<RiCloudLine />}
|
||||
loading
|
||||
isPending
|
||||
/>
|
||||
<ButtonIcon
|
||||
variant="tertiary"
|
||||
size="medium"
|
||||
icon={<RiCloudLine />}
|
||||
loading
|
||||
isPending
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
<Text>Loading vs Disabled</Text>
|
||||
<Text>Pending vs Disabled</Text>
|
||||
<Flex align="center" gap="4">
|
||||
<ButtonIcon variant="primary" icon={<RiCloudLine />} loading />
|
||||
<ButtonIcon variant="primary" icon={<RiCloudLine />} isPending />
|
||||
<ButtonIcon variant="primary" icon={<RiCloudLine />} isDisabled />
|
||||
<ButtonIcon
|
||||
variant="primary"
|
||||
icon={<RiCloudLine />}
|
||||
loading
|
||||
isPending
|
||||
isDisabled
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
@@ -30,15 +30,23 @@ export const ButtonIcon = forwardRef(
|
||||
(props: ButtonIconProps, ref: Ref<HTMLButtonElement>) => {
|
||||
const { ownProps, restProps, dataAttributes } = useDefinition(
|
||||
ButtonIconDefinition,
|
||||
props,
|
||||
// Merge deprecated `loading` into `isPending` so data attributes and
|
||||
// internal logic only need to check a single prop.
|
||||
{
|
||||
...props,
|
||||
isPending:
|
||||
props.isPending || props.loading
|
||||
? true
|
||||
: props.isPending ?? props.loading,
|
||||
},
|
||||
);
|
||||
const { classes, icon, loading } = ownProps;
|
||||
const { classes, icon, isPending } = ownProps;
|
||||
|
||||
return (
|
||||
<RAButton
|
||||
className={classes.root}
|
||||
ref={ref}
|
||||
isPending={loading}
|
||||
isPending={isPending}
|
||||
{...dataAttributes}
|
||||
{...restProps}
|
||||
>
|
||||
|
||||
@@ -33,6 +33,7 @@ export const ButtonIconDefinition = defineComponent<ButtonIconOwnProps>()({
|
||||
propDefs: {
|
||||
size: { dataAttribute: true, default: 'small' },
|
||||
variant: { dataAttribute: true, default: 'primary' },
|
||||
isPending: { dataAttribute: true },
|
||||
loading: { dataAttribute: true },
|
||||
icon: {},
|
||||
className: {},
|
||||
|
||||
@@ -23,6 +23,8 @@ export type ButtonIconOwnProps = {
|
||||
size?: Responsive<'small' | 'medium'>;
|
||||
variant?: Responsive<'primary' | 'secondary' | 'tertiary'>;
|
||||
icon?: ReactElement;
|
||||
isPending?: boolean;
|
||||
/** @deprecated Use `isPending` instead. */
|
||||
loading?: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
min-height: 0;
|
||||
|
||||
&[data-stale='true'],
|
||||
&[data-loading='true'] {
|
||||
&[data-ispending='true'] {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,6 +107,7 @@ function useLiveRegionLabel(
|
||||
export function Table<T extends TableItem>({
|
||||
columnConfig,
|
||||
data,
|
||||
isPending = false,
|
||||
loading = false,
|
||||
isStale = false,
|
||||
error,
|
||||
@@ -119,6 +120,7 @@ export function Table<T extends TableItem>({
|
||||
style,
|
||||
virtualized,
|
||||
}: TableProps<T>) {
|
||||
const pending = isPending || loading;
|
||||
const {
|
||||
ownProps: { classes },
|
||||
} = useDefinition(TableWrapperDefinition, { className });
|
||||
@@ -137,7 +139,7 @@ export function Table<T extends TableItem>({
|
||||
onSelectionChange,
|
||||
} = selection || {};
|
||||
|
||||
const isInitialLoading = loading && !data;
|
||||
const isInitialLoading = pending && !data;
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
@@ -202,7 +204,7 @@ export function Table<T extends TableItem>({
|
||||
onSortChange={sort?.onSortChange}
|
||||
disabledKeys={disabledRows}
|
||||
stale={isStale}
|
||||
loading={isInitialLoading}
|
||||
isPending={isInitialLoading}
|
||||
aria-describedby={liveRegionId}
|
||||
>
|
||||
<TableHeader columns={visibleColumns}>
|
||||
|
||||
@@ -28,14 +28,22 @@ import { TableRootProps } from '../types';
|
||||
export const TableRoot = (props: TableRootProps) => {
|
||||
const { ownProps, restProps, dataAttributes } = useDefinition(
|
||||
TableDefinition,
|
||||
props,
|
||||
// Merge deprecated `loading` into `isPending` so data attributes and
|
||||
// internal logic only need to check a single prop.
|
||||
{
|
||||
...props,
|
||||
isPending:
|
||||
props.isPending || props.loading
|
||||
? true
|
||||
: props.isPending ?? props.loading,
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
<ReactAriaTable
|
||||
className={ownProps.classes.root}
|
||||
aria-label="Data table"
|
||||
aria-busy={ownProps.stale || ownProps.loading}
|
||||
aria-busy={ownProps.stale || ownProps.isPending}
|
||||
{...dataAttributes}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -52,6 +52,7 @@ export const TableDefinition = defineComponent<TableRootOwnProps>()({
|
||||
},
|
||||
propDefs: {
|
||||
stale: { dataAttribute: true },
|
||||
isPending: { dataAttribute: true },
|
||||
loading: { dataAttribute: true },
|
||||
},
|
||||
});
|
||||
|
||||
@@ -158,7 +158,7 @@ export interface UseTableResult<T extends TableItem, TFilter = unknown> {
|
||||
/** @internal */
|
||||
export interface PaginationResult<T> {
|
||||
data: T[] | undefined;
|
||||
loading: boolean;
|
||||
isPending: boolean;
|
||||
error: Error | undefined;
|
||||
totalCount: number | undefined;
|
||||
offset?: number;
|
||||
|
||||
@@ -48,7 +48,7 @@ export function useCompletePagination<T extends TableItem, TFilter>(
|
||||
const { sort, filter, search } = query;
|
||||
|
||||
const [items, setItems] = useState<T[] | undefined>(undefined);
|
||||
const [isLoading, setIsLoading] = useState(!data);
|
||||
const [isPending, setIsPending] = useState(!data);
|
||||
const [error, setError] = useState<Error | undefined>(undefined);
|
||||
const [loadCount, setLoadCount] = useState(0);
|
||||
|
||||
@@ -64,7 +64,7 @@ export function useCompletePagination<T extends TableItem, TFilter>(
|
||||
// Load data on mount and when loadCount changes (reload trigger)
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
setIsLoading(false);
|
||||
setIsPending(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ export function useCompletePagination<T extends TableItem, TFilter>(
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
setIsLoading(true);
|
||||
setIsPending(true);
|
||||
setError(undefined);
|
||||
|
||||
(async () => {
|
||||
@@ -82,12 +82,12 @@ export function useCompletePagination<T extends TableItem, TFilter>(
|
||||
const resolvedData = result instanceof Promise ? await result : result;
|
||||
if (!cancelled) {
|
||||
setItems(resolvedData);
|
||||
setIsLoading(false);
|
||||
setIsPending(false);
|
||||
}
|
||||
} catch (err) {
|
||||
if (!cancelled) {
|
||||
setError(err instanceof Error ? err : new Error(String(err)));
|
||||
setIsLoading(false);
|
||||
setIsPending(false);
|
||||
}
|
||||
}
|
||||
})();
|
||||
@@ -164,7 +164,7 @@ export function useCompletePagination<T extends TableItem, TFilter>(
|
||||
|
||||
return {
|
||||
data: paginatedData,
|
||||
loading: isLoading,
|
||||
isPending: isPending,
|
||||
error,
|
||||
totalCount,
|
||||
offset,
|
||||
|
||||
@@ -78,7 +78,7 @@ export function useCursorPagination<T extends TableItem, TFilter>(
|
||||
|
||||
return {
|
||||
data: cache.data,
|
||||
loading: cache.loading,
|
||||
isPending: cache.isPending,
|
||||
error: cache.error,
|
||||
totalCount: cache.totalCount,
|
||||
offset: undefined,
|
||||
|
||||
@@ -91,7 +91,7 @@ export function useOffsetPagination<T extends TableItem, TFilter>(
|
||||
|
||||
return {
|
||||
data: cache.data,
|
||||
loading: cache.loading,
|
||||
isPending: cache.isPending,
|
||||
error: cache.error,
|
||||
totalCount: cache.totalCount,
|
||||
offset: cache.currentCursor ?? 0,
|
||||
|
||||
@@ -48,7 +48,7 @@ export interface UsePageCacheOptions<T, TCursor extends CursorType = string> {
|
||||
|
||||
/** @internal */
|
||||
export interface UsePageCacheResult<T, TCursor extends CursorType = string> {
|
||||
loading: boolean;
|
||||
isPending: boolean;
|
||||
error: Error | undefined;
|
||||
data: T[] | undefined;
|
||||
totalCount: number | undefined;
|
||||
@@ -149,7 +149,7 @@ export function usePageCache<T, TCursor extends CursorType = string>(
|
||||
|
||||
const cacheStore = useRef(new PageCacheStore<T, TCursor>()).current;
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [isPending, setIsPending] = useState(true);
|
||||
const [error, setError] = useState<Error | undefined>(undefined);
|
||||
const [totalCount, setTotalCount] = useState<number | undefined>(undefined);
|
||||
|
||||
@@ -189,7 +189,7 @@ export function usePageCache<T, TCursor extends CursorType = string>(
|
||||
const abortController = new AbortController();
|
||||
abortControllerRef.current = abortController;
|
||||
|
||||
setLoading(true);
|
||||
setIsPending(true);
|
||||
setError(undefined);
|
||||
|
||||
try {
|
||||
@@ -215,14 +215,14 @@ export function usePageCache<T, TCursor extends CursorType = string>(
|
||||
setTotalCount(result.totalCount);
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
setIsPending(false);
|
||||
} catch (err) {
|
||||
if (abortController.signal.aborted) {
|
||||
return;
|
||||
}
|
||||
|
||||
setError(err instanceof Error ? err : new Error(String(err)));
|
||||
setLoading(false);
|
||||
setIsPending(false);
|
||||
}
|
||||
},
|
||||
[getData, initialCurrentCursor, currentCursor, cacheStore],
|
||||
@@ -239,18 +239,18 @@ export function usePageCache<T, TCursor extends CursorType = string>(
|
||||
}, []);
|
||||
|
||||
const onNextPage = useCallback(() => {
|
||||
if (loading) return;
|
||||
if (isPending) return;
|
||||
const page = cacheStore.get(currentCursor);
|
||||
if (!page?.nextCursor) return;
|
||||
goToPage('next');
|
||||
}, [loading, currentCursor, goToPage, cacheStore]);
|
||||
}, [isPending, currentCursor, goToPage, cacheStore]);
|
||||
|
||||
const onPreviousPage = useCallback(() => {
|
||||
if (loading) return;
|
||||
if (isPending) return;
|
||||
const page = cacheStore.get(currentCursor);
|
||||
if (!page?.prevCursor) return;
|
||||
goToPage('prev');
|
||||
}, [loading, currentCursor, goToPage, cacheStore]);
|
||||
}, [isPending, currentCursor, goToPage, cacheStore]);
|
||||
|
||||
const reload = useCallback(
|
||||
(reloadOptions?: { keepCurrentCursor?: boolean }) => {
|
||||
@@ -266,7 +266,7 @@ export function usePageCache<T, TCursor extends CursorType = string>(
|
||||
);
|
||||
|
||||
return {
|
||||
loading,
|
||||
isPending,
|
||||
error,
|
||||
data,
|
||||
totalCount,
|
||||
|
||||
@@ -52,7 +52,7 @@ function useTableProps<T extends TableItem>(
|
||||
}
|
||||
|
||||
const displayData = paginationResult.data ?? previousDataRef.current;
|
||||
const isStale = paginationResult.loading && displayData !== undefined;
|
||||
const isStale = paginationResult.isPending && displayData !== undefined;
|
||||
|
||||
const pagination = useMemo(() => {
|
||||
if (paginationOptions.type === 'none') {
|
||||
@@ -104,7 +104,8 @@ function useTableProps<T extends TableItem>(
|
||||
return useMemo(
|
||||
() => ({
|
||||
data: displayData,
|
||||
loading: paginationResult.loading,
|
||||
isPending: paginationResult.isPending,
|
||||
loading: paginationResult.isPending,
|
||||
isStale,
|
||||
error: paginationResult.error,
|
||||
pagination,
|
||||
@@ -112,7 +113,7 @@ function useTableProps<T extends TableItem>(
|
||||
}),
|
||||
[
|
||||
displayData,
|
||||
paginationResult.loading,
|
||||
paginationResult.isPending,
|
||||
isStale,
|
||||
paginationResult.error,
|
||||
pagination,
|
||||
|
||||
@@ -274,7 +274,7 @@ export const LoadingState: Story = {
|
||||
<Table
|
||||
columnConfig={columns}
|
||||
data={undefined}
|
||||
loading={true}
|
||||
isPending
|
||||
pagination={{ type: 'none' }}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -42,6 +42,8 @@ export interface SortState {
|
||||
/** @public */
|
||||
export type TableRootOwnProps = {
|
||||
stale?: boolean;
|
||||
isPending?: boolean;
|
||||
/** @deprecated Use `isPending` instead. */
|
||||
loading?: boolean;
|
||||
};
|
||||
|
||||
@@ -263,6 +265,8 @@ export type VirtualizedProp =
|
||||
export interface TableProps<T extends TableItem> {
|
||||
columnConfig: readonly ColumnConfig<T>[];
|
||||
data: T[] | undefined;
|
||||
isPending?: boolean;
|
||||
/** @deprecated Use `isPending` instead. */
|
||||
loading?: boolean;
|
||||
isStale?: boolean;
|
||||
error?: Error;
|
||||
|
||||
Reference in New Issue
Block a user