Skip to content

Commit

Permalink
Merge pull request #33 from concord-consortium/187242213-hide-attributes
Browse files Browse the repository at this point in the history
[187242213] Hides attributes in the plugin as specified in CODAP.
  • Loading branch information
eireland committed Apr 10, 2024
2 parents ca91027 + c790668 commit abf07a0
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 20 deletions.
13 changes: 10 additions & 3 deletions src/components/flat-table.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from "react";
import { ITableProps, IValues } from "../types";
import { DraggableTableContainer, DraggagleTableHeader } from "./draggable-table-tags";
import { getAttrPrecisions, getAttrTypes, getAttrVisibility } from "../utils/utils";

import css from "./tables.scss";

Expand All @@ -12,8 +13,12 @@ export const FlatTable = (props: IFlatProps) => {
const {selectedDataSet, collections, collectionClasses, items, mapCellsFromValues, showHeaders} = props;
const collection = collections[0];
const {className} = collectionClasses[0];
const attrVisibilities = getAttrVisibility(collections);
const collectionAttrsToUse = collection.attrs.filter(attr => !attrVisibilities[attr.title]);

const titles = collection.attrs.map(attr => attr.title);
const titles = collectionAttrsToUse.map(attr => attr.title);
const precisions = getAttrPrecisions(collections);
const attrTypes = getAttrTypes(collections);
const orderedItems = items.map(item => {
const orderedItem: IValues = {};
titles.forEach(title => {
Expand All @@ -34,7 +39,7 @@ export const FlatTable = (props: IFlatProps) => {
<th colSpan={items.length}>{collections[0].title}</th>
</tr>}
<tr>
{collection.attrs.map((attr: any) =>
{collectionAttrsToUse.map((attr: any) =>
<DraggagleTableHeader
key={attr.title}
collectionId={collection.id}
Expand All @@ -47,7 +52,9 @@ export const FlatTable = (props: IFlatProps) => {
</tr>
{orderedItems.map((item, index) => {
return (
<tr key={`${index}-${item.id}`}>{mapCellsFromValues(collection.id, `row-${index}`, item)}</tr>
<tr key={`${index}-${item.id}`}>
{mapCellsFromValues(collection.id, `row-${index}`, item, precisions, attrTypes, attrVisibilities)}
</tr>
);
})}
</tbody>
Expand Down
18 changes: 14 additions & 4 deletions src/components/landscape-view.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from "react";
import { ICollection, IProcessedCaseObj, ITableProps } from "../types";
import { DraggagleTableHeader } from "./draggable-table-tags";
import { getAttrPrecisions, getAttrTypes, getAttrVisibility } from "../utils/utils";

import css from "./tables.scss";

Expand All @@ -12,17 +13,22 @@ export const LandscapeView = (props: ITableProps) => {
const firstRowValues = parentColl.cases.map(caseObj => caseObj.values);
const valueCount = getValueLength(firstRowValues);
const className = getClassName(parentColl.cases[0]);
const precisions = getAttrPrecisions(collections);
const attrTypes = getAttrTypes(collections);
const attrVisibilities = getAttrVisibility(collections);
return (
<>
{showHeaders &&
<tr className={css[className]}>
<th colSpan={valueCount}>{parentColl.name}</th>
</tr> }
<tr className={css[className]}>
{firstRowValues.map(values => mapHeadersFromValues(parentColl.id, "first-row", values))}
{firstRowValues.map(values => mapHeadersFromValues(parentColl.id, "first-row", values, attrVisibilities))}
</tr>
<tr className={css[className]}>
{firstRowValues.map(values => mapCellsFromValues(parentColl.id, "first-row", values))}
{firstRowValues.map(values =>
mapCellsFromValues(parentColl.id, "first-row", values, precisions, attrTypes, attrVisibilities))
}
</tr>
<tr className={css[className]}>
{parentColl.cases.map((caseObj) => {
Expand All @@ -46,6 +52,10 @@ export const LandscapeView = (props: ITableProps) => {
const renderColFromCaseObj = (collection: ICollection, caseObj: IProcessedCaseObj, index?: number) => {
const {children, values} = caseObj;
const isFirstIndex = index === 0;
const precisions = getAttrPrecisions(collections);
const attrTypes = getAttrTypes(collections);
const attrVisibilities = getAttrVisibility(collections);

if (!children.length) {
const className = getClassName(caseObj);
return (
Expand All @@ -57,10 +67,10 @@ export const LandscapeView = (props: ITableProps) => {
}
{isFirstIndex &&
<tr className={css[className]}>
{mapHeadersFromValues(collection.id, `first-row-${index}`, values)}
{mapHeadersFromValues(collection.id, `first-row-${index}`, values, attrVisibilities)}
</tr>
}
<tr>{mapCellsFromValues(collection.id, `row-${index}`, values)}</tr>
<tr>{mapCellsFromValues(collection.id, `row-${index}`, values, precisions, attrTypes, attrVisibilities)}</tr>
</>
);
} else {
Expand Down
29 changes: 24 additions & 5 deletions src/components/nested-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,12 @@ export const NestedTable = (props: IProps) => {
updateInteractiveState({displayMode: e.target.value});
}, [updateInteractiveState]);

const mapHeadersFromValues = (collectionId: number, rowKey: string, values: IValues) => {
const mapHeadersFromValues = (collectionId: number, rowKey: string, values: IValues,
attrVisibilities: Record<string, boolean>) => {
return (
<>
{(Object.keys(values)).map((key, index) => {
if (typeof values[key] === "string" || typeof values[key] === "number") {
if (!attrVisibilities[key] && (typeof values[key] === "string" || typeof values[key] === "number")) {
return (
<DraggagleTableHeader
key={`${collectionId}-${rowKey}-${key}-${index}`}
Expand All @@ -110,10 +111,28 @@ export const NestedTable = (props: IProps) => {
);
};

const mapCellsFromValues = (collectionId: number, rowKey: string, values: IValues, isParent?: boolean,
resizeCounter?: number, parentLevel?: number) => {
const mapCellsFromValues = (collectionId: number, rowKey: string, values: IValues,
precisions: Record<string, number>, attrTypes: Record<string, string | undefined | null>,
attrVisibilities: Record<string, boolean>, isParent?: boolean, resizeCounter?: number, parentLevel?: number) => {
return Object.keys(values).map((key, index) => {
const val = values[key];
const isWholeNumber = values[key] % 1 === 0;
const precision = precisions[key];
// Numbers are sometimes passed in from CODAP as a string so we use the attribute type to
// determine if it should be parsed as a number.
// Numbers that are whole numbers are treated as integers, so we should ignore the precision.
// Numeric cells that are empty should be treated as empty strings.
const val = (attrTypes[key] !== "numeric" && attrTypes[key] !== null)
|| (values[key] === "")
|| (typeof values[key] !== "number")
? values[key]
: isWholeNumber
? parseInt(values[key],10)
: precision !== undefined
? (parseFloat(values[key])).toFixed(precision)
: (parseFloat(values[key])).toFixed(2); // default to 2 decimal places
if (attrVisibilities[key]) {
return null;
}
if (typeof val === "string" || typeof val === "number") {
return (
<DraggagleTableData
Expand Down
22 changes: 17 additions & 5 deletions src/components/portrait-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,41 @@ import React, { useEffect, useMemo, useRef, useState } from "react";
import { ICollection, IProcessedCaseObj, ITableProps } from "../types";
import { DraggableTableContainer, DroppableTableData, DroppableTableHeader } from "./draggable-table-tags";
import { TableScrollTopContext, useTableScrollTop } from "../hooks/useTableScrollTop";
import { getAttrPrecisions, getAttrTypes, getAttrVisibility } from "../utils/utils";

import css from "./tables.scss";

export type PortraitViewRowProps = {collectionId: number, caseObj: IProcessedCaseObj, index?: null|number,
precisions: Record<string, number>,
attrTypes: Record<string, string | undefined | null>,
attrVisibilities: Record<string, boolean>,
isParent: boolean, resizeCounter: number, parentLevel?: number}
& ITableProps;

export const PortraitViewRow = (props: PortraitViewRowProps) => {
const {paddingStyle, mapCellsFromValues, mapHeadersFromValues, showHeaders,
const {paddingStyle, mapCellsFromValues, mapHeadersFromValues, showHeaders, precisions, attrTypes, attrVisibilities,
getClassName, collectionId, caseObj, index, isParent, resizeCounter, parentLevel} = props;

const {children, values} = caseObj;

if (!children.length) {
return (
<tr>{mapCellsFromValues(collectionId, `row-${index}`, values)}</tr>
<tr>{mapCellsFromValues(collectionId, `row-${index}`, values, precisions, attrTypes, attrVisibilities)}</tr>
);
} else {
return (
<>
{index === 0 &&
<tr className={`${css[getClassName(caseObj)]}`}>
{mapHeadersFromValues(collectionId, `first-row-${index}`, values)}
{mapHeadersFromValues(collectionId, `first-row-${index}`, values, attrVisibilities)}
{showHeaders ? (
<DroppableTableHeader collectionId={collectionId}>{children[0].collection.name}</DroppableTableHeader>
) : <th />}
</tr>
}
<tr className={`${css[getClassName(caseObj)]} parent-row`}>
{mapCellsFromValues(collectionId, `parent-row-${index}`, values, isParent, resizeCounter, parentLevel)}
{mapCellsFromValues(collectionId, `parent-row-${index}`, values, precisions, attrTypes, attrVisibilities,
isParent, resizeCounter, parentLevel)}
<DroppableTableData collectionId={collectionId} style={paddingStyle}>
<DraggableTableContainer collectionId={collectionId}>
<table style={paddingStyle} className={`${css.subTable} ${css[getClassName(children[0])]}`}>
Expand All @@ -49,7 +54,8 @@ export const PortraitViewRow = (props: PortraitViewRowProps) => {
return (
<React.Fragment key={child.collection.id}>
<tr className={`${css[getClassName(child)]}`}>
{mapHeadersFromValues(child.collection.id, `child-row-${index}-${i}`, child.values)}
{mapHeadersFromValues(child.collection.id, `child-row-${index}-${i}`, child.values,
attrVisibilities)}
</tr>
<PortraitViewRow {...nextProps} />
</React.Fragment>
Expand Down Expand Up @@ -104,6 +110,9 @@ export const PortraitView = (props: ITableProps) => {
const {className} = collectionClasses[0];
const firstRowValues = parentColl.cases.map(caseObj => caseObj.values);
const valueCount = getValueLength(firstRowValues);
const precisions = getAttrPrecisions(collections);
const attrTypes = getAttrTypes(collections);
const attrVisibilities = getAttrVisibility(collections);

return (
<DraggableTableContainer>
Expand All @@ -122,6 +131,9 @@ export const PortraitView = (props: ITableProps) => {
collectionId={caseObj.collection.id}
caseObj={caseObj}
index={index}
precisions={precisions}
attrTypes={attrTypes}
attrVisibilities={attrVisibilities}
isParent={true}
resizeCounter={resizeCounter}
parentLevel={0}
Expand Down
1 change: 1 addition & 0 deletions src/hooks/useCodapState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export const useCodapState = () => {
case `updateAttributes`:
case `hideAttributes`:
case `showAttributes`:
case `unhideAttributes`:
refreshDataSetInfo();
break;
case `updateDataContext`: // includes renaming dataset, so we have to redo the menu
Expand Down
8 changes: 5 additions & 3 deletions src/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,11 @@ export interface ITableProps {
getClassName: (caseObj: IProcessedCaseObj) => string,
selectedDataSet: IDataSet,
collections: Array<ICollection>,
mapCellsFromValues: (collectionId: number, rowKey: string, values: IValues, isParent?: boolean,
resizeCounter?: number, parentLevel?: number) => void,
mapHeadersFromValues: (collectionId: number, rowKey: string, values: IValues) => void,
mapCellsFromValues: (collectionId: number, rowKey: string, values: IValues, precisions: Record<string, number>,
attrTypes: Record<string, string | undefined | null>, attrVisibilities: Record<string, boolean>,
isParent?: boolean, resizeCounter?: number, parentLevel?: number) => void,
mapHeadersFromValues: (collectionId: number, rowKey: string, values: IValues,
attrVisibilities: Record<string, boolean>) => void,
getValueLength: (firstRow: Array<IValues>) => number
paddingStyle: Record<string, string>
}
Expand Down
39 changes: 39 additions & 0 deletions src/utils/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { ICollections } from "../types";

const getAllAttributesFromCollections = (collections: ICollections[]) => {
const attrArray: any[] = [];
collections.forEach((collection: any) => {
attrArray.push(...collection.attrs);
});
return attrArray;
};

export const getAttrPrecisions = (collections: any) => {
const attrs = getAllAttributesFromCollections(collections);
const precisions = attrs.reduce((acc: Record<string, number>, attr: any) => {
const numPrecision = parseInt(attr.precision, 10);
acc[attr.name] = isNaN(numPrecision) ? 2 : numPrecision;
return acc;
}, {});
return precisions;
};

export const getAttrTypes = (collections: any) => {
const attrs = getAllAttributesFromCollections(collections);
const attrTypes = attrs.reduce(
(acc: Record<string, string | null | undefined>, attr: any) => {
acc[attr.name] = attr.type || null;
return acc;
}, {});
return attrTypes;
};

export const getAttrVisibility = (collections: any) => {
const attrs = getAllAttributesFromCollections(collections);
const attrVisibilities = attrs.reduce(
(acc: Record<string, boolean>, attr: any) => {
acc[attr.name] = attr.hidden || false;
return acc;
}, {});
return attrVisibilities;
};

0 comments on commit abf07a0

Please sign in to comment.