mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-24 01:18:23 +00:00
Added dynamic column support in table widget (#5792)
* Created dynamic column in table widget * Added column data field in table inspector * Fixed generating previous dynamic column when dynamic column toggle is turned off * Updated logic to display dynamic column * Fixed issues on editable & updated logic * Fixed issue on displaying dynamic column in viewer
This commit is contained in:
parent
233f47630c
commit
0c6419464a
5 changed files with 145 additions and 85 deletions
|
|
@ -122,6 +122,7 @@ export function Table({
|
|||
|
||||
const [tableDetails, dispatch] = useReducer(reducer, initialState());
|
||||
const [hoverAdded, setHoverAdded] = useState(false);
|
||||
const [generatedColumn, setGeneratedColumn] = useState([]);
|
||||
const mergeToTableDetails = (payload) => dispatch(reducerActions.mergeToTableDetails(payload));
|
||||
const mergeToFilterDetails = (payload) => dispatch(reducerActions.mergeToFilterDetails(payload));
|
||||
const mounted = useMounted();
|
||||
|
|
@ -194,7 +195,6 @@ export function Table({
|
|||
const changesToBeSavedAndExposed = { dataUpdates: newDataUpdates, changeSet: newChangeset };
|
||||
mergeToTableDetails(changesToBeSavedAndExposed);
|
||||
|
||||
fireEvent('onCellValueChanged');
|
||||
return setExposedVariables({ ...changesToBeSavedAndExposed, updatedData: clonedTableData });
|
||||
}
|
||||
|
||||
|
|
@ -284,9 +284,15 @@ export function Table({
|
|||
}
|
||||
}, [color, darkMode]);
|
||||
|
||||
let tableData = [];
|
||||
let tableData = [],
|
||||
dynamicColumn = [];
|
||||
|
||||
const useDynamicColumn = resolveReferences(component.definition.properties?.useDynamicColumn?.value);
|
||||
if (currentState) {
|
||||
tableData = resolveReferences(component.definition.properties.data.value, currentState, []);
|
||||
dynamicColumn = useDynamicColumn
|
||||
? resolveReferences(component.definition.properties?.columnData?.value, currentState, []) ?? []
|
||||
: [];
|
||||
if (!Array.isArray(tableData)) tableData = [];
|
||||
}
|
||||
|
||||
|
|
@ -295,7 +301,7 @@ export function Table({
|
|||
const tableRef = useRef();
|
||||
|
||||
const columnData = generateColumnsData({
|
||||
columnProperties: component.definition.properties.columns.value,
|
||||
columnProperties: useDynamicColumn ? generatedColumn : component.definition.properties.columns.value,
|
||||
columnSizes,
|
||||
currentState,
|
||||
handleCellValueChange,
|
||||
|
|
@ -349,8 +355,6 @@ export function Table({
|
|||
] // Hack: need to fix
|
||||
);
|
||||
|
||||
console.log('columns--- ', columns);
|
||||
|
||||
const data = useMemo(
|
||||
() => tableData,
|
||||
[
|
||||
|
|
@ -362,15 +366,23 @@ export function Table({
|
|||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (tableData.length != 0 && component.definition.properties.autogenerateColumns?.value && mode === 'edit') {
|
||||
autogenerateColumns(
|
||||
if (
|
||||
tableData.length != 0 &&
|
||||
component.definition.properties.autogenerateColumns?.value &&
|
||||
(useDynamicColumn || mode === 'edit')
|
||||
) {
|
||||
const generatedColumnFromData = autogenerateColumns(
|
||||
tableData,
|
||||
component.definition.properties.columns.value,
|
||||
component.definition.properties?.columnDeletionHistory?.value ?? [],
|
||||
useDynamicColumn,
|
||||
dynamicColumn,
|
||||
setProperty
|
||||
);
|
||||
|
||||
useDynamicColumn && setGeneratedColumn(generatedColumnFromData);
|
||||
}
|
||||
}, [JSON.stringify(tableData)]);
|
||||
}, [JSON.stringify(tableData), JSON.stringify(dynamicColumn)]);
|
||||
|
||||
const computedStyles = {
|
||||
// width: `${width}px`,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,30 @@
|
|||
import _ from 'lodash';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
export default function autogenerateColumns(tableData, existingColumns, columnDeletionHistory, setProperty) {
|
||||
export default function autogenerateColumns(
|
||||
tableData,
|
||||
existingColumns,
|
||||
columnDeletionHistory,
|
||||
useDynamicColumn,
|
||||
dynamicColumn = [],
|
||||
setProperty
|
||||
) {
|
||||
if (useDynamicColumn) {
|
||||
if (dynamicColumn.length > 0 && dynamicColumn[0].name) {
|
||||
const generatedColumns = dynamicColumn.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
id: uuidv4(),
|
||||
name: item?.name,
|
||||
key: item?.key || item?.name,
|
||||
autogenerated: true,
|
||||
};
|
||||
});
|
||||
return generatedColumns;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
const firstRow = tableData?.[0] ?? {};
|
||||
|
||||
const firstRowWithoutNestedElements = Object.fromEntries(
|
||||
|
|
|
|||
|
|
@ -75,7 +75,8 @@ export default function generateColumnsData({
|
|||
customRule: column?.customRule,
|
||||
Cell: function ({ cell, isEditable }) {
|
||||
const rowChangeSet = changeSet ? changeSet[cell.row.index] : null;
|
||||
let cellValue = rowChangeSet ? rowChangeSet[column.name] ?? cell.value : cell.value;
|
||||
let cellValue = rowChangeSet ? rowChangeSet[column.key || column.name] ?? cell.value : cell.value;
|
||||
|
||||
const rowData = tableData[cell.row.index];
|
||||
if (
|
||||
cell.row.index === 0 &&
|
||||
|
|
|
|||
|
|
@ -920,6 +920,9 @@ class TableComponent extends React.Component {
|
|||
const enabledSort = component.component.definition.properties.enabledSort?.value
|
||||
? resolveReferences(component.component.definition.properties.enabledSort?.value, currentState)
|
||||
: true;
|
||||
const useDynamicColumn = component.component.definition.properties.useDynamicColumn?.value
|
||||
? resolveReferences(component.component.definition.properties.useDynamicColumn?.value, currentState) ?? false
|
||||
: false;
|
||||
|
||||
const renderCustomElement = (param, paramType = 'properties') => {
|
||||
return renderElement(component, componentMeta, paramUpdated, dataQueries, param, paramType, currentState);
|
||||
|
|
@ -946,84 +949,90 @@ class TableComponent extends React.Component {
|
|||
title: 'Columns',
|
||||
children: (
|
||||
<div>
|
||||
<div className="col-auto text-right mb-3">
|
||||
<button
|
||||
data-cy={`button-add-column`}
|
||||
onClick={this.addNewColumn}
|
||||
className="btn btn-sm border-0 font-weight-normal padding-2 col-auto color-primary inspector-add-button"
|
||||
>
|
||||
{this.props.t('widget.Table.addColumn', '+ Add column')}
|
||||
</button>
|
||||
</div>
|
||||
<DragDropContext
|
||||
onDragEnd={(result) => {
|
||||
this.onDragEnd(result);
|
||||
}}
|
||||
>
|
||||
<Droppable droppableId="droppable">
|
||||
{({ innerRef, droppableProps, placeholder }) => (
|
||||
<div className="w-100" {...droppableProps} ref={innerRef}>
|
||||
{columns.value.map((item, index) => {
|
||||
const resolvedItemName = resolveReferences(item.name, this.state.currentState);
|
||||
return (
|
||||
<Draggable key={item.id} draggableId={item.id} index={index}>
|
||||
{(provided, snapshot) => (
|
||||
<div
|
||||
className={`card p-2 column-sort-row mb-1 ${this.props.darkMode ? '' : 'bg-light'}`}
|
||||
key={index}
|
||||
ref={provided.innerRef}
|
||||
{...provided.draggableProps}
|
||||
{...provided.dragHandleProps}
|
||||
style={this.getItemStyle(snapshot.isDragging, provided.draggableProps.style)}
|
||||
>
|
||||
<OverlayTrigger
|
||||
trigger="click"
|
||||
placement="left"
|
||||
rootClose={this.state.popOverRootCloseBlockers.length === 0}
|
||||
overlay={this.columnPopover(item, index)}
|
||||
>
|
||||
<div key={resolvedItemName}>
|
||||
<div className={`row ${this.props.darkMode ? '' : 'bg-light'}`} role="button">
|
||||
<div className="col-auto">
|
||||
<img
|
||||
data-cy={`draggable-handle-column-${resolvedItemName}`}
|
||||
src="../../assets/images/icons/dragicon.svg"
|
||||
/>
|
||||
</div>
|
||||
<div className="col">
|
||||
<div className="text" data-cy={`column-${resolvedItemName}`}>
|
||||
{resolvedItemName}
|
||||
{!useDynamicColumn && (
|
||||
<>
|
||||
<div className="col-auto text-right mb-3">
|
||||
<button
|
||||
data-cy={`button-add-column`}
|
||||
onClick={this.addNewColumn}
|
||||
className="btn btn-sm border-0 font-weight-normal padding-2 col-auto color-primary inspector-add-button"
|
||||
>
|
||||
{this.props.t('widget.Table.addColumn', '+ Add column')}
|
||||
</button>
|
||||
</div>
|
||||
<DragDropContext
|
||||
onDragEnd={(result) => {
|
||||
this.onDragEnd(result);
|
||||
}}
|
||||
>
|
||||
<Droppable droppableId="droppable">
|
||||
{({ innerRef, droppableProps, placeholder }) => (
|
||||
<div className="w-100" {...droppableProps} ref={innerRef}>
|
||||
{columns.value.map((item, index) => {
|
||||
const resolvedItemName = resolveReferences(item.name, this.state.currentState);
|
||||
return (
|
||||
<Draggable key={item.id} draggableId={item.id} index={index}>
|
||||
{(provided, snapshot) => (
|
||||
<div
|
||||
className={`card p-2 column-sort-row mb-1 ${this.props.darkMode ? '' : 'bg-light'}`}
|
||||
key={index}
|
||||
ref={provided.innerRef}
|
||||
{...provided.draggableProps}
|
||||
{...provided.dragHandleProps}
|
||||
style={this.getItemStyle(snapshot.isDragging, provided.draggableProps.style)}
|
||||
>
|
||||
<OverlayTrigger
|
||||
trigger="click"
|
||||
placement="left"
|
||||
rootClose={this.state.popOverRootCloseBlockers.length === 0}
|
||||
overlay={this.columnPopover(item, index)}
|
||||
>
|
||||
<div key={resolvedItemName}>
|
||||
<div className={`row ${this.props.darkMode ? '' : 'bg-light'}`} role="button">
|
||||
<div className="col-auto">
|
||||
<img
|
||||
data-cy={`draggable-handle-column-${resolvedItemName}`}
|
||||
src="../../assets/images/icons/dragicon.svg"
|
||||
/>
|
||||
</div>
|
||||
<div className="col">
|
||||
<div className="text" data-cy={`column-${resolvedItemName}`}>
|
||||
{resolvedItemName}
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-auto">
|
||||
<svg
|
||||
data-cy={`button-delete-${resolvedItemName}`}
|
||||
onClick={() => this.removeColumn(index)}
|
||||
width="10"
|
||||
height="16"
|
||||
viewBox="0 0 10 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M0 13.8333C0 14.75 0.75 15.5 1.66667 15.5H8.33333C9.25 15.5 10 14.75 10 13.8333V3.83333H0V13.8333ZM1.66667 5.5H8.33333V13.8333H1.66667V5.5ZM7.91667 1.33333L7.08333 0.5H2.91667L2.08333 1.33333H0V3H10V1.33333H7.91667Z"
|
||||
fill="#8092AC"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-auto">
|
||||
<svg
|
||||
data-cy={`button-delete-${resolvedItemName}`}
|
||||
onClick={() => this.removeColumn(index)}
|
||||
width="10"
|
||||
height="16"
|
||||
viewBox="0 0 10 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M0 13.8333C0 14.75 0.75 15.5 1.66667 15.5H8.33333C9.25 15.5 10 14.75 10 13.8333V3.83333H0V13.8333ZM1.66667 5.5H8.33333V13.8333H1.66667V5.5ZM7.91667 1.33333L7.08333 0.5H2.91667L2.08333 1.33333H0V3H10V1.33333H7.91667Z"
|
||||
fill="#8092AC"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</OverlayTrigger>
|
||||
</div>
|
||||
</OverlayTrigger>
|
||||
</div>
|
||||
)}
|
||||
</Draggable>
|
||||
);
|
||||
})}
|
||||
{placeholder}
|
||||
</div>
|
||||
)}
|
||||
</Droppable>
|
||||
</DragDropContext>
|
||||
)}
|
||||
</Draggable>
|
||||
);
|
||||
})}
|
||||
{placeholder}
|
||||
</div>
|
||||
)}
|
||||
</Droppable>
|
||||
</DragDropContext>
|
||||
</>
|
||||
)}
|
||||
<div style={{ marginTop: useDynamicColumn ? '0px' : '30px' }}>{renderCustomElement('useDynamicColumn')}</div>
|
||||
{useDynamicColumn && <div>{renderCustomElement('columnData')}</div>}
|
||||
</div>
|
||||
),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -134,6 +134,17 @@ export const widgets = [
|
|||
// },
|
||||
// },
|
||||
},
|
||||
useDynamicColumn: {
|
||||
type: 'toggle',
|
||||
displayName: 'Use dynamic column',
|
||||
validation: {
|
||||
schema: { type: 'boolean' },
|
||||
},
|
||||
},
|
||||
columnData: {
|
||||
type: 'code',
|
||||
displayName: 'Column data',
|
||||
},
|
||||
rowsPerPage: {
|
||||
type: 'code',
|
||||
displayName: 'Number of rows per page',
|
||||
|
|
@ -400,6 +411,10 @@ export const widgets = [
|
|||
value:
|
||||
"{{ [ \n\t\t{ id: 1, name: 'Sarah', email: 'sarah@example.com'}, \n\t\t{ id: 2, name: 'Lisa', email: 'lisa@example.com'}, \n\t\t{ id: 3, name: 'Sam', email: 'sam@example.com'}, \n\t\t{ id: 4, name: 'Jon', email: 'jon@example.com'} \n] }}",
|
||||
},
|
||||
useDynamicColumn: { value: '{{false}}' },
|
||||
columnData: {
|
||||
value: "{{[{name: 'email', key: 'email'}, {name: 'Full name', key: 'name', isEditable: true}]}}",
|
||||
},
|
||||
rowsPerPage: { value: '{{10}}' },
|
||||
serverSidePagination: { value: '{{false}}' },
|
||||
enableNextButton: { value: '{{true}}' },
|
||||
|
|
|
|||
Loading…
Reference in a new issue