From 5da81943dce0ddb06db4932ab7ab1329f3588725 Mon Sep 17 00:00:00 2001 From: Florian Boulnois Date: Tue, 4 Feb 2025 13:46:04 -0500 Subject: [PATCH 1/5] fix: div cannot appear as child of p --- src/components/WelcomeView.jsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/WelcomeView.jsx b/src/components/WelcomeView.jsx index 59e8bdc62..d3e92bc9f 100755 --- a/src/components/WelcomeView.jsx +++ b/src/components/WelcomeView.jsx @@ -34,6 +34,10 @@ const SubTitle = styled(Typography)({ fontSize: '16px', }); +const SubTitleBox = styled(Box)({ + fontSize: '16px', +}); + const MainContent = styled(Box)(({ theme }) => ({ display: 'inline-block', color: theme.typography.color, @@ -100,7 +104,7 @@ function WelcomeView({ terraUrl }) { Terra Data Repository is a cloud-native platform that allows data owners to{' '} govern and share biomedical research data. - + - +
Terra Data Repository requires a Terra account.

From eb162cce943d5d5adaea89eb43d170fb05cc2d89 Mon Sep 17 00:00:00 2001 From: Florian Boulnois Date: Fri, 7 Feb 2025 10:58:10 -0500 Subject: [PATCH 2/5] feat: add auth domain component --- src/components/snapshot/AuthDomain.tsx | 77 ++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 src/components/snapshot/AuthDomain.tsx diff --git a/src/components/snapshot/AuthDomain.tsx b/src/components/snapshot/AuthDomain.tsx new file mode 100644 index 000000000..aab605bdc --- /dev/null +++ b/src/components/snapshot/AuthDomain.tsx @@ -0,0 +1,77 @@ +import React from 'react'; +import { Box, FormLabel, Link, styled } from '@mui/material'; +import { withStyles } from '@mui/styles'; +import { ManagedGroupMembershipEntry } from 'src/models/group'; +import { AppDispatch } from 'src/store'; +import { TdrState } from 'src/reducers'; +import { useOnMount } from 'src/libs/utils'; +import { LaunchOutlined } from '@mui/icons-material'; +import { connect } from 'react-redux'; +import { getUserGroups } from 'src/actions'; +import JadeDropdown from '../dataset/data/JadeDropdown'; + +const styles = () => ({ + /* empty styles */ +}); + +const JadeLink = styled('span')(({ theme }) => theme.mixins.jadeLink); + +type AuthDomainProps = { + dispatch: AppDispatch; + userGroups: Array; + setParentAuthDomain: (domain: string) => void; +}; + +function AuthDomain({ dispatch, userGroups, setParentAuthDomain }: AuthDomainProps) { + const [selectedAuthDomain, setSelectedAuthDomain] = React.useState(undefined); + + useOnMount(() => { + dispatch(getUserGroups()); + }); + + return ( + <> + + Authorization Domain + - (optional) + + + Authorization Domains restrict data access to only specified individuals in a group and are + intended to fulfill requirements you may have for data governed by a compliance standard, + such as federal controlled-access data or HIPAA protected data. They follow all snapshot + copies and cannot be removed. For more details, see{' '} + + + When to use an Authorization Domain + + + + . + + group.groupName) : []} + name="authorization-domain" + onSelectedItem={(event) => { + const authDomain = event.target.value; + setParentAuthDomain(authDomain); + setSelectedAuthDomain(authDomain); + }} + value={selectedAuthDomain || ''} + /> + + ); +} + +function mapStateToProps(state: TdrState) { + return { + userGroups: state.user.userGroups, + }; +} + +export default connect(mapStateToProps)(withStyles(styles)(AuthDomain)); From d1ff04dd377c019657671ae77fa86d687bb3b505 Mon Sep 17 00:00:00 2001 From: Florian Boulnois Date: Fri, 7 Feb 2025 10:58:36 -0500 Subject: [PATCH 3/5] test: add unit tests for auth domain functionality --- src/components/snapshot/AuthDomain.test.tsx | 81 +++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 src/components/snapshot/AuthDomain.test.tsx diff --git a/src/components/snapshot/AuthDomain.test.tsx b/src/components/snapshot/AuthDomain.test.tsx new file mode 100644 index 000000000..9dac2224f --- /dev/null +++ b/src/components/snapshot/AuthDomain.test.tsx @@ -0,0 +1,81 @@ +import { mount } from 'cypress/react'; +import { Router } from 'react-router-dom'; +import { ThemeProvider } from '@mui/material/styles'; +import { Provider } from 'react-redux'; +import React from 'react'; +import createMockStore from 'redux-mock-store'; +import { ManagedGroupMembershipEntry } from 'src/models/group'; +import history from '../../modules/hist'; +import AuthDomain from './AuthDomain'; +import globalTheme from '../../modules/theme'; + +const mountAuthDomain = (userGroups: Array) => { + const state = { + user: { + userGroups, + }, + }; + + const mockStore = createMockStore([]); + const store = mockStore(state); + + cy.intercept('GET', 'https://sam.dsde-dev.broadinstitute.org/api/groups/v1').as('getUserGroups'); + + mount( + + + + { + /* no-op */ + }} + /> + + + , + ); +}; + +describe('Test AuthDomain component', () => { + it('Displays authorization domain section', () => { + mountAuthDomain([]); + + cy.get('label[for="authorization-domain"]') + .should('contain.text', 'Authorization Domain') + .should('contain.text', '(optional)'); + }); + + it('Shows authorization domain dropdown with options when user groups exist', () => { + const userGroups = [ + { groupEmail: 'email1', groupName: 'group1', role: 'READER' }, + { groupEmail: 'email2', groupName: 'group2', role: 'READER' }, + ]; + mountAuthDomain(userGroups); + + cy.get('#authorization-domain-select') + .should('exist') + .should('not.be.disabled') + .should('have.value', ''); + + cy.get('#authorization-domain-select').parent().click(); + cy.get('[data-cy^=menuItem]').should('have.length', userGroups.length); + }); + + it('Select an authorization domain when user groups exist', () => { + const userGroups = [ + { groupEmail: 'email1', groupName: 'group1', role: 'READER' }, + { groupEmail: 'email2', groupName: 'group2', role: 'READER' }, + ]; + mountAuthDomain(userGroups); + + cy.get('#authorization-domain-select').parent().click(); + cy.get('[data-cy=menuItem-group2]').click(); + cy.get('#authorization-domain-select').should('have.value', 'group2'); + }); + + it('Disables authorization domain dropdown when insufficient user groups', () => { + const userGroups = [{ groupEmail: 'default', groupName: 'default', role: 'READER' }]; + mountAuthDomain(userGroups); + cy.get('#authorization-domain-select').should('be.disabled'); + }); +}); From bd5b678f00114e47c511c363b394e01ec3f5585d Mon Sep 17 00:00:00 2001 From: Florian Boulnois Date: Fri, 7 Feb 2025 10:58:52 -0500 Subject: [PATCH 4/5] feat: set auth domain on parent component --- .../data/sidebar/panels/ShareSnapshot.jsx | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/components/dataset/data/sidebar/panels/ShareSnapshot.jsx b/src/components/dataset/data/sidebar/panels/ShareSnapshot.jsx index 24056b9e5..37b14dc9c 100644 --- a/src/components/dataset/data/sidebar/panels/ShareSnapshot.jsx +++ b/src/components/dataset/data/sidebar/panels/ShareSnapshot.jsx @@ -18,6 +18,7 @@ import { MoreVert } from '@mui/icons-material'; import { isEmail } from 'validator'; import { createSnapshot } from 'actions/index'; import SnapshotAccess from 'components/snapshot/SnapshotAccess'; +import AuthDomain from 'src/components/snapshot/AuthDomain'; const drawerWidth = 600; const sidebarWidth = 56; @@ -95,6 +96,7 @@ export class ShareSnapshot extends React.PureComponent { anchor: null, hasError: false, errorMsg: '', + authDomain: undefined, }; } @@ -107,6 +109,10 @@ export class ShareSnapshot extends React.PureComponent { setIsSharing: PropTypes.func, }; + setAuthDomain = (domain) => { + this.setState({ authDomain: domain }); + }; + saveSnapshot = () => { const { dispatch } = this.props; dispatch(createSnapshot(undefined)); @@ -126,18 +132,21 @@ export class ShareSnapshot extends React.PureComponent { {!isModal && ( -

- -
+ <> + +
+ +
+ )} {isModal && (
From 1f00b0a960816ce89dc1216e71b032e64c6b170d Mon Sep 17 00:00:00 2001 From: Florian Boulnois Date: Fri, 7 Feb 2025 11:03:03 -0500 Subject: [PATCH 5/5] feat: dispatch auth domain state to snapshot create --- .../dataset/data/sidebar/panels/ShareSnapshot.jsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/dataset/data/sidebar/panels/ShareSnapshot.jsx b/src/components/dataset/data/sidebar/panels/ShareSnapshot.jsx index 37b14dc9c..f10adeff0 100644 --- a/src/components/dataset/data/sidebar/panels/ShareSnapshot.jsx +++ b/src/components/dataset/data/sidebar/panels/ShareSnapshot.jsx @@ -16,7 +16,7 @@ import { } from '@mui/material'; import { MoreVert } from '@mui/icons-material'; import { isEmail } from 'validator'; -import { createSnapshot } from 'actions/index'; +import { createSnapshot, snapshotCreateDetails } from 'actions/index'; import SnapshotAccess from 'components/snapshot/SnapshotAccess'; import AuthDomain from 'src/components/snapshot/AuthDomain'; @@ -115,6 +115,12 @@ export class ShareSnapshot extends React.PureComponent { saveSnapshot = () => { const { dispatch } = this.props; + const { authDomain } = this.state; + dispatch( + snapshotCreateDetails({ + authDomain, + }), + ); dispatch(createSnapshot(undefined)); };