Merge pull request #19391 from overleaf/rh-readd-collaborator

[web] Re-add collaborator email after removed from invite input

GitOrigin-RevId: 629ac28292978d24323ff2ba53ae1c9987bce9a2
This commit is contained in:
roo hutton 2024-07-22 14:13:04 +01:00 committed by Copybot
parent bfc6ac8745
commit fca6c952f8
3 changed files with 112 additions and 0 deletions

View file

@ -77,6 +77,15 @@ export default function SelectCollaborators({
return true return true
}, [inputValue, selectedItems]) }, [inputValue, selectedItems])
function stateReducer(state, actionAndChanges) {
const { type, changes } = actionAndChanges
// force selected item to be null so that adding, removing, then re-adding the same collaborator is recognised as a selection change
if (type === useCombobox.stateChangeTypes.InputChange) {
return { ...changes, selectedItem: null }
}
return changes
}
const { const {
isOpen, isOpen,
getLabelProps, getLabelProps,
@ -91,6 +100,7 @@ export default function SelectCollaborators({
defaultHighlightedIndex: 0, defaultHighlightedIndex: 0,
items: filteredOptions, items: filteredOptions,
itemToString: item => item && item.name, itemToString: item => item && item.name,
stateReducer,
onStateChange: ({ inputValue, type, selectedItem }) => { onStateChange: ({ inputValue, type, selectedItem }) => {
switch (type) { switch (type) {
// add a selected item on Enter (keypress), click or blur // add a selected item on Enter (keypress), click or blur

View file

@ -77,6 +77,15 @@ export default function SelectCollaborators({
return true return true
}, [inputValue, selectedItems]) }, [inputValue, selectedItems])
function stateReducer(state, actionAndChanges) {
const { type, changes } = actionAndChanges
// force selected item to be null so that adding, removing, then re-adding the same collaborator is recognised as a selection change
if (type === useCombobox.stateChangeTypes.InputChange) {
return { ...changes, selectedItem: null }
}
return changes
}
const { const {
isOpen, isOpen,
getLabelProps, getLabelProps,
@ -91,6 +100,7 @@ export default function SelectCollaborators({
defaultHighlightedIndex: 0, defaultHighlightedIndex: 0,
items: filteredOptions, items: filteredOptions,
itemToString: item => item && item.name, itemToString: item => item && item.name,
stateReducer,
onStateChange: ({ inputValue, type, selectedItem }) => { onStateChange: ({ inputValue, type, selectedItem }) => {
switch (type) { switch (type) {
// add a selected item on Enter (keypress), click or blur // add a selected item on Enter (keypress), click or blur

View file

@ -888,4 +888,96 @@ describe('<ShareProjectModal/>', function () {
) )
}) })
}) })
it('selects contact by typing a partial email and selecting the suggestion', async function () {
renderWithEditorContext(<ShareProjectModal {...modalProps} />, {
scope: { project },
})
const [inputElement] = await screen.findAllByLabelText(
'Share with your collaborators'
)
// Wait for contacts to load
await waitFor(() => {
expect(fetchMock.called('express:/user/contacts')).to.be.true
})
// Enter a prefix that matches a contact
await userEvent.type(inputElement, 'pto')
// The matching contact should now be present and selected
await userEvent.click(
screen.getByRole('option', {
name: `Claudius Ptolemy <ptolemy@example.com>`,
selected: true,
})
)
// Click anywhere on the form to blur the input
await userEvent.click(screen.getByRole('dialog'))
// The contact should be added
await waitFor(() => {
expect(screen.getAllByRole('button', { name: 'Remove' })).to.have.length(
1
)
})
})
it('allows an email address to be selected, removed, then re-added', async function () {
renderWithEditorContext(<ShareProjectModal {...modalProps} />, {
scope: { project },
})
const [inputElement] = await screen.findAllByLabelText(
'Share with your collaborators'
)
// Wait for contacts to load
await waitFor(() => {
expect(fetchMock.called('express:/user/contacts')).to.be.true
})
// Enter a prefix that matches a contact
await userEvent.type(inputElement, 'pto')
// Select the suggested contact
await userEvent.click(
screen.getByRole('option', {
name: `Claudius Ptolemy <ptolemy@example.com>`,
selected: true,
})
)
// Click anywhere on the form to blur the input
await userEvent.click(screen.getByRole('dialog'))
// Remove the just-added collaborator
await userEvent.click(screen.getByRole('button', { name: 'Remove' }))
// Remove button should now be gone
expect(screen.queryByRole('button', { name: 'Remove' })).to.be.null
// Add the same collaborator again
await userEvent.type(inputElement, 'pto')
// Click the suggested contact again
await userEvent.click(
screen.getByRole('option', {
name: `Claudius Ptolemy <ptolemy@example.com>`,
selected: true,
})
)
// Click anywhere on the form to blur the input
await userEvent.click(screen.getByRole('dialog'))
// The contact should be added
await waitFor(() => {
expect(screen.getAllByRole('button', { name: 'Remove' })).to.have.length(
1
)
})
})
}) })