Skip to content

feat(AnalyticalTable): allow passing custom header popovers #6576

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import ValueState from '@ui5/webcomponents-base/dist/types/ValueState.js';
import { ThemingParameters } from '@ui5/webcomponents-react-base';
import { useCallback, useEffect, useMemo, useRef, useState, version as reactVersion } from 'react';
import type { AnalyticalTableDomRef, AnalyticalTablePropTypes } from '../..';
import type {
AnalyticalTableColumnDefinition,
AnalyticalTableDomRef,
AnalyticalTablePropTypes,
PopoverDomRef
} from '../..';
import {
Popover,
AnalyticalTable,
AnalyticalTableHooks,
AnalyticalTableScaleWidthMode,
Expand All @@ -17,6 +23,7 @@ import {
import { useManualRowSelect } from './pluginHooks/useManualRowSelect';
import { useRowDisableSelection } from './pluginHooks/useRowDisableSelection';
import { cssVarToRgb, cypressPassThroughTestsFactory } from '@/cypress/support/utils';
import { getUi5TagWithSuffix } from '@/packages/main/src/internal/utils.js';

const generateMoreData = (count) => {
return new Array(count).fill('').map((item, index) => ({
Expand Down Expand Up @@ -3233,6 +3240,62 @@ describe('AnalyticalTable', () => {
cy.get('[data-visible-row-index="2"][data-visible-column-index="0"]').should('have.attr', 'aria-label', 'Name A ');
});

it('custom header popover', () => {
const columns: AnalyticalTableColumnDefinition[] = [
{ Header: 'Name', accessor: 'name' },
{
Header: 'Custom Popover',
accessor: 'age',
Popover: (instance) => {
const ref = useRef<PopoverDomRef>(null);
const { popoverProps } = instance;
const { setOpen, openerRef } = popoverProps;

useEffect(() => {
if (ref.current && openerRef.current) {
void customElements.whenDefined(getUi5TagWithSuffix('ui5-popover')).then(() => {
ref.current.opener = openerRef.current;
ref.current.open = true;
});
}
}, []);

return (
<Popover
ref={ref}
data-testid="popover"
onClose={() => {
setOpen(false);
}}
>
<button
onClick={(e) => {
e.stopPropagation();
setOpen(false);
}}
>
Close Popover
</button>
</Popover>
);
}
}
];
cy.mount(<AnalyticalTable data={groupableData} columns={columns} sortable />);

cy.findByText('Name').click();
cy.get('[data-component-name="ATHeaderPopover"]').should('be.visible');
cy.findByTestId('popover').should('not.exist');

cy.findByText('Custom Popover').click();
cy.get('[data-component-name="ATHeaderPopover"]').should('not.exist');
cy.findByTestId('popover').should('be.visible');

cy.findByText('Close Popover').click();
cy.findByTestId('popover').should('not.exist');
cy.get('[data-component-name="ATHeaderPopover"]').should('not.exist');
});

cypressPassThroughTestsFactory(AnalyticalTable, { data, columns });
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,10 @@ import * as ComponentStories from './AnalyticalTable.stories';

**Required Attributes**

| Attribute | Type | Description |
| ---------- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `accessor` | `string OR ((row: any, rowIndex: number) => any)` | This `string`/`function` is used to build the data model for your column.<br /><br />**Note**: You can also specify deeply nested values with accessors like `info.hobby` or even `address[0].street`<br /> |
| `id` | `string` | Defines the unique ID for the column. It is used by reference in things like sorting, grouping, filtering etc.<br /><br />**Note:** If no `accessor` is set, or the `accessor` is a function, the `id` property has to be set. |
| Attribute | Type | Description |
| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `accessor` | `string` OR <br />`(originalRow: Record<string, any>, rowIndex: number, row: RowType,` <br />`parentRows: RowType[], data: Record<string, any>[]) => any` | This `string`/`function` is used to build the data model for your column.<br /><br />**Note**: You can also specify deeply nested values with accessors like `info.hobby` or even `address[0].street`.<br /> |
| `id` | `string` | Defines the unique ID for the column. It is used by reference in things like sorting, grouping, filtering, etc.<br /><br />**Note:** If no `accessor` is set, or the `accessor` is a function, the `id` property is mandatory. |

**Optional Properties**

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@ import type { Virtualizer } from '@tanstack/react-virtual';
import { useStylesheet } from '@ui5/webcomponents-react-base';
import { forwardRef, Fragment } from 'react';
import type { DivWithCustomScrollProp } from '../types/index.js';
import { RenderColumnTypes } from '../types/index.js';
import { classNames, styleData } from './Resizer.module.css.js';
import { ColumnHeader } from './index.js';

interface ColumnHeaderContainerProps {
headerProps: Record<string, any>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
headerGroup: Record<string, any>;
onSort: (e: CustomEvent<{ column: unknown; sortDirection: string }>) => void;
onGroupByChanged: (e: CustomEvent<{ column?: Record<string, unknown>; isGrouped?: boolean }>) => void;
resizeInfo: Record<string, unknown>;
isRtl: boolean;
columnVirtualizer: Virtualizer<DivWithCustomScrollProp, Element>;
Expand All @@ -19,17 +18,7 @@ interface ColumnHeaderContainerProps {
}

export const ColumnHeaderContainer = forwardRef<HTMLDivElement, ColumnHeaderContainerProps>((props, ref) => {
const {
headerProps,
headerGroup,
onSort,
onGroupByChanged,
resizeInfo,
isRtl,
columnVirtualizer,
uniqueId,
showVerticalEndBorder
} = props;
const { headerProps, headerGroup, resizeInfo, isRtl, columnVirtualizer, uniqueId, showVerticalEndBorder } = props;

useStylesheet(styleData, 'Resizer');

Expand Down Expand Up @@ -77,15 +66,13 @@ export const ColumnHeaderContainer = forwardRef<HTMLDivElement, ColumnHeaderCont
id={`${uniqueId}${rest?.id ?? ''}`}
columnId={rest.id}
visibleColumnIndex={index}
onSort={onSort}
onGroupBy={onGroupByChanged}
headerTooltip={column.headerTooltip}
isDraggable={!column.disableDragAndDrop && !resizeInfo.isResizingColumn}
virtualColumn={virtualColumn}
columnVirtualizer={columnVirtualizer}
isRtl={isRtl}
>
{column.render('Header')}
{column.render(RenderColumnTypes.Header)}
</ColumnHeader>
</Fragment>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,12 @@ import type {
import { useRef, useState } from 'react';
import { Icon } from '../../../webComponents/Icon/index.js';
import { Text } from '../../../webComponents/Text/index.js';
import type { ColumnType } from '../types/ColumnType.js';
import type { DivWithCustomScrollProp } from '../types/index.js';
import type { ColumnType, DivWithCustomScrollProp } from '../types/index.js';
import { RenderColumnTypes } from '../types/index.js';
import { classNames, styleData } from './ColumnHeader.module.css.js';
import { ColumnHeaderModal } from './ColumnHeaderModal.js';

export interface ColumnHeaderProps {
visibleColumnIndex: number;
onSort?: (e: CustomEvent<{ column: unknown; sortDirection: string }>) => void;
onGroupBy?: (e: CustomEvent<{ column: unknown; isGrouped: boolean }>) => void;
onDragStart: DragEventHandler<HTMLDivElement>;
onDragOver: DragEventHandler<HTMLDivElement>;
onDrop: DragEventHandler<HTMLDivElement>;
Expand Down Expand Up @@ -64,8 +61,6 @@ export const ColumnHeader = (props: ColumnHeaderProps) => {
columnId,
className,
style,
onSort,
onGroupBy,
onDragEnter,
onDragOver,
onDragStart,
Expand Down Expand Up @@ -110,7 +105,7 @@ export const ColumnHeader = (props: ColumnHeaderProps) => {
const style: CSSProperties = {};

if (column.hAlign) {
style.textAlign = column.hAlign.toLowerCase() as any;
style.textAlign = column.hAlign.toLowerCase();
}

if (column.isSorted) margin++;
Expand Down Expand Up @@ -246,17 +241,15 @@ export const ColumnHeader = (props: ColumnHeaderProps) => {
{column.isGrouped && <Icon name={iconGroup} aria-hidden />}
</div>
</div>
{hasPopover && popoverOpen && (
<ColumnHeaderModal
isRtl={isRtl}
column={column}
onSort={onSort}
onGroupBy={onGroupBy}
openerRef={columnHeaderRef}
open={popoverOpen}
setPopoverOpen={setPopoverOpen}
/>
)}
{hasPopover &&
popoverOpen &&
// render the popover and add the props to the table instance
column.render(RenderColumnTypes.Popover, {
popoverProps: {
openerRef: columnHeaderRef,
setOpen: setPopoverOpen
}
})}
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ import type {
TableInstance,
TriggerScrollState
} from '../types/index.js';
import { RenderColumnTypes } from '../types/index.js';
import { getSubRowsByString } from '../util/index.js';
import { EmptyRow } from './EmptyRow.js';
import { RowSubComponent as SubComponent } from './RowSubComponent.js';

interface VirtualTableBodyProps {
classes: Record<string, string>;
prepareRow: (row: unknown) => void;
prepareRow: TableInstance['prepareRow'];
rows: TableInstance['rows'];
isTreeTable?: AnalyticalTablePropTypes['isTreeTable'];
internalRowHeight: number;
Expand Down Expand Up @@ -244,29 +245,29 @@ export const VirtualTableBody = (props: VirtualTableBodyProps) => {
...directionStyles
}
};
let contentToRender;
let contentToRender: RenderColumnTypes;
if (
cell.column.id === '__ui5wcr__internal_highlight_column' ||
cell.column.id === '__ui5wcr__internal_selection_column' ||
cell.column.id === '__ui5wcr__internal_navigation_column'
) {
contentToRender = 'Cell';
contentToRender = RenderColumnTypes.Cell;
} else if (isTreeTable || (!alwaysShowSubComponent && RowSubComponent)) {
contentToRender = 'Expandable';
contentToRender = RenderColumnTypes.Expandable;
} else if (
cell.isGrouped ||
(manualGroupBy &&
cell.column.isGrouped &&
getSubRowsByString(subRowsKey, row.original) != null &&
cell.value !== undefined)
) {
contentToRender = 'Grouped';
contentToRender = RenderColumnTypes.Grouped;
} else if (cell.isAggregated) {
contentToRender = 'Aggregated';
contentToRender = RenderColumnTypes.Aggregated;
} else if (cell.isPlaceholder) {
contentToRender = 'RepeatedValue';
contentToRender = RenderColumnTypes.RepeatedValue;
} else {
contentToRender = 'Cell';
contentToRender = RenderColumnTypes.Cell;
}

return (
Expand All @@ -276,7 +277,7 @@ export const VirtualTableBody = (props: VirtualTableBodyProps) => {
data-selection-cell={cell.column.id === '__ui5wcr__internal_selection_column'}
>
{popInRowHeight !== internalRowHeight && popInColumn.id === cell.column.id
? cell.render('PopIn', { contentToRender, internalRowHeight })
? cell.render(RenderColumnTypes.PopIn, { contentToRender, internalRowHeight })
: cell.render(contentToRender, isNavigatedCell === true ? { isNavigatedCell } : {})}
</div>
);
Expand Down
Loading
Loading