Skip to content

Commit

Permalink
Merge pull request #44953 from software-mansion-labs/@cdOut/code-repo…
Browse files Browse the repository at this point in the history
…rt-fixed

[Android / iOS] Code blocks are overflowing the app border
  • Loading branch information
puneetlath authored Oct 31, 2024
2 parents 91214d3 + 8b76cc9 commit 26e9523
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 3 deletions.
54 changes: 51 additions & 3 deletions src/components/InlineCodeBlock/WrappedText.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import React, {Fragment} from 'react';
import React, {Fragment, useMemo} from 'react';
import type {StyleProp, TextStyle, ViewStyle} from 'react-native';
import {View} from 'react-native';
import Text from '@components/Text';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import {containsOnlyEmojis} from '@libs/EmojiUtils';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import type ChildrenProps from '@src/types/utils/ChildrenProps';

Expand Down Expand Up @@ -40,15 +42,59 @@ function containsEmoji(text: string): boolean {
return CONST.REGEX.EMOJIS.test(text);
}

/**
* Takes a long word and splits it into an array of sub-strings.
*
* The function tests whether the length of the provided word exceeds the provided maximum length.
* If the word's length is less than or equal to `maxLength`, it returns an array with the original word.
* If the word's length exceeds 'maxLength', it utilizes a regular expression to split the word into
* substrings with a specified 'maxLength' and returns them as an array of strings.
*
* @param word The original word to be split.
* @param maxLength The maximum length of each substring.
* @return An array of substrings derived from the original word.
*
* @example
* splitLongWord('longteststring', 4);
* // Output: ['long', 'test', 'stri', 'ng']
*/
function splitLongWord(word: string, maxLength: number): string[] {
if (word.length <= maxLength) {
return [word];
}

return word.match(new RegExp(`.{1,${maxLength}}`, 'g')) ?? [];
}

function getFontSizeFromStyles(textStyles: StyleProp<TextStyle>): number {
if (Array.isArray(textStyles)) {
for (const style of textStyles) {
if (style && 'fontSize' in style && style.fontSize) {
return style.fontSize;
}
}
} else if (textStyles && 'fontSize' in textStyles && textStyles.fontSize) {
return textStyles.fontSize;
}

// if we cannot infer fontSize from styles, a default value is returned
return variables.fontSizeLabel;
}

function WrappedText({children, wordStyles, textStyles}: WrappedTextProps) {
const styles = useThemeStyles();
const {windowWidth} = useWindowDimensions();

const fontSize = useMemo(() => getFontSizeFromStyles(textStyles), [textStyles]);
const childrenString = typeof children === 'string' ? children : '';
const charsPerLine = useMemo(() => Math.floor(windowWidth / (fontSize * variables.fontSizeToWidthRatio)), [windowWidth, fontSize]);

const textMatrix = getTextMatrix(childrenString).map((row) => row.flatMap((word) => splitLongWord(word, charsPerLine)));

if (typeof children !== 'string') {
return null;
}

const textMatrix = getTextMatrix(children);

return textMatrix.map((rowText, rowIndex) => (
<Fragment
// eslint-disable-next-line react/no-array-index-key
Expand Down Expand Up @@ -87,3 +133,5 @@ function WrappedText({children, wordStyles, textStyles}: WrappedTextProps) {
WrappedText.displayName = 'WrappedText';

export default WrappedText;

export {splitLongWord};
1 change: 1 addition & 0 deletions src/styles/variables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ export default {
restrictedActionIllustrationHeight: 136,
photoUploadPopoverWidth: 335,
onboardingModalWidth: 500,
fontSizeToWidthRatio: getValueUsingPixelRatio(0.8, 1),

// The height of the empty list is 14px (2px for borders and 12px for vertical padding)
// This is calculated based on the values specified in the 'getGoogleListViewStyle' function of the 'StyleUtils' utility
Expand Down
47 changes: 47 additions & 0 deletions tests/unit/splitLongWordTest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import {splitLongWord} from '@components/InlineCodeBlock/WrappedText';

describe('splitLongWord', () => {
const testCases = [
{
word: 'thissadasdasdsadsadasdadsadasdasdasdasdasdasdasdasdasdsadsadggggggggggggggggg',
maxLength: 4,
output: ['this', 'sada', 'sdas', 'dsad', 'sada', 'sdad', 'sada', 'sdas', 'dasd', 'asda', 'sdas', 'dasd', 'asda', 'sdsa', 'dsad', 'gggg', 'gggg', 'gggg', 'gggg', 'g'],
},
{
word: 'https://www.google.com/search?q=google&oq=goog&gs_lcrp=EgZjaHJvbWUqEAgAEAAYgwEY4wIYsQMYgAQyEAgAEAAYgwEY4wIYsQMYgAQyEwgBEC4YgwEYxwEYsQMY0QMYgAQyDQgCEAAYgwEYsQMYgAQyBggDEEUYOzIGCAQQRRg8MgYIBRBFGDwyBggGEEUYPDIGCAcQBRhA0gEHNzM1ajBqN6gCALACAA&sourceid=chrome&ie=UTF-8',
maxLength: 20,
output: [
'https://www.google.c',
'om/search?q=google&o',
'q=goog&gs_lcrp=EgZja',
'HJvbWUqEAgAEAAYgwEY4',
'wIYsQMYgAQyEAgAEAAYg',
'wEY4wIYsQMYgAQyEwgBE',
'C4YgwEYxwEYsQMY0QMYg',
'AQyDQgCEAAYgwEYsQMYg',
'AQyBggDEEUYOzIGCAQQR',
'Rg8MgYIBRBFGDwyBggGE',
'EUYPDIGCAcQBRhA0gEHN',
'zM1ajBqN6gCALACAA&so',
'urceid=chrome&ie=UTF',
'-8',
],
},
{
word: 'superkalifragilistischexpialigetisch',
maxLength: 5,
output: ['super', 'kalif', 'ragil', 'istis', 'chexp', 'ialig', 'etisc', 'h'],
},
{
word: 'Este es un ejemplo de texto en español para la prueba',
maxLength: 8,
output: ['Este es ', 'un ejemp', 'lo de te', 'xto en e', 'spañol p', 'ara la p', 'rueba'],
},
];

testCases.forEach(({word, maxLength, output}) => {
test(`should split ${word} into ${output.join()} with maxLength of ${maxLength}`, () => {
expect(splitLongWord(word, maxLength)).toEqual(output);
});
});
});

0 comments on commit 26e9523

Please sign in to comment.