Skip to content

Commit

Permalink
Merge pull request #415 from visdesignlab/user-perms-updating
Browse files Browse the repository at this point in the history
Add userEditPerms to manage user's plotinformation edit permissions
  • Loading branch information
JakeWags authored Oct 30, 2024
2 parents 22e8d12 + 47376b7 commit da522b9
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 39 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ const main = () => {
- `visualizeAttributes` (optional)(`string[]`): List of attribute names (strings) which should be visualized. Defaults to the first 3 if no value is provided. If an empty list is provided, displays no attributes.
- `visualizeUpsetAttributes` (optional)(`boolean`): Whether or not to visualize UpSet generated attributes (`degree` and `deviation`). Defaults to `false`.
- `allowAttributeRemoval` (optional)(`boolean`): Whether or not to allow the user to remove attribute columns. This should be enabled only if there is an option within the parent application which allows for attributes to be added after removal. Default attribute removal behavior in UpSet 2.0 is done via context menu on attribute headers. Defaults to `false`.
- `canEditPlotInformation` (optional)(`boolean`): Whether or not the user can edit the plot information in the text descriptions sidebar.
- `hideSettings` (optional)(`boolean`): Hide the aggregations/filter settings sidebar.
- `parentHasHeight` (optional)(`boolean`): Indicates if the parent component has a fixed height. If this is set to `false`, the plot will occupy the full viewport height. When set to `true`, the plot will fit entirely within the parent component. Defaults to `false`.
- `extProvenance` (optional): External provenance actions and [TrrackJS](https://github.com/Trrack/trrackjs) object for provenance history tracking and actions. This should only be used if your tool is using TrrackJS and the Trrack object you provide has all the actions used by UpSet 2.0. Provenance is still tracked if nothing is provided. See [App.tsx](https://github.com/visdesignlab/upset2/blob/main/packages/app/src/App.tsx) to see how UpSet 2.0 and Multinet use an external Trrack object. Note that [initializeProvenanceTracking](https://github.com/visdesignlab/upset2/blob/main/packages/upset/src/provenance/index.ts#L300) and [getActions](https://github.com/visdesignlab/upset2/blob/main/packages/upset/src/provenance/index.ts#L322) are used to ensure that the provided provenance object is compatible. The provided provenance object must have a type compatible with the [extProvenance](https://vdl.sci.utah.edu/upset2/interfaces/_visdesignlab_upset2_react.UpsetProps.html#extProvenance) UpSet 2.0 prop type.
Expand Down
3 changes: 3 additions & 0 deletions e2e-tests/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ export async function beforeTest({ page }: {page: Page}) {
await route.fulfill({ json });
} else if (url.includes('workspaces/Upset%20Examples/sessions/table/193/')) {
await route.fulfill({ status: 200 });
} else if (url.includes('workspaces/Upset%20Examples/permissions/me')) {
// User has owner permissions, this will allow plot information editing
await route.fulfill({ status: 200, json: { permission_label: 'owner' } });
} else {
await route.continue();
}
Expand Down
22 changes: 21 additions & 1 deletion packages/app/src/components/Body.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ import { encodedDataAtom } from '../atoms/dataAtom';
import { doesHaveSavedQueryParam, queryParamAtom, saveQueryParam } from '../atoms/queryParamAtom';
import { ErrorModal } from './ErrorModal';
import { ProvenanceContext } from '../App';
import { useContext, useEffect } from 'react';
import { useContext, useEffect, useState } from 'react';
import { provenanceVisAtom } from '../atoms/provenanceVisAtom';
import { elementSidebarAtom } from '../atoms/elementSidebarAtom';
import { altTextSidebarAtom } from '../atoms/altTextSidebarAtom';
import { loadingAtom } from '../atoms/loadingAtom';
import { Backdrop, CircularProgress } from '@mui/material';
import { updateMultinetSession } from '../api/session';
import { generateAltText } from '../api/generateAltText';
import { api } from '../api/api';
import { rowsSelector } from '../atoms/selectors';

type Props = {
Expand Down Expand Up @@ -51,6 +52,24 @@ export const Body = ({ data, config }: Props) => {
})
}, [provObject.provenance, sessionId, workspace]);

