Prechádzať zdrojové kódy

Merge pull request #17192 from overleaf/ii-dictionary-modal

[web] Remove dictionary word only if request is successful

GitOrigin-RevId: 43ffd0dac3ca9729213cf7901f39094c54806412
ilkin-overleaf 2 rokov pred
rodič
commit
6e95e230e8

+ 29 - 26
services/web/frontend/js/features/dictionary/components/dictionary-modal-content.tsx

@@ -1,4 +1,4 @@
-import { useCallback } from 'react'
+import { useCallback, useState } from 'react'
 import { useTranslation } from 'react-i18next'
 import { Alert, Button, Modal } from 'react-bootstrap'
 import Icon from '../../../shared/components/icon'
@@ -18,19 +18,24 @@ export default function DictionaryModalContent({
   handleHide,
 }: DictionaryModalContentProps) {
   const { t } = useTranslation()
+  const [learnedWords, setLearnedWords] = useState(ignoredWords.learnedWords)
 
   const { isError, runAsync } = useAsync()
 
   const handleRemove = useCallback(
     word => {
-      ignoredWords.remove(word)
       runAsync(
         postJSON('/spelling/unlearn', {
           body: {
             word,
           },
         })
-      ).catch(debugConsole.error)
+      )
+        .then(() => {
+          ignoredWords.remove(word)
+          setLearnedWords(new Set(ignoredWords.learnedWords))
+        })
+        .catch(debugConsole.error)
     },
     [runAsync]
   )
@@ -46,31 +51,29 @@ export default function DictionaryModalContent({
           <Alert bsStyle="danger">{t('generic_something_went_wrong')}</Alert>
         ) : null}
 
-        {ignoredWords.learnedWords?.size > 0 ? (
+        {learnedWords?.size > 0 ? (
           <ul className="list-unstyled dictionary-entries-list">
-            {[...ignoredWords.learnedWords]
-              .sort(wordsSortFunction)
-              .map(learnedWord => (
-                <li key={learnedWord} className="dictionary-entry">
-                  <span className="dictionary-entry-name">{learnedWord}</span>
-                  <Tooltip
-                    id={`tooltip-remove-learned-word-${learnedWord}`}
-                    description={t('edit_dictionary_remove')}
-                    overlayProps={{ delay: 0 }}
+            {[...learnedWords].sort(wordsSortFunction).map(learnedWord => (
+              <li key={learnedWord} className="dictionary-entry">
+                <span className="dictionary-entry-name">{learnedWord}</span>
+                <Tooltip
+                  id={`tooltip-remove-learned-word-${learnedWord}`}
+                  description={t('edit_dictionary_remove')}
+                  overlayProps={{ delay: 0 }}
+                >
+                  <Button
+                    bsStyle="danger"
+                    bsSize="xs"
+                    onClick={() => handleRemove(learnedWord)}
                   >
-                    <Button
-                      bsStyle="danger"
-                      bsSize="xs"
-                      onClick={() => handleRemove(learnedWord)}
-                    >
-                      <Icon
-                        type="trash-o"
-                        accessibilityLabel={t('edit_dictionary_remove')}
-                      />
-                    </Button>
-                  </Tooltip>
-                </li>
-              ))}
+                    <Icon
+                      type="trash-o"
+                      accessibilityLabel={t('edit_dictionary_remove')}
+                    />
+                  </Button>
+                </Tooltip>
+              </li>
+            ))}
           </ul>
         ) : (
           <p className="dictionary-empty-body text-center">

+ 10 - 4
services/web/test/frontend/features/dictionary/components/dictionary-modal-content.test.jsx

@@ -1,5 +1,8 @@
-import { screen, fireEvent } from '@testing-library/react'
-import { expect } from 'chai'
+import {
+  screen,
+  fireEvent,
+  waitForElementToBeRemoved,
+} from '@testing-library/react'
 import fetchMock from 'fetch-mock'
 import DictionaryModal from '../../../../../frontend/js/features/dictionary/components/dictionary-modal'
 import { renderWithEditorContext } from '../../../helpers/render-with-context'
@@ -16,6 +19,7 @@ describe('<DictionaryModalContent />', function () {
   afterEach(function () {
     window.metaAttributesCache = new Map()
     fetchMock.reset()
+    window.dispatchEvent(new CustomEvent('learnedWords:doreset'))
   })
 
   it('list words', async function () {
@@ -35,12 +39,13 @@ describe('<DictionaryModalContent />', function () {
     fetchMock.post('/spelling/unlearn', 200)
     setLearnedWords(['Foo', 'bar'])
     renderWithEditorContext(<DictionaryModal show handleHide={() => {}} />)
+    screen.getByText('Foo')
     screen.getByText('bar')
     const [firstButton] = screen.getAllByRole('button', {
       name: 'Remove from dictionary',
     })
     fireEvent.click(firstButton)
-    expect(screen.queryByText('bar')).to.not.exist
+    await waitForElementToBeRemoved(() => screen.getByText('bar'))
     screen.getByText('Foo')
   })
 
@@ -48,12 +53,13 @@ describe('<DictionaryModalContent />', function () {
     fetchMock.post('/spelling/unlearn', 500)
     setLearnedWords(['foo'])
     renderWithEditorContext(<DictionaryModal show handleHide={() => {}} />)
+    screen.getByText('foo')
     const [firstButton] = screen.getAllByRole('button', {
       name: 'Remove from dictionary',
     })
     fireEvent.click(firstButton)
     await fetchMock.flush()
     screen.getByText('Sorry, something went wrong')
-    screen.getByText('Your custom dictionary is empty.')
+    screen.getByText('foo')
   })
 })