chore: Use WDS Tokens in Table Body (#32421)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
  - Enhanced typography styling with dynamic font size adjustments.
  - Introduced feature flag checks for new functionalities.
  - Implemented `VirtualTableBody` for efficient data rendering.
- Added `onClickRow` functionality in table rows for improved
interactivity.
  - Improved table accessibility with explicit cell roles.
  - Added pagination controls refinement in table headers.

- **Refactor**
- Simplified table header and body component structures for better
maintainability.
  - Streamlined feature flag usage for cleaner logic.
- Refined `WDSTableWidget` component by optimizing width calculations
and event handling.

- **Style**
  - Introduced a new CSS module for comprehensive table styling.
- Removed outdated styling configurations to align with updated design
system.

- **Bug Fixes**
  - Fixed accessibility issues by adding proper roles to table cells.

- **Chores**
- Removed unused functions and properties related to theme and styling
adjustments.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Pawan Kumar <pawankumar@Pawans-MacBook-Pro-2.local>
This commit is contained in:
Pawan Kumar 2024-04-05 18:35:38 +05:30 committed by GitHub
parent 159399d8f4
commit 46498aa93a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 522 additions and 922 deletions

View File

@ -81,6 +81,11 @@
padding: 0;
}
/* this is required so that separator ( <Item isSeprator /> ) if passed with a text as children, the text is hidden */
.menuList li[data-separator] > * {
display: none;
}
/* making sure the first and last child are not displayed when they have the data-separator attribute */
.menuList li:is(:first-child, :last-child):is([data-separator]) {
display: none;

View File

@ -85,7 +85,6 @@ const StaticTable = (props: StaticTableProps) => {
selectTableRow={props.selectTableRow}
selectedRowIndex={props.selectedRowIndex}
selectedRowIndices={props.selectedRowIndices}
tableSizes={props.tableSizes}
useVirtual={props.useVirtual}
width={props.width - TABLE_SCROLLBAR_WIDTH / 2}
/>

View File

@ -19,17 +19,11 @@ import type {
AddNewRowActions,
StickyType,
} from "./Constants";
import {
TABLE_SIZES,
CompactModeTypes,
TABLE_SCROLLBAR_HEIGHT,
} from "./Constants";
import { TABLE_SIZES, CompactModeTypes } from "./Constants";
import { Colors } from "constants/Colors";
import type { EventType } from "constants/AppsmithActionConstants/ActionConstants";
import type { EditableCell, TableVariant } from "../constants";
import "simplebar-react/dist/simplebar.min.css";
import { createGlobalStyle } from "styled-components";
import { Classes as PopOver2Classes } from "@blueprintjs/popover2";
import StaticTable from "./StaticTable";
import VirtualTable from "./VirtualTable";
import { ConnectDataOverlay } from "widgets/ConnectDataOverlay";
@ -38,25 +32,7 @@ import {
createMessage,
CONNECT_BUTTON_TEXT,
} from "@appsmith/constants/messages";
const SCROLL_BAR_OFFSET = 2;
const HEADER_MENU_PORTAL_CLASS = ".header-menu-portal";
const PopoverStyles = createGlobalStyle<{
widgetId: string;
borderRadius: string;
}>`
${HEADER_MENU_PORTAL_CLASS}-${({ widgetId }) => widgetId} {
font-family: var(--wds-font-family) !important;
& .${PopOver2Classes.POPOVER2},
.${PopOver2Classes.POPOVER2_CONTENT},
.bp3-menu {
border-radius: ${({ borderRadius }) =>
borderRadius >= `1.5rem` ? `0.375rem` : borderRadius} !important;
}
}
`;
import styles from "./styles.module.css";
export interface TableProps {
width: number;
@ -286,21 +262,18 @@ export function Table(props: TableProps) {
props.isVisiblePagination ||
props.allowAddNewRow;
const scrollContainerStyles = useMemo(() => {
return {
height: isHeaderVisible
? props.height -
tableSizes.TABLE_HEADER_HEIGHT -
TABLE_SCROLLBAR_HEIGHT -
SCROLL_BAR_OFFSET
: props.height - TABLE_SCROLLBAR_HEIGHT - SCROLL_BAR_OFFSET,
};
}, [isHeaderVisible, props.height, tableSizes.TABLE_HEADER_HEIGHT]);
const shouldUseVirtual =
props.serverSidePaginationEnabled &&
!props.columns.some((column) => column.columnProperties.allowCellWrapping);
const variant = (() => {
if (props.variant === "DEFAULT") return "default";
if (props.variant === "VARIANT2") return "no-borders";
if (props.variant === "VARIANT3") return "horizontal-borders";
return "default";
})();
return (
<>
{showConnectDataOverlay && (
@ -317,21 +290,20 @@ export function Table(props: TableProps) {
borderRadius={props.borderRadius}
borderWidth={props.borderWidth}
boxShadow={props.boxShadow}
className={styles.table}
data-status={props.isAddRowInProgress ? "add-row-in-progress" : ""}
data-type={shouldUseVirtual ? "virtualized" : "static"}
data-variant={variant}
height={props.height}
id={`table${props.widgetId}`}
isAddRowInProgress={props.isAddRowInProgress}
isHeaderVisible={isHeaderVisible}
isResizingColumn={isResizingColumn.current}
multiRowSelection={props.multiRowSelection}
tableSizes={tableSizes}
triggerRowSelection={props.triggerRowSelection}
variant={props.variant}
width={props.width}
>
<PopoverStyles
borderRadius={props.borderRadius}
widgetId={props.widgetId}
/>
{isHeaderVisible && (
<TableHeader
allowAddNewRow={props.allowAddNewRow}
@ -375,6 +347,7 @@ export function Table(props: TableProps) {
? " virtual"
: ""
}`}
data-table-wrapper=""
>
<div {...getTableProps()} className="table column-freeze">
{!shouldUseVirtual && (
@ -435,7 +408,6 @@ export function Table(props: TableProps) {
prepareRow={prepareRow}
primaryColumnId={props.primaryColumnId}
rowSelectionState={rowSelectionState}
scrollContainerStyles={scrollContainerStyles}
selectTableRow={props.selectTableRow}
selectedRowIndex={props.selectedRowIndex}
selectedRowIndices={props.selectedRowIndices}

View File

@ -1,48 +0,0 @@
import React from "react";
import { Tooltip } from "@blueprintjs/core";
import { IconWrapper } from "constants/IconConstants";
import { Colors } from "constants/Colors";
import { TableIconWrapper } from "./TableStyledWrappers";
interface TableActionIconProps {
tooltip: string;
selected: boolean;
selectMenu: (selected: boolean) => void;
className: string;
children: React.ReactNode;
icon?: React.ReactNode;
}
function TableActionIcon(props: TableActionIconProps) {
return (
<Tooltip
autoFocus={false}
content={props.tooltip}
hoverOpenDelay={1000}
modifiers={{
preventOverflow: { enabled: false },
flip: { enabled: false },
}}
position="top"
>
<TableIconWrapper
className={props.className}
onClick={(e) => {
props.selectMenu(!props.selected);
e.stopPropagation();
}}
selected={props.selected}
>
<IconWrapper
color={props.selected ? Colors.CODE_GRAY : Colors.GRAY}
height={20}
width={20}
>
{props.children}
</IconWrapper>
</TableIconWrapper>
</Tooltip>
);
}
export default TableActionIcon;

View File

@ -44,28 +44,29 @@ export function Row(props: RowType) {
(primaryColumnId && (props.row.original[primaryColumnId] as Key)) ||
props.index;
if (!isAddRowInProgress) {
rowProps["role"] = "button";
}
const onClickRow = (e: React.MouseEvent) => {
props.row.toggleRowSelected();
selectTableRow?.(props.row);
e.stopPropagation();
};
return (
<div
{...rowProps}
aria-checked={isRowSelected}
className={`tr ${isRowSelected ? "selected-row" : ""} ${
props.className || ""
} ${isAddRowInProgress && props.index === 0 ? "new-row" : ""}`}
}`}
data-is-new={isAddRowInProgress && props.index === 0 ? "" : undefined}
data-rowindex={props.index}
key={key}
onClick={(e) => {
props.row.toggleRowSelected();
selectTableRow?.(props.row);
e.stopPropagation();
}}
onClick={onClickRow}
>
{multiRowSelection &&
renderBodyCheckBoxCell(isRowSelected, accentColor, borderRadius)}
{props.row.cells.map((cell, cellIndex) => {
const cellProperties = cell.getCellProps();
cellProperties["style"] = {
...cellProperties.style,
left:
@ -76,6 +77,7 @@ export function Row(props: RowType) {
return (
<div
{...cellProperties}
aria-hidden={columns[cellIndex].isHidden ? "true" : undefined}
className={
columns[cellIndex].isHidden
? "td hidden-cell"

View File

@ -0,0 +1,16 @@
import React from "react";
import { EmptyRows, Row } from "./Row";
import type { StaticTableProps } from "./types";
export const StaticTableBody = (props: StaticTableProps) => {
return (
<div {...props.getTableBodyProps()} className="tbody body">
{props.rows.map((row, index) => {
return <Row index={index} key={index} row={row} />;
})}
{props.pageSize > props.rows.length && (
<EmptyRows rowCount={props.pageSize - props.rows.length} />
)}
</div>
);
};

View File

@ -0,0 +1,107 @@
import React, { useCallback } from "react";
import { useLayoutEffect, useRef } from "react";
import type { VirtualTableBodyProps } from "./types";
import { FixedSizeList, areEqual } from "react-window";
import type { ListChildComponentProps } from "react-window";
import { Row, EmptyRow } from "./Row";
import { useResizeObserver } from "@react-aria/utils";
export const VirtualTableBody = (props: VirtualTableBodyProps) => {
const ref = useRef<HTMLDivElement>(null);
const [horizontalScrollbarHeight, setHorizontalScrollbarHeight] =
React.useState(0);
const [itemHeight, setItemHeight] = React.useState("0px");
const [headerHeight, setHeaderHeight] = React.useState("0px");
useLayoutEffect(() => {
if (ref.current) {
const horizontalScrollbar = ref.current;
if (horizontalScrollbar) {
setHorizontalScrollbarHeight(
horizontalScrollbar.offsetHeight - horizontalScrollbar.clientHeight,
);
}
}
}, [ref.current]);
useLayoutEffect(() => {
if (ref.current) {
const table = ref.current.closest("[role='table']");
if (table && table instanceof HTMLElement) {
const style = window.getComputedStyle(table);
const thHeight = style.getPropertyValue("background-position-x");
const trHeight = style.getPropertyValue("background-position-y");
setHeaderHeight(thHeight);
setItemHeight(trHeight);
}
}
}, [props.pageSize, ref.current]);
const onSizeChange = useCallback(() => {
if (ref.current) {
const table = ref.current.closest("[role='table']");
if (table && table instanceof HTMLElement) {
const style = window.getComputedStyle(table);
const thHeight = style.getPropertyValue("background-position-x");
const trHeight = style.getPropertyValue("background-position-y");
setHeaderHeight(thHeight);
setItemHeight(trHeight);
}
}
}, [props.pageSize, ref.current]);
useResizeObserver({
ref: ref,
onResize: onSizeChange,
});
return (
<FixedSizeList
data-virtual-list=""
height={
props.pageSize * parseFloat(itemHeight) +
parseFloat(headerHeight) +
horizontalScrollbarHeight
}
innerElementType={props.innerElementType}
itemCount={Math.max(props.rows.length, props.pageSize)}
itemData={props.rows}
itemSize={parseFloat(itemHeight)}
outerRef={ref}
style={{
overflow: "auto",
scrollbarColor: "initial",
}}
width="100cqw"
>
{rowRenderer}
</FixedSizeList>
);
};
const rowRenderer = React.memo((rowProps: ListChildComponentProps) => {
const { data, index, style } = rowProps;
if (index < data.length) {
const row = data[index];
return (
<Row
className="t--virtual-row"
index={index}
key={index}
row={row}
style={style}
/>
);
} else {
return <EmptyRow style={style} />;
}
}, areEqual);

View File

@ -1,14 +1,14 @@
import React, { useLayoutEffect } from "react";
import React from "react";
import type {
Row as ReactTableRowType,
TableBodyPropGetter,
TableBodyProps,
} from "react-table";
import type { ListChildComponentProps, ReactElementType } from "react-window";
import { FixedSizeList, areEqual } from "react-window";
import { EmptyRows, EmptyRow, Row } from "./Row";
import type { ReactTableColumnProps, TableSizes } from "../Constants";
import type { ReactTableColumnProps } from "../Constants";
import type { HeaderComponentProps } from "../Table";
import { StaticTableBody } from "./StaticTableBody";
import { VirtualTableBody } from "./VirtualTableBody";
import type { VirtualTableBodyProps } from "./types";
export type BodyContextType = {
accentColor: string;
@ -46,97 +46,8 @@ export const BodyContext = React.createContext<BodyContextType>({
totalColumnsWidth: 0,
});
const rowRenderer = React.memo((rowProps: ListChildComponentProps) => {
const { data, index, style } = rowProps;
if (index < data.length) {
const row = data[index];
return (
<Row
className="t--virtual-row"
index={index}
key={index}
row={row}
style={style}
/>
);
} else {
return <EmptyRow style={style} />;
}
}, areEqual);
interface BodyPropsType {
getTableBodyProps(
propGetter?: TableBodyPropGetter<Record<string, unknown>> | undefined,
): TableBodyProps;
pageSize: number;
rows: ReactTableRowType<Record<string, unknown>>[];
height: number;
width?: number;
tableSizes: TableSizes;
innerElementType?: ReactElementType;
}
const TableVirtualBodyComponent = (props: BodyPropsType) => {
const ref = React.useRef<HTMLDivElement>(null);
const [horizontalScrollbarHeight, setHorizontalScrollbarHeight] =
React.useState(0);
useLayoutEffect(() => {
if (ref.current) {
const horizontalScrollbar = ref.current;
if (horizontalScrollbar) {
setHorizontalScrollbarHeight(
horizontalScrollbar.offsetHeight - horizontalScrollbar.clientHeight,
);
}
}
}, [ref.current]);
return (
<FixedSizeList
data-virtual-list=""
height={
props.height +
props.tableSizes.COLUMN_HEADER_HEIGHT +
horizontalScrollbarHeight
}
innerElementType={props.innerElementType}
itemCount={Math.max(props.rows.length, props.pageSize)}
itemData={props.rows}
itemSize={props.tableSizes.ROW_HEIGHT}
outerRef={ref}
style={{
overflow: "auto",
scrollbarColor: "initial",
}}
width="100cqw"
>
{rowRenderer}
</FixedSizeList>
);
};
const TableBodyComponent = (props: BodyPropsType) => {
return (
<div
{...props.getTableBodyProps()}
className="tbody body"
style={{ height: props.height }}
>
{props.rows.map((row, index) => {
return <Row index={index} key={index} row={row} />;
})}
{props.pageSize > props.rows.length && (
<EmptyRows rowCount={props.pageSize - props.rows.length} />
)}
</div>
);
};
export const TableBody = (
props: BodyPropsType & BodyContextType & { useVirtual: boolean },
props: VirtualTableBodyProps & BodyContextType & { useVirtual: boolean },
) => {
const {
accentColor,
@ -203,9 +114,9 @@ export const TableBody = (
}}
>
{useVirtual ? (
<TableVirtualBodyComponent rows={rows} width={width} {...restOfProps} />
<VirtualTableBody rows={rows} width={width} {...restOfProps} />
) : (
<TableBodyComponent rows={rows} {...restOfProps} />
<StaticTableBody rows={rows} {...restOfProps} />
)}
</BodyContext.Provider>
);

View File

@ -0,0 +1,20 @@
import type {
TableBodyProps,
TableBodyPropGetter,
Row as ReactTableRowType,
} from "react-table";
import type { ReactElementType } from "react-window";
export interface StaticTableProps {
getTableBodyProps(
propGetter?: TableBodyPropGetter<Record<string, unknown>> | undefined,
): TableBodyProps;
pageSize: number;
rows: ReactTableRowType<Record<string, unknown>>[];
height: number;
width?: number;
}
export interface VirtualTableBodyProps extends StaticTableProps {
innerElementType?: ReactElementType;
}

View File

@ -51,11 +51,11 @@ export const Pagination = (props: PaginationProps) => {
props.isVisiblePagination &&
props.serverSidePaginationEnabled && (
<div data-table-header-pagination="">
{props.totalRecordsCount && (
{props.totalRecordsCount ? (
<Text lineClamp={1} variant="footnote">
{props.totalRecordsCount} Records
</Text>
)}
) : null}
<IconButton
icon="chevron-left"
isDisabled={props.pageNo === 0}

View File

@ -6,7 +6,6 @@ import { StickyType } from "../Constants";
import type { Row as ReactTableRowType } from "react-table";
import { renderHeaderCheckBoxCell } from "../cellComponents/SelectionCheckboxCell";
import { renderEmptyRows } from "../cellComponents/EmptyCell";
import styled from "styled-components";
export interface TableColumnHeaderProps {
enableDrag: () => void;
@ -34,12 +33,6 @@ export interface TableColumnHeaderProps {
widgetId: string;
}
const StyledHeaderGroup = styled.div<{
headerWidth: number;
}>`
display: flex;
width: ${(props) => props.headerWidth}px !important;
`;
const TableColumnHeader = (props: TableColumnHeaderProps) => {
const currentDraggedColumn = React.useRef<string>("");
const columnOrder = props.columns.map((col) => col.alias);
@ -59,22 +52,17 @@ const TableColumnHeader = (props: TableColumnHeaderProps) => {
);
return (
<div
className="thead"
onMouseLeave={props.enableDrag}
onMouseOver={props.disableDrag}
>
<thead onMouseLeave={props.enableDrag} onMouseOver={props.disableDrag}>
{props.headerGroups.map((headerGroup: any, index: number) => {
const headerRowProps = {
...headerGroup.getHeaderGroupProps(),
};
return (
<StyledHeaderGroup
<tr
{...headerRowProps}
className="tr header"
headerWidth={props.headerWidth}
key={index}
style={{ width: props.headerWidth }}
>
{props.multiRowSelection &&
renderHeaderCheckBoxCell(
@ -123,7 +111,7 @@ const TableColumnHeader = (props: TableColumnHeaderProps) => {
/>
);
})}
</StyledHeaderGroup>
</tr>
);
})}
@ -139,7 +127,7 @@ const TableColumnHeader = (props: TableColumnHeaderProps) => {
{},
props.prepareRow,
)}
</div>
</thead>
);
};

View File

@ -23,7 +23,6 @@ import { lightenColor, darkenColor } from "widgets/WidgetUtils";
import { FontStyleTypes } from "constants/WidgetConstants";
import { Classes } from "@blueprintjs/core";
import type { TableVariant } from "../constants";
import { TableVariantTypes } from "../constants";
import { Layers } from "constants/Layers";
const BORDER_RADIUS = "border-radius: 4px;";
@ -44,271 +43,8 @@ export const TableWrapper = styled.div<{
isResizingColumn?: boolean;
variant?: TableVariant;
isAddRowInProgress: boolean;
multiRowSelection?: boolean;
}>`
width: 100%;
height: 100%;
background: var(--color-bg-secondary);
border-style: solid;
border-width: ${({ borderWidth }) => `${borderWidth}px`};
border-color: ${({ borderColor }) => borderColor};
border-radius: var(--border-radius-elevation-3);
box-shadow: ${({ boxShadow }) => `${boxShadow}`} !important;
box-sizing: border-box;
display: flex;
justify-content: space-between;
flex-direction: column;
overflow: hidden;
/* wriiten exclusively for safari */
position: sticky;
}
.tableWrap {
height: 100%;
display: block;
position: relative;
width: 100%;
overflow: auto hidden;
scrollbar-color: initial;
container-type: inline-size;
&.virtual {
overflow: hidden;
}
}
.table {
border-spacing: 0;
color: ${Colors.THUNDER};
position: relative;
display: table;
width: 100%;
tab .tbody {
height: fit-content;
width: fit-content;
}
.tr {
cursor: ${(props) => props.triggerRowSelection && "pointer"};
background: ${Colors.WHITE};
&.selected-row {
background: ${({ accentColor }) =>
`${lightenColor(accentColor)}`} !important;
&:hover {
background: ${({ accentColor }) =>
`${lightenColor(accentColor, "0.9")}`} !important;
}
}
${(props) => {
if (!props.isAddRowInProgress) {
return `&:hover {
background: var(--wds-color-bg-hover) !important;
}`;
}
}}
&.new-row {
background: ${({ accentColor }) =>
`${lightenColor(accentColor)}`} !important;
}
}
.th,
.td {
margin: 0;
border-bottom: ${(props) =>
props.variant === TableVariantTypes.DEFAULT ||
props.variant === undefined ||
props.variant === TableVariantTypes.VARIANT3
? "1px solid var(--wds-color-border-onaccent)"
: "none"};
border-right: ${(props) =>
props.variant === TableVariantTypes.DEFAULT ||
props.variant === undefined ||
props.isResizingColumn
? "1px solid var(--wds-color-border-onaccent)"
: "none"};
position: relative;
font-size: ${(props) => props.tableSizes.ROW_FONT_SIZE}px;
line-height: ${(props) => props.tableSizes.ROW_FONT_SIZE}px;
:last-child {
border-right: 0;
.resizer {
right: 5px;
}
}
.resizer {
display: inline-block;
width: 10px;
height: 100%;
position: absolute;
right: 0;
top: 0;
transform: translateX(50%);
z-index: 1;
${"" /* prevents from scrolling while dragging on touch devices */}
touch-action:none;
&.isResizing {
cursor: isResizing;
}
}
}
.th {
font-size: 14px;
}
.th {
padding: 0 10px 0 0;
height: ${(props) =>
props.isHeaderVisible ? props.tableSizes.COLUMN_HEADER_HEIGHT : 40}px;
line-height: ${(props) =>
props.isHeaderVisible ? props.tableSizes.COLUMN_HEADER_HEIGHT : 40}px;
background: var(--wds-color-bg);
font-weight: bold;
}
.td {
min-height: ${(props) => props.tableSizes.ROW_HEIGHT}px;
padding: 0;
}
.thead {
position: sticky;
top: 0;
z-index: 1;
width: fit-content;
}
}
.column-freeze {
.body {
position: relative;
z-index: 0;
}
[role="columnheader"] {
background-color: var(--wds-color-bg) !important;
}
[data-sticky-td] {
position: sticky;
position: -webkit-sticky;
background-color: inherit;
border-bottom: ${(props) =>
props.variant === TableVariantTypes.VARIANT2
? "none"
: "1px solid var(--wds-color-border-onaccent)"};
& .draggable-header {
cursor: pointer;
}
&.hidden-cell,
&:has(> .hidden-header) {
z-index: 0;
position: unset !important;
}
&:has(> .hidden-header) .resizer {
position: relative;
}
}
[data-sticky-last-left-td] {
left: 0px;
border-right: 3px solid var(--wds-color-border);
&.hidden-cell,
&:has(> .hidden-header) {
border-right: 0.5px solid var(--wds-color-border);
}
}
[data-sticky-first-right-td] {
right: 0px;
border-left: 3px solid var(--wds-color-border);
&.hidden-cell,
&:has(> .hidden-header) {
border-left: none;
}
}
& .sticky-right-modifier {
border-left: 3px solid var(--wds-color-border);
}
}
.draggable-header,
.hidden-header {
width: 100%;
text-overflow: ellipsis;
overflow: hidden;
color: ${Colors.OXFORD_BLUE};
padding-left: 10px;
&.sorted {
padding-left: 5px;
}
}
.draggable-header {
cursor: grab;
display: inline-block;
width: 100%;
height: ${(props) => props.tableSizes.COLUMN_HEADER_HEIGHT};
&.reorder-line {
width: 1px;
height: 100%;
}
}
.hidden-header {
opacity: 0.6;
${invisible};
}
.header-menu {
cursor: pointer;
width: 24px;
display: flex;
align-items: center;
.bp3-popover2-target {
display: block;
}
&.hide-menu {
display: none;
}
&.hide {
&:hover {
.bp3-popover2-target {
display: block;
}
}
.bp3-popover2-target {
display: none;
}
}
}
.column-menu {
cursor: pointer;
height: ${(props) => props.tableSizes.COLUMN_HEADER_HEIGHT}px;
line-height: ${(props) => props.tableSizes.COLUMN_HEADER_HEIGHT}px;
}
.th {
display: flex !important;
justify-content: space-between;
&.highlight-left {
border-left: 2px solid ${Colors.GREEN};
}
&.highlight-right {
border-right: 2px solid ${Colors.GREEN};
}
& .draggable-header--dragging {
background: #efefef;
border-radius: 4px;
z-index: 100;
width: 100%;
text-overflow: none;
overflow: none;
}
}
.input-group {
height: ${(props) => props.tableSizes.COLUMN_HEADER_HEIGHT}px;
line-height: ${(props) => props.tableSizes.COLUMN_HEADER_HEIGHT}px;
padding: 0 5px;
}
`;
@ -352,51 +88,6 @@ export const OptionWrapper = styled.div<{
}
`;
export const PaginationItemWrapper = styled.div<{
disabled?: boolean;
selected?: boolean;
borderRadius: string;
accentColor: string;
}>`
background: ${(props) =>
props.disabled ? `var(--wds-color-bg-disabled)` : `var(--wds-color-bg)`};
border: 1px solid
${(props) =>
props.disabled
? `var(--wds-color-border-disabled)`
: `var(--wds-color-border)`};
box-sizing: border-box;
width: 24px;
height: 24px;
display: flex;
justify-content: center;
align-items: center;
margin: 0 4px;
cursor: ${(props) => (props.disabled ? "not-allowed" : "pointer")};
border-radius: ${({ borderRadius }) => borderRadius};
& > * {
pointer-events: ${(props) => props.disabled && "none"};
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
& svg {
fill: ${(props) =>
props.disabled
? `var(--wds-color-icon-disabled)`
: `var(--wds-color-icon)`};
}
${({ disabled }) =>
!disabled &&
`&:hover {
border-color: var(--wds-color-border-hover);
background-color: var(--wds-color-bg-hover);
}`}
`;
export const MenuColumnWrapper = styled.div<{ selected: boolean }>`
display: flex;
justify-content: flex-start;

View File

@ -31,7 +31,6 @@ type VirtualTableProps = TableColumnHeaderProps & {
primaryColumnId?: string;
isAddRowInProgress: boolean;
totalColumnsWidth?: number;
scrollContainerStyles: any;
useVirtual: boolean;
};
@ -65,7 +64,6 @@ const VirtualTable = (props: VirtualTableProps) => {
selectedRowIndex={props.selectedRowIndex}
selectedRowIndices={props.selectedRowIndices}
sortTableColumn={props.sortTableColumn}
tableSizes={props.tableSizes}
totalColumnsWidth={props?.totalColumnsWidth}
useVirtual={props.useVirtual}
widgetId={props.widgetId}

View File

@ -99,7 +99,7 @@ export const renderEmptyRows = (
return rows.map((row: string, index: number) => {
return (
<EmptyRow className="tr" key={index} style={style}>
<EmptyRow className="tr" key={index} role="row" style={style}>
{multiRowSelection &&
renderBodyCheckBoxCell(false, accentColor, borderRadius)}
{tableColumns.map((column: any, colIndex: number) => {
@ -155,6 +155,7 @@ export const renderEmptyRows = (
? "td hidden-cell"
: `td${addStickyModifierClass(columns, colIndex)}`
}
role="cell"
{...stickyAttributes}
key={colIndex}
sticky={column?.sticky ?? StickyType.NONE}

View File

@ -1,131 +1,17 @@
import React, {
createRef,
useCallback,
useEffect,
useState,
memo,
} from "react";
import { MenuItem, Tooltip, Menu } from "@blueprintjs/core";
import type { Key } from "react";
import React, { useCallback, memo } from "react";
import { Colors } from "constants/Colors";
import styled from "styled-components";
import { ControlIcons } from "icons/ControlIcons";
import type { CellAlignment } from "../Constants";
import {
HEADER_MENU_PORTAL_CLASS,
JUSTIFY_CONTENT,
MENU_CONTENT_CLASS,
MULTISELECT_CHECKBOX_WIDTH,
POPOVER_ITEMS_TEXT_MAP,
StickyType,
} from "../Constants";
import { TooltipContentWrapper } from "../TableStyledWrappers";
import { MULTISELECT_CHECKBOX_WIDTH, StickyType } from "../Constants";
import { isColumnTypeEditable } from "widgets/wds/WDSTableWidget/widget/utilities";
import { Popover2 } from "@blueprintjs/popover2";
import { MenuDivider } from "@design-system/widgets-old";
import { importRemixIcon, importSvg } from "@design-system/widgets-old";
import { CANVAS_ART_BOARD } from "constants/componentClassNameConstants";
const Check = importRemixIcon(
async () => import("remixicon-react/CheckFillIcon"),
);
const ArrowDownIcon = importRemixIcon(
async () => import("remixicon-react/ArrowDownSLineIcon"),
);
const EditIcon = importSvg(
async () => import("assets/icons/control/edit-variant1.svg"),
);
const AscendingIcon = styled(ControlIcons.SORT_CONTROL)`
padding: 0;
position: relative;
top: 3px;
cursor: pointer;
transform: rotate(180deg);
&& svg {
path {
fill: ${Colors.LIGHT_GREYISH_BLUE};
}
}
`;
const DescendingIcon = styled(ControlIcons.SORT_CONTROL)`
padding: 0;
position: relative;
top: 3px;
cursor: pointer;
&& svg {
path {
fill: ${Colors.LIGHT_GREYISH_BLUE};
}
}
`;
const ColumnNameContainer = styled.div<{
horizontalAlignment: CellAlignment;
}>`
display: flex;
align-items: center;
justify-content: ${(props) =>
props?.horizontalAlignment && JUSTIFY_CONTENT[props.horizontalAlignment]};
`;
const StyledEditIcon = styled(EditIcon)`
width: 14px;
min-width: 14px;
margin-right: 3px;
`;
const TitleWrapper = styled.div`
&,
span {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
`;
interface TitleProps {
children: React.ReactNode;
tableWidth?: number;
width?: number;
}
function Title(props: TitleProps) {
const ref = createRef<HTMLDivElement>();
const [useToolTip, updateToolTip] = useState(false);
useEffect(() => {
const element = ref.current;
if (element && element.offsetWidth < element.scrollWidth) {
updateToolTip(true);
} else {
updateToolTip(false);
}
}, [ref.current, props.width, props.children]);
return (
<TitleWrapper ref={ref}>
{useToolTip && props.children ? (
<Tooltip
autoFocus={false}
content={
<TooltipContentWrapper width={(props.tableWidth || 300) - 32}>
{props.children}
</TooltipContentWrapper>
}
hoverOpenDelay={1000}
position="top"
>
{props.children}
</Tooltip>
) : (
props.children
)}
</TitleWrapper>
);
}
const ICON_SIZE = 16;
import {
Flex,
Icon,
IconButton,
Item,
Menu,
MenuList,
Text,
} from "@design-system/widgets";
interface HeaderProps {
canFreezeColumn?: boolean;
@ -162,11 +48,11 @@ interface HeaderProps {
const HeaderCellComponent = (props: HeaderProps) => {
const { column, editMode, isSortable } = props;
const [isMenuOpen, setIsMenuOpen] = useState(false);
const headerProps = { ...column.getHeaderProps() };
headerProps["style"] = {
...headerProps.style,
display: "flex",
left:
column.sticky === StickyType.LEFT && props.multiRowSelection
? MULTISELECT_CHECKBOX_WIDTH + column.totalLeft
@ -239,14 +125,35 @@ const HeaderCellComponent = (props: HeaderProps) => {
[props.onDrop, props.columnIndex],
);
const onActionOnMenu = (key: Key) => {
switch (key) {
case "sort-asc":
props.sortTableColumn(props.columnIndex, true);
break;
case "sort-desc":
props.sortTableColumn(props.columnIndex, false);
break;
case "freeze-left":
toggleColumnFreeze(StickyType.LEFT);
break;
case "freeze-right":
toggleColumnFreeze(StickyType.RIGHT);
break;
default:
break;
}
};
return (
<div
<th
{...headerProps}
aria-hidden={props.isHidden ? "true" : undefined}
className={`th header-reorder ${props.stickyRightModifier}`}
data-header={props.columnName}
>
<div
className={!props.isHidden ? `draggable-header` : "hidden-header"}
data-draggable-header=""
draggable={
(props.column.sticky === StickyType.NONE && !props.isHidden) ||
undefined
@ -259,101 +166,64 @@ const HeaderCellComponent = (props: HeaderProps) => {
onDragOver={onDragOver}
onDragStart={onDragStart}
onDrop={onDrop}
style={
{
"--padding-inline-end": props.isAscOrder
? "calc((var(--outer-spacing-2) * 2) + (2 *var(--sizing-7)))"
: "calc((var(--outer-spacing-2) * 2) + var(--sizing-7))",
} as React.CSSProperties
}
>
<ColumnNameContainer
horizontalAlignment={column.columnProperties.horizontalAlignment}
<Flex
alignItems="center"
gap="spacing-1"
justifyContent={column.columnProperties.horizontalAlignment}
>
{isColumnEditable && <StyledEditIcon />}
<Title width={props.width}>
{isColumnEditable && <Icon name="edit" size="small" />}
<Text
lineClamp={1}
title={props.columnName.replace(/\s/g, "\u00a0")}
variant="caption"
>
{props.columnName.replace(/\s/g, "\u00a0")}
</Title>
</ColumnNameContainer>
</Text>
</Flex>
</div>
<div
className={`header-menu ${
!isSortable && !props.canFreezeColumn && "hide-menu"
} ${!isMenuOpen && "hide"}`}
>
<Popover2
content={
<Menu className={MENU_CONTENT_CLASS}>
<MenuItem
disabled={disableSort}
labelElement={props.isAscOrder === true ? <Check /> : undefined}
onClick={() => {
props.sortTableColumn(props.columnIndex, true);
}}
text={POPOVER_ITEMS_TEXT_MAP.SORT_ASC}
/>
<MenuItem
disabled={disableSort}
labelElement={
props.isAscOrder === false ? <Check /> : undefined
}
onClick={() => {
props.sortTableColumn(props.columnIndex, false);
}}
text={POPOVER_ITEMS_TEXT_MAP.SORT_DSC}
/>
<MenuDivider
style={{
marginLeft: 0,
marginRight: 0,
}}
/>
<MenuItem
disabled={!props.canFreezeColumn}
labelElement={
column.sticky === StickyType.LEFT ? <Check /> : undefined
}
onClick={() => {
toggleColumnFreeze(StickyType.LEFT);
}}
text={POPOVER_ITEMS_TEXT_MAP.FREEZE_LEFT}
/>
<MenuItem
disabled={!props.canFreezeColumn}
labelElement={
column.sticky === StickyType.RIGHT ? <Check /> : undefined
}
onClick={() => {
toggleColumnFreeze(StickyType.RIGHT);
}}
text={POPOVER_ITEMS_TEXT_MAP.FREEZE_RIGHT}
/>
</Menu>
}
interactionKind="hover"
isOpen={isMenuOpen}
minimal
onInteraction={setIsMenuOpen}
placement="bottom-end"
portalClassName={`${HEADER_MENU_PORTAL_CLASS}-${props.widgetId}`}
portalContainer={
document.getElementById(CANVAS_ART_BOARD) || undefined
}
>
<ArrowDownIcon className="w-5 h-5" color="var(--wds-color-icon)" />
</Popover2>
</div>
{props.isAscOrder !== undefined ? (
<div>
{props.isAscOrder ? (
<AscendingIcon height={ICON_SIZE} width={ICON_SIZE} />
) : (
<DescendingIcon height={ICON_SIZE} width={ICON_SIZE} />
)}
</div>
) : null}
<Flex alignItems="center" gap="spacing-1">
{props.isAscOrder !== undefined && (
<Icon
name={props.isAscOrder ? "arrow-up" : "arrow-down"}
size="small"
/>
)}
<Menu disabledKeys={["separator"]} onAction={onActionOnMenu}>
<IconButton
color="neutral"
icon="chevron-down"
size="small"
variant="ghost"
/>
<MenuList>
<Item key="sort-asc">Sort column ascending</Item>
<Item key="sort-desc">Sort column descending</Item>
<Item isSeparator key="separator">
Separator
</Item>
<Item key="freeze-left">Freeze column left</Item>
<Item key="freeze-right">Freeze column right</Item>
</MenuList>
</Menu>
</Flex>
<div
{...column.getResizerProps()}
className={`resizer ${column.isResizing ? "isResizing" : ""}`}
data-resizor=""
data-status={column.isResizing ? "resizing" : ""}
onClick={(e: React.MouseEvent<HTMLElement>) => {
e.preventDefault();
e.stopPropagation();
}}
/>
</div>
</th>
);
};
export const HeaderCell = memo(HeaderCellComponent);

View File

@ -23,6 +23,7 @@ export const renderBodyCheckBoxCell = (
data-sticky-td="true"
isCellVisible
isChecked={isChecked}
role="cell"
>
<CellCheckbox>
{isChecked && <CheckBoxCheckIcon className="th-svg" />}

View File

@ -0,0 +1,224 @@
.table {
--th-height: calc(
var(--caption-line-height) + var(--caption-margin-end) +
var(--caption-margin-start) + (2 * var(--outer-spacing-3))
);
--tr-height: calc(
var(--body-line-height) + var(--body-margin-end) + var(--body-margin-start) +
(2 * var(--outer-spacing-4))
);
display: flex;
justify-content: space-between;
flex-direction: column;
flex-grow: 1;
background: var(--color-bg-elevation-3);
border-style: solid;
border-width: var(--border-width-1);
border-color: var(--color-bd);
border-radius: var(--border-radius-elevation-3);
box-sizing: border-box;
/* adding overflow hidden so that the scollbar at the bottom does not flow out */
overflow: hidden;
& [aria-hidden] {
opacity: 0.6;
}
/* --------------------------------------------------------------------------
* WRAPPER
*-------------------------------------------------------------------------- */
[data-table-wrapper] {
display: block;
position: relative;
overflow: auto hidden;
scrollbar-color: initial;
container-type: inline-size;
}
&[data-type="virtualized"] [data-table-wrapper] {
overflow: hidden;
}
/* --------------------------------------------------------------------------
* TABLE
*-------------------------------------------------------------------------- */
& [role="table"] {
background-position-x: var(--th-height);
background-position-y: var(--tr-height);
border-spacing: 0;
display: table;
}
/* --------------------------------------------------------------------------
* THEAD
*-------------------------------------------------------------------------- */
& thead {
position: sticky;
top: 0;
z-index: 1;
width: fit-content;
}
/* --------------------------------------------------------------------------
* ROWGROUP
*-------------------------------------------------------------------------- */
& [role="rowgroup"] {
position: relative;
}
/* --------------------------------------------------------------------------
* TH
*-------------------------------------------------------------------------- */
& thead [role="columnheader"] {
padding-inline: var(--outer-spacing-1);
padding-block: var(--outer-spacing-1);
background: var(--color-bg-elevation-3);
font-weight: bold;
align-items: center;
justify-content: flex-end;
border-block-end: var(--border-width-1) solid var(--color-bd);
}
&[data-variant="default"] thead [role="columnheader"] {
border-inline-end: var(--border-width-1) solid var(--color-bd);
}
& thead [role="columnheader"]:last-child {
border-inline-end: none;
}
& thead [role="columnheader"][data-sticky-td] {
position: sticky;
left: 0;
}
& thead [role="columnheader"][data-highlight-position="start"] {
border-inline-start: 2px solid var(--color-bd-positive);
}
& thead [role="columnheader"][data-highlight-position="end"] {
border-inline-end: 2px solid var(--color-bd-positive);
}
& thead [role="columnheader"] [data-draggable-header] {
position: absolute;
inset: 0;
display: flex;
align-items: items;
padding-inline: var(--outer-spacing-2);
padding-inline-end: var(--padding-inline-end);
}
&
thead
[role="columnheader"]:has([data-status="dragging"])
[data-draggable-header] {
background: var(--color-bg);
border-radius: 4px;
z-index: 100;
width: 100%;
text-overflow: none;
overflow: none;
}
& thead [role="columnheader"][data-sticky-last-left-td]:not([aria-hidden]) {
/*important is added below because the useSticky hooks adds left as inline css */
left: 0px !important;
border-right: 3px solid var(--color-bd);
}
& thead [role="columnheader"][data-sticky-first-right-td]:not([aria-hidden]) {
/*important is added below because the useSticky hooks adds left as inline css */
right: 0px !important;
border-left: 3px solid var(--color-bd);
}
/* --------------------------------------------------------------------------
* TR
*-------------------------------------------------------------------------- */
& [role="row"] {
display: flex;
block-size: var(--tr-height);
}
& [role="row"]:has([role="columnheader"]) {
block-size: var(--th-height);
}
/*-------------------------------------------------------------------------
* Cell ( td )
*-------------------------------------------------------------------------- */
& [role="cell"] {
background-color: var(--color-bg-elevation-3);
}
& [role="row"][aria-checked="true"] [role="cell"] {
background-color: var(--color-bg-accent-subtle);
}
&[data-status="add-row-in-progress"] [role="cell"] {
background: var(--color-bg);
}
& [role="row"][data-is-new] [role="cell"] {
background: var(--color-bg-accent-subtle);
}
&[data-variant="default"] [role="cell"]:not(:last-child) {
border-inline-end: var(--border-width-1) solid var(--color-bd);
}
&:is([data-variant="horizontal-borders"], [data-variant="default"])
[role="cell"] {
border-block-end: var(--border-width-1) solid var(--color-bd);
}
& [role="cell"][data-sticky-td] {
position: sticky;
position: -webkit-sticky;
}
& [role="cell"][data-sticky-last-left-td]:not([aria-hidden]) {
/*important is added below because the useSticky hooks adds left as inline css */
left: 0px !important;
border-right: 3px solid var(--color-bd);
}
& [role="cell"][data-sticky-first-right-td]:not([aria-hidden]) {
/*important is below here because the useSticky hooks adds left as inline css */
right: 0px !important;
border-left: 3px solid var(--color-bd);
}
/*important is added below because the useSticky hooks adds styles as inline css */
& :is([role="cell"], th)[data-sticky-td][aria-hidden] {
position: relative !important;
left: unset !important;
right: unset !important;
}
/* --------------------------------------------------------------------------
* Resizor
*-------------------------------------------------------------------------- */
& [data-resizor] {
display: inline-block;
width: 10px;
height: 100%;
position: absolute;
right: 0;
top: 0;
transform: translateX(50%);
z-index: 1;
/* prevents from scrolling while dragging on touch devices */
touch-action: none;
}
& [data-resizor][data-status="resizing"] {
cursor: isResizing;
}
& thead th:last-child [data-resizor] {
right: 5px;
}
}

View File

@ -13,7 +13,6 @@ import {
updateCurrencyDefaultValues,
updateMenuItemsSource,
updateNumberColumnTypeTextAlignment,
updateThemeStylesheetsInColumns,
} from "../../../widget/propertyUtils";
import { AutocompleteDataType } from "utils/autocomplete/AutocompleteDataType";
import { composePropertyUpdateHook } from "widgets/WidgetUtils";
@ -84,7 +83,6 @@ export default {
],
updateHook: composePropertyUpdateHook([
updateNumberColumnTypeTextAlignment,
updateThemeStylesheetsInColumns,
updateMenuItemsSource,
updateCurrencyDefaultValues,
]),

View File

@ -194,47 +194,6 @@ export const styleConfig = [
},
],
},
{
propertyName: "borderRadius",
label: "Border radius",
helpText: "Rounds the corners of the icon button's outer border edge",
controlType: "BORDER_RADIUS_OPTIONS",
isJSConvertible: true,
isBindProperty: true,
isTriggerProperty: false,
validation: { type: ValidationTypes.TEXT },
},
{
propertyName: "boxShadow",
label: "Box shadow",
helpText:
"Enables you to cast a drop shadow from the frame of the widget",
controlType: "BOX_SHADOW_OPTIONS",
isJSConvertible: true,
isBindProperty: true,
isTriggerProperty: false,
validation: { type: ValidationTypes.TEXT },
},
{
helpText: "Use a html color name, HEX, RGB or RGBA value",
placeholderText: "#FFFFFF / Gray / rgb(255, 99, 71)",
propertyName: "borderColor",
label: "Border color",
controlType: "COLOR_PICKER",
isBindProperty: true,
isTriggerProperty: false,
validation: { type: ValidationTypes.TEXT },
},
{
helpText: "Enter value for border width",
propertyName: "borderWidth",
label: "Border width",
placeholderText: "Enter value in px",
controlType: "INPUT_TEXT",
isBindProperty: true,
isTriggerProperty: false,
validation: { type: ValidationTypes.NUMBER },
},
],
},
];

View File

@ -20,7 +20,7 @@ import _, {
import type { WidgetState } from "widgets/BaseWidget";
import BaseWidget from "widgets/BaseWidget";
import { RenderModes, WIDGET_PADDING } from "constants/WidgetConstants";
import { RenderModes } from "constants/WidgetConstants";
import { EventType } from "constants/AppsmithActionConstants/ActionConstants";
import Skeleton from "components/utils/Skeleton";
import { noop, retryPromise } from "utils/AppsmithUtils";
@ -910,8 +910,7 @@ export class WDSTableWidget extends BaseWidget<TableWidgetProps, WidgetState> {
document
.getElementById(getAnvilWidgetDOMId(this.props.widgetId))
?.getBoundingClientRect().width || this.props.componentWidth;
// (2 * WIDGET_PADDING) gives the total horizontal padding (i.e. paddingLeft + paddingRight)
componentWidth = componentWidth - 2 * WIDGET_PADDING;
componentWidth = componentWidth;
return { componentHeight: 300, componentWidth };
};

View File

@ -6,10 +6,6 @@ import type { TableWidgetProps } from "../constants";
import { ColumnTypes, InlineEditingSaveOptions } from "../constants";
import _, { findIndex, get, isBoolean } from "lodash";
import { Colors } from "constants/Colors";
import {
combineDynamicBindings,
getDynamicBindings,
} from "utils/DynamicBindingUtils";
import {
createEditActionColumn,
generateNewColumnOrderFromStickyValue,
@ -559,72 +555,6 @@ export const updateNumberColumnTypeTextAlignment = (
return;
};
/**
* updates theme stylesheets
*
* @param props
* @param propertyPath
* @param propertyValue
*/
export function updateThemeStylesheetsInColumns(
props: TableWidgetProps,
propertyPath: string,
propertyValue: any,
): Array<PropertyUpdates> | undefined {
const regex = /^primaryColumns\.(\w+)\.(.*)$/;
const matches = propertyPath.match(regex);
const columnId = matches?.[1];
const columnProperty = matches?.[2];
if (columnProperty === "columnType") {
const propertiesToUpdate: Array<PropertyUpdates> = [];
const oldColumnType = get(props, `primaryColumns.${columnId}.columnType`);
const newColumnType = propertyValue;
const propertiesToRemove = Object.keys(
props.childStylesheet[oldColumnType] || {},
);
const propertiesToAdd = Object.keys(
props.childStylesheet[newColumnType] || {},
);
propertiesToRemove.forEach((propertyKey) => {
propertiesToUpdate.push({
propertyPath: `primaryColumns.${columnId}.${propertyKey}`,
shouldDeleteProperty: true,
});
});
propertiesToAdd.forEach((propertyKey) => {
const { jsSnippets, stringSegments } = getDynamicBindings(
props.childStylesheet[newColumnType][propertyKey],
);
const js = combineDynamicBindings(jsSnippets, stringSegments);
propertiesToUpdate.push({
propertyPath: `primaryColumns.${columnId}.${propertyKey}`,
propertyValue: `{{${props.widgetName}.processedTableData.map((currentRow, currentIndex) => ( ${js}))}}`,
});
});
if (propertiesToUpdate.length) {
/*
* Temporary patch to make evaluations to compute inverseDependencyMap when
* column type is changed.
* TODO(Balaji): remove once https://github.com/appsmithorg/appsmith/issues/14436 gets fixed
*/
propertiesToUpdate.push({
propertyPath: `primaryColumns.${columnId}.customAlias`,
propertyValue: "",
});
return propertiesToUpdate;
}
}
}
/**
* A function for updateHook to remove the boxShadowColor property post migration.
* @param props

View File

@ -9,7 +9,6 @@ import {
getArrayPropertyValue,
getColumnType,
getDerivedColumns,
getHeaderClassNameOnDragDirection,
getOriginalRowIndex,
getSelectOptions,
getSelectRowIndex,
@ -2542,20 +2541,6 @@ describe("generateNewColumnOrderFromStickyValue", () => {
});
});
describe("getHeaderClassNameOnDragDirection", () => {
test("Should return left highlight class when dragging from right to left", () => {
expect(getHeaderClassNameOnDragDirection(3, 2)).toEqual(
"th header-reorder highlight-left",
);
});
test("Should return right highlight class when dragging from left to right", () => {
expect(getHeaderClassNameOnDragDirection(1, 2)).toEqual(
"th header-reorder highlight-right",
);
});
});
describe("getSelectOptions", () => {
it("Should return select options when user is not adding a new row", () => {
const columnProperties = {

View File

@ -25,11 +25,6 @@ import { SelectColumnOptionsValidations } from "./propertyUtils";
import type { TableWidgetProps } from "../constants";
import { get } from "lodash";
import { getNextEntityName } from "utils/AppsmithUtils";
import {
combineDynamicBindings,
getDynamicBindings,
} from "utils/DynamicBindingUtils";
import { ButtonVariantTypes } from "components/constants";
import { dateFormatOptions } from "WidgetProvider/constants";
import moment from "moment";
import type { Stylesheet } from "entities/AppTheming";
@ -668,32 +663,11 @@ export const createColumn = (props: TableWidgetProps, baseName: string) => {
};
export const createEditActionColumn = (props: TableWidgetProps) => {
const themeProps: Record<string, string> = {};
if (props.childStylesheet[ColumnTypes.EDIT_ACTIONS]) {
Object.entries(props.childStylesheet[ColumnTypes.EDIT_ACTIONS]).forEach(
([key, value]) => {
const { jsSnippets, stringSegments } = getDynamicBindings(
value as string,
);
const js = combineDynamicBindings(jsSnippets, stringSegments);
themeProps[
key
] = `{{${props.widgetName}.processedTableData.map((currentRow, currentIndex) => ( ${js}))}}`;
},
);
}
const column = {
...createColumn(props, "EditActions"),
...getEditActionColumnProperties(),
...themeProps,
columnType: ColumnTypes.EDIT_ACTIONS,
label: "Save / Discard",
discardButtonVariant: ButtonVariantTypes.TERTIARY,
discardButtonColor: Colors.DANGER_SOLID,
sticky: StickyType.RIGHT,
};
const columnOrder = [...(props.columnOrder || [])];
@ -944,26 +918,27 @@ export const getAllStickyColumnsCount = (columns: TableColumnProps[]) => {
};
/**
* returns the highlight position when the column header is dragged
*
* @param currentIndex: current dragging item index
* @param targetIndex: Index poistion of of header that is being hovered
* @returns
* @returns "start" | "end" | "none
*/
export const getHeaderClassNameOnDragDirection = (
export const getHighlightPosition = (
currentIndex: number,
targetIndex: number,
) => {
let parentClasses = "th header-reorder";
let position = "none";
if (currentIndex !== -1) {
if (targetIndex > currentIndex) {
parentClasses += " highlight-right";
position = "end";
} else if (targetIndex < currentIndex) {
parentClasses += " highlight-left";
position = "start";
}
}
return parentClasses;
return position;
};
export const getIndexByColumnName = (
@ -1001,7 +976,7 @@ export const getDragHandlers = (
) => {
// We get the parent element(.th) so as to apply left and right highlighting
const targetElem = e.target as HTMLDivElement;
const parentTargetElem = targetElem.closest(".th.header-reorder");
const parentTargetElem = targetElem.closest("th");
const currentIndex = getIndexByColumnName(
currentDraggedColumn.current,
@ -1009,30 +984,30 @@ export const getDragHandlers = (
);
if (parentTargetElem) {
parentTargetElem.className = getHeaderClassNameOnDragDirection(
currentIndex,
targetIndex,
);
if (parentTargetElem) {
parentTargetElem.dataset.highlightPosition = getHighlightPosition(
currentIndex,
targetIndex,
);
}
}
e.stopPropagation();
e.preventDefault();
};
const onDragEnd = (e: React.DragEvent<HTMLDivElement>) => {
const targetElem = e.target as HTMLDivElement;
targetElem.className = targetElem.className.replace(
" draggable-header--dragging",
"",
);
targetElem.dataset.status = "";
e.preventDefault();
};
const onDragLeave = (e: React.DragEvent<HTMLDivElement>) => {
const targetElem = e.target as HTMLDivElement;
const parentTargetElem = targetElem.closest(".th.header-reorder");
const parentTargetElem = targetElem.closest("th");
if (parentTargetElem) {
parentTargetElem.className = "th header-reorder";
parentTargetElem.dataset.highlightPosition = "none";
}
e.preventDefault();
@ -1043,7 +1018,7 @@ export const getDragHandlers = (
) => {
// We get the parent element(.th) so as to apply left and right highlighting
const targetElem = e.target as HTMLDivElement;
const parentTargetElem = targetElem.closest(".th.header-reorder");
const parentTargetElem = targetElem.closest("th");
const currentIndex = getIndexByColumnName(
currentDraggedColumn.current,
@ -1051,7 +1026,7 @@ export const getDragHandlers = (
);
if (parentTargetElem) {
parentTargetElem.className = getHeaderClassNameOnDragDirection(
parentTargetElem.dataset.highlightPosition = getHighlightPosition(
currentIndex,
targetIndex,
);
@ -1064,7 +1039,7 @@ export const getDragHandlers = (
const onDragStart = (e: React.DragEvent<HTMLDivElement>, index: number) => {
currentDraggedColumn.current = columns[index].alias;
const targetElem = e.target as HTMLDivElement;
targetElem.className = targetElem.className + " draggable-header--dragging";
targetElem.dataset.status = "dragging";
e.stopPropagation();
};
@ -1078,10 +1053,7 @@ export const getDragHandlers = (
partialColumnOrder.splice(index, 0, currentDraggedColumn.current);
handleReorderColumn(partialColumnOrder);
}
targetElem.className = targetElem.className.replace(
" draggable-header--dragging",
"",
);
targetElem.dataset.status = "";
e.stopPropagation();
};