// Check if the user has permissions to edit the plot
const [permissions, setPermissions] = useState(false);

useEffect(() => {
const fetchPermissions = async () => {
try {
const r = await api.getCurrentUserWorkspacePermissions(workspace || '');
// https://api.multinet.app/swagger/?format=openapi#/definitions/PermissionsReturn for possible permissions returns
setPermissions(r.permission_label === 'owner' || r.permission_label === 'maintainer');
} catch (e) {
setPermissions(false)
return;
}
};

fetchPermissions();
}, [workspace]);

/**
* Generates alt text for a plot based on the current state and configuration.
* If an error occurs during the generation, an error message is returned.
Expand Down Expand Up @@ -109,6 +128,7 @@ export const Body = ({ data, config }: Props) => {
<Upset
data={data}
extProvenance={provObject}
canEditPlotInformation={permissions}
config={config}
provVis={provVis}
elementSidebar={elementSidebar}
Expand Down
6 changes: 6 additions & 0 deletions packages/upset/src/atoms/config/canEditPlotInformationAtom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { atom } from 'recoil';

export const canEditPlotInformationAtom = atom<boolean>({
key: 'canEditPlotInformationAtom',
default: false,
});
58 changes: 37 additions & 21 deletions packages/upset/src/components/AltTextSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import '../index.css';
import { PlotInformation } from './custom/PlotInformation';
import { UpsetActions } from '../provenance';
import { plotInformationSelector } from '../atoms/config/plotInformationAtom';
import { canEditPlotInformationAtom } from '../atoms/config/canEditPlotInformationAtom';

/**
* Props for the AltTextSidebar component.
Expand Down Expand Up @@ -66,6 +67,7 @@ export const AltTextSidebar: FC<Props> = ({ open, close, generateAltText }) => {

const { actions }: {actions: UpsetActions} = useContext(ProvenanceContext);
const currState = useRecoilValue(upsetConfigAtom);
const canEditPlotInformation = useRecoilValue(canEditPlotInformationAtom);
const [altText, setAltText] = useState<AltText | null>(null);
const [textGenErr, setTextGenErr] = useState<string | false>(false);
const [textEditing, setTextEditing] = useState(false);
Expand All @@ -83,6 +85,10 @@ export const AltTextSidebar: FC<Props> = ({ open, close, generateAltText }) => {
* Handler for when the save button is clicked
*/
const saveButtonClick: () => void = useCallback(() => {
// if the user doesn't have edit permissions, don't allow saving
// The user shouldn't be able to edit in this case, but this is a failsafe
if (!canEditPlotInformation) return;

setTextEditing(false);
if (!currState.useUserAlt) actions.setUseUserAltText(true);
if (currState.userAltText?.shortDescription !== userShortText
Expand All @@ -93,6 +99,10 @@ export const AltTextSidebar: FC<Props> = ({ open, close, generateAltText }) => {
* Sets text editing to true and sets default user alttexts if necessary
*/
const enableTextEditing: () => void = useCallback(() => {
// if the user doesn't have edit permissions, don't allow editing
// The button should be hidden in this case, but this is a failsafe
if (!canEditPlotInformation) return;

setTextEditing(true);
if (!currState.userAltText?.shortDescription) setUserShortText(altText?.shortDescription);
if (!currState.userAltText?.longDescription) setUserLongText(altText?.longDescription);
Expand Down Expand Up @@ -206,12 +216,15 @@ export const AltTextSidebar: FC<Props> = ({ open, close, generateAltText }) => {
setEditing={setPlotInfoEditing}
/>
) : (
<Button
onClick={() => setPlotInfoEditing(true)}
tabIndex={PLOT_INFO_TAB_INDEX}
>
Add Plot Information
</Button>
// only show "Add Plot Information" if the user has edit permissions
canEditPlotInformation ? (
<Button
onClick={() => setPlotInfoEditing(true)}
tabIndex={PLOT_INFO_TAB_INDEX}
>
Add Plot Information
</Button>
) : null
)}
{displayPlotInfo && (
<>
Expand Down Expand Up @@ -268,21 +281,24 @@ export const AltTextSidebar: FC<Props> = ({ open, close, generateAltText }) => {
}}
tabIndex={3}
>
<Button
style={{
display: 'inline-block',
width: '24px',
float: 'right',
cursor: 'pointer',
}}
onClick={enableTextEditing}
tabIndex={5}
aria-label="Alt Text Description Editor"
>
<Icon style={{ overflow: 'visible' }}>
<EditIcon />
</Icon>
</Button>
{canEditPlotInformation && (
// Only show the edit button if the user has edit permissions
<Button
style={{
display: 'inline-block',
width: '24px',
float: 'right',
cursor: 'pointer',
}}
onClick={enableTextEditing}
tabIndex={5}
aria-label="Alt Text Description Editor"
>
<Icon style={{ overflow: 'visible' }}>
<EditIcon />
</Icon>
</Button>
)}
<ReactMarkdownWrapper
text={displayAltText}
/>
Expand Down
12 changes: 10 additions & 2 deletions packages/upset/src/components/Root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { dataAtom } from '../atoms/dataAtom';
import { allowAttributeRemovalAtom } from '../atoms/config/allowAttributeRemovalAtom';
import { contextMenuAtom } from '../atoms/contextMenuAtom';
import { upsetConfigAtom } from '../atoms/config/upsetConfigAtoms';
import { canEditPlotInformationAtom } from '../atoms/config/canEditPlotInformationAtom';
import {
getActions, initializeProvenanceTracking, UpsetActions, UpsetProvenance,
} from '../provenance';
Expand Down Expand Up @@ -41,6 +42,7 @@ type Props = {
config: UpsetConfig;
allowAttributeRemoval?: boolean;
hideSettings?: boolean;
canEditPlotInformation?: boolean;
extProvenance?: {
provenance: UpsetProvenance;
actions: UpsetActions;
Expand All @@ -61,13 +63,13 @@ type Props = {
};

export const Root: FC<Props> = ({
data, config, allowAttributeRemoval, hideSettings, extProvenance, provVis, elementSidebar, altTextSidebar, generateAltText,
data, config, allowAttributeRemoval, hideSettings, canEditPlotInformation, extProvenance, provVis, elementSidebar, altTextSidebar, generateAltText,
}) => {
// Get setter for recoil config atom
const setState = useSetRecoilState(upsetConfigAtom);

const [sets, setSets] = useRecoilState(setsAtom);
const [items, setItems] = useRecoilState(itemsAtom);
const setcanEditPlotInformation = useSetRecoilState(canEditPlotInformationAtom);
const setAttributeColumns = useSetRecoilState(attributeAtom);
const setAllColumns = useSetRecoilState(columnsAtom);
const setData = useSetRecoilState(dataAtom);
Expand All @@ -79,6 +81,12 @@ export const Root: FC<Props> = ({
setData(data);
}, []);

useEffect(() => {
if (canEditPlotInformation !== undefined) {
setcanEditPlotInformation(canEditPlotInformation);
}
}, [canEditPlotInformation]);

// Initialize Provenance and pass it setter to connect
const { provenance, actions } = useMemo(() => {
if (extProvenance) {
Expand Down
3 changes: 3 additions & 0 deletions packages/upset/src/components/Upset.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const defaultVisibleSets = 6;
* @param {boolean} [allowAttributeRemoval=false] - Whether or not to allow the user to remove attribute columns. This should be enabled only if there is an option within the parent application which allows for attributes to be added after removal. Default attribute removal behavior in UpSet 2.0 is done via context menu on attribute headers. Defaults to `false`.
* @param {boolean} [hideSettings] - Hide the aggregations/filter settings sidebar.
* @param {boolean} [parentHasHeight=false] - Indicates if the parent component has a fixed height. If this is set to `false`, the plot will occupy the full viewport height. When set to `true`, the plot will fit entirely within the parent component. Defaults to `false`.
* @param {boolean} [canEditPlotInformation=false] - Whether or not the user has plot information edit permissions.
* @param {Object} [extProvenance] - External provenance actions and [TrrackJS](https://github.com/Trrack/trrackjs) object for provenance history tracking and actions. This should only be used if your tool is using TrrackJS and has all the actions used by UpSet 2.0. Provenance is still tracked if nothing is provided. See [App.tsx](https://github.com/visdesignlab/upset2/blob/main/packages/app/src/App.tsx) to see how UpSet 2.0 and Multinet use an external Trrack object. Note that [initializeProvenanceTracking](https://github.com/visdesignlab/upset2/blob/main/packages/upset/src/provenance/index.ts#L300) and [getActions](https://github.com/visdesignlab/upset2/blob/main/packages/upset/src/provenance/index.ts#L322) are used to ensure that the provided provenance object is compatible.
* @param {SidebarProps} [provVis] - The provenance visualization sidebar options.
* @param {SidebarProps} [elementSidebar] - The element sidebar options. This sidebar is used for element queries, element selection datatable, and supplimental plot generation.
Expand All @@ -34,6 +35,7 @@ export const Upset: FC<UpsetProps> = ({
visualizeDatasetAttributes,
visualizeUpsetAttributes = false,
allowAttributeRemoval = false,
canEditPlotInformation = false,
hideSettings,
extProvenance,
provVis,
Expand Down Expand Up @@ -107,6 +109,7 @@ export const Upset: FC<UpsetProps> = ({
data={processData}
config={combinedConfig}
allowAttributeRemoval={allowAttributeRemoval}
canEditPlotInformation={canEditPlotInformation}
hideSettings={hideSettings}
extProvenance={extProvenance}
provVis={provVis}
Expand Down
35 changes: 20 additions & 15 deletions packages/upset/src/components/custom/PlotInformation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
useContext, useState, useCallback,
} from 'react';
import { plotInformationSelector } from '../../atoms/config/plotInformationAtom';
import { canEditPlotInformationAtom } from '../../atoms/config/canEditPlotInformationAtom';
import { ProvenanceContext } from '../Root';

/**
Expand Down Expand Up @@ -85,6 +86,7 @@ export const PlotInformation = ({
*/

const plotInformationState = useRecoilValue(plotInformationSelector);
const canEditPlotInformation = useRecoilValue(canEditPlotInformationAtom);
const [plotInformation, setPlotInformation] = useState(plotInformationState);
const { actions } = useContext(ProvenanceContext);

Expand Down Expand Up @@ -142,21 +144,24 @@ export const PlotInformation = ({
}}
>
<div style={{ minHeight: '1.6em' }}>
<Button
aria-label="Plot Information Editor"
style={{
float: 'right',
position: 'relative',
bottom: '10px',
cursor: 'pointer',
}}
tabIndex={tabIndex + 1}
onClick={() => setEditing(true)}
>
<Icon style={{ overflow: 'visible' }}>
<EditIcon />
</Icon>
</Button>
{canEditPlotInformation && (
// Hide the edit button if the user does not have permissions
<Button
aria-label="Plot Information Editor"
style={{
float: 'right',
position: 'relative',
bottom: '10px',
cursor: 'pointer',
}}
tabIndex={tabIndex + 1}
onClick={() => setEditing(true)}
>
<Icon style={{ overflow: 'visible' }}>
<EditIcon />
</Icon>
</Button>
)}
<Typography>{plotInformation.caption}</Typography>
</div>
<br />
Expand Down
5 changes: 5 additions & 0 deletions packages/upset/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@ export interface UpsetProps {
*/
allowAttributeRemoval?: boolean;

/**
* Whether or not the user has plot information edit permissions.
*/
canEditPlotInformation?: boolean;

/**
* Hide the aggregations/filter settings sidebar.
*/
Expand Down

0 comments on commit da522b9

Please sign in to comment.