Skip to content

Commit 4e99e5f

Browse files
authored
feat(AnalyticalTable): add noDataReason & accessibleRole prop to NoDataComponent (#7732)
1 parent afe9f15 commit 4e99e5f

File tree

7 files changed

+135
-29
lines changed

7 files changed

+135
-29
lines changed

packages/main/src/components/AnalyticalTable/AnalyticalTable.mdx

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,67 @@ const TableComponent = () => {
567567
568568
<Canvas of={ComponentStories.NoData} />
569569
570+
### Code
571+
572+
<details>
573+
574+
<summary>Show static code</summary>
575+
576+
```tsx
577+
function NoDataTable(props) {
578+
const [selected, setSelected] = useState('noData');
579+
const [filtered, setFiltered] = useState(false);
580+
const handleChange: SegmentedButtonPropTypes['onSelectionChange'] = (e) => {
581+
const { key } = e.detail.selectedItems[0].dataset;
582+
setSelected(key);
583+
if (key === 'data') {
584+
setFiltered(false);
585+
}
586+
};
587+
const handleClick: ToggleButtonPropTypes['onClick'] = (e) => {
588+
setFiltered(!!e.target.pressed);
589+
};
590+
591+
const NoDataComponent: AnalyticalTablePropTypes['NoDataComponent'] =
592+
selected === 'noData'
593+
? undefined
594+
: (props) => {
595+
return filtered ? (
596+
<IllustratedMessage role={props.accessibleRole} name={NoFilterResults} />
597+
) : (
598+
<IllustratedMessage role={props.accessibleRole} name={NoDataIllustration} />
599+
);
600+
};
601+
return (
602+
<>
603+
<SegmentedButton onSelectionChange={handleChange} accessibleName="Select data view mode">
604+
<SegmentedButtonItem selected={selected === 'noData'} data-key="noData">
605+
Default NoData Component
606+
</SegmentedButtonItem>
607+
<SegmentedButtonItem selected={selected === 'illustratedMessage'} data-key="illustratedMessage">
608+
IllustratedMessage NoData Component
609+
</SegmentedButtonItem>
610+
<SegmentedButtonItem selected={selected === 'data'} data-key="data">
611+
With Data
612+
</SegmentedButtonItem>
613+
</SegmentedButton>{' '}
614+
|{' '}
615+
<ToggleButton onClick={handleClick} pressed={filtered} disabled={selected === 'data'}>
616+
Table filtered
617+
</ToggleButton>
618+
<AnalyticalTable
619+
{...props}
620+
data={selected === 'data' ? props.data : []}
621+
globalFilterValue={filtered ? 'Non-existing text' : undefined}
622+
NoDataComponent={NoDataComponent}
623+
/>
624+
</>
625+
);
626+
}
627+
```
628+
629+
</details>
630+
570631
## Kitchen Sink
571632
572633
<Canvas of={ComponentStories.KitchenSink} />

packages/main/src/components/AnalyticalTable/AnalyticalTable.stories.tsx

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import '@ui5/webcomponents-icons/dist/delete.js';
55
import '@ui5/webcomponents-icons/dist/edit.js';
66
import '@ui5/webcomponents-icons/dist/settings.js';
77
import NoDataIllustration from '@ui5/webcomponents-fiori/dist/illustrations/NoData.js';
8+
import NoFilterResults from '@ui5/webcomponents-fiori/dist/illustrations/NoFilterResults.js';
89
import { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react';
910
import {
1011
AnalyticalTablePopinDisplay,
@@ -29,6 +30,8 @@ import { SegmentedButtonItem } from '../../webComponents/SegmentedButtonItem/ind
2930
import { Select } from '../../webComponents/Select/index.js';
3031
import { Tag } from '../../webComponents/Tag/index.js';
3132
import { Text } from '../../webComponents/Text/index.js';
33+
import type { ToggleButtonPropTypes } from '../../webComponents/ToggleButton/index.js';
34+
import { ToggleButton } from '../../webComponents/ToggleButton/index.js';
3235
import { FlexBox } from '../FlexBox/index.js';
3336
import { ObjectStatus } from '../ObjectStatus/index.js';
3437
import type { AnalyticalTableColumnDefinition, AnalyticalTablePropTypes } from './index.js';
@@ -590,8 +593,34 @@ export const CustomFilter: Story = {
590593
export const NoData: Story = {
591594
render(args, context) {
592595
const [selected, setSelected] = useState('noData');
596+
const [filtered, setFiltered] = useState(false);
593597
const handleChange: SegmentedButtonPropTypes['onSelectionChange'] = (e) => {
594-
setSelected(e.detail.selectedItems[0].dataset.key);
598+
const { key } = e.detail.selectedItems[0].dataset;
599+
setSelected(key);
600+
if (key === 'data') {
601+
setFiltered(false);
602+
}
603+
};
604+
const handleClick: ToggleButtonPropTypes['onClick'] = (e) => {
605+
setFiltered(!!e.target.pressed);
606+
};
607+
608+
const NoDataComponent: AnalyticalTablePropTypes['NoDataComponent'] =
609+
selected === 'noData'
610+
? undefined
611+
: (props) => {
612+
return filtered ? (
613+
<IllustratedMessage role={props.accessibleRole} name={NoFilterResults} />
614+
) : (
615+
<IllustratedMessage role={props.accessibleRole} name={NoDataIllustration} />
616+
);
617+
};
618+
619+
const tableProps = {
620+
...args,
621+
data: selected === 'data' ? args.data : [],
622+
globalFilterValue: filtered ? 'Non-existing text' : undefined,
623+
NoDataComponent: NoDataComponent,
595624
};
596625

597626
return (
@@ -606,27 +635,17 @@ export const NoData: Story = {
606635
<SegmentedButtonItem selected={selected === 'data'} data-key="data">
607636
With Data
608637
</SegmentedButtonItem>
609-
</SegmentedButton>
638+
</SegmentedButton>{' '}
639+
|{' '}
640+
<ToggleButton onClick={handleClick} pressed={filtered} disabled={selected === 'data'}>
641+
Table filtered
642+
</ToggleButton>
610643
{context.viewMode === 'story' ? (
611-
<AnalyticalTable
612-
{...args}
613-
data={selected === 'data' ? args.data : []}
614-
NoDataComponent={
615-
selected === 'noData' ? undefined : () => <IllustratedMessage role="gridcell" name={NoDataIllustration} />
616-
}
617-
/>
644+
<AnalyticalTable {...tableProps} />
618645
) : (
619646
<>
620647
<hr />
621-
<ToggleableTable
622-
{...args}
623-
data={selected === 'data' ? args.data : []}
624-
NoDataComponent={
625-
selected === 'noData'
626-
? undefined
627-
: () => <IllustratedMessage role="gridcell" name={NoDataIllustration} />
628-
}
629-
/>
648+
<ToggleableTable {...tableProps} />
630649
</>
631650
)}
632651
</>

packages/main/src/components/AnalyticalTable/defaults/NoDataComponent/index.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
interface NoDataComponentProps {
2-
noDataText: string;
3-
className: string;
4-
}
1+
import type { AnalyticalTablePropTypes } from '../../types/index.js';
52

6-
export const DefaultNoDataComponent = ({ noDataText, className }: NoDataComponentProps) => {
3+
export const DefaultNoDataComponent: AnalyticalTablePropTypes['NoDataComponent'] = (props) => {
4+
const { noDataText, className, accessibleRole } = props;
75
return (
8-
<div className={className} data-component-name="AnalyticalTableNoData" role="gridcell">
6+
<div className={className} data-component-name="AnalyticalTableNoData" role={accessibleRole}>
97
{noDataText}
108
</div>
119
);

packages/main/src/components/AnalyticalTable/index.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
useSortBy,
2525
useTable,
2626
} from 'react-table';
27+
import { AnalyticalTableNoDataReason } from '../../enums/AnalyticalTableNoDataReason.js';
2728
import { AnalyticalTablePopinDisplay } from '../../enums/AnalyticalTablePopinDisplay.js';
2829
import { AnalyticalTableScaleWidthMode } from '../../enums/AnalyticalTableScaleWidthMode.js';
2930
import { AnalyticalTableSelectionBehavior } from '../../enums/AnalyticalTableSelectionBehavior.js';
@@ -85,9 +86,9 @@ import type {
8586
AnalyticalTableDomRef,
8687
AnalyticalTablePropTypes,
8788
AnalyticalTableState,
89+
CellInstance,
8890
DivWithCustomScrollProp,
8991
TableInstance,
90-
CellInstance,
9192
} from './types/index.js';
9293
import {
9394
getCombinedElementsHeight,
@@ -325,8 +326,8 @@ const AnalyticalTable = forwardRef<AnalyticalTableDomRef, AnalyticalTablePropTyp
325326

326327
const noDataTextI18n = i18nBundle.getText(LIST_NO_DATA);
327328
const noDataTextFiltered = i18nBundle.getText(NO_DATA_FILTERED);
328-
const noDataTextLocal =
329-
noDataText ?? (tableState.filters?.length > 0 || tableState.globalFilter ? noDataTextFiltered : noDataTextI18n);
329+
const noDataFiltered = tableState.filters?.length > 0 || tableState.globalFilter;
330+
const noDataTextLocal = noDataText ?? (noDataFiltered ? noDataTextFiltered : noDataTextI18n);
330331

331332
const [componentRef, analyticalTableRef] = useSyncRef<AnalyticalTableDomRef>(ref);
332333
const [cbRef, scrollToRef] = useScrollToRef(componentRef, dispatch);
@@ -844,7 +845,14 @@ const AnalyticalTable = forwardRef<AnalyticalTableDomRef, AnalyticalTablePropTyp
844845
pleaseWaitText={i18nBundle.getText(PLEASE_WAIT)}
845846
/>
846847
) : (
847-
<NoDataComponent noDataText={noDataTextLocal} className={classNames.noData} />
848+
<NoDataComponent
849+
noDataText={noDataTextLocal}
850+
className={classNames.noData}
851+
noDataReason={
852+
noDataFiltered ? AnalyticalTableNoDataReason.Filtered : AnalyticalTableNoDataReason.Empty
853+
}
854+
accessibleRole="gridcell"
855+
/>
848856
)}
849857
</div>
850858
)}

packages/main/src/components/AnalyticalTable/types/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type {
1010
RefObject,
1111
SetStateAction,
1212
} from 'react';
13+
import type { AnalyticalTableNoDataReason } from '../../../enums/AnalyticalTableNoDataReason.js';
1314
import type { AnalyticalTablePopinDisplay } from '../../../enums/AnalyticalTablePopinDisplay.js';
1415
import type { AnalyticalTableScaleWidthMode } from '../../../enums/AnalyticalTableScaleWidthMode.js';
1516
import type { AnalyticalTableScrollMode } from '../../../enums/AnalyticalTableScrollMode.js';
@@ -1040,7 +1041,12 @@ export interface AnalyticalTablePropTypes extends Omit<CommonProps, 'title'> {
10401041
*
10411042
* @default DefaultNoDataComponent
10421043
*/
1043-
NoDataComponent?: ComponentType<any>;
1044+
NoDataComponent?: ComponentType<{
1045+
noDataText: string;
1046+
className: string;
1047+
noDataReason: AnalyticalTableNoDataReason | keyof typeof AnalyticalTableNoDataReason;
1048+
accessibleRole: 'gridcell';
1049+
}>;
10441050

10451051
/**
10461052
* Exposes the internal table instance.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/**
2+
* Different reasons why the `NoDataComponent` is displayed.
3+
*/
4+
export enum AnalyticalTableNoDataReason {
5+
/*
6+
* No data was passed to the table.
7+
*/
8+
Empty = 'Empty',
9+
/*
10+
* No results match the applied filters.
11+
*/
12+
Filtered = 'Filtered',
13+
}

packages/main/src/enums/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export * from './AnalyticalTableNoDataReason.js';
12
export * from './AnalyticalTablePopinDisplay.js';
23
export * from './AnalyticalTableScaleWidthMode.js';
34
export * from './AnalyticalTableScrollMode.js';

0 commit comments

Comments
 (0)