Skip to content
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

feat(flat-table, flat-table-row): components can now be wrapped, subRows no longer need to be arrays FE-6050 #6220

Merged
merged 2 commits into from
Aug 30, 2023
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
21 changes: 19 additions & 2 deletions cypress/components/flat-table/flat-table.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
import * as stories from "../../../src/components/flat-table/flat-table-test.stories";
import { FlatTableProps } from "../../../src/components/flat-table/flat-table.component";
import { FlatTableRowProps } from "../../../src/components/flat-table/flat-table-row/flat-table-row.component";
import { FlatTableCellProps } from "../../../src/components/flat-table/flat-table-cell/flat-table-cell.component";
import Icon from "../../../src/components/icon";
import CypressMountWithProviders from "../../support/component-helper/cypress-mount";
import { getDataElementByValue, cyRoot } from "../../locators";
Expand Down Expand Up @@ -60,6 +59,7 @@ import {
positionOfElement,
getRotationAngle,
} from "../../support/helper";
import { FlatTableRowContextProps } from "../../../src/components/flat-table/flat-table-row/__internal__/flat-table-row-context";

const sizes = [
["compact", "8px", "13px", 24],
Expand Down Expand Up @@ -299,6 +299,21 @@ context("Tests for Flat Table component", () => {
}
});

it("should render Flat Table with sticky header and multiple rows", () => {
CypressMountWithProviders(
<div style={{ height: "150px" }}>
<stories.FlatTableWithMultipleStickyHeaderRows />
</div>
);

flatTableHeaderRowByPosition(0)
.find("th")
.should("have.css", "top", "0px");
flatTableHeaderRowByPosition(1)
.find("th")
.should("have.css", "top", "40px");
});

