Skip to content

Commit

Permalink
HP-1724 Feat/mixed avail icons (#1604)
Browse files Browse the repository at this point in the history
* add mixed avail state and icon

* add mixed avail icon

* add new access descriptor

* fix logic

* fix words

* update words

* fix whitespace

* update docs

* address PR feedbacks

* separate tooltip component
  • Loading branch information
mfshao authored Oct 11, 2024
1 parent 0ce1345 commit 47fae20
Show file tree
Hide file tree
Showing 10 changed files with 161 additions and 106 deletions.
3 changes: 2 additions & 1 deletion docs/portal_config.md
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,8 @@ Below is an example, with inline comments describing what each JSON block config
"menuText": "Not Accessible"
},
"waiting": {...},
"notAvailable": {...}
"notAvailable": {...},
"mixed": {...} // this is a special state, it will only has effect if the "accessible" level has also been enabled. This state won't show up in the data access filter
}
},
"tagsColumn" : {
Expand Down
1 change: 1 addition & 0 deletions src/Discovery/Discovery.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const initStoreData = {
[AccessLevel.UNACCESSIBLE]: true,
[AccessLevel.WAITING]: true,
[AccessLevel.NOT_AVAILABLE]: true,
[AccessLevel.MIXED]: true,
},
selectedTags: {},
pagination: {
Expand Down
99 changes: 22 additions & 77 deletions src/Discovery/Discovery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import React, {
import * as JsSearch from 'js-search';
import jsonpath from 'jsonpath';
import {
Tag, Popover, Space, Collapse, Button, Dropdown, Pagination, Tooltip, Spin,
Tag, Space, Collapse, Button, Dropdown, Pagination, Tooltip, Spin,
} from 'antd';
import {
LockOutlined,
Expand Down Expand Up @@ -35,6 +35,7 @@ import DiscoveryMDSSearch from './DiscoveryMDSSearch';
import DiscoveryAccessibilityLinks from './DiscoveryAccessibilityLinks';
import doSearchFilterSort from './Utils/Search/doSearchFilterSort';
import './Discovery.css';
import DiscoveryDataAvailabilityTooltips from './DiscoveryDataAvailabilityTooltips';

export const accessibleFieldName = '__accessible';

Expand All @@ -44,6 +45,7 @@ export enum AccessLevel {
WAITING = 3,
NOT_AVAILABLE = 4,
OTHER = 5,
MIXED = 6,
}

export enum AccessSortDirection {
Expand All @@ -52,8 +54,6 @@ export enum AccessSortDirection {

const { Panel } = Collapse;

const ARBORIST_READ_PRIV = 'read';

const setUpMenuItemInfo = (menuItemInfo, supportedValues) => {
if (supportedValues?.waiting?.enabled === true) {
menuItemInfo.push(
Expand Down Expand Up @@ -483,10 +483,20 @@ const Discovery: React.FunctionComponent<Props> = (props: Props) => {
checked={props.accessFilters[accessLevel]}
onChange={
() => {
props.onAccessFilterSet({
const updatedAccessFilter = {
...props.accessFilters,
[accessLevel]: !props.accessFilters[accessLevel],
});
};
// If "mixed availability" is enabled, set its value so it would show when either "accessible" or "unaccessible" is set
const isMixedAvailabilityEnabled = config.features?.authorization?.supportedValues?.mixed?.enabled === true;
const setMixedAvailabilityToShowWhenAccessibleOrUnaccessibleIsSet = () => {
updatedAccessFilter[AccessLevel.MIXED] = Boolean(updatedAccessFilter[AccessLevel.ACCESSIBLE])
|| Boolean(updatedAccessFilter[AccessLevel.UNACCESSIBLE]);
};
if (isMixedAvailabilityEnabled) {
setMixedAvailabilityToShowWhenAccessibleOrUnaccessibleIsSet();
}
props.onAccessFilterSet(updatedAccessFilter);
}
}
>
Expand All @@ -513,6 +523,7 @@ const Discovery: React.FunctionComponent<Props> = (props: Props) => {
[AccessLevel.NOT_AVAILABLE]: true,
[AccessLevel.WAITING]: true,
[AccessLevel.UNACCESSIBLE]: true,
[AccessLevel.MIXED]: true,
},
)}
> Reset
Expand Down Expand Up @@ -578,78 +589,12 @@ const Discovery: React.FunctionComponent<Props> = (props: Props) => {
ellipsis: false,
width: '200px',
textWrap: 'word-break',
render: (_, record) => {
if (record[accessibleFieldName] === AccessLevel.WAITING) {
return (
<Popover
overlayClassName='discovery-popover'
placement='topRight'
arrowPointAtCenter
content={(
<div className='discovery-popover__text'>
Data are not yet available for this study
</div>
)}
>
<ClockCircleOutlined className='discovery-table__access-icon' />
</Popover>
);
}
if (record[accessibleFieldName] === AccessLevel.NOT_AVAILABLE) {
return (
<Popover
overlayClassName='discovery-popover'
placement='topRight'
arrowPointAtCenter
content={(
<div className='discovery-popover__text'>
No data will be shared by this study
</div>
)}
>
<DashOutlined className='discovery-table__access-icon' />
</Popover>
);
}
if (record[accessibleFieldName] === AccessLevel.ACCESSIBLE) {
return (
<Popover
overlayClassName='discovery-popover'
placement='topRight'
arrowPointAtCenter
title={'You have access to these data.'}
content={(
<div className='discovery-popover__text'>
<React.Fragment>You have <code>{ARBORIST_READ_PRIV}</code> access to </React.Fragment>
<React.Fragment><code>{record[config.minimalFieldMapping.authzField]}</code>.</React.Fragment>
</div>
)}
>
<UnlockOutlined className='discovery-table__access-icon' />
</Popover>
);
}
if (record[accessibleFieldName] === AccessLevel.UNACCESSIBLE) {
return (
<Popover
overlayClassName='discovery-popover'
placement='topRight'
arrowPointAtCenter
title={'You do not currently have access to these data.'}
content={(
<div className='discovery-popover__text'>
<React.Fragment>You don&apos;t have <code>{ARBORIST_READ_PRIV}</code> access to </React.Fragment>
<React.Fragment><code>{record[config.minimalFieldMapping.authzField]}</code>. </React.Fragment>
<React.Fragment>Visit the repository to request access to these data</React.Fragment>
</div>
)}
>
<LockOutlined className='discovery-table__access-icon' />
</Popover>
);
}
return <React.Fragment />;
},
render: (_, record) => (
<DiscoveryDataAvailabilityTooltips
dataAvailabilityLevel={record[accessibleFieldName]}
authzFieldName={record[config.minimalFieldMapping.authzField]}
/>
),
});
}
// -----
Expand Down
106 changes: 106 additions & 0 deletions src/Discovery/DiscoveryDataAvailabilityTooltips.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import {
ClockCircleOutlined, DashOutlined, UnlockOutlined, LockOutlined,
} from '@ant-design/icons';
import { Popover } from 'antd';
import React from 'react';
import { AccessLevel } from './Discovery';

const ARBORIST_READ_PRIV = 'read';

interface Props {
dataAvailabilityLevel: AccessLevel,
authzFieldName: string
}

const DiscoveryDataAvailabilityTooltips = (props: Props) => {
if (props.dataAvailabilityLevel === AccessLevel.WAITING) {
return (
<Popover
overlayClassName='discovery-popover'
placement='topRight'
arrowPointAtCenter
content={(
<div className='discovery-popover__text'>
Data are not yet available for this study
</div>
)}
>
<ClockCircleOutlined className='discovery-table__access-icon' />
</Popover>
);
}
if (props.dataAvailabilityLevel === AccessLevel.NOT_AVAILABLE) {
return (
<Popover
overlayClassName='discovery-popover'
placement='topRight'
arrowPointAtCenter
content={(
<div className='discovery-popover__text'>
No data will be shared by this study
</div>
)}
>
<DashOutlined className='discovery-table__access-icon' />
</Popover>
);
}
if (props.dataAvailabilityLevel === AccessLevel.ACCESSIBLE) {
return (
<Popover
overlayClassName='discovery-popover'
placement='topRight'
arrowPointAtCenter
title={'You have access to these data.'}
content={(
<div className='discovery-popover__text'>
<React.Fragment>You have <code>{ARBORIST_READ_PRIV}</code> access to </React.Fragment>
<React.Fragment><code>{props.authzFieldName}</code>.</React.Fragment>
</div>
)}
>
<UnlockOutlined className='discovery-table__access-icon' />
</Popover>
);
}
if (props.dataAvailabilityLevel === AccessLevel.UNACCESSIBLE) {
return (
<Popover
overlayClassName='discovery-popover'
placement='topRight'
arrowPointAtCenter
title={'You do not currently have access to these data.'}
content={(
<div className='discovery-popover__text'>
<React.Fragment>You don&apos;t have <code>{ARBORIST_READ_PRIV}</code> access to </React.Fragment>
<React.Fragment><code>{props.authzFieldName}</code>. </React.Fragment>
<React.Fragment>Visit the repository to request access to these data</React.Fragment>
</div>
)}
>
<LockOutlined className='discovery-table__access-icon' />
</Popover>
);
}
if (props.dataAvailabilityLevel === AccessLevel.MIXED) {
return (
<Popover
overlayClassName='discovery-popover'
placement='topRight'
arrowPointAtCenter
title={'You have access to some of these data.'}
content={(
<div className='discovery-popover__text'>
<React.Fragment>Some of these data require visiting the repository to request access.</React.Fragment>
</div>
)}
>
<UnlockOutlined className='discovery-table__access-icon' />
<LockOutlined className='discovery-table__access-icon' />
</Popover>
);
}
return <React.Fragment />;
};

export default DiscoveryDataAvailabilityTooltips;
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
import React from 'react';
import { Alert } from 'antd';
import { UnlockOutlined } from '@ant-design/icons';
import { AccessLevel } from '../../../Discovery';

interface NonTabbedDiscoveryDetailsProps {userHasAccess:boolean, userDoesNotHaveAccess:boolean}
interface NonTabbedDiscoveryDetailsProps {accessibleFieldValue: AccessLevel}

const AccessDescriptor = ({ userHasAccess, userDoesNotHaveAccess }: NonTabbedDiscoveryDetailsProps) => {
if (userHasAccess) {
const AccessDescriptor = ({ accessibleFieldValue }: NonTabbedDiscoveryDetailsProps) => {
if (accessibleFieldValue === AccessLevel.ACCESSIBLE) {
return (
<Alert
className='discovery-modal__access-alert'
type='success'
message={(
<React.Fragment>
<UnlockOutlined /> You have access to this data.
<UnlockOutlined />You have access to this data.
</React.Fragment>
)}
/>
);
}
if (userDoesNotHaveAccess) {
if (accessibleFieldValue === AccessLevel.UNACCESSIBLE) {
return (
<Alert
className='discovery-modal__access-alert'
Expand All @@ -29,14 +30,23 @@ const AccessDescriptor = ({ userHasAccess, userDoesNotHaveAccess }: NonTabbedDis
/>
);
}
if (accessibleFieldValue === AccessLevel.MIXED) {
return (
<Alert
className='discovery-modal__access-alert'
type='info'
message={
<React.Fragment>This study contains both open and restricted access data.</React.Fragment>
}
/>
);
}
return (
<Alert
className='discovery-modal__access-alert'
type='info'
message={(
<React.Fragment>
This does not include data access authorization details.
</React.Fragment>
<React.Fragment>This does not include data access authorization details.</React.Fragment>
)}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import DataDownloadList from './DataDownloadList/DataDownloadList';
import {
renderFieldContent,
DiscoveryResource,
AccessLevel,
accessibleFieldName,
} from '../../../../Discovery';
import { User } from '../../../DiscoveryDetailsInterfaces';
Expand All @@ -28,12 +27,9 @@ const TabField = (
): JSX.Element | null => {
// Setup special fields first
if (fieldConfig.type === 'accessDescriptor') {
// return accessDescriptor(resource);
// return AccessDescriptor(resource);
return (
<AccessDescriptor
userHasAccess={resource[accessibleFieldName] === AccessLevel.ACCESSIBLE}
userDoesNotHaveAccess={resource[accessibleFieldName] === AccessLevel.UNACCESSIBLE}
accessibleFieldValue={resource[accessibleFieldName]}
/>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Button, Collapse, List } from 'antd';
import { DownloadOutlined } from '@ant-design/icons';
import jsonpath from 'jsonpath';
import {
AccessLevel,
accessibleFieldName,
renderFieldContent,
} from '../../../Discovery';
Expand All @@ -15,15 +14,6 @@ import AccessDescriptor from '../AccessDescriptor/AccessDescriptor';
const { Panel } = Collapse;

const NonTabbedDiscoveryDetails = ({ props }) => {
const userHasAccess = props.config.features.authorization.enabled
&& props.modalData[accessibleFieldName] !== AccessLevel.NOT_AVAILABLE
&& props.modalData[accessibleFieldName] !== AccessLevel.WAITING
&& props.modalData[accessibleFieldName] === AccessLevel.ACCESSIBLE;
const userDoesNotHaveAccess = props.config.features.authorization.enabled
&& props.modalData[accessibleFieldName] !== AccessLevel.NOT_AVAILABLE
&& props.modalData[accessibleFieldName] !== AccessLevel.WAITING
&& props.modalData[accessibleFieldName] !== AccessLevel.ACCESSIBLE;

const showDownloadPanel = props.config.studyPageFields.downloadLinks
&& props.config.studyPageFields.downloadLinks.field
&& props.modalData[props.config.studyPageFields.downloadLinks.field];
Expand All @@ -43,8 +33,7 @@ const NonTabbedDiscoveryDetails = ({ props }) => {
<div className='discovery-modal-content'>
<StudyHeader props={props} />
<AccessDescriptor
userHasAccess={userHasAccess}
userDoesNotHaveAccess={userDoesNotHaveAccess}
accessibleFieldValue={props.modalData[accessibleFieldName]}
/>
<div className='discovery-modal-attributes-container'>
{props.config.studyPageFields.fieldsToShow.map(
Expand Down
2 changes: 1 addition & 1 deletion src/Discovery/DiscoveryListView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ const DiscoveryListView: React.FunctionComponent<Props> = (props: Props) => {
let disabled;
// if auth is enabled, disable checkbox if user doesn't have access
if (props.config.features.authorization.enabled) {
disabled = record[props.accessibleFieldName] !== AccessLevel.ACCESSIBLE;
disabled = (record[props.accessibleFieldName] !== AccessLevel.ACCESSIBLE) && (record[props.accessibleFieldName] !== AccessLevel.MIXED);
}
// disable checkbox if there's no manifest or external file metadata (if metadata handoff is enabled) found for this study
const exportToWorkspaceConfig = props.config.features.exportToWorkspace;
Expand Down
Loading

0 comments on commit 47fae20

Please sign in to comment.