Skip to content

Commit

Permalink
refactor: Refactor modals (#898)
Browse files Browse the repository at this point in the history
* Remove unused line

* Create modalContext

* Update modalContext and add CustomModal component

* Update modalContext w/ setComponent

* Initial display of CustomModal component

* Refactor NewsWidget and RemoveSectionModal

* Updates to refactor CustomCollection and RemoveCustomCollectionModal

* Temporarily add handleRemoveCollection back

* Updates to add AddCustomLinkModal functionality to CustomModal

* Move functionality of EditCustomLinkModal to CustomModal

* Clean up

* Clean up

* Clean up

* Update renderWithModalRoot

* Update props for storybook components

* Update

* Update w/ defaultMockModalContext

* Fix NewsWidget tests

* Update tests

* Update tests

* Remove comments

* Update tests

* Add initial tests for modalContext

* Update test

* Update test

* Fix add bookmark bug and add test in modalContext

* Update tests w/ added mocks

* Update test to save updated bookmark

* Update tests

* Update to click Delete button in modal

* Update test and add test for deleting custom bookmark

* Ignore functions that should not be factored into test coverage

* Add check to close dropdown on modal close

* Update conditional render

* Update test

* Update type

* Remove useRef

* Remove/add comments

* Add comment

* Update test

* Update tests
  • Loading branch information
jcbcapps authored Jan 24, 2023
1 parent cf089d0 commit 1fd5af7
Show file tree
Hide file tree
Showing 25 changed files with 1,253 additions and 731 deletions.
31 changes: 31 additions & 0 deletions src/__fixtures__/operations/addBookmark.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { ObjectId } from 'mongodb'
import { AddBookmarkDocument } from 'operations/portal/mutations/addBookmark.g'

const mockBookmark = {
url: 'example.com',
label: 'My Custom Label',
cmsId: null,
isRemoved: null,
}

export const mockCollectionId = ObjectId()

export const addBookmarkMock = [
{
request: {
query: AddBookmarkDocument,
variables: {
collectionId: mockCollectionId,
url: mockBookmark.url,
label: mockBookmark.label,
},
},
result: {
data: {
_id: ObjectId(),
url: mockBookmark.url,
label: mockBookmark.label,
},
},
},
]
32 changes: 32 additions & 0 deletions src/__fixtures__/operations/editBookmark.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { ObjectId } from 'mongodb'
import { EditBookmarkDocument } from 'operations/portal/mutations/editBookmark.g'
import { Bookmark } from 'types'

export const mockBookmark: Bookmark = {
_id: ObjectId(),
url: 'example.com',
label: 'Custom Label',
}

export const mockCollectionIdForEditBookmark = ObjectId()

export const editBookmarkMock = [
{
request: {
query: EditBookmarkDocument,
variables: {
_id: mockBookmark._id,
collectionId: mockCollectionIdForEditBookmark,
url: mockBookmark.url,
label: mockBookmark.label,
},
},
result: {
data: {
_id: mockBookmark._id,
label: 'Updated Label',
url: mockBookmark.url,
},
},
},
]
27 changes: 27 additions & 0 deletions src/__fixtures__/operations/removeBookmark.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { ObjectId } from 'mongodb'
import { RemoveBookmarkDocument } from 'operations/portal/mutations/removeBookmark.g'

export const mockRemoveBookmark = {
_id: ObjectId(),
url: 'example.com',
label: 'Remove me',
}

export const mockRemoveBookmarkCollectionId = ObjectId()

export const removeBookmarkMock = [
{
request: {
query: RemoveBookmarkDocument,
variables: {
_id: mockRemoveBookmark._id,
collectionId: mockRemoveBookmarkCollectionId,
},
},
result: {
data: {
_id: mockRemoveBookmark._id,
},
},
},
]
26 changes: 26 additions & 0 deletions src/__fixtures__/operations/removeCollection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { ObjectId } from 'mongodb'
import { RemoveCollectionDocument } from 'operations/portal/mutations/removeCollection.g'
import { Collection } from 'types'

export const mockCollection: Collection = {
_id: ObjectId(),
title: 'Test Collection',
type: 'Collection',
bookmarks: [],
}

export const removeCollectionMock = [
{
request: {
query: RemoveCollectionDocument,
variables: {
_id: mockCollection._id,
},
},
result: {
data: {
_id: mockCollection._id,
},
},
},
]
22 changes: 22 additions & 0 deletions src/__fixtures__/operations/removeWidget.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ObjectId } from 'mongodb'
import { RemoveWidgetDocument } from 'operations/portal/mutations/removeWidget.g'

