Skip to content

Commit

Permalink
feat: Editable inline component for Collection Title (#268)
Browse files Browse the repository at this point in the history
* Update CustomCollection story with handleEditCollection

* Update CustomCollection test with handleEditCollection

* Fix save and local resolver

* Add max length to input

* CSS tweaks

* Add tests for editable title component

* Add test for editCollection to myspace

* Update home page test to reflect new heading button

* Updating test coverage

* Add cypress test for editing title


Co-authored-by: Suzanne Rozier <[email protected]>
  • Loading branch information
abbyoung and Suzanne Rozier authored Oct 19, 2021
1 parent 16d1956 commit 15c4ee5
Show file tree
Hide file tree
Showing 13 changed files with 320 additions and 19 deletions.
7 changes: 6 additions & 1 deletion cypress/integration/sites-and-applications.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,12 @@ describe('Sites and Applications', () => {
name: 'My Custom Link (opens in a new window)',
}).should('exist')
})

it('can edit an existing collection title', () => {
cy.contains('Example Collection').click()
cy.findByRole('textbox').clear()
cy.findByRole('textbox').type('Updated Title{enter}')
cy.findByRole('button', { name: 'Updated Title' }).should('exist')
})
it('can delete an existing collection', () => {
cy.contains('Example Collection')
.parent()
Expand Down
3 changes: 1 addition & 2 deletions src/__tests__/pages/beta/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,7 @@ describe('Beta Home page', () => {
})
)
expect(
await screen.findByRole('heading', {
level: 3,
await screen.findByRole('button', {
name: 'Example Collection',
})
)
Expand Down
6 changes: 3 additions & 3 deletions src/components/Collection/Collection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ import React from 'react'
import styles from './Collection.module.scss'

type PropTypes = {
title: React.ReactNode
title?: string
children: React.ReactNode | React.ReactNode[]
footer?: React.ReactNode
header?: React.ReactNode
}

const Collection = ({ title, children, header, footer }: PropTypes) => {
const Collection = ({ title = '', children, header, footer }: PropTypes) => {
return (
<div className={styles.collection}>
<div className={styles.header}>
<h3>{title}</h3>
{title && <h3>{title}</h3>}
{header}
</div>

Expand Down
15 changes: 15 additions & 0 deletions src/components/CustomCollection/CustomCollection.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,18 @@ button.collectionSettingsDropdown {
border: none;
cursor: pointer;
}

h3.collectionTitle {
font-size: font-size('heading', 7);
line-height: lh('heading', 2);
@include u-padding(1);
@include u-margin-left(-1);
border: 2px transparent solid;
flex-grow: 1;
}

input[type='text'].collectionTitle {
font-size: font-size('heading', 7);
line-height: lh('heading', 2);
margin: 0;
}
3 changes: 3 additions & 0 deletions src/components/CustomCollection/CustomCollection.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ type StorybookArgTypes = {
handleAddBookmark: () => void
handleRemoveBookmark: () => void
handleRemoveCollection: () => void
handleEditCollection: () => void
}

export default {
Expand All @@ -15,6 +16,7 @@ export default {
handleAddBookmark: { action: 'Add bookmark' },
handleRemoveBookmark: { action: 'Remove bookmark' },
handleRemoveCollection: { action: 'Remove collection' },
handleEditCollection: { action: 'Edit collection' },
},
} as Meta

Expand All @@ -25,5 +27,6 @@ export const ExampleCustomCollection = (argTypes: StorybookArgTypes) => (
handleAddBookmark={argTypes.handleAddBookmark}
handleRemoveBookmark={argTypes.handleRemoveBookmark}
handleRemoveCollection={argTypes.handleRemoveCollection}
handleEditCollection={argTypes.handleEditCollection}
/>
)
20 changes: 16 additions & 4 deletions src/components/CustomCollection/CustomCollection.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,13 @@ describe('CustomCollection component', () => {
handleRemoveBookmark={jest.fn()}
handleAddBookmark={jest.fn()}
handleRemoveCollection={jest.fn()}
handleEditCollection={jest.fn()}
/>
)
expect(screen.getByRole('list')).toBeInTheDocument()
expect(screen.getByRole('heading', { level: 3 })).toHaveTextContent(
exampleCollection.title
)
expect(
screen.getByRole('button', { name: exampleCollection.title })
).toBeInTheDocument()
expect(screen.getAllByRole('listitem')).toHaveLength(
exampleCollection.bookmarks.length
)
Expand All @@ -87,6 +88,7 @@ describe('CustomCollection component', () => {
handleRemoveBookmark={jest.fn()}
handleAddBookmark={jest.fn()}
handleRemoveCollection={jest.fn()}
handleEditCollection={jest.fn()}
/>
)

Expand Down Expand Up @@ -116,6 +118,7 @@ describe('CustomCollection component', () => {
handleRemoveBookmark={jest.fn()}
handleAddBookmark={mockAddLink}
handleRemoveCollection={jest.fn()}
handleEditCollection={jest.fn()}
/>
)

Expand Down Expand Up @@ -153,6 +156,7 @@ describe('CustomCollection component', () => {
handleRemoveBookmark={jest.fn()}
handleAddBookmark={mockAddLink}
handleRemoveCollection={jest.fn()}
handleEditCollection={jest.fn()}
/>
)

Expand Down Expand Up @@ -195,6 +199,7 @@ describe('CustomCollection component', () => {
handleRemoveBookmark={jest.fn()}
handleAddBookmark={mockAddLink}
handleRemoveCollection={jest.fn()}
handleEditCollection={jest.fn()}
/>
)

Expand Down Expand Up @@ -274,6 +279,7 @@ describe('CustomCollection component', () => {
handleRemoveBookmark={jest.fn()}
handleAddBookmark={mockAddLink}
handleRemoveCollection={jest.fn()}
handleEditCollection={jest.fn()}
/>
)

Expand All @@ -297,6 +303,7 @@ describe('CustomCollection component', () => {
handleRemoveBookmark={jest.fn()}
handleAddBookmark={jest.fn()}
handleRemoveCollection={jest.fn()}
handleEditCollection={jest.fn()}
/>
)
const menuToggleButton = screen.getByRole('button', {
Expand All @@ -318,6 +325,7 @@ describe('CustomCollection component', () => {
handleRemoveBookmark={jest.fn()}
handleAddBookmark={jest.fn()}
handleRemoveCollection={mockRemoveCollection}
handleEditCollection={jest.fn()}
/>
)

Expand Down Expand Up @@ -348,6 +356,7 @@ describe('CustomCollection component', () => {
handleRemoveBookmark={jest.fn()}
handleAddBookmark={jest.fn()}
handleRemoveCollection={mockRemoveCollection}
handleEditCollection={jest.fn()}
/>
)

Expand Down Expand Up @@ -392,6 +401,7 @@ describe('CustomCollection component', () => {
handleRemoveBookmark={jest.fn()}
handleAddBookmark={jest.fn()}
handleRemoveCollection={mockRemoveCollection}
handleEditCollection={jest.fn()}
/>
)
expect(screen.queryByRole('dialog', removeCollectionDialog)).toHaveClass(
Expand Down Expand Up @@ -430,6 +440,7 @@ describe('CustomCollection component', () => {
handleRemoveBookmark={jest.fn()}
handleAddBookmark={jest.fn()}
handleRemoveCollection={mockRemoveCollection}
handleEditCollection={jest.fn()}
/>
)

Expand All @@ -447,7 +458,7 @@ describe('CustomCollection component', () => {
expect(menuToggleButton).toBeInTheDocument()

// Click outside menu
const outsideEl = screen.getByRole('heading', {
const outsideEl = screen.getByRole('button', {
name: 'Example Collection',
})
userEvent.click(outsideEl)
Expand All @@ -465,6 +476,7 @@ describe('CustomCollection component', () => {
handleRemoveBookmark={jest.fn()}
handleAddBookmark={jest.fn()}
handleRemoveCollection={mockRemoveCollection}
handleEditCollection={jest.fn()}
/>
)

Expand Down
19 changes: 11 additions & 8 deletions src/components/CustomCollection/CustomCollection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
ComboBoxOption,
} from '@trussworks/react-uswds'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'

import { EditableCollectionTitle } from './EditableCollectionTitle'
import { RemovableBookmark } from './RemovableBookmark'
import styles from './CustomCollection.module.scss'

Expand All @@ -26,6 +26,7 @@ type PropTypes = {
handleRemoveBookmark: (id: string) => void
handleAddBookmark: (url: string, label?: string) => void
handleRemoveCollection: () => void
handleEditCollection: (title: string) => void
}

const CustomCollection = ({
Expand All @@ -35,6 +36,7 @@ const CustomCollection = ({
handleRemoveBookmark,
handleAddBookmark,
handleRemoveCollection,
handleEditCollection,
}: PropTypes) => {
const [isAdding, setIsAdding] = useState<boolean>(false)
const urlInputValue = useRef<string>('')
Expand Down Expand Up @@ -175,10 +177,14 @@ const CustomCollection = ({
</>
)

// Component and modal for Settings to pass as
// Custom Collection header
const customCollectionSettings = (
const customCollectionHeader = (
<>
<EditableCollectionTitle
text={title}
placeholder={'Add a title for this collection'}
onSave={handleEditCollection}
/>

<DropdownMenu
toggleEl={
<button
Expand All @@ -203,10 +209,7 @@ const CustomCollection = ({

return (
<>
<Collection
title={title}
header={customCollectionSettings}
footer={addLinkForm}>
<Collection header={customCollectionHeader} footer={addLinkForm}>
{bookmarks.map((bookmark: BookmarkType) => (
<RemovableBookmark
key={`bookmark_${bookmark.id}`}
Expand Down
105 changes: 105 additions & 0 deletions src/components/CustomCollection/EditableCollectionTitle.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/**
* @jest-environment jsdom
*/

import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import React from 'react'

import { EditableCollectionTitle } from './EditableCollectionTitle'

describe('EditableCollectionTitle component', () => {
it('renders an editable collection title', () => {
render(
<EditableCollectionTitle
text="Test Collection"
placeholder="Add Collection Title"
onSave={jest.fn()}
/>
)

expect(
screen.getByRole('button', { name: 'Test Collection' })
).toBeInTheDocument()
})

it('renders a placeholder title if no title is passed', () => {
render(
<EditableCollectionTitle
text=""
placeholder="Add Collection Title"
onSave={jest.fn()}
/>
)

expect(
screen.getByRole('button', { name: 'Add Collection Title' })
).toBeInTheDocument()
})

it('renders an input when button is clicked', () => {
render(
<EditableCollectionTitle
text="Test Collection"
placeholder="Add Collection Title"
onSave={jest.fn()}
/>
)
const editTitle = screen.getByRole('button')
userEvent.click(editTitle)
const input = screen.getByRole('textbox')
expect(input).toBeInTheDocument()
})

it('calls save function on enter', () => {
const handleOnSave = jest.fn()
render(
<EditableCollectionTitle
text="Test Collection"
placeholder="Add Collection Title"
onSave={handleOnSave}
/>
)
const editTitle = screen.getByRole('button')
userEvent.click(editTitle)
const input = screen.getByRole('textbox')
userEvent.click(input)
userEvent.type(input, 'Updated Title {enter}')
expect(handleOnSave).toHaveBeenCalled()
})

it('calls save function on esc', () => {
const handleOnSave = jest.fn()
render(
<EditableCollectionTitle
text="Test Collection"
placeholder="Add Collection Title"
onSave={handleOnSave}
/>
)
const editTitle = screen.getByRole('button')
userEvent.click(editTitle)
const input = screen.getByRole('textbox')
userEvent.click(input)
userEvent.type(input, 'Updated Title {esc}')
expect(handleOnSave).toHaveBeenCalled()
})

it('calls save function on tab', () => {
const handleOnSave = jest.fn()
render(
<EditableCollectionTitle
text="Test Collection"
placeholder="Add Collection Title"
onSave={handleOnSave}
/>
)
const editTitle = screen.getByRole('button')
userEvent.click(editTitle)
const input = screen.getByRole('textbox')
userEvent.click(input)
userEvent.type(input, 'Updated Title')
userEvent.tab()
expect(handleOnSave).toHaveBeenCalled()
})
})
Loading

0 comments on commit 15c4ee5

Please sign in to comment.