Skip to content

Commit

Permalink
Merge branch 'main' into 458-simplify-selection
Browse files Browse the repository at this point in the history
  • Loading branch information
NateLanza authored Feb 26, 2025
2 parents 671f74d + d2528f1 commit 05f2fd2
Show file tree
Hide file tree
Showing 15 changed files with 133 additions and 78 deletions.
32 changes: 32 additions & 0 deletions e2e-tests/queryBySets.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { expect, test } from '@playwright/test';
import { beforeTest } from './common';

test.beforeEach(beforeTest);

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

// open Query by sets interface
await page.getByTestId('AddIcon').locator('path').click();
// await page.getByLabel('Query By Sets').locator('rect').click();

// select first two sets as 'No', third as 'Yes'
await page.locator('g:nth-child(2) > g > circle').first().click();
await page.locator('g:nth-child(2) > g > circle:nth-child(3)').click();
await page.locator('g:nth-child(4) > g > circle:nth-child(4)').click();

// TODO: Add a test for changing the name. As is, playwright struggles to handle web dialog inputs

// Ensure that the text is correct
await page.getByText('intersections of set [Evil]').click();

// Add the query
await page.getByLabel('Add query').locator('rect').click();

// This specific query size is 5
await page.locator('text').filter({ hasText: /^5$/ }).click();

