diff --git a/packages/main/src/components/AnalyticalTable/docs/FeatureExamples/AnalyticalTableFeatureExamples.stories.tsx b/packages/main/src/components/AnalyticalTable/docs/FeatureExamples/AnalyticalTableFeatureExamples.stories.tsx index 07b2ec088dc..b43b46b796b 100644 --- a/packages/main/src/components/AnalyticalTable/docs/FeatureExamples/AnalyticalTableFeatureExamples.stories.tsx +++ b/packages/main/src/components/AnalyticalTable/docs/FeatureExamples/AnalyticalTableFeatureExamples.stories.tsx @@ -1,6 +1,7 @@ import dataSmall from '@sb/mockData/Friends50.json'; import dataLarge from '@sb/mockData/Friends500.json'; import type { Meta, StoryObj } from '@storybook/react-vite'; +import NoDataIllustration from '@ui5/webcomponents-fiori/dist/illustrations/NoData.js'; import '@ui5/webcomponents-icons/dist/sort-ascending.js'; import '@ui5/webcomponents-icons/dist/sort-descending.js'; import '@ui5/webcomponents-icons/dist/reset.js'; @@ -18,6 +19,7 @@ import { } from '../../../../enums/index.js'; import { Button } from '../../../../webComponents/Button/index.js'; import { Dialog } from '../../../../webComponents/Dialog/index.js'; +import { IllustratedMessage } from '../../../../webComponents/IllustratedMessage/index.js'; import { Input } from '../../../../webComponents/Input/index.js'; import { MultiComboBox } from '../../../../webComponents/MultiComboBox/index.js'; import { MultiComboBoxItem } from '../../../../webComponents/MultiComboBoxItem/index.js'; @@ -29,7 +31,7 @@ import { Text } from '../../../../webComponents/Text/index.js'; import { ToggleButton } from '../../../../webComponents/ToggleButton/index.js'; import { FlexBox } from '../../../FlexBox/index.js'; import { ObjectStatus } from '../../../ObjectStatus/index.js'; -import type { AnalyticalTableColumnDefinition } from '../../index.js'; +import type { AnalyticalTableColumnDefinition, AnalyticalTablePropTypes } from '../../index.js'; import { AnalyticalTable } from '../../index.js'; import meta from '../AnalyticalTable.stories.js'; @@ -456,9 +458,16 @@ export const LoadingStates: Story = { const [loading, setLoading] = useState(false); const [showOverlay, setShowOverlay] = useState(false); const [alwaysShowBusyIndicator, setAlwaysShowBusyIndicator] = useState(false); + const [customNoData, setCustomNoData] = useState(false); const data = useMemo(() => (hasData ? dataSmall : []), [hasData]); + const NoDataComponent: AnalyticalTablePropTypes['NoDataComponent'] = !customNoData + ? undefined + : loading + ? undefined + : (props) => ; + return ( <> @@ -474,6 +483,9 @@ export const LoadingStates: Story = { setHasData((prev) => !prev)}> Empty data + setCustomNoData((prev) => !prev)}> + Custom NoDataComponent + diff --git a/packages/main/src/components/AnalyticalTable/docs/FeatureExamples/FeatureExamples.mdx b/packages/main/src/components/AnalyticalTable/docs/FeatureExamples/FeatureExamples.mdx index d3df4365ed1..d364d9ad302 100644 --- a/packages/main/src/components/AnalyticalTable/docs/FeatureExamples/FeatureExamples.mdx +++ b/packages/main/src/components/AnalyticalTable/docs/FeatureExamples/FeatureExamples.mdx @@ -378,7 +378,20 @@ const ServerSideTable = () => { The table supports multiple loading indicators: a skeleton placeholder (when `loading` is true and data is empty), a `BusyIndicator` overlay (when `loading` is true and data exists), and `showOverlay` which displays a semi-transparent overlay without a loading indicator. When `loading` is active, user interactions are blocked as well. Set `alwaysShowBusyIndicator` to always show the BusyIndicator instead of the skeleton. -**Recommended:** Set `alwaysShowBusyIndicator` to `true` in most cases. The default skeleton loading indicator is only sufficient when loading times exceed 1 second. See [Fiori Skeleton Loading](https://www.sap.com/design-system/fiori-design-ios/ui-elements/patterns/skeleton-loading/?external) for design guidance. +**Recommended:** Set `alwaysShowBusyIndicator` to `true` in most cases. The default skeleton loading indicator should only be used when loading times exceed 1 second. See [Fiori Skeleton Loading](https://www.sap.com/design-system/fiori-design-ios/v26-4/components/progress-indicators/skeleton-loading) for design guidance. + +The following table summarizes which indicator is rendered for the empty and non-empty states: + +| `loading` | `data` | `alwaysShowBusyIndicator` | Result | +| --------- | ------ | ------------------------- | ------------------------------------------------------------------------- | +| `true` | filled | `false` | `BusyIndicator` overlay on top of the existing rows | +| `true` | empty | `false` | Skeleton loader (`TablePlaceholder`) — `NoDataComponent` is **not** shown | +| `true` | empty | `true` | `BusyIndicator` overlay on top of the `NoDataComponent` | +| `false` | empty | any | `NoDataComponent` only | + +The `BusyIndicator` is an overlay: it is layered on top of the table and dims the content beneath it, but it does not replace the normal render logic. This is why, with `alwaysShowBusyIndicator` and empty data, the `NoDataComponent` is rendered underneath the overlay while loading — there is no skeleton loader in this mode by design. + +For the default text `NoDataComponent` this is unobtrusive, but a richer custom empty state — like an `IllustratedMessage` — would flash beneath the overlay during the initial load, before any data has arrived. To avoid this, render the custom component only once loading has finished and fall back to the default "No data" placeholder in the meantime by returning `undefined`. Toggle **Custom NoDataComponent**, **loading**, and **alwaysShowBusyIndicator** in the example below to compare the patterns. @@ -396,6 +409,13 @@ The table supports multiple loading indicators: a skeleton placeholder (when `lo // Overlay without loading indicator + +// Custom NoDataComponent (e.g. IllustratedMessage) with alwaysShowBusyIndicator: +// guard it so the illustration doesn't flash beneath the overlay while loading. +const NoDataComponent = loading + ? undefined // fall back to the default "No data" placeholder during loading + : (props) => ; + ``` ## Controlled Selection