Skip to content

Commit

Permalink
Merge branch 'main' into isu324-alttxt-err-handl
Browse files Browse the repository at this point in the history
  • Loading branch information
NateLanza authored Apr 1, 2024
2 parents abb8630 + e9ad950 commit 44c87b8
Show file tree
Hide file tree
Showing 9 changed files with 1,142 additions and 1,350 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ sketch
/playwright-report/
/blob-report/
/playwright/.cache/
*playwright-report*

# typedoc
/docs/
Expand Down
109 changes: 109 additions & 0 deletions e2e-tests/elementView.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/* eslint-disable testing-library/prefer-screen-queries */
import { test, expect } from '@playwright/test';
import mockData from '../playwright/mock-data/simpsons/simpsons_data.json';
import mockAnnotations from '../playwright/mock-data/simpsons/simpsons_annotations.json';
import mockAltText from '../playwright/mock-data/simpsons/simpsons_alttxt.json';

test.beforeEach(async ({ page }) => {
await page.route('*/**/api/**', async (route) => {
const url = route.request().url();
let json;

if (url) {
if (url.includes('workspaces/Upset%20Examples/tables/simpsons/rows/?limit=9007199254740991')) {
json = mockData;
await route.fulfill({ json });
} else if (url.includes('workspaces/Upset%20Examples/tables/simpsons/annotations/')) {
json = mockAnnotations;
await route.fulfill({ json });
} else if (url.includes('alttxt')) {
json = mockAltText;
await route.fulfill({ json });
} else if (url.includes('workspaces/Upset%20Examples/sessions/table/193/state/')) {
await route.fulfill({ status: 200 });
} else {
await route.continue();
}
} else {
await route.abort();
}
});
});

test('Element View', async ({ page }) => {
await page.goto('http://localhost:3000/?workspace=Upset+Examples&table=simpsons&sessionId=193');

// Make selection
const row = await page.locator('g > circle').first(); // row
await row.dispatchEvent('click');

// Open element view
await page.getByLabel('Open element view sidebar').click();

// test expansion buttons
await page.getByLabel('Expand the sidebar in full').click();
await page.getByLabel('Reduce the sidebar to normal').click();

// Ensure all headings are visible
const elementViewHeading = await page.getByRole('heading', { name: 'Element View' });
await expect(elementViewHeading).toBeVisible();
const elementQueriesHeading = await page.getByRole('heading', { name: 'Element Queries' });
await expect(elementQueriesHeading).toBeVisible();
const elementVisualizationHeading = await page.getByRole('heading', { name: 'Element Visualization' });
await expect(elementVisualizationHeading).toBeVisible();
const queryResultHeading = await page.getByRole('heading', { name: 'Query Result' });
await expect(queryResultHeading).toBeVisible();

// Check to see that the selection chip is visible
const selectionChip = await page.getByLabel('Selected intersection School');
await expect(selectionChip).toBeVisible();

// Check that the datatable is visible and populated
const dataTable = await page.locator('div').filter({ hasText: /^simpsons\/40726826Bart10simpsons\/40726838Ralph8simpsons\/40726848Martin Prince10$/ }).nth(2);
await expect(dataTable).toBeVisible();
const cell1 = await page.getByRole('cell', { name: 'simpsons/40726826' });
await expect(cell1).toBeVisible();
const cell2 = await page.getByRole('cell', { name: 'Bart' });
await expect(cell2).toBeVisible();
const cell3 = await page.getByRole('cell', { name: '10' }).first();
await expect(cell3).toBeVisible();

// Check that the add plot button is visible
const addPlot = await page.getByRole('button', { name: 'Add Plot' });
await expect(addPlot).toBeVisible();
await addPlot.click();

// Check that plot options and plot preview are visible
const histogramHeader = await page.getByRole('tab', { name: 'Histogram' });
await expect(histogramHeader).toBeVisible();
await histogramHeader.click();

const vis = await page.locator('canvas');
await expect(vis).toBeVisible();

const option1 = await page.locator('div').filter({ hasText: /^AttributeAgeAttribute$/ }).first();
await expect(option1).toBeVisible();

const option2 = page.locator('div').filter({ hasText: /^BinsBins$/ }).first();
await expect(option2).toBeVisible();

const option3 = await page.locator('label').filter({ hasText: 'Frequency' });
await expect(option3).toBeVisible();

// close the plot preview
const closeButton = await page.getByRole('heading', { name: 'Add Plot' }).getByRole('button');
await expect(closeButton).toBeVisible();
await closeButton.click();

// Check that the download button is visible and works
const downloadPromise = page.waitForEvent('download');
const downloadButton = await page.getByLabel('Download 3 elements');
await expect(downloadButton).toBeVisible();
await downloadButton.click();
const download = await downloadPromise;

// Check that the close button is visible and works
const elementViewClose = await page.getByLabel('Close the sidebar');
await expect(elementViewClose).toBeVisible();
await elementViewClose.click();
});
1 change: 1 addition & 0 deletions packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@types/react-dom": "^17.0.11",
"@visdesignlab/upset2-core": "^0.1.0",
"@visdesignlab/upset2-react": "^0.1.0",
"localforage": "^1.10.0",
"multinet": "0.23.3",
"react": "^17.0.2",
"react-dom": "^17.0.2",
Expand Down
6 changes: 6 additions & 0 deletions packages/app/src/atoms/loadingAtom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { atom } from "recoil";

