Skip to content

Commit

Permalink
Merge pull request #433 from visdesignlab/418-element-view-UI
Browse files Browse the repository at this point in the history
418 element view UI
  • Loading branch information
NateLanza authored Dec 3, 2024
2 parents 031c462 + 75c7147 commit f859df7
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 19 deletions.
1 change: 0 additions & 1 deletion e2e-tests/elementView.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,6 @@ test('Query Selection', async ({ page }) => {
await page.goto('http://localhost:3000/?workspace=Upset+Examples&table=simpsons&sessionId=193');
await page.getByLabel('Element View Sidebar Toggle').click();
await page.locator('[id="Subset_School\\~\\&\\~Male"] g').filter({ hasText: /^Blue Hair$/ }).locator('circle').click();
await page.getByLabel('Selected intersection School').click();

// Selected elements for testing
const ralphCell = page.getByRole('cell', { name: 'Ralph' });
Expand Down
2 changes: 2 additions & 0 deletions packages/upset/src/components/ElementView/BookmarkChips.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ export const BookmarkChips = () => {
});
}
}}
onClick={() => actions.setSelected(null)}
label={`${currentIntersectionDisplayName} - ${currentIntersection.size}`}
onDelete={() => {
actions.addBookmark<BookmarkedIntersection>({
Expand Down Expand Up @@ -144,6 +145,7 @@ export const BookmarkChips = () => {
actions.addBookmark(structuredClone(currentSelection));
}
}}
onClick={() => actions.setElementSelection(null)}
label={`${currentSelection.label}`}
onDelete={() => {
actions.addBookmark(structuredClone(currentSelection));
Expand Down
38 changes: 27 additions & 11 deletions packages/upset/src/components/ElementView/ElementSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { ElementVisualization } from './ElementVisualization';
import { UpsetActions } from '../../provenance';
import { ProvenanceContext } from '../Root';
import { QueryInterface } from './QueryInterface';
import { bookmarkSelector, currentIntersectionSelector } from '../../atoms/config/currentIntersectionAtom';

/**
* Props for the ElementSidebar component
Expand All @@ -33,8 +34,14 @@ type Props = {
close: () => void
}

const initialDrawerWidth = 450;
const minDrawerWidth = 100;
/**
* The *exact* width at which we don't get a horizontal scrollbar in the table controls
*/
const initialDrawerWidth = 462;
/**
* The *exact* width at which the 'apply' button in the element query controls is forced onto a new line
*/
const minDrawerWidth = 368;

/**
* Immediately downloads a csv containing items with the given columns
Expand Down Expand Up @@ -80,12 +87,14 @@ function downloadElementsAsCSV(items: Item[], columns: string[], name: string) {
export const ElementSidebar = ({ open, close }: Props) => {
const [fullWidth, setFullWidth] = useState(false);
const [drawerWidth, setDrawerWidth] = useState(initialDrawerWidth);
const currentSelection = useRecoilValue(selectedElementSelector);
const currentElementSelection = useRecoilValue(selectedElementSelector);
const selectedItems = useRecoilValue(selectedItemsSelector);
const itemCount = useRecoilValue(selectedItemsCounter);
const columns = useRecoilValue(columnsAtom);
const [hideElementSidebar, setHideElementSidebar] = useState(!open);
const { actions }: {actions: UpsetActions} = useContext(ProvenanceContext);
const bookmarked = useRecoilValue(bookmarkSelector);
const currentIntersection = useRecoilValue(currentIntersectionSelector);

/**
* Effects
Expand Down Expand Up @@ -156,6 +165,9 @@ export const ElementSidebar = ({ open, close }: Props) => {
left: 0,
zIndex: 100,
backgroundColor: '#f4f7f9',
// I cannot comprehend why this is the value that works. It is.
// The 'rows per page' controls overflow otherwise (:
paddingBottom: '1625px',
}}
onMouseDown={(e) => handleMouseDown(e)}
/>
Expand Down Expand Up @@ -189,7 +201,7 @@ export const ElementSidebar = ({ open, close }: Props) => {
<IconButton
onClick={() => {
setHideElementSidebar(true);
actions.setElementSelection(currentSelection);
actions.setElementSelection(currentElementSelection);
close();
}}
aria-label="Close the sidebar"
Expand All @@ -198,16 +210,20 @@ export const ElementSidebar = ({ open, close }: Props) => {
</IconButton>
</div>
<div style={{ marginBottom: '1em' }}>
<Typography variant="h2" fontSize="1.2em" fontWeight="inherit" gutterBottom>
<Typography variant="h2" fontSize="1.4em" fontWeight="inherit" gutterBottom>
Element View
</Typography>
<Divider />
</div>
<Typography variant="h3" fontSize="1.2em">
Bookmarked Queries
</Typography>
<Divider />
<BookmarkChips />
{(bookmarked.length > 0 || currentIntersection || currentElementSelection) && (
<>
<Typography variant="h3" fontSize="1.2em">
Bookmarked Queries
</Typography>
<Divider />
<BookmarkChips />
</>
)}
<Typography variant="h3" fontSize="1.2em">
Element Visualization
</Typography>
Expand All @@ -226,7 +242,7 @@ export const ElementSidebar = ({ open, close }: Props) => {
downloadElementsAsCSV(
selectedItems,
columns,
currentSelection?.label ?? 'upset_elements',
currentElementSelection?.label ?? 'upset_elements',
);
}}
>
Expand Down
6 changes: 4 additions & 2 deletions packages/upset/src/components/Header/AttributeButton.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FC, useContext } from 'react';
import React, { FC, useContext } from 'react';
import { useSetRecoilState, useRecoilValue } from 'recoil';
import { SortByOrder, AttributePlotType } from '@visdesignlab/upset2-core';

Expand Down Expand Up @@ -63,12 +63,14 @@ export const AttributeButton: FC<Props> = ({ label, tooltip }) => {
* If the attribute is not currently sorted, it sorts it in ascending order.
* If the attribute is already sorted, it toggles between ascending and descending order.
*/
const handleOnClick = () => {
const handleOnClick = (e: React.MouseEvent<SVGElement>) => {
if (sortBy !== label) {
sortByHeader('Ascending');
} else {
sortByHeader(sortByOrder === 'Ascending' ? 'Descending' : 'Ascending');
}
// To prevent the handler on SvgBase that deselects the current intersection
e.stopPropagation();
};

/**
Expand Down
6 changes: 4 additions & 2 deletions packages/upset/src/components/Header/DegreeHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { css } from '@emotion/react';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import { useContext } from 'react';
import React, { useContext } from 'react';
import { Tooltip } from '@mui/material';
import { sortByOrderSelector, sortBySelector } from '../../atoms/config/sortByAtom';
import translate from '../../utils/transform';
Expand All @@ -23,12 +23,14 @@ export const DegreeHeader = () => {
actions.sortBy('Degree', order);
};

const handleOnClick = () => {
const handleOnClick = (e: React.MouseEvent<SVGElement>) => {
if (sortBy !== 'Degree') {
sortByDegree('Ascending');
} else {
sortByDegree(sortByOrder === 'Ascending' ? 'Descending' : 'Ascending');
}
// To prevent the handler on SvgBase that deselects the current intersection
e.stopPropagation();
};

const handleContextMenuClose = () => {
Expand Down
6 changes: 4 additions & 2 deletions packages/upset/src/components/Header/SizeHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { css } from '@emotion/react';
import { drag } from 'd3-drag';
import { select } from 'd3-selection';
import {
import React, {
FC, useContext, useEffect, useRef, useState,
} from 'react';
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
Expand Down Expand Up @@ -51,12 +51,14 @@ export const SizeHeader: FC = () => {
actions.sortBy('Size', order);
};

const handleOnClick = () => {
const handleOnClick = (e: React.MouseEvent<SVGElement>) => {
if (sortBy !== 'Size') {
sortBySize('Ascending');
} else {
sortBySize(sortByOrder === 'Ascending' ? 'Descending' : 'Ascending');
}
// To prevent the handler on SvgBase that deselects the current intersection
e.stopPropagation();
};

const handleContextMenuClose = () => {
Expand Down
9 changes: 8 additions & 1 deletion packages/upset/src/components/SvgBase.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { css } from '@emotion/react';
import { FC } from 'react';
import { FC, useContext } from 'react';
import { useRecoilValue } from 'recoil';

import translate from '../utils/transform';
import { dimensionsSelector } from '../atoms/dimensionsAtom';
import { ProvenanceContext } from './Root';
import { currentIntersectionSelector } from '../atoms/config/currentIntersectionAtom';

/** @jsxImportSource @emotion/react */
type SvgBaseSettings = {
Expand All @@ -18,13 +20,18 @@ type Props = {

export const SvgBase: FC<Props> = ({ children, defaultSettings }) => {
const { height, width, margin } = defaultSettings || useRecoilValue(dimensionsSelector);
const { actions } = useContext(ProvenanceContext);
const selectedIntersection = useRecoilValue(currentIntersectionSelector);

return (
// These rules are for accessibility; unnecessary here as the plot is not accessible anyway.
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
<div
css={css`
height: 100%;
width: 100%;
`}
onClick={() => { if (selectedIntersection != null) actions.setSelected(null); }}
>
<svg id="upset-svg" height={height + 50 * margin} width={width + 2 * margin} xmlns="http://www.w3.org/2000/svg" version="1.1" baseProfile="full" fontFamily="Roboto, Arial">
<g transform={translate(margin)}>
Expand Down

0 comments on commit f859df7

Please sign in to comment.