Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MMT-3411: As a metadata user, I want to publish a UMM record #1107

Merged
merged 12 commits into from
Jan 25, 2024
4 changes: 2 additions & 2 deletions static.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
},
"ummVersions": {
"ummC": "1.17.3",
"ummS": "1.4",
"ummT": "1.1",
"ummS": "1.5.2",
"ummT": "1.2.0",
"ummV": "1.9.0"
}
}
2 changes: 2 additions & 0 deletions static/src/js/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import REDIRECTS from './constants/redirectsMap/redirectsMap'
import { getApplicationConfig } from './utils/getConfig'

import '../css/index.scss'
import PublishPreview from './components/PublishPreview/PublishPreview'
dmistry1 marked this conversation as resolved.
Show resolved Hide resolved

const redirectKeys = Object.keys(REDIRECTS)

Expand Down Expand Up @@ -101,6 +102,7 @@ const App = () => {
<Route path="drafts/:draftType/*" element={<DraftsPage />} />
<Route path="/404" element={<Page title="404 Not Found" pageType="secondary">Not Found :(</Page>} />
<Route path="*" element={<Navigate to="/404" replace />} />
<Route path="/:type/:conceptId/:revisionId" element={<PublishPreview />} />
</Route>
</Routes>
</BrowserRouter>
Expand Down
65 changes: 65 additions & 0 deletions static/src/js/components/DeleteModal/DeleteModal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React from 'react'
import PropTypes from 'prop-types'

import Button from 'react-bootstrap/Button'
import Modal from 'react-bootstrap/Modal'

/**
* @typedef {Object} DeleteModalProps
* @property {Boolean} show Should the modal be open.
* @property {Function} closeModal A function to close the modal.
* @property {Function} onDelete A callback function triggered when the user selects `Yes`.
*/

/**
* Renders a DeleteModal component
*
* @component
* @example <caption>Render a DeleteModal</caption>
* return (
* <DeleteModal
* show={showDeleteModal}
* closeModal={handleClose}
* onDelete={handleDelete}
* />
* )
*/

// TODO check if the record being deleted is in the current provider,
// if not show the messages to change it
const DeleteModal = ({
dmistry1 marked this conversation as resolved.
Show resolved Hide resolved
closeModal,
onDelete,
show
}) => (
<Modal
onHide={closeModal}
show={show}
>
<Modal.Body>Are you sure you want to delete this record?</Modal.Body>

<Modal.Footer>
<Button
variant="secondary"
onClick={closeModal}
>
No
</Button>

<Button
variant="primary"
onClick={onDelete}
>
Yes
</Button>
</Modal.Footer>
</Modal>
)

DeleteModal.propTypes = {
closeModal: PropTypes.func.isRequired,
onDelete: PropTypes.func.isRequired,
show: PropTypes.bool.isRequired
}

export default DeleteModal
49 changes: 49 additions & 0 deletions static/src/js/components/DeleteModal/__tests__/DeleteModal.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from 'react'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'

import DeleteModal from '../DeleteModal'

const setup = () => {
const props = {
closeModal: jest.fn(),
onDelete: jest.fn(),
show: true
}

render(<DeleteModal {...props} />)

return {
props
}
}

describe('DeleteModal', () => {
test('renders a modal', () => {
setup()

expect(screen.getByText('Are you sure you want to delete this record?')).toBeInTheDocument()
})

describe('when selecting `No`', () => {
test('calls closeModal', async () => {
const { props } = setup()

const button = screen.getByText('No')
await userEvent.click(button)

expect(props.closeModal).toHaveBeenCalledTimes(1)
})
})

describe('when selecting `Yes`', () => {
test('calls onDelete', async () => {
const { props } = setup()

const button = screen.getByText('Yes')
await userEvent.click(button)

expect(props.onDelete).toHaveBeenCalledTimes(1)
})
})
})
71 changes: 16 additions & 55 deletions static/src/js/components/DraftPreview/DraftPreview.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,7 @@ import { useNavigate, useParams } from 'react-router'
import { useLazyQuery, useMutation } from '@apollo/client'
import validator from '@rjsf/validator-ajv8'
import { startCase } from 'lodash-es'
import {
CollectionPreview,
ServicePreview,
ToolPreview,
VariablePreview
} from '@edsc/metadata-preview'

import pluralize from 'pluralize'

import DeleteDraftModal from '../DeleteDraftModal/DeleteDraftModal'
Expand All @@ -35,6 +30,8 @@ import parseError from '../../utils/parseError'
import { DELETE_DRAFT } from '../../operations/mutations/deleteDraft'

import conceptTypeDraftQueries from '../../constants/conceptTypeDraftQueries'
import usePublishMutation from '../../hooks/usePublishMutation'
import MetadataPreview from '../MetadataPreview/MetadataPreview'

/**
* Renders a DraftPreview component
Expand Down Expand Up @@ -66,6 +63,7 @@ const DraftPreview = () => {
const [retries, setRetries] = useState(0)

const [deleteDraftMutation] = useMutation(DELETE_DRAFT)
const publishMutation = usePublishMutation()

const [getDraft] = useLazyQuery(conceptTypeDraftQueries[derivedConceptType], {
variables: {
Expand Down Expand Up @@ -167,6 +165,12 @@ const DraftPreview = () => {
ummMetadata
} = draft || {}

// Handle the user selecting publish draft
const handlePublish = () => {
// Calls publish mutation hook
publishMutation(derivedConceptType, nativeId)
}

// Handle the user selecting delete from the delete draft modal
const handleDelete = () => {
deleteDraftMutation({
Expand Down Expand Up @@ -215,51 +219,6 @@ const DraftPreview = () => {
// Pull the formSections out of the formConfigurations
const formSections = formConfigurations[derivedConceptType]

// Determine which MetadataPreview component to show
const metadataPreviewComponent = () => {
if (derivedConceptType === 'Collection') {
return (
<CollectionPreview
collection={previewMetadata}
conceptId={conceptId}
conceptType="collection-draft"
/>
)
}

if (derivedConceptType === 'Service') {
return (
<ServicePreview
conceptId={conceptId}
conceptType="service-draft"
service={previewMetadata}
/>
)
}

if (derivedConceptType === 'Tool') {
return (
<ToolPreview
conceptId={conceptId}
conceptType="tool-draft"
tool={previewMetadata}
/>
)
}

if (derivedConceptType === 'Variable') {
return (
<VariablePreview
conceptId={conceptId}
conceptType="variable-draft"
variable={previewMetadata}
/>
)
}

return null
}

// Accessible event props for the delete link
const accessibleEventProps = useAccessibleEvent(() => {
setShowDeleteModal(true)
Expand Down Expand Up @@ -289,8 +248,7 @@ const DraftPreview = () => {
className="eui-btn--blue display-modal"
onClick={
() => {
// TODO MMT-3411
console.log('Publish draft')
handlePublish()
}
}
>
Expand All @@ -317,7 +275,6 @@ const DraftPreview = () => {
/>
</Col>
</Row>

<Row>
<Col md={12}>
<Row>
Expand All @@ -334,7 +291,11 @@ const DraftPreview = () => {
</Col>
<Row>
<Col md={12}>
{metadataPreviewComponent()}
<MetadataPreview
previewMetadata={previewMetadata}
conceptId={conceptId}
conceptType={derivedConceptType}
/>
</Col>
</Row>
</Row>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
Route,
Routes
} from 'react-router-dom'
import { ToolPreview } from '@edsc/metadata-preview'
import * as router from 'react-router'

import ummTSchema from '../../../schemas/umm/ummTSchema'
Expand All @@ -27,8 +26,8 @@ import DraftPreview from '../DraftPreview'
import ErrorBanner from '../../ErrorBanner/ErrorBanner'
import PreviewProgress from '../../PreviewProgress/PreviewProgress'
import Providers from '../../../providers/Providers/Providers'
import { PUBLISH_DRAFT } from '../../../operations/mutations/publishDraft'

jest.mock('@edsc/metadata-preview')
jest.mock('../../ErrorBanner/ErrorBanner')
jest.mock('../../PreviewProgress/PreviewProgress')
jest.mock('../../../utils/errorLogger')
Expand Down Expand Up @@ -144,23 +143,6 @@ const setup = ({
}

describe('DraftPreview', () => {
describe('when the concept type is tool', () => {
test('renders a ToolPreview component', async () => {
setup({})

await waitForResponse()

expect(ToolPreview).toHaveBeenCalledTimes(1)
expect(ToolPreview).toHaveBeenCalledWith({
conceptId: 'TD1000000-MMT',
conceptType: 'tool-draft',
conceptUrlTemplate: '/{conceptType}/{conceptId}',
isPlugin: true,
tool: mockDraft.previewMetadata
}, {})
})
})

test('renders the breadcrumbs', async () => {
setup({})

Expand Down Expand Up @@ -572,5 +554,70 @@ describe('DraftPreview', () => {
expect(navigateSpy).toHaveBeenCalledTimes(0)
})
})

describe('when clicking on Publish Draft button with no errors', () => {
test('calls the publish mutation and navigates to the conceptId/revisionId page', async () => {
const navigateSpy = jest.fn()
jest.spyOn(router, 'useNavigate').mockImplementation(() => navigateSpy)

const { user } = setup({
additionalMocks: [{
request: {
query: PUBLISH_DRAFT,
variables: {
draftConceptId: 'TD1000000-MMT',
nativeId: 'MMT_2331e312-cbbc-4e56-9d6f-fe217464be2c',
ummVersion: '1.2.0'
}
},
result: {
data: {
publishDraft: {
conceptId: 'T1000000-MMT',
revisionId: '2'
}
}
}
}]
})

await waitForResponse()

const button = screen.getByRole('button', { name: 'Publish Tool Draft' })
await user.click(button)
await waitForResponse()
expect(navigateSpy).toHaveBeenCalledTimes(1)
expect(navigateSpy).toHaveBeenCalledWith('/tools/T1000000-MMT/2')
})
})

describe('when clicking on Publish Draft button with errors', () => {
test('calls the publish mutation and returns errors', async () => {
const navigateSpy = jest.fn()
jest.spyOn(router, 'useNavigate').mockImplementation(() => navigateSpy)

const { user } = setup({
additionalMocks: [{
request: {
query: PUBLISH_DRAFT,
variables: {
draftConceptId: 'TD1000000-MMT',
nativeId: 'MMT_2331e312-cbbc-4e56-9d6f-fe217464be2c',
ummVersion: '1.2.0'
}
},
error: new Error('#: required key [Name] not found,#: required key [LongName] not found')
}]
})

await waitForResponse()

const button = screen.getByRole('button', { name: 'Publish Tool Draft' })
await user.click(button)
await waitForResponse()
expect(errorLogger).toHaveBeenCalledTimes(1)
expect(errorLogger).toHaveBeenCalledWith('#: required key [Name] not found,#: required key [LongName] not found', 'PublishMutation: publishMutation')
})
})
})
})
Loading