// Remove the query
await page.getByLabel('Remove query').locator('rect').click();

});
6 changes: 5 additions & 1 deletion packages/core/src/convertConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,18 +272,22 @@ export function convertConfig(config: unknown): UpsetConfig {
if (!Object.hasOwn(config, 'version')) preVersionConversion(config as PreVersionConfig);

/* eslint-disable no-void */
/* eslint-disable no-fallthrough */
// Switch case is designed to fallthrough to the next version's conversion function
// so that all versions are converted cumulatively.

switch ((config as {version: string}).version) {
/* eslint-disable no-fallthrough */
// @ts-expect-error: Fallthrough is intended behavior. This is needed because Typescript build is not parsing eslint flags
case '0.1.0':
convert0_1_0(config as Version0_1_0);
// @ts-expect-error: Fallthrough is intended behavior.
case '0.1.1':
convert0_1_1(config as Version0_1_1);
case '0.1.2':
convert0_1_2(config as Version0_1_2);
default:
void 0;
/* eslint-enable no-fallthrough */
}

if (!isUpsetConfig(config)) {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/defaultConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const DefaultConfig: UpsetConfig = {
title: null,
},
horizontal: false,
firstAggregateBy: 'Degree',
firstAggregateBy: 'None',
firstOverlapDegree: 2,
secondAggregateBy: 'None',
secondOverlapDegree: 2,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { atom, selector } from 'recoil';
import { SetQuery } from '@visdesignlab/upset2-core';
import { upsetConfigAtom } from './config/upsetConfigAtoms';
import { upsetConfigAtom } from './upsetConfigAtoms';

/**
* Atom to manage the state of the query-by-sets interface.
*
*
* This atom holds a boolean value indicating whether the query-by-sets
* interface is enabled or not. The default value is `false`.
*
*
* @constant
* @type {boolean}
* @default false
Expand Down
4 changes: 2 additions & 2 deletions packages/upset/src/components/Body.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { useRecoilValue } from 'recoil';

import { isPopulatedSetQuery } from '@visdesignlab/upset2-core';
import { dimensionsSelector } from '../atoms/dimensionsAtom';
import translate from '../utils/transform';
import { MatrixRows } from './Rows/MatrixRows';
import { flattenedRowsSelector } from '../atoms/renderRowsAtom';
import { QueryBySetInterface } from './custom/QueryBySet/QueryBySetInterface';
import { SetQueryRow } from './custom/QueryBySet/SetQueryRow';
import { queryBySetsInterfaceAtom, setQueryAtom } from '../atoms/queryBySetsAtoms';
import { isPopulatedSetQuery } from '@visdesignlab/upset2-core';
import { queryBySetsInterfaceAtom, setQueryAtom } from '../atoms/config/queryBySetsAtoms';

export const Body = () => {
const dimensions = useRecoilValue(dimensionsSelector);
Expand Down
2 changes: 1 addition & 1 deletion packages/upset/src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { SizeHeader } from './SizeHeader';
import { MatrixHeader } from './MatrixHeader';
import { CollapseAllButton } from './CollapseAllButton';
import { QueryButton } from './QueryButton';
import { setQueryAtom } from '../../atoms/queryBySetsAtoms';
import { setQueryAtom } from '../../atoms/config/queryBySetsAtoms';

export const Header = () => {
const setQuery = useRecoilValue(setQueryAtom);
Expand Down
14 changes: 5 additions & 9 deletions packages/upset/src/components/Header/QueryButton.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { SvgIcon, Tooltip } from '@mui/material';
import { Add, Remove } from '@mui/icons-material';
import { useRecoilState, useRecoilValue } from 'recoil';
import { useMemo } from 'react';
import { useCallback, useMemo } from 'react';
import Group from '../custom/Group';
import { mousePointer } from '../../utils/styles';
import { dimensionsSelector } from '../../atoms/dimensionsAtom';
import { queryBySetsInterfaceAtom } from '../../atoms/queryBySetsAtoms';
import { queryBySetsInterfaceAtom } from '../../atoms/config/queryBySetsAtoms';

/**
* The size of the icon in pixels.
Expand All @@ -22,13 +22,9 @@ export const QueryButton = () => {
/**
* Toggles the query by set interface.
*/
const toggleQueryBySetsInterface = () => {
if (queryBySetsInterface) {
setQueryBySetsInterface(false);
} else {
setQueryBySetsInterface(true);
}
};
const toggleQueryBySetsInterface = useCallback(() => {
setQueryBySetsInterface((prev) => !prev);
}, [setQueryBySetsInterface]);

// for whatever reason this needed to be memoized for the icon to update...
// some weird recoil thing I expect
Expand Down
12 changes: 6 additions & 6 deletions packages/upset/src/components/Rows/AggregateRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import SvgIcon from '@mui/material/SvgIcon';

import { visibleSetSelector } from '../../atoms/config/visibleSetsAtoms';
import { dimensionsSelector } from '../../atoms/dimensionsAtom';
import { currentIntersectionSelector } from '../../atoms/config/currentIntersectionAtom';
import { currentIntersectionSelector } from '../../atoms/config/currentIntersectionAtom';
import translate from '../../utils/transform';
import { highlight, mousePointer } from '../../utils/styles';
import { highlight, mousePointer, DEFAULT_ROW_BACKGROUND_COLOR, ROW_BORDER_STROKE_COLOR, ROW_BORDER_STROKE_WIDTH, DEFAULT_ROW_BACKGROUND_OPACITY } from '../../utils/styles';
import { SizeBar } from '../Columns/SizeBar';
import { Matrix } from '../Columns/Matrix/Matrix';
import { BookmarkStar } from '../Columns/BookmarkStar';
Expand Down Expand Up @@ -117,10 +117,10 @@ export const AggregateRow: FC<Props> = ({ aggregateRow }) => {
width={width}
rx={5}
ry={10}
fill="#cccccc"
opacity="0.3"
stroke="#555555"
strokeWidth="1px"
fill={DEFAULT_ROW_BACKGROUND_COLOR}
opacity={DEFAULT_ROW_BACKGROUND_OPACITY}
stroke={ROW_BORDER_STROKE_COLOR}
strokeWidth={ROW_BORDER_STROKE_WIDTH}
/>
<g>
{collapsedIds.includes(aggregateRow.id) ? collapsed : expanded}
Expand Down
2 changes: 1 addition & 1 deletion packages/upset/src/components/Rows/MatrixRows.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import translate from '../../utils/transform';
import { AggregateRow } from './AggregateRow';
import { SubsetRow } from './SubsetRow';
import { collapsedSelector } from '../../atoms/collapsedAtom';
import { queryBySetsInterfaceAtom, setQueryAtom } from '../../atoms/queryBySetsAtoms';
import { queryBySetsInterfaceAtom, setQueryAtom } from '../../atoms/config/queryBySetsAtoms';

type Props = {
rows: RenderRow[];
Expand Down
2 changes: 1 addition & 1 deletion packages/upset/src/components/SvgBase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { dimensionsSelector } from '../atoms/dimensionsAtom';
import { ProvenanceContext } from './Root';
import { currentIntersectionSelector } from '../atoms/config/currentIntersectionAtom';
import { calculateDimensions } from '../dimensions';
import { queryBySetsInterfaceAtom } from '../atoms/queryBySetsAtoms';
import { queryBySetsInterfaceAtom } from '../atoms/config/queryBySetsAtoms';

export const SvgBase: FC = ({ children }) => {
const dimensions = useRecoilValue(dimensionsSelector);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ import {
} from '@visdesignlab/upset2-core';
import { dimensionsSelector } from '../../../atoms/dimensionsAtom';
import translate from '../../../utils/transform';
import { mousePointer } from '../../../utils/styles';
import { mousePointer, DEFAULT_ROW_BACKGROUND_COLOR, ROW_BORDER_STROKE_COLOR, ROW_BORDER_STROKE_WIDTH, DEFAULT_ROW_BACKGROUND_OPACITY } from '../../../utils/styles';
import { SetMembershipRow } from './SetMembershipRow';
import { visibleSetSelector } from '../../../atoms/config/visibleSetsAtoms';
import { SizeBar } from '../../Columns/SizeBar';
import { dataAtom } from '../../../atoms/dataAtom';
import { ProvenanceContext } from '../../Root';
import { queryBySetsInterfaceAtom } from '../../../atoms/queryBySetsAtoms';
import { queryBySetsInterfaceAtom } from '../../../atoms/config/queryBySetsAtoms';

// edit icon size
const EDIT_ICON_SIZE = 14;
Expand Down Expand Up @@ -68,8 +68,9 @@ export const QueryBySetInterface = () => {

const queryResults = Object.values(queryResult.values);
queryResults.forEach((row) => {
if (!isRowAggregate(row))
if (!isRowAggregate(row)) {
size += row.size;
}
});

return size;
Expand All @@ -96,7 +97,7 @@ export const QueryBySetInterface = () => {
}

// base string. All results must begin with this
let queryResultString = 'intersections of ';
let queryString = 'intersections of ';

// 'May' sets have no string representation and so are ignored
const yesSets = Object.entries(membership).filter(([_, status]) => status === 'Yes');
Expand All @@ -107,16 +108,16 @@ export const QueryBySetInterface = () => {
*/
if (yesSets.length > 0) {
if (yesSets.length === 1) {
queryResultString += 'set ';
queryString += 'set ';
} else {
queryResultString += 'sets ';
queryString += 'sets ';
}
}

yesSets.forEach(([set], index) => {
queryResultString += `[${set.replace('Set_', '')}]`;
queryString += `[${set.replace('Set_', '')}]`;
if (index < yesSets.length - 1) {
queryResultString += ' and ';
queryString += ' and ';
}
});

Expand All @@ -125,23 +126,23 @@ export const QueryBySetInterface = () => {
*/
if (noSets.length > 0) {
if (yesSets.length > 0) {
queryResultString += ' but excluding set';
queryString += ' but excluding set';
} else {
queryResultString += 'excluding set';
queryString += 'excluding set';
}
if (noSets.length > 1) {
queryResultString += 's';
queryString += 's';
}
}

noSets.forEach(([set], index) => {
queryResultString += ` [${set.replace('Set_', '')}]`;
queryString += ` [${set.replace('Set_', '')}]`;
if (index < noSets.length - 1) {
queryResultString += ' and ';
queryString += ' and ';
}
});

return queryResultString;
return queryString;
}, [membership, queryResult]);

/**
Expand Down Expand Up @@ -179,19 +180,19 @@ export const QueryBySetInterface = () => {
width={dimensions.setQuery.width}
opacity="0.2"
fill="transparent"
stroke="#555555"
strokeWidth="1px"
stroke={ROW_BORDER_STROKE_COLOR}
strokeWidth={ROW_BORDER_STROKE_WIDTH}
/>
{/* Query Header */}
<g>
<rect
transform={translate(0, 0)}
height={dimensions.body.rowHeight}
width={dimensions.setQuery.width}
fill="#cccccc"
opacity="0.3"
stroke="#555555"
strokeWidth="1px"
fill={DEFAULT_ROW_BACKGROUND_COLOR}
opacity={DEFAULT_ROW_BACKGROUND_OPACITY}
stroke={ROW_BORDER_STROKE_COLOR}
strokeWidth={ROW_BORDER_STROKE_WIDTH}
/>
<g
transform={translate(20, 0)}
Expand Down Expand Up @@ -239,9 +240,9 @@ export const QueryBySetInterface = () => {
y1={dimensions.body.rowHeight / 2}
x2={dimensions.matrixColumn.visibleSetsWidth - 10}
y2={dimensions.body.rowHeight / 2}
stroke="#555555"
opacity="0.4"
strokeWidth="1px"
stroke={ROW_BORDER_STROKE_COLOR}
strokeWidth={ROW_BORDER_STROKE_WIDTH}
/>
</g>
<g transform={translate(0, dimensions.body.rowHeight * 2)}>
Expand All @@ -266,7 +267,7 @@ export const QueryBySetInterface = () => {
height={CHECK_ICON_SIZE}
width={CHECK_ICON_SIZE}
fill="transparent"
onClick={() => addQuery()}
onClick={addQuery}
/>
<SvgIcon height={CHECK_ICON_SIZE} width={CHECK_ICON_SIZE}>
<Check />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Dispatch, FC, SetStateAction } from 'react';
import { Dispatch, FC, SetStateAction, useCallback } from 'react';
import { useRecoilValue } from 'recoil';
import { SetMembershipStatus, SetQueryMembership } from '@visdesignlab/upset2-core';
import { css } from '@emotion/react';
Expand All @@ -17,7 +17,7 @@ type Props = {
*/
setMembers: Dispatch<SetStateAction<SetQueryMembership>>;
/**
* Membership type for the row, can only be 'not', 'maybe', or 'must'.
* Membership type for the row, can only be 'Not', 'Maybe', or 'Must'.
*/
membershipType?: SetMembershipStatus
/**
Expand All @@ -43,11 +43,11 @@ export const SetMembershipRow: FC<Props> = ({
const visibleSets = useRecoilValue(visibleSetSelector);

/**
* Retrieves the membership status for a given set.
* Retrieves the membership status for a given set within the current query interface selections. If the row is combined, it returns the membership status for the combined row.
* @param set - The name of the set.
* @returns The membership status for the set.
*/
function getMembershipStatus(set: string): SetMembershipStatus {
function getMembershipStatusInQuery(set: string): SetMembershipStatus {
if (combined || !membershipType) {
return members[set];
}
Expand All @@ -59,13 +59,13 @@ export const SetMembershipRow: FC<Props> = ({
*
* @param set - The name of the set.
*/
function selectMembershipCircle(set: string) {
const selectMembershipCircle = useCallback((set: string) => {
if (combined || !membershipType) return;

const newMembers = { ...members };
newMembers[set] = membershipType;
setMembers(newMembers);
}
}, [combined, membershipType, members, setMembers]);

return (
<g>
Expand All @@ -81,9 +81,9 @@ export const SetMembershipRow: FC<Props> = ({
<MemberShipCircle
onClick={() => selectMembershipCircle(set)}
transform={translate(((dimensions.set.width / 2) + dimensions.gap / 2) * index, 0)}
membershipStatus={getMembershipStatus(set)}
membershipStatus={getMembershipStatusInQuery(set)}
showoutline
css={(!combined && getMembershipStatus(set) === members[set]) && highlightedSetMemberCircle}
css={(!combined && getMembershipStatusInQuery(set) === members[set]) && highlightedSetMemberCircle}
/>
))}
</g>
Expand Down
Loading

0 comments on commit 05f2fd2

Please sign in to comment.