Przeglądaj źródła

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

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

GitOrigin-RevId: 629ac28292978d24323ff2ba53ae1c9987bce9a2
roo hutton 2 lat temu
rodzic
commit
fca6c952f8

+ 10 - 0
services/web/frontend/js/features/share-project-modal/components/restricted-link-sharing/select-collaborators.jsx

@@ -77,6 +77,15 @@ export default function SelectCollaborators({
     return true
   }, [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 {
     isOpen,
     getLabelProps,
@@ -91,6 +100,7 @@ export default function SelectCollaborators({
     defaultHighlightedIndex: 0,
     items: filteredOptions,
     itemToString: item => item && item.name,
+    stateReducer,
     onStateChange: ({ inputValue, type, selectedItem }) => {
       switch (type) {
         // add a selected item on Enter (keypress), click or blur

+ 10 - 0
services/web/frontend/js/features/share-project-modal/components/select-collaborators.jsx

@@ -77,6 +77,15 @@ export default function SelectCollaborators({
     return true
   }, [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 {
     isOpen,
     getLabelProps,
@@ -91,6 +100,7 @@ export default function SelectCollaborators({
     defaultHighlightedIndex: 0,
     items: filteredOptions,
     itemToString: item => item && item.name,
+    stateReducer,
     onStateChange: ({ inputValue, type, selectedItem }) => {
       switch (type) {
         // add a selected item on Enter (keypress), click or blur

+ 92 - 0
services/web/test/frontend/features/share-project-modal/components/share-project-modal.test.jsx

@@ -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
+      )
+    })
+  })
 })