From 4bd86c8bb6fd8750ad64d5a70d3926a558bde6e0 Mon Sep 17 00:00:00 2001 From: Hari Krishna Date: Tue, 2 Jan 2024 17:04:52 +0000 Subject: [PATCH 1/5] Enhance swap-langs button to swap both languages and text inside boxes --- .../translator/LanguageSelector.tsx | 16 ++++++- .../translator/TextTranslationForm.tsx | 18 ++++---- src/components/translator/Translator.tsx | 24 +++++++++- .../__tests__/LanguageSelector.test.tsx | 4 +- .../__tests__/TextTranslationForm.test.tsx | 44 ++++++++++++++++++- 5 files changed, 92 insertions(+), 14 deletions(-) diff --git a/src/components/translator/LanguageSelector.tsx b/src/components/translator/LanguageSelector.tsx index 23cec8d8f..7d26e94f5 100644 --- a/src/components/translator/LanguageSelector.tsx +++ b/src/components/translator/LanguageSelector.tsx @@ -28,6 +28,7 @@ export type Props = { tgtLang: string; setTgtLang: (code: string) => void; recentTgtLangs: Array; + swapLangText: () => void; detectLangEnabled: boolean; detectedLang: string | null; @@ -423,7 +424,17 @@ const DesktopLanguageSelector = ({ }; const LanguageSelector = (props: Props): React.ReactElement => { - const { pairs, srcLang, setSrcLang, recentSrcLangs, setRecentSrcLangs, tgtLang, setTgtLang, setDetectedLang } = props; + const { + pairs, + srcLang, + setSrcLang, + recentSrcLangs, + setRecentSrcLangs, + tgtLang, + setTgtLang, + setDetectedLang, + swapLangText, + } = props; const swapLangs = React.useMemo( () => @@ -431,9 +442,10 @@ const LanguageSelector = (props: Props): React.ReactElement => { ? () => { setSrcLang(tgtLang); setTgtLang(srcLang); + swapLangText(); } : undefined, - [pairs, setSrcLang, setTgtLang, srcLang, tgtLang], + [pairs, setSrcLang, setTgtLang, srcLang, tgtLang, swapLangText], ); const [detectingLang, setDetectingLang] = React.useState(false); diff --git a/src/components/translator/TextTranslationForm.tsx b/src/components/translator/TextTranslationForm.tsx index 061b76aa8..277bfadca 100644 --- a/src/components/translator/TextTranslationForm.tsx +++ b/src/components/translator/TextTranslationForm.tsx @@ -11,11 +11,10 @@ import { useHistory } from 'react-router-dom'; import { useMatomo } from '@datapunt/matomo-tracker-react'; import { DetectCompleteEvent, DetectEvent, PairPrefValues, TranslateEvent, baseUrlParams } from '.'; -import { MaxURLLength, buildNewSearch, getUrlParam } from '../../util/url'; +import { MaxURLLength, buildNewSearch } from '../../util/url'; import { APyContext } from '../../context'; import { buildUrl as buildWebpageTranslationUrl } from './WebpageTranslationForm'; import { langDirection } from '../../util/languages'; -import useLocalStorage from '../../util/useLocalStorage'; import { useLocalization } from '../../util/localization'; const textUrlParam = 'q'; @@ -36,6 +35,10 @@ export type Props = { markUnknown: boolean; pairPrefs: PairPrefValues; setLoading: (loading: boolean) => void; + srcText: string; + tgtText: string; + setSrcText: (text: string) => void; + setTgtText: (text: string) => void; }; const TextTranslationForm = ({ @@ -45,6 +48,10 @@ const TextTranslationForm = ({ instantTranslation, pairPrefs, setLoading, + srcText, + tgtText, + setSrcText, + setTgtText, }: Props): React.ReactElement => { const { t } = useLocalization(); const history = useHistory(); @@ -54,11 +61,6 @@ const TextTranslationForm = ({ const srcTextareaRef = React.useRef(null); const tgtTextareaRef = React.useRef(null); - const [srcText, setSrcText] = useLocalStorage('srcText', '', { - overrideValue: getUrlParam(history.location.search, textUrlParam), - }); - const [tgtText, setTgtText] = React.useState(''); - React.useEffect(() => { const baseParams = baseUrlParams({ srcLang, tgtLang }); let search = buildNewSearch({ ...baseParams, [textUrlParam]: srcText }); @@ -130,7 +132,7 @@ const TextTranslationForm = ({ } })(); }, - [apyFetch, markUnknown, prefs, setLoading, srcLang, srcText, tgtLang, trackEvent], + [apyFetch, markUnknown, prefs, setLoading, setTgtText, srcLang, srcText, tgtLang, trackEvent], ); const translationTimer = React.useRef(null); diff --git a/src/components/translator/Translator.tsx b/src/components/translator/Translator.tsx index 2dadf31bf..776543a26 100644 --- a/src/components/translator/Translator.tsx +++ b/src/components/translator/Translator.tsx @@ -243,11 +243,16 @@ const WithTgtLang = ({ const Translator = ({ mode: initialMode }: { mode?: Mode }): React.ReactElement => { const mode: Mode = initialMode || Mode.Text; + const textUrlParam = 'q'; const { t } = useLocalization(); const history = useHistory(); const config = React.useContext(ConfigContext); const [loading, setLoading] = React.useState(false); + const [srcText, setSrcText] = useLocalStorage('srcText', '', { + overrideValue: getUrlParam(history.location.search, textUrlParam), + }); + const [tgtText, setTgtText] = React.useState(''); const [markUnknown, setMarkUnknown] = useLocalStorage('markUnknown', false); const [instantTranslation, setInstantTranslation] = useLocalStorage('instantTranslation', true); @@ -272,6 +277,11 @@ const Translator = ({ mode: initialMode }: { mode?: Mode }): React.ReactElement const onTranslate = React.useCallback(() => window.dispatchEvent(new Event(TranslateEvent)), []); + const swapLangText = () => { + setSrcText(tgtText); + setTgtText(srcText); + }; + return (
{(mode === Mode.Text || !mode) && ( <> diff --git a/src/components/translator/__tests__/LanguageSelector.test.tsx b/src/components/translator/__tests__/LanguageSelector.test.tsx index 7b6b9e20c..16869a793 100644 --- a/src/components/translator/__tests__/LanguageSelector.test.tsx +++ b/src/components/translator/__tests__/LanguageSelector.test.tsx @@ -23,6 +23,7 @@ const renderLanguageSelector = (props_: Partial = {}): Props => { detectLangEnabled: true, detectedLang: null, setDetectedLang: jest.fn(), + swapLangText: jest.fn(), ...props_, }; @@ -90,7 +91,7 @@ describe('swapping', () => { }); it('allow swapping when swapped pair valid', () => { - const { srcLang, tgtLang, setSrcLang, setTgtLang } = renderLanguageSelector({ + const { srcLang, tgtLang, setSrcLang, setTgtLang, swapLangText } = renderLanguageSelector({ tgtLang: 'spa', }); @@ -98,6 +99,7 @@ describe('swapping', () => { expect(setSrcLang).toHaveBeenCalledWith(tgtLang); expect(setTgtLang).toHaveBeenCalledWith(srcLang); + expect(swapLangText).toHaveBeenCalled(); }); }); diff --git a/src/components/translator/__tests__/TextTranslationForm.test.tsx b/src/components/translator/__tests__/TextTranslationForm.test.tsx index 82ccc1557..112c900fe 100644 --- a/src/components/translator/__tests__/TextTranslationForm.test.tsx +++ b/src/components/translator/__tests__/TextTranslationForm.test.tsx @@ -7,6 +7,10 @@ import userEvent from '@testing-library/user-event'; import { DetectCompleteEvent, DetectEvent, TranslateEvent } from '..'; import TextTranslationForm, { Props } from '../TextTranslationForm'; +import { getUrlParam } from '../../../util/url'; +import useLocalStorage from '../../../util/useLocalStorage'; + +const textUrlParam = 'q'; const renderTextTranslationForm = ( props_: Partial = {}, @@ -20,13 +24,34 @@ const renderTextTranslationForm = ( srcLang: 'eng', tgtLang: 'spa', pairPrefs: {}, + srcText: '', + tgtText: '', + setSrcText: jest.fn(), + setTgtText: jest.fn(), setLoading: jest.fn(), ...props_, }; + const Wrapper = () => { + const [srcText, setSrcText] = useLocalStorage('srcText', '', { + overrideValue: getUrlParam(history.location.search, textUrlParam), + }); + const [tgtText, setTgtText] = React.useState(''); + + return ( + + ); + }; + render( - + , ); @@ -149,16 +174,31 @@ describe('translation', () => { srcLang: 'eng', tgtLang: 'spa', pairPrefs: {}, + srcText: '', + tgtText: '', + setSrcText: jest.fn(), + setTgtText: jest.fn(), setLoading: jest.fn(), }; const Container = () => { const [srcLang, setSrcLang] = React.useState('eng'); + const [srcText, setSrcText] = useLocalStorage('srcText', '', { + overrideValue: getUrlParam(history.location.search, textUrlParam), + }); + const [tgtText, setTgtText] = React.useState(''); return ( <> - + ); From 86a85cde0fb5fa5943b9abf9128245ca03e4ef4c Mon Sep 17 00:00:00 2001 From: Hari Krishna Date: Mon, 8 Jan 2024 06:37:55 +0000 Subject: [PATCH 2/5] updated Translator.tsx --- src/components/translator/Translator.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/translator/Translator.tsx b/src/components/translator/Translator.tsx index 776543a26..85431ed7d 100644 --- a/src/components/translator/Translator.tsx +++ b/src/components/translator/Translator.tsx @@ -34,6 +34,7 @@ import useLocalStorage from '../../util/useLocalStorage'; import { useLocalization } from '../../util/localization'; const recentLangsCount = 3; +const textUrlParam = 'q'; const defaultSrcLang = (pairs: Pairs): string => { const browserLangs = window.navigator.languages; @@ -243,7 +244,6 @@ const WithTgtLang = ({ const Translator = ({ mode: initialMode }: { mode?: Mode }): React.ReactElement => { const mode: Mode = initialMode || Mode.Text; - const textUrlParam = 'q'; const { t } = useLocalization(); const history = useHistory(); const config = React.useContext(ConfigContext); @@ -277,9 +277,12 @@ const Translator = ({ mode: initialMode }: { mode?: Mode }): React.ReactElement const onTranslate = React.useCallback(() => window.dispatchEvent(new Event(TranslateEvent)), []); - const swapLangText = () => { + const swapLangs = React.useCallback(() => { setSrcText(tgtText); - setTgtText(srcText); + }, [setSrcText, tgtText]); + + const swapLangText = () => { + swapLangs(); }; return ( From dd4e28bfe06fe22db3cb77b142d1baf0ced918ac Mon Sep 17 00:00:00 2001 From: Hari Krishna Date: Tue, 9 Jan 2024 10:34:19 +0000 Subject: [PATCH 3/5] exported TexUrlParam --- src/components/translator/TextTranslationForm.tsx | 2 +- src/components/translator/Translator.tsx | 1 + .../translator/__tests__/TextTranslationForm.test.tsx | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/translator/TextTranslationForm.tsx b/src/components/translator/TextTranslationForm.tsx index 277bfadca..0e9f9fb9e 100644 --- a/src/components/translator/TextTranslationForm.tsx +++ b/src/components/translator/TextTranslationForm.tsx @@ -16,8 +16,8 @@ import { APyContext } from '../../context'; import { buildUrl as buildWebpageTranslationUrl } from './WebpageTranslationForm'; import { langDirection } from '../../util/languages'; import { useLocalization } from '../../util/localization'; +import { textUrlParam } from './Translator'; -const textUrlParam = 'q'; const instantTranslationPunctuationDelay = 1000, instantTranslationDelay = 3000; diff --git a/src/components/translator/Translator.tsx b/src/components/translator/Translator.tsx index 85431ed7d..ed03cca88 100644 --- a/src/components/translator/Translator.tsx +++ b/src/components/translator/Translator.tsx @@ -402,4 +402,5 @@ const Translator = ({ mode: initialMode }: { mode?: Mode }): React.ReactElement ); }; +export { textUrlParam }; export default Translator; diff --git a/src/components/translator/__tests__/TextTranslationForm.test.tsx b/src/components/translator/__tests__/TextTranslationForm.test.tsx index 112c900fe..79ed98091 100644 --- a/src/components/translator/__tests__/TextTranslationForm.test.tsx +++ b/src/components/translator/__tests__/TextTranslationForm.test.tsx @@ -8,9 +8,9 @@ import userEvent from '@testing-library/user-event'; import { DetectCompleteEvent, DetectEvent, TranslateEvent } from '..'; import TextTranslationForm, { Props } from '../TextTranslationForm'; import { getUrlParam } from '../../../util/url'; +import { textUrlParam } from '../Translator'; import useLocalStorage from '../../../util/useLocalStorage'; -const textUrlParam = 'q'; const renderTextTranslationForm = ( props_: Partial = {}, From 77e73cf3dc17b40603aadd7015a3136d91a21d1a Mon Sep 17 00:00:00 2001 From: Hari Krishna Date: Tue, 9 Jan 2024 10:37:55 +0000 Subject: [PATCH 4/5] fixed lint errors --- src/components/translator/TextTranslationForm.tsx | 3 +-- src/components/translator/Translator.tsx | 2 +- .../translator/__tests__/TextTranslationForm.test.tsx | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/translator/TextTranslationForm.tsx b/src/components/translator/TextTranslationForm.tsx index 0e9f9fb9e..3e3f7306b 100644 --- a/src/components/translator/TextTranslationForm.tsx +++ b/src/components/translator/TextTranslationForm.tsx @@ -15,9 +15,8 @@ import { MaxURLLength, buildNewSearch } from '../../util/url'; import { APyContext } from '../../context'; import { buildUrl as buildWebpageTranslationUrl } from './WebpageTranslationForm'; import { langDirection } from '../../util/languages'; -import { useLocalization } from '../../util/localization'; import { textUrlParam } from './Translator'; - +import { useLocalization } from '../../util/localization'; const instantTranslationPunctuationDelay = 1000, instantTranslationDelay = 3000; diff --git a/src/components/translator/Translator.tsx b/src/components/translator/Translator.tsx index ed03cca88..f324e198c 100644 --- a/src/components/translator/Translator.tsx +++ b/src/components/translator/Translator.tsx @@ -402,5 +402,5 @@ const Translator = ({ mode: initialMode }: { mode?: Mode }): React.ReactElement ); }; -export { textUrlParam }; +export { textUrlParam }; export default Translator; diff --git a/src/components/translator/__tests__/TextTranslationForm.test.tsx b/src/components/translator/__tests__/TextTranslationForm.test.tsx index 79ed98091..3834daadf 100644 --- a/src/components/translator/__tests__/TextTranslationForm.test.tsx +++ b/src/components/translator/__tests__/TextTranslationForm.test.tsx @@ -11,7 +11,6 @@ import { getUrlParam } from '../../../util/url'; import { textUrlParam } from '../Translator'; import useLocalStorage from '../../../util/useLocalStorage'; - const renderTextTranslationForm = ( props_: Partial = {}, historyOptions?: MemoryHistoryBuildOptions, From 9af1056f565ea687e0dab0ba59398e276e11a711 Mon Sep 17 00:00:00 2001 From: Hari Krishna Date: Fri, 12 Jan 2024 14:18:04 +0000 Subject: [PATCH 5/5] moved swapLangs from LanguageSelector to Translator --- .../translator/LanguageSelector.tsx | 27 +------------- src/components/translator/Translator.tsx | 37 ++++++++++++------- .../__tests__/LanguageSelector.test.tsx | 10 ++--- 3 files changed, 30 insertions(+), 44 deletions(-) diff --git a/src/components/translator/LanguageSelector.tsx b/src/components/translator/LanguageSelector.tsx index 7d26e94f5..1f51e5e04 100644 --- a/src/components/translator/LanguageSelector.tsx +++ b/src/components/translator/LanguageSelector.tsx @@ -28,7 +28,7 @@ export type Props = { tgtLang: string; setTgtLang: (code: string) => void; recentTgtLangs: Array; - swapLangText: () => void; + swapLangs?: () => void; detectLangEnabled: boolean; detectedLang: string | null; @@ -38,7 +38,6 @@ export type Props = { type SharedProps = Props & { srcLangs: NamedLangs; tgtLangs: NamedLangs; - swapLangs?: () => void; detectingLang: boolean; setDetectingLang: (detecting: boolean) => void; onDetectLang: () => void; @@ -424,29 +423,7 @@ const DesktopLanguageSelector = ({ }; const LanguageSelector = (props: Props): React.ReactElement => { - const { - pairs, - srcLang, - setSrcLang, - recentSrcLangs, - setRecentSrcLangs, - tgtLang, - setTgtLang, - setDetectedLang, - swapLangText, - } = props; - - const swapLangs = React.useMemo( - () => - isPair(pairs, tgtLang, srcLang) - ? () => { - setSrcLang(tgtLang); - setTgtLang(srcLang); - swapLangText(); - } - : undefined, - [pairs, setSrcLang, setTgtLang, srcLang, tgtLang, swapLangText], - ); + const { pairs, srcLang, setSrcLang, recentSrcLangs, setRecentSrcLangs, setDetectedLang, swapLangs } = props; const [detectingLang, setDetectingLang] = React.useState(false); diff --git a/src/components/translator/Translator.tsx b/src/components/translator/Translator.tsx index f324e198c..c26616869 100644 --- a/src/components/translator/Translator.tsx +++ b/src/components/translator/Translator.tsx @@ -151,11 +151,15 @@ type WithTgtLangsProps = { setRecentTgtLangs: (langs: Array) => void; pairPrefs: PairPrefValues; setPairPrefs: (prefs: PairPrefValues) => void; + swapLangs?: () => void; }; const WithTgtLang = ({ pairs, srcLang, + tgtText, + setSrcText, + setSrcLang, urlTgtLang, selectedPrefs, setSelectedPrefs, @@ -163,6 +167,9 @@ const WithTgtLang = ({ }: { pairs: Pairs; srcLang: string; + tgtText: string; + setSrcText: (text: string) => void; + setSrcLang: (lang: string) => void; urlTgtLang: string | null; selectedPrefs: Record; setSelectedPrefs: (prefs: Record) => void; @@ -237,8 +244,18 @@ const WithTgtLang = ({ }, [pair, selectedPrefs, setSelectedPrefs], ); - - return children({ tgtLang, setTgtLang, recentTgtLangs, setRecentTgtLangs, pairPrefs, setPairPrefs }); + const swapLangs = React.useMemo( + () => + isPair(pairs, tgtLang, srcLang) + ? () => { + setSrcLang(tgtLang); + setTgtLang(srcLang); + setSrcText(tgtText); + } + : undefined, + [pairs, setSrcLang, setTgtLang, setSrcText, srcLang, tgtLang, tgtText], + ); + return children({ tgtLang, setTgtLang, recentTgtLangs, setRecentTgtLangs, pairPrefs, setPairPrefs, swapLangs }); }; const Translator = ({ mode: initialMode }: { mode?: Mode }): React.ReactElement => { @@ -277,14 +294,6 @@ const Translator = ({ mode: initialMode }: { mode?: Mode }): React.ReactElement const onTranslate = React.useCallback(() => window.dispatchEvent(new Event(TranslateEvent)), []); - const swapLangs = React.useCallback(() => { - setSrcText(tgtText); - }, [setSrcText, tgtText]); - - const swapLangText = () => { - swapLangs(); - }; - return ( ( - - {({ tgtLang, setTgtLang, recentTgtLangs, pairPrefs, setPairPrefs }: WithTgtLangsProps) => ( + + {({ tgtLang, setTgtLang, recentTgtLangs, pairPrefs, setPairPrefs, swapLangs }: WithTgtLangsProps) => ( <> {(mode === Mode.Text || !mode) && ( diff --git a/src/components/translator/__tests__/LanguageSelector.test.tsx b/src/components/translator/__tests__/LanguageSelector.test.tsx index 16869a793..cd1829218 100644 --- a/src/components/translator/__tests__/LanguageSelector.test.tsx +++ b/src/components/translator/__tests__/LanguageSelector.test.tsx @@ -23,7 +23,7 @@ const renderLanguageSelector = (props_: Partial = {}): Props => { detectLangEnabled: true, detectedLang: null, setDetectedLang: jest.fn(), - swapLangText: jest.fn(), + swapLangs: jest.fn(), ...props_, }; @@ -85,21 +85,19 @@ it('switches between mobile and desktop', () => { describe('swapping', () => { it('does not allow swapping when swapped pair invalid', () => { - renderLanguageSelector(); + renderLanguageSelector({ swapLangs: undefined }); expect((screen.getByTestId('swap-langs-button') as HTMLButtonElement).disabled).toBeTruthy(); }); it('allow swapping when swapped pair valid', () => { - const { srcLang, tgtLang, setSrcLang, setTgtLang, swapLangText } = renderLanguageSelector({ + const { swapLangs } = renderLanguageSelector({ tgtLang: 'spa', }); userEvent.click(screen.getByTestId('swap-langs-button')); - expect(setSrcLang).toHaveBeenCalledWith(tgtLang); - expect(setTgtLang).toHaveBeenCalledWith(srcLang); - expect(swapLangText).toHaveBeenCalled(); + expect(swapLangs).toHaveBeenCalled(); }); });