Skip to content

Improve memoization of processedMarkdownStyle on web #684

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,6 @@ lib/

# react-native-live-markdown
.build_complete

# jest
coverage/
14 changes: 13 additions & 1 deletion WebExample/__tests__/textManipulation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
});
9 changes: 8 additions & 1 deletion WebExample/__tests__/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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};
24 changes: 11 additions & 13 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,19 @@ export default function App() {
};
}, [textColorState, textFontSizeState]);

const markdownStyle = React.useMemo(() => {
return {
emoji: {
fontSize: emojiFontSizeState ? 15 : 20,
},
link: {
color: linkColorState ? 'red' : 'blue',
},
};
}, [emojiFontSizeState, linkColorState]);
const markdownStyle = {
emoji: {
fontSize: emojiFontSizeState ? 15 : 20,
},
link: {
color: linkColorState ? 'red' : 'blue',
},
};

const ref = React.useRef<MarkdownTextInput>(null);

return (
<ScrollView
contentContainerStyle={styles.container}
style={styles.content}>
<ScrollView contentContainerStyle={styles.container} style={styles.content}>
<PlatformInfo />
<MarkdownTextInput
multiline={multiline}
Expand Down Expand Up @@ -109,6 +105,7 @@ export default function App() {
onPress={() => setTextColorState(prev => !prev)}
/>
<Button
testID={TEST_CONST.TOGGLE_LINK_COLOR}
title="Toggle link color"
onPress={() => setLinkColorState(prev => !prev)}
/>
Expand All @@ -135,6 +132,7 @@ export default function App() {
}}
/>
<Button
testID={TEST_CONST.CHANGE_SELECTION}
title="Change selection"
onPress={() => {
if (!ref.current) {
Expand Down
13 changes: 12 additions & 1 deletion example/src/testConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,16 @@ const EXAMPLE_CONTENT = [

const INPUT_ID = 'MarkdownInput_Example';
const INPUT_HISTORY_DEBOUNCE_TIME_MS = 150;
const TOGGLE_LINK_COLOR = 'toggle-link-color';
const CHANGE_SELECTION = 'change-selection';
const SELECTION_END = 20;

export {LOCAL_URL, EXAMPLE_CONTENT, INPUT_ID, INPUT_HISTORY_DEBOUNCE_TIME_MS};
export {
LOCAL_URL,
EXAMPLE_CONTENT,
INPUT_ID,
INPUT_HISTORY_DEBOUNCE_TIME_MS,
TOGGLE_LINK_COLOR,
CHANGE_SELECTION,
SELECTION_END,
};
5 changes: 4 additions & 1 deletion src/MarkdownTextInput.web.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ const MarkdownTextInput = React.forwardRef<MarkdownTextInput, MarkdownTextInputP
}

const flattenedStyle = useMemo(() => 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]);
Expand Down Expand Up @@ -200,7 +202,8 @@ const MarkdownTextInput = React.forwardRef<MarkdownTextInput, MarkdownTextInputP
parseText(parser, divRef.current, divRef.current.value, newMarkdownStyle, null, false, false);
}
return newMarkdownStyle;
}, [parser, markdownStyle, parseText]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [hashedMarkdownStyle, parser, parseText]);

const inputStyles = useMemo(
() =>
Expand Down