From bfb5c8a260b2d501e3d1ddd7b6edb41c760cf712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Musia=C5=82?= Date: Wed, 28 May 2025 12:16:12 +0200 Subject: [PATCH 1/3] fix text selection needs use memo on web --- .gitignore | 3 +++ example/src/App.tsx | 20 ++++++++------------ src/MarkdownTextInput.web.tsx | 5 ++++- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index d6cadc5d..b0b314aa 100644 --- a/.gitignore +++ b/.gitignore @@ -79,3 +79,6 @@ lib/ # react-native-live-markdown .build_complete + +# jest +coverage/ diff --git a/example/src/App.tsx b/example/src/App.tsx index 729436cd..32792ed5 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -25,23 +25,19 @@ export default function App() { }; }, [textColorState, textFontSizeState]); - const markdownStyle = React.useMemo(() => { - return { - emoji: { - fontSize: emojiFontSizeState ? 15 : 20, - }, - link: { + const markdownStyle = { + emoji: { + fontSize: emojiFontSizeState ? 15 : 20, + }, + link: { color: linkColorState ? 'red' : 'blue', - }, - }; - }, [emojiFontSizeState, linkColorState]); + }, + }; const ref = React.useRef(null); return ( - + StyleSheet.flatten(style), [style]); + // Using JSON.stringify(flattenedMarkdownStyle) as a simple styles object hash to avoid rerenders when not memoized markdownStyle is passed + const hashedMarkdownStyle = useMemo(() => JSON.stringify(StyleSheet.flatten(markdownStyle)), [markdownStyle]); // Empty placeholder would collapse the div, so we need to use zero-width space to prevent it const heightSafePlaceholder = useMemo(() => getPlaceholderValue(placeholder), [placeholder]); @@ -200,7 +202,8 @@ const MarkdownTextInput = React.forwardRef From 65d16c75a4a25eb9d29d1aae2870851dffc4be0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Musia=C5=82?= Date: Wed, 28 May 2025 12:32:29 +0200 Subject: [PATCH 2/3] add the test from previous PR --- WebExample/__tests__/textManipulation.spec.ts | 14 +++++++++++++- WebExample/__tests__/utils.ts | 9 ++++++++- example/src/App.tsx | 4 +++- example/src/testConstants.ts | 13 ++++++++++++- 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/WebExample/__tests__/textManipulation.spec.ts b/WebExample/__tests__/textManipulation.spec.ts index 748d237e..c9cce0fa 100644 --- a/WebExample/__tests__/textManipulation.spec.ts +++ b/WebExample/__tests__/textManipulation.spec.ts @@ -2,7 +2,7 @@ import {test, expect} from '@playwright/test'; import type {Locator, Page} from '@playwright/test'; // eslint-disable-next-line import/no-relative-packages import * as TEST_CONST from '../../example/src/testConstants'; -import {getCursorPosition, setupInput, getElementStyle, pressCmd, getElementValue} from './utils'; +import {getCursorPosition, setupInput, getElementStyle, pressCmd, getElementValue, setSelection, changeMarkdownStyle} from './utils'; const pasteContent = async ({text, page, inputLocator}: {text: string; page: Page; inputLocator: Locator}) => { await page.evaluate(async (pasteText) => navigator.clipboard.writeText(pasteText), text); @@ -136,3 +136,15 @@ test('cut content changes', async ({page, browserName}) => { const spans = await inputLocator.locator('span[data-type="text"]'); expect(await spans.count()).toBe(1); }); + +test('keep selection when changing markdown style', async ({page}) => { + const inputLocator = await setupInput(page, 'reset'); + + await setSelection(page); + await changeMarkdownStyle(page); + await inputLocator.focus(); + + const cursorPosition = await getCursorPosition(inputLocator); + + expect(cursorPosition.end).toBe(TEST_CONST.SELECTION_END); +}); diff --git a/WebExample/__tests__/utils.ts b/WebExample/__tests__/utils.ts index af23dd7c..3c4121f0 100644 --- a/WebExample/__tests__/utils.ts +++ b/WebExample/__tests__/utils.ts @@ -65,4 +65,11 @@ const getElementValue = async (elementHandle: Locator) => { return value; }; -export {setupInput, getCursorPosition, setCursorPosition, getElementStyle, pressCmd, getElementValue}; +const changeMarkdownStyle = async (page: Page) => { + await page.click(`[data-testid="${TEST_CONST.TOGGLE_LINK_COLOR}"]`); +}; + +const setSelection = async (page: Page) => { + await page.click(`[data-testid="${TEST_CONST.CHANGE_SELECTION}"]`); +}; +export {setupInput, getCursorPosition, setCursorPosition, getElementStyle, pressCmd, getElementValue, changeMarkdownStyle, setSelection}; diff --git a/example/src/App.tsx b/example/src/App.tsx index 32792ed5..1fb62852 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -30,7 +30,7 @@ export default function App() { fontSize: emojiFontSizeState ? 15 : 20, }, link: { - color: linkColorState ? 'red' : 'blue', + color: linkColorState ? 'red' : 'blue', }, }; @@ -105,6 +105,7 @@ export default function App() { onPress={() => setTextColorState(prev => !prev)} />