export const loadingAtom = atom({
key: 'loading-atom',
default: false,
});
30 changes: 19 additions & 11 deletions packages/app/src/components/Body.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import React from 'react';
import { elementSidebarAtom } from '../atoms/elementSidebarAtom';
import { api } from '../atoms/authAtoms';
import { altTextSidebarAtom } from '../atoms/altTextSidebarAtom';
import { loadingAtom } from '../atoms/loadingAtom';
import { Backdrop, CircularProgress } from '@mui/material';

type Props = {
yOffset: number;
Expand All @@ -25,6 +27,7 @@ export const Body = ({ yOffset, data, config }: Props) => {
const [ isProvVisOpen, setIsProvVisOpen ] = useRecoilState(provenanceVisAtom);
const [ isElementSidebarOpen, setIsElementSidebarOpen ] = useRecoilState(elementSidebarAtom);
const [ isAltTextSidebarOpen, setIsAltTextSidebarOpen ] = useRecoilState(altTextSidebarAtom);
const loading = useRecoilValue(loadingAtom);

const provVis = {
open: isProvVisOpen,
Expand Down Expand Up @@ -92,17 +95,22 @@ export const Body = ({ yOffset, data, config }: Props) => {
<div style={{maxWidth: "100vw"}}>
{ data.setColumns.length === 0 ?
<ErrorModal />:
<Upset
data={data}
loadAttributes={3}
yOffset={yOffset === -1 ? 0 : yOffset}
extProvenance={provObject}
config={config}
provVis={provVis}
elementSidebar={elementSidebar}
altTextSidebar={altTextSidebar}
generateAltText={generateAltText}
/>
<div>
<Backdrop open={loading} style={{zIndex: 1000}}>
<CircularProgress color="inherit" />
</Backdrop>
<Upset
data={data}
loadAttributes={3}
yOffset={yOffset === -1 ? 0 : yOffset}
extProvenance={provObject}
config={config}
provVis={provVis}
elementSidebar={elementSidebar}
altTextSidebar={altTextSidebar}
generateAltText={generateAltText}
/>
</div>
}
</div>
);
Expand Down
169 changes: 97 additions & 72 deletions packages/app/src/components/DataTable.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Box, Button } from "@mui/material"
import { Backdrop, Box, Button, CircularProgress } from "@mui/material"
import { AccessibleDataEntry, CoreUpsetData } from "@visdesignlab/upset2-core";
import { useMemo } from "react";
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 getRowData = (row: AccessibleDataEntry) => {
return {id: row.id, elementName: `${(row.type === "Aggregate") ? "Aggregate: " : ""}${row.elementName.replaceAll("~&~", " & ")}`, size: row.size}
Expand Down Expand Up @@ -87,16 +88,34 @@ const DownloadButton = ({onClick}: DownloadButtonProps) => {
}

export const DataTable = () => {
const storedData = localStorage.getItem("data");
const storedRows = localStorage.getItem("rows");
const storedVisibleSets = localStorage.getItem("visibleSets");
const storedHiddenSets = localStorage.getItem("hiddenSets");

const data = storedData ? JSON.parse(storedData) as CoreUpsetData : null;
const rows = storedRows ? JSON.parse(storedRows) as ReturnType<typeof getAccessibleData> : null;
const visibleSets = storedVisibleSets ? JSON.parse(storedVisibleSets) as string[] : null;
const hiddenSets = storedHiddenSets ? JSON.parse(storedHiddenSets) as string[] : null;
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);

useEffect(() => {
setLoading(true);
Promise.all([
localforage.getItem("data"),
localforage.getItem("rows"),
localforage.getItem("visibleSets"),
localforage.getItem("hiddenSets")
]).then(([storedData, storedRows, storedVisibleSets, storedHiddenSets]) => {
if (storedData === null || storedRows === null || storedVisibleSets === null || storedHiddenSets === null) {
setError(true);
setLoading(false);
return;
}
setData(storedData as CoreUpsetData);
setRows(storedRows as ReturnType<typeof getAccessibleData>);
setVisibleSets(storedVisibleSets as string[]);
setHiddenSets(storedHiddenSets as string[]);
})
setLoading(false);
}, []);

// fetch subset data and create row objects with subset name and size
const tableRows: ReturnType<typeof getRowData>[] = useMemo(() => {
if (rows === null) {
Expand Down Expand Up @@ -182,68 +201,74 @@ export const DataTable = () => {

return (
<>
<Box sx={{display: "flex", justifyContent: "space-between"}}>
<Box sx={{width: "50%", margin: "20px"}}>
<div style={headerCSS}>
<h2>UpSet Data Table</h2>
<DownloadButton onClick={() => downloadElementsAsCSV(tableRows, ["elementName", "size"], "upset2_datatable")} />
</div>
<DataGrid
columns={dataColumns}
rows={tableRows}
autoHeight
disableSelectionOnClick
initialState={{
pagination: {
page: 0,
pageSize: 10,
},
}}
paginationMode="client"
rowsPerPageOptions={[5, 10, 20]}
></DataGrid>
{ error ?
<h1>Error fetching data...</h1> :
<Box sx={{display: "flex", justifyContent: "space-between"}}>
<Backdrop open={loading} style={{zIndex: 1000}}>
<CircularProgress color="inherit" />
</Backdrop>
<Box sx={{width: "50%", margin: "20px"}}>
<div style={headerCSS}>
<h2>UpSet Data Table</h2>
<DownloadButton onClick={() => downloadElementsAsCSV(tableRows, ["elementName", "size"], "upset2_datatable")} />
</div>
<DataGrid
columns={dataColumns}
rows={tableRows}
autoHeight
disableSelectionOnClick
initialState={{
pagination: {
page: 0,
pageSize: 10,
},
}}
paginationMode="client"
rowsPerPageOptions={[5, 10, 20]}
></DataGrid>
</Box>
<Box sx={{width: "25%", margin: "20px"}}>
<div style={headerCSS}>
<h2>Visible Sets</h2>
<DownloadButton onClick={() => downloadElementsAsCSV(visibleSetRows, ["setName", "size"], "upset2_visiblesets_table")} />
</div>
<DataGrid
columns={setColumns}
rows={visibleSetRows}
autoHeight
disableSelectionOnClick
initialState={{
pagination: {
page: 0,
pageSize: 10,
},
}}
paginationMode="client"
rowsPerPageOptions={[5, 10, 20]}
></DataGrid>
</Box>
<Box sx={{width: "25%", margin: "20px"}}>
<div style={headerCSS}>
<h2>Hidden Sets</h2>
<DownloadButton onClick={() => downloadElementsAsCSV(hiddenSetRows, ["setName", "size"], "upset2_hiddensets_table")} />
</div>
<DataGrid
columns={setColumns}
rows={hiddenSetRows}
autoHeight
disableSelectionOnClick
initialState={{
pagination: {
page: 0,
pageSize: 10,
},
}}
paginationMode="client"
rowsPerPageOptions={[5, 10, 20]}
></DataGrid>
</Box>
</Box>
<Box sx={{width: "25%", margin: "20px"}}>
<div style={headerCSS}>
<h2>Visible Sets</h2>
<DownloadButton onClick={() => downloadElementsAsCSV(visibleSetRows, ["setName", "size"], "upset2_visiblesets_table")} />
</div>
<DataGrid
columns={setColumns}
rows={visibleSetRows}
autoHeight
disableSelectionOnClick
initialState={{
pagination: {
page: 0,
pageSize: 10,
},
}}
paginationMode="client"
rowsPerPageOptions={[5, 10, 20]}
></DataGrid>
</Box>
<Box sx={{width: "25%", margin: "20px"}}>
<div style={headerCSS}>
<h2>Hidden Sets</h2>
<DownloadButton onClick={() => downloadElementsAsCSV(hiddenSetRows, ["setName", "size"], "upset2_hiddensets_table")} />
</div>
<DataGrid
columns={setColumns}
rows={hiddenSetRows}
autoHeight
disableSelectionOnClick
initialState={{
pagination: {
page: 0,
pageSize: 10,
},
}}
paginationMode="client"
rowsPerPageOptions={[5, 10, 20]}
></DataGrid>
</Box>
</Box>
}
</>
)
}
Loading

0 comments on commit 44c87b8

Please sign in to comment.