Merge branch 'fix/table-bugs' into 'release'
Fixing table bugs This MR fixes a bunch of bugs associated with the react-base-table. Some of them include: 1. Show loading state for the table 2. Handling nested objects when displaying table data 3. Truncate long text instead of overlapping it with the next column 4. Displaying Youtube video URLs in the table column See merge request theappsmith/internal-tools-client!704
This commit is contained in:
commit
0abd0fb682
|
|
@ -216,8 +216,10 @@ export class ReactTableComponent extends React.Component<
|
|||
Cell: (props: any) => {
|
||||
return renderCell(
|
||||
props.cell.value,
|
||||
props.cell.row.index,
|
||||
columnType.type,
|
||||
isHidden,
|
||||
this.props.widgetId,
|
||||
columnType.format,
|
||||
);
|
||||
},
|
||||
|
|
@ -232,7 +234,10 @@ export class ReactTableComponent extends React.Component<
|
|||
columns = this.reorderColumns(columns);
|
||||
if (this.props.columnActions?.length) {
|
||||
columns.push({
|
||||
Header: "Actions",
|
||||
Header:
|
||||
this.props.columnNameMap && this.props.columnNameMap["actions"]
|
||||
? this.props.columnNameMap["actions"]
|
||||
: "Actions",
|
||||
accessor: "actions",
|
||||
width: 150,
|
||||
minWidth: 60,
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@ export const TableWrapper = styled.div<{ width: number; height: number }>`
|
|||
justify-content: space-between;
|
||||
flex-direction: column;
|
||||
.tableWrap {
|
||||
height: 100%;
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
border-bottom: 1px solid ${Colors.GEYSER_LIGHT};
|
||||
}
|
||||
.table {
|
||||
border-spacing: 0;
|
||||
|
|
@ -29,16 +29,11 @@ export const TableWrapper = styled.div<{ width: number; height: number }>`
|
|||
height: ${props => props.height - 5 - 102}px;
|
||||
}
|
||||
.tr {
|
||||
:last-child {
|
||||
.td {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
:nth-child(even) {
|
||||
background: ${Colors.ATHENS_GRAY_DARKER};
|
||||
}
|
||||
&.selected-row {
|
||||
background: ${Colors.ATHENS_GRAY};
|
||||
background: ${Colors.POLAR};
|
||||
}
|
||||
&:hover {
|
||||
background: ${Colors.ATHENS_GRAY};
|
||||
|
|
@ -237,7 +232,7 @@ export const CellWrapper = styled.div<{ isHidden: boolean }>`
|
|||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: normal;
|
||||
white-space: nowrap;
|
||||
opacity: ${props => (props.isHidden ? "0.6" : "1")};
|
||||
.image-cell {
|
||||
width: 40px;
|
||||
|
|
@ -251,4 +246,11 @@ export const CellWrapper = styled.div<{ isHidden: boolean }>`
|
|||
video {
|
||||
border-radius: 4px;
|
||||
}
|
||||
&.video-cell {
|
||||
height: 100%;
|
||||
iframe {
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {
|
|||
} from "./TableStyledWrappers";
|
||||
import { ColumnAction } from "components/propertyControls/ColumnActionSelectorControl";
|
||||
import { ColumnMenuOptionProps } from "./ReactTableComponent";
|
||||
import { isString } from "lodash";
|
||||
|
||||
interface MenuOptionProps {
|
||||
columnAccessor?: string;
|
||||
|
|
@ -272,8 +273,10 @@ export const getMenuOptions = (props: MenuOptionProps) => {
|
|||
|
||||
export const renderCell = (
|
||||
value: any,
|
||||
rowIndex: number,
|
||||
columnType: string,
|
||||
isHidden: boolean,
|
||||
widgetId: string,
|
||||
format?: string,
|
||||
) => {
|
||||
if (!value) {
|
||||
|
|
@ -281,6 +284,13 @@ export const renderCell = (
|
|||
}
|
||||
switch (columnType) {
|
||||
case "image":
|
||||
if (!isString(value)) {
|
||||
return (
|
||||
<CellWrapper isHidden={isHidden}>
|
||||
<div>Invalid Image </div>
|
||||
</CellWrapper>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<CellWrapper isHidden={isHidden}>
|
||||
{value
|
||||
|
|
@ -302,13 +312,25 @@ export const renderCell = (
|
|||
</CellWrapper>
|
||||
);
|
||||
case "video":
|
||||
return (
|
||||
<CellWrapper isHidden={isHidden}>
|
||||
<video width="56" height="32" autoPlay={false}>
|
||||
<source src={`${value}`} type="video/mp4" />
|
||||
</video>
|
||||
</CellWrapper>
|
||||
const youtubeRegex = new RegExp(
|
||||
"^(https?://)?(www.)?(youtube.com|youtu.?be)/embed/.+$",
|
||||
);
|
||||
if (isString(value) && youtubeRegex.test(value)) {
|
||||
return (
|
||||
<CellWrapper isHidden={isHidden} className="video-cell">
|
||||
<iframe
|
||||
title={`video-${widgetId}-${rowIndex}`}
|
||||
width="56"
|
||||
height="32"
|
||||
src={`${value}`}
|
||||
></iframe>
|
||||
</CellWrapper>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<CellWrapper isHidden={isHidden}>Invalid Video Link</CellWrapper>
|
||||
);
|
||||
}
|
||||
case "currency":
|
||||
if (!isNaN(value)) {
|
||||
return (
|
||||
|
|
@ -352,9 +374,11 @@ export const renderCell = (
|
|||
return <CellWrapper isHidden={isHidden}>Invalid Time</CellWrapper>;
|
||||
}
|
||||
case "text":
|
||||
return <CellWrapper isHidden={isHidden}>{value}</CellWrapper>;
|
||||
const text = isString(value) ? value : JSON.stringify(value);
|
||||
return <CellWrapper isHidden={isHidden}>{text}</CellWrapper>;
|
||||
default:
|
||||
return <CellWrapper isHidden={isHidden}>{value}</CellWrapper>;
|
||||
const data = isString(value) ? value : JSON.stringify(value);
|
||||
return <CellWrapper isHidden={isHidden}>{data}</CellWrapper>;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -50,13 +50,13 @@ export function InputText(props: {
|
|||
class InputTextControl extends BaseControl<InputControlProps> {
|
||||
render() {
|
||||
const {
|
||||
errorMessage,
|
||||
expected,
|
||||
propertyValue,
|
||||
isValid,
|
||||
label,
|
||||
placeholderText,
|
||||
dataTreePath,
|
||||
validationMessage,
|
||||
} = this.props;
|
||||
return (
|
||||
<InputText
|
||||
|
|
@ -64,7 +64,7 @@ class InputTextControl extends BaseControl<InputControlProps> {
|
|||
value={propertyValue}
|
||||
onChange={this.onTextChange}
|
||||
isValid={isValid}
|
||||
errorMessage={errorMessage}
|
||||
errorMessage={validationMessage}
|
||||
expected={expected}
|
||||
placeholder={placeholderText}
|
||||
dataTreePath={dataTreePath}
|
||||
|
|
@ -101,6 +101,7 @@ class InputTextControl extends BaseControl<InputControlProps> {
|
|||
export interface InputControlProps extends ControlProps {
|
||||
placeholderText: string;
|
||||
inputType: InputType;
|
||||
validationMessage?: string;
|
||||
isDisabled?: boolean;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -245,22 +245,24 @@ export const VALIDATORS: Record<ValidationType, Validator> = {
|
|||
if (!isValid) {
|
||||
return {
|
||||
isValid,
|
||||
parsed,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Table Data`,
|
||||
parsed: [],
|
||||
transformed: parsed,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: [{ "Col1" : "val1", "Col2" : "val2" }]`,
|
||||
};
|
||||
} else if (
|
||||
!_.every(parsed, datum => {
|
||||
return (
|
||||
_.isObject(datum) &&
|
||||
Object.keys(datum).filter(key => _.isString(key) && key.length === 0)
|
||||
.length === 0
|
||||
);
|
||||
})
|
||||
) {
|
||||
}
|
||||
const isValidTableData = _.every(parsed, datum => {
|
||||
return (
|
||||
_.isObject(datum) &&
|
||||
Object.keys(datum).filter(key => _.isString(key) && key.length === 0)
|
||||
.length === 0
|
||||
);
|
||||
});
|
||||
if (!isValidTableData) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: [],
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: [{ "key1" : "val1", "key2" : "val2" }, { "key1" : "val3", "key2" : "val4" }]`,
|
||||
transformed: parsed,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: [{ "Col1" : "val1", "Col2" : "val2" }]`,
|
||||
};
|
||||
}
|
||||
return { isValid, parsed };
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import React, { lazy, Suspense } from "react";
|
||||
import React, { Suspense } from "react";
|
||||
import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
|
||||
import { WidgetType } from "constants/WidgetConstants";
|
||||
import { EventType } from "constants/ActionConstants";
|
||||
import { forIn } from "lodash";
|
||||
// import { forIn } from "lodash";
|
||||
import ReactTableComponent from "components/designSystems/appsmith/ReactTableComponent";
|
||||
|
||||
import { VALIDATION_TYPES } from "constants/WidgetValidation";
|
||||
|
|
@ -10,43 +10,44 @@ import {
|
|||
WidgetPropertyValidationType,
|
||||
BASE_WIDGET_VALIDATION,
|
||||
} from "utils/ValidationFactory";
|
||||
import { ColumnModel } from "@syncfusion/ej2-grids";
|
||||
import { ColumnDirTypecast } from "@syncfusion/ej2-react-grids";
|
||||
// import { ColumnModel } from "@syncfusion/ej2-grids";
|
||||
// import { ColumnDirTypecast } from "@syncfusion/ej2-react-grids";
|
||||
import { ColumnAction } from "components/propertyControls/ColumnActionSelectorControl";
|
||||
import { TriggerPropertiesMap } from "utils/WidgetFactory";
|
||||
import Skeleton from "components/utils/Skeleton";
|
||||
import { Classes } from "@blueprintjs/core";
|
||||
|
||||
const TableComponent = lazy(() =>
|
||||
import(
|
||||
/* webpackPrefetch: true, webpackChunkName: "table" */ "components/designSystems/syncfusion/TableComponent"
|
||||
),
|
||||
);
|
||||
// const TableComponent = React.lazy(() =>
|
||||
// import(
|
||||
// /* webpackPrefetch: true, webpackChunkName: "table" */ "components/designSystems/syncfusion/TableComponent"
|
||||
// ),
|
||||
// );
|
||||
|
||||
const ROW_HEIGHT = 37;
|
||||
const TABLE_HEADER_HEIGHT = 39;
|
||||
const TABLE_FOOTER_HEIGHT = 48;
|
||||
const TABLE_EXPORT_HEIGHT = 43;
|
||||
// const ROW_HEIGHT = 37;
|
||||
// const TABLE_HEADER_HEIGHT = 39;
|
||||
// const TABLE_FOOTER_HEIGHT = 48;
|
||||
// const TABLE_EXPORT_HEIGHT = 43;
|
||||
|
||||
function constructColumns(
|
||||
data: object[],
|
||||
hiddenColumns?: string[],
|
||||
): ColumnModel[] | ColumnDirTypecast[] {
|
||||
let cols: ColumnModel[] | ColumnDirTypecast[] = [];
|
||||
const listItemWithAllProperties = {};
|
||||
data.forEach(dataItem => {
|
||||
Object.assign(listItemWithAllProperties, dataItem);
|
||||
});
|
||||
forIn(listItemWithAllProperties, (value: any, key: string) => {
|
||||
cols.push({
|
||||
field: key,
|
||||
visible: !hiddenColumns?.includes(key),
|
||||
});
|
||||
});
|
||||
cols = (cols as any[]).filter(col => col.field !== "_color") as
|
||||
| ColumnModel[]
|
||||
| ColumnDirTypecast[];
|
||||
return cols;
|
||||
}
|
||||
// function constructColumns(
|
||||
// data: object[],
|
||||
// hiddenColumns?: string[],
|
||||
// ): ColumnModel[] | ColumnDirTypecast[] {
|
||||
// let cols: ColumnModel[] | ColumnDirTypecast[] = [];
|
||||
// const listItemWithAllProperties = {};
|
||||
// data.forEach(dataItem => {
|
||||
// Object.assign(listItemWithAllProperties, dataItem);
|
||||
// });
|
||||
// forIn(listItemWithAllProperties, (value: any, key: string) => {
|
||||
// cols.push({
|
||||
// field: key,
|
||||
// visible: !hiddenColumns?.includes(key),
|
||||
// });
|
||||
// });
|
||||
// cols = (cols as any[]).filter(col => col.field !== "_color") as
|
||||
// | ColumnModel[]
|
||||
// | ColumnDirTypecast[];
|
||||
// return cols;
|
||||
// }
|
||||
|
||||
class TableWidget extends BaseWidget<TableWidgetProps, WidgetState> {
|
||||
static getPropertyValidationMap(): WidgetPropertyValidationType {
|
||||
|
|
@ -85,7 +86,7 @@ class TableWidget extends BaseWidget<TableWidgetProps, WidgetState> {
|
|||
|
||||
getPageView() {
|
||||
const { tableData, hiddenColumns } = this.props;
|
||||
const columns = constructColumns(tableData, hiddenColumns);
|
||||
// const columns = constructColumns(tableData, hiddenColumns);
|
||||
|
||||
const serverSidePaginationEnabled = (this.props
|
||||
.serverSidePaginationEnabled &&
|
||||
|
|
@ -98,14 +99,14 @@ class TableWidget extends BaseWidget<TableWidgetProps, WidgetState> {
|
|||
}
|
||||
const { componentWidth, componentHeight } = this.getComponentDimensions();
|
||||
|
||||
const exportHeight =
|
||||
this.props.exportCsv || this.props.exportPDF || this.props.exportCsv
|
||||
? TABLE_EXPORT_HEIGHT
|
||||
: 0;
|
||||
const tableHeaderHeight =
|
||||
this.props.tableData.length === 0 ? 2 : TABLE_HEADER_HEIGHT;
|
||||
const tableContentHeight =
|
||||
componentHeight - TABLE_FOOTER_HEIGHT - tableHeaderHeight - exportHeight;
|
||||
// const exportHeight =
|
||||
// this.props.exportCsv || this.props.exportPDF || this.props.exportCsv
|
||||
// ? TABLE_EXPORT_HEIGHT
|
||||
// : 0;
|
||||
// const tableHeaderHeight =
|
||||
// this.props.tableData.length === 0 ? 2 : TABLE_HEADER_HEIGHT;
|
||||
// const tableContentHeight =
|
||||
// componentHeight - TABLE_FOOTER_HEIGHT - tableHeaderHeight - exportHeight;
|
||||
// Use below code to calculate page size for old table component
|
||||
// const pageSize = Math.floor(tableContentHeight / ROW_HEIGHT);
|
||||
const pageSize = Math.floor((componentHeight - 104) / 52);
|
||||
|
|
@ -113,59 +114,60 @@ class TableWidget extends BaseWidget<TableWidgetProps, WidgetState> {
|
|||
if (pageSize !== this.props.pageSize) {
|
||||
super.updateWidgetMetaProperty("pageSize", pageSize);
|
||||
}
|
||||
|
||||
// /*
|
||||
return (
|
||||
<Suspense fallback={<Skeleton />}>
|
||||
<ReactTableComponent
|
||||
height={componentHeight}
|
||||
width={componentWidth}
|
||||
tableData={tableData}
|
||||
widgetId={this.props.widgetId}
|
||||
renderMode={this.props.renderMode}
|
||||
hiddenColumns={hiddenColumns}
|
||||
columnActions={this.props.columnActions}
|
||||
columnNameMap={this.props.columnNameMap}
|
||||
columnTypeMap={this.props.columnTypeMap}
|
||||
columnOrder={this.props.columnOrder}
|
||||
pageSize={pageSize}
|
||||
onCommandClick={this.onCommandClick}
|
||||
selectedRowIndex={
|
||||
this.props.selectedRowIndex === undefined
|
||||
? -1
|
||||
: this.props.selectedRowIndex
|
||||
}
|
||||
serverSidePaginationEnabled={serverSidePaginationEnabled}
|
||||
onRowClick={this.handleRowClick}
|
||||
pageNo={pageNo}
|
||||
nextPageClick={this.handleNextPageClick}
|
||||
prevPageClick={this.handlePrevPageClick}
|
||||
updatePageNo={(pageNo: number) => {
|
||||
super.updateWidgetMetaProperty("pageNo", pageNo);
|
||||
}}
|
||||
updateHiddenColumns={(hiddenColumns?: string[]) => {
|
||||
super.updateWidgetProperty("hiddenColumns", hiddenColumns);
|
||||
}}
|
||||
updateColumnType={(columnTypeMap: {
|
||||
[key: string]: { type: string; format: string };
|
||||
}) => {
|
||||
super.updateWidgetProperty("columnTypeMap", columnTypeMap);
|
||||
}}
|
||||
updateColumnName={(columnNameMap: { [key: string]: string }) => {
|
||||
super.updateWidgetProperty("columnNameMap", columnNameMap);
|
||||
}}
|
||||
handleResizeColumn={(columnSizeMap: { [key: string]: number }) => {
|
||||
super.updateWidgetProperty("columnSizeMap", columnSizeMap);
|
||||
}}
|
||||
handleReorderColumn={(columnOrder: string[]) => {
|
||||
super.updateWidgetProperty("columnOrder", columnOrder);
|
||||
}}
|
||||
columnSizeMap={this.props.columnSizeMap}
|
||||
resetSelectedRowIndex={this.resetSelectedRowIndex}
|
||||
disableDrag={(disable: boolean) => {
|
||||
this.disableDrag(disable);
|
||||
}}
|
||||
/>
|
||||
<div className={this.props.isLoading ? Classes.SKELETON : ""}>
|
||||
<ReactTableComponent
|
||||
height={componentHeight}
|
||||
width={componentWidth}
|
||||
tableData={tableData}
|
||||
widgetId={this.props.widgetId}
|
||||
renderMode={this.props.renderMode}
|
||||
hiddenColumns={hiddenColumns}
|
||||
columnActions={this.props.columnActions}
|
||||
columnNameMap={this.props.columnNameMap}
|
||||
columnTypeMap={this.props.columnTypeMap}
|
||||
columnOrder={this.props.columnOrder}
|
||||
pageSize={pageSize}
|
||||
onCommandClick={this.onCommandClick}
|
||||
selectedRowIndex={
|
||||
this.props.selectedRowIndex === undefined
|
||||
? -1
|
||||
: this.props.selectedRowIndex
|
||||
}
|
||||
serverSidePaginationEnabled={serverSidePaginationEnabled}
|
||||
onRowClick={this.handleRowClick}
|
||||
pageNo={pageNo}
|
||||
nextPageClick={this.handleNextPageClick}
|
||||
prevPageClick={this.handlePrevPageClick}
|
||||
updatePageNo={(pageNo: number) => {
|
||||
super.updateWidgetMetaProperty("pageNo", pageNo);
|
||||
}}
|
||||
updateHiddenColumns={(hiddenColumns?: string[]) => {
|
||||
super.updateWidgetProperty("hiddenColumns", hiddenColumns);
|
||||
}}
|
||||
updateColumnType={(columnTypeMap: {
|
||||
[key: string]: { type: string; format: string };
|
||||
}) => {
|
||||
super.updateWidgetProperty("columnTypeMap", columnTypeMap);
|
||||
}}
|
||||
updateColumnName={(columnNameMap: { [key: string]: string }) => {
|
||||
super.updateWidgetProperty("columnNameMap", columnNameMap);
|
||||
}}
|
||||
handleResizeColumn={(columnSizeMap: { [key: string]: number }) => {
|
||||
super.updateWidgetProperty("columnSizeMap", columnSizeMap);
|
||||
}}
|
||||
handleReorderColumn={(columnOrder: string[]) => {
|
||||
super.updateWidgetProperty("columnOrder", columnOrder);
|
||||
}}
|
||||
columnSizeMap={this.props.columnSizeMap}
|
||||
resetSelectedRowIndex={this.resetSelectedRowIndex}
|
||||
disableDrag={(disable: boolean) => {
|
||||
this.disableDrag(disable);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Suspense>
|
||||
);
|
||||
// */
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user