Skip to content

Commit

Permalink
Datatable: error handling and test update
Browse files Browse the repository at this point in the history
  • Loading branch information
JakeWags committed Jun 13, 2024
1 parent 22a16c4 commit 9c55bbf
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 34 deletions.
6 changes: 3 additions & 3 deletions e2e-tests/datatable.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ test('Datatable', async ({ page }) => {
// Test downloads
// //////////////////
const page1 = await page1Promise;
const heading1 = await page1.getByRole('heading', { name: 'UpSet Data Table' });
const heading1 = await page1.getByRole('heading', { name: 'Intersection Data' });
await expect(heading1).toBeVisible();

const downloadPromise = page1.waitForEvent('download');
const downloadButton = await page1.locator('div').filter({ hasText: /^UpSet Data TableDownload$/ }).getByRole('button');
const downloadButton = await page1.locator('div').filter({ hasText: /^Intersection DataDownload$/ }).getByRole('button');
await expect(downloadButton).toBeVisible();
await downloadButton.click();
const download = await downloadPromise;
Expand All @@ -71,7 +71,7 @@ test('Datatable', async ({ page }) => {
// //////////////////
// Test that the tables exist
// //////////////////
const datatable = await page1.getByText('IntersectionSizeSchool & Male3Unincluded3Male3Duff Fan & Male & Power Plant3Evil & Male2Evil & Male & Power Plant2Duff Fan & Male2Blue Hair2School1School & Evil & Male1Rows per page:101–10 of');
const datatable = await page1.getByText('IntersectionSizeDeviationAgeSchool & Male35.199.33Unincluded35.1930.33Male3-9.4257.33Duff Fan & Male & Power Plant310.5838.33Evil & Male21.0343.50Evil & Male & Power Plant26.4161.50Duff Fan & Male21.0340.00Blue Hair27.2956.00School11.738.00School & Evil & Male11.7311.00Rows per page:101–10 of');
await expect(datatable).toBeVisible();
const visibleSets = await page1.getByText('SetSizeSchool6Blue Hair3Duff Fan6Evil6Male18Power Plant5Rows per page:101–6 of');
await expect(visibleSets).toBeVisible();
Expand Down
146 changes: 115 additions & 31 deletions packages/app/src/components/DataTable.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,40 @@
import { Backdrop, Box, Button, CircularProgress } from "@mui/material"
import { Backdrop, Box, Button, CircularProgress, Dialog } from "@mui/material"
import { AccessibleDataEntry, CoreUpsetData, SixNumberSummary } from "@visdesignlab/upset2-core";
import { useEffect, useMemo, useState } from "react";
import { DataGrid, GridColDef } from '@mui/x-data-grid';
import { getAccessibleData } from "@visdesignlab/upset2-react";
import DownloadIcon from '@mui/icons-material/Download';
import localforage from "localforage";

const downloadCSS = {
m: "4px",
height: "40%",
}

const headerCSS = {
display: "flex",
justifyContent: "space-between",
alignItems: "center",
m: "2px"
}

/**
* Represents the columns configuration for the data table.
*/
const setColumns: GridColDef[] = [
/**
* Represents the column for the set name.
*/
{
field: 'setName',
headerName: 'Set',
width: 250,
editable: false,
description: 'The name of the set.'
},
/**
* Represents the column for the set size.
*/
{
field: 'size',
headerName: 'Size',
Expand All @@ -23,6 +44,11 @@ const setColumns: GridColDef[] = [
}
]

/**
* Converts an AccessibleDataEntry object into a row data object.
* @param row - The AccessibleDataEntry object to convert.
* @returns The converted row data object.
*/
const getRowData = (row: AccessibleDataEntry) => {
const retVal: { [key: string]: any } = {
id: row.id,
Expand All @@ -39,6 +65,11 @@ const getRowData = (row: AccessibleDataEntry) => {
return retVal;
}

/**
* Retrieves the aggregated rows from the given row of accessible data.
* @param row - The row of accessible data.
* @returns An array of aggregated row data.
*/
const getAggRows = (row: AccessibleDataEntry) => {
const retVal: ReturnType<typeof getRowData>[] = [];
if (row.items === undefined) return retVal;
Expand All @@ -54,18 +85,13 @@ const getAggRows = (row: AccessibleDataEntry) => {
return retVal;
}

const downloadCSS = {
m: "4px",
height: "40%",
}

const headerCSS = {
display: "flex",
justifyContent: "space-between",
alignItems: "center",
m: "2px"
}

/**
* Generates a CSV file and downloads it.
*
* @param items - The array of items to be downloaded.
* @param columns - The array of column names.
* @param name - The name of the CSV file.
*/
function downloadElementsAsCSV(items: any[], columns: string[], name: string) {
if (items.length < 1 || columns.length < 1) return;

Expand Down Expand Up @@ -95,10 +121,22 @@ function downloadElementsAsCSV(items: any[], columns: string[], name: string) {
anchor.remove();
}

/**
* Props for the DownloadButton component.
*/
type DownloadButtonProps = {
// The click event handler function for the button.
onClick: () => void;
}

/**
* DownloadButton component.
*
* @component
* @param {Object} props - The component props.
* @param {Function} props.onClick - The click event handler for the button.
* @returns {JSX.Element} The DownloadButton component.
*/
const DownloadButton = ({onClick}: DownloadButtonProps) => {
return (
<Button
Expand All @@ -115,14 +153,25 @@ const DownloadButton = ({onClick}: DownloadButtonProps) => {
)
}

/**
* Renders a data table component that displays intersection data, visible sets, and hidden sets.
* The component fetches data from local storage and populates the table with the retrieved data.
* If there is an error fetching the data, an error message is displayed.
*
* @returns The DataTable component.
*/
export const DataTable = () => {
const [data , setData] = useState<CoreUpsetData | null>(null);
const [rows, setRows] = useState<ReturnType<typeof getAccessibleData> | null>(null);
const [visibleSets, setVisibleSets] = useState<string[] | null>(null);
const [hiddenSets, setHiddenSets] = useState<string[] | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(false);
const [error, setError] = useState<string | null>(null);

/**
* Fetches data from local storage and sets the state variables.
* If the data is not found in local storage, an error message is displayed.
*/
useEffect(() => {
setLoading(true);
Promise.all([
Expand All @@ -132,19 +181,28 @@ export const DataTable = () => {
localforage.getItem("hiddenSets")
]).then(([storedData, storedRows, storedVisibleSets, storedHiddenSets]) => {
if (storedData === null || storedRows === null || storedVisibleSets === null || storedHiddenSets === null) {
setError(true);
setLoading(false);
return;
console.error("Data not found in local storage")
setError("Error: Data not found in local storage");
} else {
setData(storedData as CoreUpsetData);
setRows(storedRows as ReturnType<typeof getAccessibleData>);
setVisibleSets(storedVisibleSets as string[]);
setHiddenSets(storedHiddenSets as string[]);
}
setData(storedData as CoreUpsetData);
setRows(storedRows as ReturnType<typeof getAccessibleData>);
setVisibleSets(storedVisibleSets as string[]);
setHiddenSets(storedHiddenSets as string[]);
})
}).catch((e) => {
setError(`Error: ${e}`);
});
setLoading(false);
}, []);


/**
* Generates an array of data columns for the data table.
* Each column object contains information such as field name, header name, width, and description.
* It iterates through the rows and adds any missing attribute columns to the array.
*
* @param rows - The rows of data for the table.
* @returns An array of data columns for the data table.
*/
const dataColumns: GridColDef[] = useMemo(() => {
const cols = [
{
Expand Down Expand Up @@ -175,6 +233,7 @@ export const DataTable = () => {
Object.values(rows.values).forEach((r: AccessibleDataEntry) => {
for (const key in r.attributes) {
if (!cols.find((m) => m.field === key)) {
// skip deviation and degree. Deviation is added above and degree is not needed in the table
if (key === "deviation" || key === "degree") continue;
cols.push({
field: key,
Expand All @@ -191,7 +250,15 @@ export const DataTable = () => {
return cols;
}, [rows]);

// fetch subset data and create row objects
/**
* Returns an array of table rows based on the provided data.
* If the rows are null, an empty array is returned.
* For each row, it calls the `getRowData` function to get the row data.
* If a row has a type of "Aggregate", it also adds additional rows using the `getAggRows` function.
*
* @param rows - The data rows to generate the table rows from.
* @returns An array of table rows.
*/
const tableRows: ReturnType<typeof getRowData>[] = useMemo(() => {
if (rows === null) {
return [];
Expand All @@ -210,15 +277,30 @@ export const DataTable = () => {
return retVal;
}, [rows]);

/**
* Retrieves an array of objects containing information about the sets.
* Each object includes the set name and its corresponding size.
*
* @param sets - An array of set names.
* @param data - An object containing the data for the sets.
* @returns An array of objects with the set name and size.
*/
const getSetRows = (sets: string[], data: CoreUpsetData) => {
const retVal: {setName: string, size: number}[] = [];
const retVal: { setName: string, size: number }[] = [];
retVal.push(...sets.map((s: string) => {
return {id: s, setName: s.replace('Set_', ''), size: data.sets[s].size};
return { id: s, setName: s.replace('Set_', ''), size: data.sets[s].size };
}));

return retVal;
}

/**
* Returns an array of visible set rows.
*
* @param visibleSets - The array of visible sets.
* @param data - The data used to generate the set rows.
* @returns An array of objects representing the visible set rows.
*/
const visibleSetRows: {setName: string, size: number}[] = useMemo(() => {
if (visibleSets === null || data === null) {
return [];
Expand All @@ -227,6 +309,12 @@ export const DataTable = () => {
return getSetRows(visibleSets, data);
}, [visibleSets, data]);

/**
* Calculates the hidden set rows based on the provided hidden sets and data.
* @param hiddenSets - The hidden sets.
* @param data - The data.
* @returns An array of objects containing the set name and size.
*/
const hiddenSetRows: {setName: string, size: number}[] = useMemo(() => {
if (hiddenSets === null || data === null) {
return [];
Expand All @@ -235,14 +323,10 @@ export const DataTable = () => {
return getSetRows(hiddenSets, data);
}, [hiddenSets, data]);

if (data === null) {
return null;
}

return (
<>
{ error ?
<h1>Error fetching data...</h1> :
<h1>{error}</h1>:
<Box sx={{display: "flex", justifyContent: "space-between"}}>
<Backdrop open={loading} style={{zIndex: 1000}}>
<CircularProgress color="inherit" />
Expand Down

0 comments on commit 9c55bbf

Please sign in to comment.