it("should render Flat Table with sticky footer", () => {
CypressMountWithProviders(
<stories.FlatTableFooterComponent hasStickyFooter />
Expand Down Expand Up @@ -2670,7 +2685,9 @@ context("Tests for Flat Table component", () => {
});

it("should call onClick when first Flat Table column is sorted", () => {
const callback: FlatTableCellProps["onClick"] = cy.stub().as("onClick");
const callback: FlatTableRowContextProps["onClick"] = cy
.stub()
.as("onClick");
CypressMountWithProviders(
<stories.FlatTableSortingComponent onClick={callback} />
);
Expand Down
18 changes: 18 additions & 0 deletions src/components/flat-table/__internal__/build-position-map.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export default (
array: HTMLElement[],
propertyName: "offsetWidth" | "offsetHeight"
) =>
array.reduce((acc: Record<string, number>, _, index) => {
const currentId = array[index].getAttribute("id");
if (currentId) {
if (index === 0) {
acc[currentId] = 0;
} else {
const previousId = array[index - 1].getAttribute("id");
if (previousId) {
acc[currentId] = acc[previousId] + array[index - 1][propertyName];
}
}
}
return acc;
}, {});
2 changes: 2 additions & 0 deletions src/components/flat-table/__internal__/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as useCalculateStickyCells } from "./use-calculate-sticky-cells";
export { default as buildPositionMap } from "./build-position-map";
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useContext } from "react";
import FlatTableRowContext from "../flat-table-row/__internal__/flat-table-row-context";

export default (id: string) => {
const {
expandable,
firstCellId,
firstColumnExpandable,
leftPositions,
rightPositions,
onClick,
onKeyDown,
} = useContext(FlatTableRowContext);

const leftPosition = leftPositions[id];
const rightPosition = rightPositions[id];
const makeCellSticky =
leftPosition !== undefined || rightPosition !== undefined;
const isFirstCell = id === firstCellId;
const isExpandableCell = expandable && isFirstCell && firstColumnExpandable;

return {
robinzigmond marked this conversation as resolved.
Show resolved Hide resolved
expandable,
leftPosition,
rightPosition,
makeCellSticky,
onClick,
onKeyDown,
isFirstCell,
isExpandableCell,
};
};
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
import React, {
useLayoutEffect,
useRef,
useState,
useEffect,
useContext,
} from "react";
import React, { useRef, useState, useEffect, useContext } from "react";
import { PaddingProps } from "styled-system";
import { TableBorderSize, TableCellAlign } from "..";

Expand All @@ -15,6 +9,7 @@ import {
import Icon from "../../icon";
import { FlatTableThemeContext } from "../flat-table.component";
import guid from "../../../__internal__/utils/helpers/guid";
import useCalculateStickyCells from "../__internal__/use-calculate-sticky-cells";

export interface FlatTableCellProps extends PaddingProps {
/** Content alignment */
Expand All @@ -35,110 +30,69 @@ export interface FlatTableCellProps extends PaddingProps {
verticalBorder?: TableBorderSize;
/** Sets the color of the right border */
verticalBorderColor?: string;
/** Sets an id string on the DOM element */
/** Sets an id string on the element */
id?: string;
/**
* @private
* @ignore
*/
expandable?: boolean;
/**
* @private
* @ignore
*/
onClick?: () => void;
/**
* @private
* @ignore
*/
onKeyDown?: () => void;
/**
* @private
* @ignore
* Sets the left position when sticky column found
*/
leftPosition?: number;
/**
* @private
* @ignore
* Sets the right position when sticky column found
*/
rightPosition?: number;
/**
* @private
* @ignore
* Index of cell within row
*/
cellIndex?: number;
/**
* @private
* @ignore
* Callback to report the offsetWidth
*/
reportCellWidth?: (offset: number, index?: number) => void;
}

export const FlatTableCell = ({
align = "left",
children,
pl,
expandable = false,
onClick,
onKeyDown,
reportCellWidth,
cellIndex,
leftPosition,
rightPosition,
width,
truncate = false,
title,
colspan,
rowspan,
id,
...rest
}: FlatTableCellProps) => {
const ref = useRef<HTMLTableCellElement>(null);
const id = useRef(guid());
const internalId = useRef(id || guid());
const [tabIndex, setTabIndex] = useState(-1);
const { selectedId } = useContext(FlatTableThemeContext);

useLayoutEffect(() => {
if (ref.current && reportCellWidth) {
reportCellWidth(ref.current.offsetWidth, cellIndex);
}
}, [reportCellWidth, cellIndex]);
const {
leftPosition,
rightPosition,
expandable,
onClick,
onKeyDown,
isFirstCell,
isExpandableCell,
makeCellSticky,
} = useCalculateStickyCells(internalId.current);

useEffect(() => {
setTabIndex(selectedId === id.current ? 0 : -1);
}, [selectedId]);
setTabIndex(isExpandableCell && selectedId === internalId.current ? 0 : -1);
}, [selectedId, isExpandableCell]);

return (
<StyledFlatTableCell
leftPosition={leftPosition}
rightPosition={rightPosition}
makeCellSticky={!!reportCellWidth}
className={reportCellWidth ? "isSticky" : undefined}
makeCellSticky={makeCellSticky}
className={makeCellSticky ? "isSticky" : undefined}
ref={ref}
align={align}
data-element="flat-table-cell"
pl={pl}
onClick={expandable && onClick ? onClick : undefined}
tabIndex={expandable && onClick ? tabIndex : undefined}
onKeyDown={expandable && onKeyDown ? onKeyDown : undefined}
onClick={isExpandableCell ? onClick : undefined}
tabIndex={isExpandableCell ? tabIndex : undefined}
onKeyDown={isExpandableCell ? onKeyDown : undefined}
colWidth={width}
isTruncated={truncate}
expandable={expandable}
id={id.current}
{...(colspan !== undefined && { colSpan: Number(colspan) })}
{...(rowspan !== undefined && { rowSpan: Number(rowspan) })}
{...rest}
id={internalId.current}
>
<StyledCellContent
title={
truncate && !title && typeof children === "string" ? children : title
}
expandable={expandable}
>
{expandable && (
{expandable && isFirstCell && (
<Icon type="chevron_down_thick" bgSize="extra-small" mr="8px" />
)}
{children}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,15 @@ const verticalBorderSizes = {
interface StyledFlatTableCellProps
extends Pick<
FlatTableCellProps,
| "align"
| "leftPosition"
| "rightPosition"
| "expandable"
| "verticalBorder"
| "verticalBorderColor"
"align" | "verticalBorder" | "verticalBorderColor"
>,
PaddingProps {
makeCellSticky: boolean;
colWidth?: number;
isTruncated: boolean;
leftPosition: number;
rightPosition: number;
expandable?: boolean;
}

const StyledFlatTableCell = styled.td<StyledFlatTableCellProps>`
Expand Down Expand Up @@ -112,7 +110,7 @@ const StyledFlatTableCell = styled.td<StyledFlatTableCellProps>`
`}
`;

const StyledCellContent = styled.div<Pick<FlatTableCellProps, "expandable">>`
const StyledCellContent = styled.div<{ expandable?: boolean }>`
${({ expandable }) =>
expandable &&
css`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import React, { useLayoutEffect, useRef } from "react";
import React, { useContext, useRef } from "react";
import StyledFlatTableCheckbox from "./flat-table-checkbox.style";
import { Checkbox } from "../../checkbox";
import Events from "../../../__internal__/utils/helpers/events/events";
import { TagProps } from "../../../__internal__/utils/helpers/tags";
import tagComponent, {
TagProps,
} from "../../../__internal__/utils/helpers/tags";
import guid from "../../../__internal__/utils/helpers/guid";
import FlatTableRowContext from "../flat-table-row/__internal__/flat-table-row-context";

export interface FlatTableCheckboxProps extends TagProps {
/** Prop to polymorphically render either a 'th' or 'td' element */
Expand All @@ -17,30 +21,8 @@ export interface FlatTableCheckboxProps extends TagProps {
onClick?: (ev: React.MouseEvent<HTMLElement>) => void;
/** The id of the element that labels the input */
ariaLabelledBy?: string;
/**
* @private
* @ignore
* Sets the left position when sticky column found
*/
leftPosition?: number;
/**
* @private
* @ignore
* Sets the right position when sticky column found
*/
rightPosition?: number;
/**
* @private
* @ignore
* Index of cell within row
*/
cellIndex?: number;
/**
* @private
* @ignore
* Callback to report the offsetWidth
*/
reportCellWidth?: (offset: number, index?: number) => void;
/** Sets an id string on the element */
id?: string;
}

export const FlatTableCheckbox = ({
Expand All @@ -49,20 +31,18 @@ export const FlatTableCheckbox = ({
onChange,
selectable = true,
onClick,
leftPosition,
rightPosition,
cellIndex,
reportCellWidth,
ariaLabelledBy,
id,
...rest
}: FlatTableCheckboxProps) => {
const ref = useRef<HTMLTableCellElement>(null);
const internalId = useRef(id || guid());
const { leftPositions, rightPositions } = useContext(FlatTableRowContext);

useLayoutEffect(() => {
if (ref.current && reportCellWidth) {
reportCellWidth(ref.current.offsetWidth, cellIndex);
}
}, [reportCellWidth, cellIndex]);
const leftPosition = leftPositions[internalId.current];
const rightPosition = rightPositions[internalId.current];
const makeCellSticky =
leftPosition !== undefined || rightPosition !== undefined;

const dataElement = `flat-table-checkbox-${as === "td" ? "cell" : "header"}`;

Expand All @@ -80,13 +60,16 @@ export const FlatTableCheckbox = ({
return (
<StyledFlatTableCheckbox
ref={ref}
makeCellSticky={!!reportCellWidth}
className={reportCellWidth ? "isSticky" : undefined}
makeCellSticky={makeCellSticky}
className={makeCellSticky ? "isSticky" : undefined}
leftPosition={leftPosition}
rightPosition={rightPosition}
data-element={dataElement}
as={as}
{...rest}
{...tagComponent("flat-table-checkbox", {
"data-element": dataElement,
...rest,
})}
id={internalId.current}
>
{selectable && (
<Checkbox
Expand Down
Loading