export const mockWidget = {
_id: ObjectId(),
}

export const removeWidgetMock = [
{
request: {
query: RemoveWidgetDocument,
variables: {
_id: mockWidget._id,
},
},
result: {
data: {
_id: mockWidget._id,
},
},
},
]
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@ const testBookmark = {
label: 'Webmail',
}

const testHandlers = {
onSave: jest.fn(),
onDelete: jest.fn(),
}
const testWidgetId = ObjectId()

const testCollectionTitle = 'Test Collection Title'

describe('CustomBookmark component', () => {
afterEach(() => {
Expand All @@ -29,7 +28,11 @@ describe('CustomBookmark component', () => {

it('renders a bookmark with an edit handler', async () => {
renderWithModalRoot(
<CustomBookmark bookmark={testBookmark} {...testHandlers} />
<CustomBookmark
bookmark={testBookmark}
widgetId={testWidgetId}
collectionTitle={testCollectionTitle}
/>
)
await screen.findByText(testBookmark.label)

Expand All @@ -45,76 +48,58 @@ describe('CustomBookmark component', () => {
url: 'https://example.com',
}

render(<CustomBookmark bookmark={testBookmarkNoLabel} {...testHandlers} />)
render(
<CustomBookmark
bookmark={testBookmarkNoLabel}
widgetId={testWidgetId}
collectionTitle={testCollectionTitle}
/>
)
await screen.findByText(testBookmarkNoLabel.url)

expect(screen.getByRole('link')).toHaveTextContent(testBookmarkNoLabel.url)
})

it('can save the bookmark', async () => {
it('calls each function associated with editing a bookmark', async () => {
const user = userEvent.setup()
const mockUpdateModalId = jest.fn()
const mockUpdateModalText = jest.fn()
const mockUpdateWidget = jest.fn()
const mockUpdateBookmark = jest.fn()
const mockModalRef = jest.spyOn(React, 'useRef')

renderWithModalRoot(
<CustomBookmark bookmark={testBookmark} {...testHandlers} />
<CustomBookmark
bookmark={testBookmark}
widgetId={testWidgetId}
collectionTitle={testCollectionTitle}
/>,
{
updateModalId: mockUpdateModalId,
updateModalText: mockUpdateModalText,
updateWidget: mockUpdateWidget,
updateBookmark: mockUpdateBookmark,
modalRef: mockModalRef,
}
)

const editButton = await screen.findByRole('button', {
name: 'Edit this link',
})
await user.click(editButton)
expect(
screen.getByRole('dialog', { name: 'Edit custom link' })
).not.toHaveClass('is-hidden')

const saveButton = screen.getByRole('button', { name: 'Save custom link' })
expect(saveButton).toBeInTheDocument()
await user.click(saveButton)
expect(testHandlers.onSave).toHaveBeenCalled()
})

it('can delete the bookmark', async () => {
const user = userEvent.setup()

renderWithModalRoot(
<CustomBookmark bookmark={testBookmark} {...testHandlers} />
)

const editButton = await screen.findByRole('button', {
name: 'Edit this link',
expect(mockUpdateModalId).toHaveBeenCalledWith('editCustomLinkModal')
expect(mockUpdateModalText).toHaveBeenCalledWith({
headingText: 'Edit custom link',
})
await user.click(editButton)
expect(
screen.getByRole('dialog', { name: 'Edit custom link' })
).not.toHaveClass('is-hidden')

const deleteButton = screen.getByRole('button', { name: 'Delete' })
expect(deleteButton).toBeInTheDocument()
await user.click(deleteButton)
expect(testHandlers.onDelete).toHaveBeenCalled()
})

it('can start editing and cancel', async () => {
const user = userEvent.setup()

renderWithModalRoot(
<CustomBookmark bookmark={testBookmark} {...testHandlers} />
)

const editButton = await screen.findByRole('button', {
name: 'Edit this link',
expect(mockUpdateWidget).toHaveBeenCalledWith({
_id: testWidgetId,
title: testCollectionTitle,
type: 'Collection',
})
await user.click(editButton)
expect(
screen.getByRole('dialog', { name: 'Edit custom link' })
).not.toHaveClass('is-hidden')
expect(mockUpdateBookmark).toHaveBeenCalledWith(testBookmark)

const cancelButton = screen.getByRole('button', { name: 'Cancel' })
expect(cancelButton).toBeInTheDocument()
await user.click(cancelButton)
expect(
screen.getByRole('dialog', { name: 'Edit custom link' })
).toHaveClass('is-hidden')
expect(testHandlers.onSave).not.toHaveBeenCalled()
expect(testHandlers.onDelete).not.toHaveBeenCalled()
expect(mockModalRef).toHaveBeenCalled()
})
})
58 changes: 27 additions & 31 deletions src/components/CustomCollection/CustomBookmark/CustomBookmark.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,42 @@
import React, { useRef } from 'react'
import { ModalRef } from '@trussworks/react-uswds'
import React from 'react'
import type { ObjectId } from 'bson'

import styles from '../CustomCollection.module.scss'

import Bookmark from 'components/Bookmark/Bookmark'
import type { Bookmark as BookmarkType } from 'types/index'
import EditCustomLinkModal from 'components/modals/EditCustomLinkModal'
import { useModalContext } from 'stores/modalContext'

export const CustomBookmark = ({
bookmark,
onSave,
onDelete,
widgetId,
collectionTitle,
}: {
bookmark: BookmarkType
onSave: (url: string, label: string) => void
onDelete: () => void
widgetId: ObjectId
collectionTitle: string
}) => {
const editCustomLinkModal = useRef<ModalRef>(null)
const { url, label } = bookmark

const handleEditLink = () =>
editCustomLinkModal.current?.toggleModal(undefined, true)

const handleSaveLink = (label: string, url: string) => {
editCustomLinkModal.current?.toggleModal(undefined, false)
onSave(url, label)
}

const handleCancel = () =>
editCustomLinkModal.current?.toggleModal(undefined, false)

const handleDeleteLink = () => {
editCustomLinkModal.current?.toggleModal(undefined, false)
onDelete()
const {
updateModalId,
updateModalText,
modalRef,
updateBookmark,
updateWidget,
} = useModalContext()

const handleEditLink = () => {
updateModalId('editCustomLinkModal')
updateModalText({
headingText: 'Edit custom link',
})

// The collectionTitle isn't needed here, but adding it in to maintain the Widget type
// while performing operations against the bookmark
updateWidget({ _id: widgetId, title: collectionTitle, type: 'Collection' })
updateBookmark(bookmark)

modalRef?.current?.toggleModal(undefined, true)
}

return (
Expand All @@ -43,14 +47,6 @@ export const CustomBookmark = ({
className={styles.customLink}>
<span className={styles.customLinkText}>{label || url}</span>
</Bookmark>

<EditCustomLinkModal
bookmark={bookmark}
modalRef={editCustomLinkModal}
onCancel={handleCancel}
onSave={handleSaveLink}
onDelete={handleDeleteLink}
/>
</>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
Alert,
ErrorMessage,
} from '@trussworks/react-uswds'
import { useModalContext } from 'stores/modalContext'

type CustomBookmarkFormProps = {
onSave: (url: string, label: string) => void
Expand All @@ -34,6 +35,8 @@ export const CustomBookmarkForm = ({
// #TODO: Integrate Formik into our forms following the
// wrapper component pattern used in other Truss projects

const { isAddingLinkContext } = useModalContext()

const formik = useFormik({
initialValues: {
// We have to provide label and url as possible initial values
Expand Down Expand Up @@ -147,7 +150,7 @@ export const CustomBookmarkForm = ({
onClick={handleOnCancel}>
Cancel
</Button>
{onDelete && (
{isAddingLinkContext ? null : (
<Button
type="button"
data-close-modal
Expand Down
Loading

0 comments on commit 1fd5af7

Please sign in to comment.