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.

diff --git a/src/components/dataset/data/sidebar/panels/ShareSnapshot.jsx b/src/components/dataset/data/sidebar/panels/ShareSnapshot.jsx index 24056b9e5..f10adeff0 100644 --- a/src/components/dataset/data/sidebar/panels/ShareSnapshot.jsx +++ b/src/components/dataset/data/sidebar/panels/ShareSnapshot.jsx @@ -16,8 +16,9 @@ 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'; const drawerWidth = 600; const sidebarWidth = 56; @@ -95,6 +96,7 @@ export class ShareSnapshot extends React.PureComponent { anchor: null, hasError: false, errorMsg: '', + authDomain: undefined, }; } @@ -107,8 +109,18 @@ export class ShareSnapshot extends React.PureComponent { setIsSharing: PropTypes.func, }; + setAuthDomain = (domain) => { + this.setState({ authDomain: domain }); + }; + saveSnapshot = () => { const { dispatch } = this.props; + const { authDomain } = this.state; + dispatch( + snapshotCreateDetails({ + authDomain, + }), + ); dispatch(createSnapshot(undefined)); }; @@ -126,18 +138,21 @@ export class ShareSnapshot extends React.PureComponent { {!isModal && ( -

- -
+ <> + +
+ +
+ )} {isModal && (
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'); + }); +}); 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));