Skip to content

Commit

Permalink
feat: edit data in nested table
Browse files Browse the repository at this point in the history
  • Loading branch information
emcelroy committed Sep 12, 2024
1 parent f3e6218 commit e5b9abf
Show file tree
Hide file tree
Showing 14 changed files with 5,337 additions and 2,437 deletions.
7,544 changes: 5,173 additions & 2,371 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@
"webpack-dev-server": "^4.10.1"
},
"dependencies": {
"@chakra-ui/react": "^2.8.2",
"@dnd-kit/core": "^6.0.8",
"@dnd-kit/sortable": "^7.0.2",
"@dnd-kit/utilities": "^3.2.1",
Expand Down
11 changes: 6 additions & 5 deletions src/components/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import { ICaseObjCommon } from "../types";
import css from "./app.scss";

function App() {
const {connected, selectedDataSet, dataSets, collections, items, interactiveState,
const {connected, selectedDataSet, dataSets, collections, cases, interactiveState,
updateInteractiveState: _updateInteractiveState, init,
handleSelectDataSet: _handleSelectDataSet, handleUpdateAttributePosition,
handleAddCollection, handleAddAttribute, handleSetCollections, handleSelectSelf,
updateTitle, selectCODAPCases, listenForSelectionChanges,
handleCreateCollectionFromAttribute
handleCreateCollectionFromAttribute, handleUpdateCollections
} = useCodapState();

useEffect(() => {
Expand Down Expand Up @@ -89,21 +89,24 @@ function App() {
return <div className={css.loading}>Loading...</div>;
}

console.log("connected", connected);

Check warning on line 92 in src/components/app.tsx

View workflow job for this annotation

GitHub Actions / Build and Run Jest Tests

Unexpected console statement

Check warning on line 92 in src/components/app.tsx

View workflow job for this annotation

GitHub Actions / S3 Deploy

Unexpected console statement

switch (interactiveState.view) {
case "nested-table":
return (
<NestedTable
selectedDataSet={selectedDataSet}
dataSets={dataSets}
collections={collections}
items={items}
cases={cases}
interactiveState={interactiveState}
handleSelectDataSet={handleSelectDataSet}
updateInteractiveState={updateInteractiveState}
handleShowComponent={handleShowComponent}
handleUpdateAttributePosition={handleUpdateAttributePosition}
handleSetCollections={handleSetCollections}
handleCreateCollectionFromAttribute={handleCreateCollectionFromAttribute}
handleUpdateCollections={handleUpdateCollections}
/>
);

Expand All @@ -113,7 +116,6 @@ function App() {
selectedDataSet={selectedDataSet}
dataSets={dataSets}
collections={collections}
items={items}
interactiveState={interactiveState}
handleSelectDataSet={handleSelectDataSet}
handleUpdateAttributePosition={handleUpdateAttributePosition}
Expand All @@ -131,7 +133,6 @@ function App() {
selectedDataSet={selectedDataSet}
dataSets={dataSets}
collections={collections}
items={items}
interactiveState={interactiveState}
handleSelectDataSet={handleSelectDataSet}
updateTitle={updateTitle}
Expand Down
1 change: 0 additions & 1 deletion src/components/card-view/card-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ interface ICardViewProps {
selectedDataSet: any;
dataSets: IDataSet[];
collections: ICollections;
items: any[];
interactiveState: InteractiveState
handleSelectDataSet: (e: React.ChangeEvent<HTMLSelectElement>) => void
updateTitle: (title: string) => Promise<void>
Expand Down
27 changes: 23 additions & 4 deletions src/components/draggable-table-tags.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useCodapState } from "../hooks/useCodapState";
import { getAttribute } from "@concord-consortium/codap-plugin-api";
import { getCollectionById } from "../utils/apiHelpers";
import { PropsWithChildren } from "../types";
import { EditableTableCell } from "./editable-table-cell";

import AddIcon from "../assets/plus-level-1.svg";
import DropdownIcon from "../assets/dropdown-arrow-icon.svg";
Expand Down Expand Up @@ -45,7 +46,7 @@ export const DraggagleTableHeader: React.FC<PropsWithChildren<DraggagleTableHead
handleDragLeave, handleDragEnd} = useDraggableTableContext();
const {handleSortAttribute} = useCodapState();
const {id, style} = getIdAndStyle(collectionId, attrTitle, dragOverId, dragSide);
const headerRef = useRef<HTMLTableHeaderCellElement | null>(null);
const headerRef = useRef<HTMLTableCellElement | null>(null);
const [showDropdownIcon, setShowDropdownIcon] = useState(false);
const [showHeaderMenu, setShowHeaderMenu] = useState(false);
const headerPos = headerRef.current?.getBoundingClientRect();
Expand Down Expand Up @@ -157,14 +158,18 @@ export const DroppableTableHeader: React.FC<PropsWithChildren<DroppableTableHead
interface DraggagleTableDataProps {
collectionId: number;
attrTitle: string;
caseId: string;
style?: React.CSSProperties;
isParent?: boolean;
resizeCounter?: number;
parentLevel?: number;
selectedDataSetName: string;
handleUpdateCollections: () => void;
}

export const DraggagleTableData: React.FC<PropsWithChildren<DraggagleTableDataProps>> = (props) => {
const {collectionId, attrTitle, children, isParent, resizeCounter, parentLevel=0} = props;
const {collectionId, attrTitle, children, caseId, isParent, resizeCounter, parentLevel=0,
selectedDataSetName, handleUpdateCollections} = props;
const {dragOverId, dragSide} = useDraggableTableContext();
const {style} = getIdAndStyle(collectionId, attrTitle, dragOverId, dragSide);
const {tableScrollTop, scrollY} = useTableTopScrollTopContext();
Expand Down Expand Up @@ -204,6 +209,18 @@ export const DraggagleTableData: React.FC<PropsWithChildren<DraggagleTableDataPr
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [tableScrollTop, isParent, scrollY, parentLevel, resizeCounter]);

const EditableCell = () => {
return (
<EditableTableCell
attrTitle={attrTitle}
handleUpdateCollections={handleUpdateCollections}
caseId={caseId}
selectedDataSetName={selectedDataSetName}
>
{children}
</EditableTableCell>
);
};

const textStyle: React.CSSProperties = {top: cellTextTop};
if (cellTextTop === 0) {
Expand All @@ -215,9 +232,11 @@ export const DraggagleTableData: React.FC<PropsWithChildren<DraggagleTableDataPr
{isParent
? <>
<span style={{opacity: 0}}>{children}</span>
<div style={textStyle} className={css.cellTextValue}>{children}</div>
<div style={textStyle} className={css.cellTextValue}>
<EditableCell />
</div>
</>
: children
: <EditableCell />
}
</td>
);
Expand Down
51 changes: 51 additions & 0 deletions src/components/editable-table-cell.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React, { ReactNode, useState } from "react";
import { Editable, EditablePreview, EditableInput } from "@chakra-ui/react";
import { updateCaseById } from "@concord-consortium/codap-plugin-api";

interface IProps {
attrTitle: string;
caseId: string;
children: ReactNode;
handleUpdateCollections: () => void;
selectedDataSetName: string;
}

export const EditableTableCell = (props: IProps) => {
const { attrTitle, caseId, children, handleUpdateCollections, selectedDataSetName } = props;
const displayValue = String(children);
const [editingValue, setEditingValue] = useState(displayValue);
const [isEditing, setIsEditing] = useState(false);

const handleChangeValue = (newValue: string) => {
setEditingValue(newValue);
};

const handleCancel = () => {
setIsEditing(false);
};

const handleSubmit = async (nextValue: string) => {
try {
await updateCaseById(selectedDataSetName, caseId, {[attrTitle]: nextValue});
} catch (e) {
console.error("Case not updated: ", e);
}
setIsEditing(false);
handleUpdateCollections();
};

return (
<Editable
isPreviewFocusable={true}
onCancel={handleCancel}
onChange={handleChangeValue}
onEdit={() => setIsEditing(true)}
onSubmit={handleSubmit}
submitOnBlur={true}
value={isEditing ? editingValue : displayValue}
>
{!isEditing && <EditablePreview />}
<EditableInput value={editingValue} />
</Editable>
);
};
24 changes: 12 additions & 12 deletions src/components/flat-table.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import React from "react";
import { ITableProps, IValues } from "../types";
import { CaseValuesWithId, ITableProps } from "../types";
import { DraggableTableContainer, DraggagleTableHeader } from "./draggable-table-tags";
import { getAttrPrecisions, getAttrTypes, getAttrVisibility } from "../utils/utils";

import css from "./tables.scss";

interface IFlatProps extends ITableProps {
items: Array<any>
cases: Array<any>
}

export const FlatTable = (props: IFlatProps) => {
const {selectedDataSet, collections, collectionClasses, items, mapCellsFromValues, showHeaders} = props;
const {selectedDataSet, collections, collectionClasses, cases, mapCellsFromValues, showHeaders } = props;
const collection = collections[0];
const {className} = collectionClasses[0];
const attrVisibilities = getAttrVisibility(collections);
Expand All @@ -19,24 +19,24 @@ export const FlatTable = (props: IFlatProps) => {
const titles = collectionAttrsToUse.map(attr => attr.title);
const precisions = getAttrPrecisions(collections);
const attrTypes = getAttrTypes(collections);
const orderedItems = items.map(item => {
const orderedItem: IValues = {};
const orderedCases = cases.map(c => {
const orderedCase: CaseValuesWithId = {id: c.id};
titles.forEach(title => {
orderedItem[title] = item[title];
orderedCase[title] = c.values[title];
});
return orderedItem;
return orderedCase;
});

return (
<DraggableTableContainer collectionId="root">
<table className={`${css.mainTable} ${css.flatTable} ${css[className]}}`}>
<tbody>
<tr className={css.mainHeader}>
<th colSpan={items.length}>{selectedDataSet.title}</th>
<th colSpan={cases.length}>{selectedDataSet.title}</th>
</tr>
{showHeaders &&
<tr className={css[className]}>
<th colSpan={items.length}>{collections[0].title}</th>
<th colSpan={cases.length}>{collections[0].title}</th>
</tr>}
<tr>
{collectionAttrsToUse.map((attr: any) =>
Expand All @@ -50,10 +50,10 @@ export const FlatTable = (props: IFlatProps) => {
{attr.title}
</DraggagleTableHeader>)}
</tr>
{orderedItems.map((item, index) => {
{orderedCases.map((c, index) => {
return (
<tr key={`${index}-${item.id}`}>
{mapCellsFromValues(collection.id, `row-${index}`, item, precisions, attrTypes, attrVisibilities)}
<tr key={`${index}-${c.id}`}>
{mapCellsFromValues(collection.id, `row-${index}`, c, precisions, attrTypes, attrVisibilities )}
</tr>
);
})}
Expand Down
1 change: 0 additions & 1 deletion src/components/hierarchy-view/hierarchy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ interface IProps {
selectedDataSet: any;
dataSets: IDataSet[];
collections: ICollections;
items: any[];
interactiveState: InteractiveState
handleSelectDataSet: (e: React.ChangeEvent<HTMLSelectElement>) => void
updateInteractiveState: (update: Partial<InteractiveState>) => void
Expand Down
21 changes: 16 additions & 5 deletions src/components/landscape-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ export const LandscapeView = (props: ITableProps) => {
getClassName, selectedDataSet, collections, getValueLength, paddingStyle} = props;

const renderNestedTable = (parentColl: ICollection) => {
const firstRowValues = parentColl.cases.map(caseObj => caseObj.values);
const headers = parentColl.cases.map((caseObj) => caseObj.values);
const firstRowValues = parentColl.cases.map(caseObj => {
return {...caseObj.values, id: caseObj.id};
});
const valueCount = getValueLength(firstRowValues);
const className = getClassName(parentColl.cases[0]);
const precisions = getAttrPrecisions(collections);
Expand All @@ -23,11 +26,13 @@ export const LandscapeView = (props: ITableProps) => {
<th colSpan={valueCount}>{parentColl.name}</th>
</tr> }
<tr className={css[className]}>
{firstRowValues.map(values => mapHeadersFromValues(parentColl.id, "first-row", values, attrVisibilities))}
{headers.map(values => mapHeadersFromValues(parentColl.id, "first-row", values, attrVisibilities))}
</tr>
<tr className={css[className]}>
{firstRowValues.map(values =>
mapCellsFromValues(parentColl.id, "first-row", values, precisions, attrTypes, attrVisibilities))
mapCellsFromValues(
parentColl.id, "first-row", values, precisions, attrTypes, attrVisibilities
))
}
</tr>
<tr className={css[className]}>
Expand All @@ -50,7 +55,8 @@ export const LandscapeView = (props: ITableProps) => {
};

const renderColFromCaseObj = (collection: ICollection, caseObj: IProcessedCaseObj, index?: number) => {
const {children, values} = caseObj;
const {children, id, values} = caseObj;
const caseValuesWithId = {...values, id};
const isFirstIndex = index === 0;
const precisions = getAttrPrecisions(collections);
const attrTypes = getAttrTypes(collections);
Expand All @@ -70,7 +76,12 @@ export const LandscapeView = (props: ITableProps) => {
{mapHeadersFromValues(collection.id, `first-row-${index}`, values, attrVisibilities)}
</tr>
}
<tr>{mapCellsFromValues(collection.id, `row-${index}`, values, precisions, attrTypes, attrVisibilities)}</tr>
<tr>
{mapCellsFromValues(
collection.id, `row-${index}`, caseValuesWithId, precisions, attrTypes, attrVisibilities
)
}
</tr>
</>
);
} else {
Expand Down
Loading

0 comments on commit e5b9abf

Please sign in to comment.