From af3a0df7ba3de17aaaab2647ba252223e2f5a6e3 Mon Sep 17 00:00:00 2001 From: Aniket Katkar <51777795+aniketkatkar97@users.noreply.github.com> Date: Thu, 10 Nov 2022 19:47:23 +0530 Subject: [PATCH] Fix(UI) #8549: Fixed issue with URL in the description breaking due to characters limit. (#8604) * - Fixed issue where url was breaking in the description preview due to character limit - Added localization in RichTextEditorPreviewer * changed the limit of max description characters to show in preview * Fixed failing unit tests * Worked on comments * - Fixed errors - Improved the function to trim the description for preview * Fixed failing unit tests --- .../RichTextEditorPreviewer.test.tsx | 119 +++++++++++++++--- .../RichTextEditorPreviewer.tsx | 12 +- .../resources/ui/src/constants/constants.ts | 1 + .../ui/src/locale/languages/en-us.json | 2 + .../resources/ui/src/utils/CommonUtils.tsx | 24 ++++ 5 files changed, 139 insertions(+), 19 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/rich-text-editor/RichTextEditorPreviewer.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/rich-text-editor/RichTextEditorPreviewer.test.tsx index a4d4fbeb5f5..4d5d688f443 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/rich-text-editor/RichTextEditorPreviewer.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/rich-text-editor/RichTextEditorPreviewer.test.tsx @@ -14,6 +14,7 @@ import { findByTestId, fireEvent, render } from '@testing-library/react'; import React from 'react'; import { MemoryRouter } from 'react-router-dom'; +import { act } from 'react-test-renderer'; import RichTextEditorPreviewer from './RichTextEditorPreviewer'; const mockDescription = @@ -71,7 +72,9 @@ describe('Test RichTextEditor Previewer Component', () => { expect(readMoreButton).toBeInTheDocument(); - fireEvent.click(readMoreButton); + act(() => { + fireEvent.click(readMoreButton); + }); expect(markdownParser.querySelector('del')).toBeInTheDocument(); @@ -89,9 +92,21 @@ describe('Test RichTextEditor Previewer Component', () => { const heading2 = markdownParser.querySelector('h2'); const heading3 = markdownParser.querySelector('h3'); - expect(heading1).toBeInTheDocument(); - expect(heading2).toBeInTheDocument(); - expect(heading3).toBeInTheDocument(); + expect(heading1).not.toBeInTheDocument(); + expect(heading2).not.toBeInTheDocument(); + expect(heading3).not.toBeInTheDocument(); + + const readMoreButton = await findByTestId(container, 'read-more-button'); + + expect(readMoreButton).toBeInTheDocument(); + + act(() => { + fireEvent.click(readMoreButton); + }); + + expect(markdownParser.querySelector('h1')).toBeInTheDocument(); + expect(markdownParser.querySelector('h2')).toBeInTheDocument(); + expect(markdownParser.querySelector('h3')).toBeInTheDocument(); expect(markdownParser).toBeInTheDocument(); }); @@ -105,7 +120,17 @@ describe('Test RichTextEditor Previewer Component', () => { const italicMarkdown = markdownParser.querySelector('em'); - expect(italicMarkdown).toBeInTheDocument(); + expect(italicMarkdown).not.toBeInTheDocument(); + + const readMoreButton = await findByTestId(container, 'read-more-button'); + + expect(readMoreButton).toBeInTheDocument(); + + act(() => { + fireEvent.click(readMoreButton); + }); + + expect(markdownParser.querySelector('em')).toBeInTheDocument(); expect(markdownParser).toBeInTheDocument(); }); @@ -119,7 +144,17 @@ describe('Test RichTextEditor Previewer Component', () => { const blockquoteMarkdown = markdownParser.querySelector('blockquote'); - expect(blockquoteMarkdown).toBeInTheDocument(); + expect(blockquoteMarkdown).not.toBeInTheDocument(); + + const readMoreButton = await findByTestId(container, 'read-more-button'); + + expect(readMoreButton).toBeInTheDocument(); + + act(() => { + fireEvent.click(readMoreButton); + }); + + expect(markdownParser.querySelector('blockquote')).toBeInTheDocument(); expect(markdownParser).toBeInTheDocument(); }); @@ -133,7 +168,17 @@ describe('Test RichTextEditor Previewer Component', () => { const orderedList = markdownParser.querySelector('ol'); - expect(orderedList).toBeInTheDocument(); + expect(orderedList).not.toBeInTheDocument(); + + const readMoreButton = await findByTestId(container, 'read-more-button'); + + expect(readMoreButton).toBeInTheDocument(); + + act(() => { + fireEvent.click(readMoreButton); + }); + + expect(markdownParser.querySelector('ol')).toBeInTheDocument(); expect(markdownParser).toBeInTheDocument(); }); @@ -147,7 +192,17 @@ describe('Test RichTextEditor Previewer Component', () => { const unorderedList = markdownParser.querySelector('ul'); - expect(unorderedList).toBeInTheDocument(); + expect(unorderedList).not.toBeInTheDocument(); + + const readMoreButton = await findByTestId(container, 'read-more-button'); + + expect(readMoreButton).toBeInTheDocument(); + + act(() => { + fireEvent.click(readMoreButton); + }); + + expect(markdownParser.querySelector('ul')).toBeInTheDocument(); expect(markdownParser).toBeInTheDocument(); }); @@ -161,7 +216,17 @@ describe('Test RichTextEditor Previewer Component', () => { const code = markdownParser.querySelector('code'); - expect(code).toBeInTheDocument(); + expect(code).not.toBeInTheDocument(); + + const readMoreButton = await findByTestId(container, 'read-more-button'); + + expect(readMoreButton).toBeInTheDocument(); + + act(() => { + fireEvent.click(readMoreButton); + }); + + expect(markdownParser.querySelector('code')).toBeInTheDocument(); expect(markdownParser).toBeInTheDocument(); }); @@ -179,7 +244,9 @@ describe('Test RichTextEditor Previewer Component', () => { expect(readMoreButton).toBeInTheDocument(); - fireEvent.click(readMoreButton); + act(() => { + fireEvent.click(readMoreButton); + }); expect(markdownParser.querySelector('pre')).toBeInTheDocument(); @@ -195,7 +262,17 @@ describe('Test RichTextEditor Previewer Component', () => { const horizontalRule = markdownParser.querySelector('hr'); - expect(horizontalRule).toBeInTheDocument(); + expect(horizontalRule).not.toBeInTheDocument(); + + const readMoreButton = await findByTestId(container, 'read-more-button'); + + expect(readMoreButton).toBeInTheDocument(); + + act(() => { + fireEvent.click(readMoreButton); + }); + + expect(markdownParser.querySelector('hr')).toBeInTheDocument(); expect(markdownParser).toBeInTheDocument(); }); @@ -209,7 +286,17 @@ describe('Test RichTextEditor Previewer Component', () => { const link = markdownParser.querySelector('a'); - expect(link).toBeInTheDocument(); + expect(link).toBeNull(); + + const readMoreButton = await findByTestId(container, 'read-more-button'); + + expect(readMoreButton).toBeInTheDocument(); + + act(() => { + fireEvent.click(readMoreButton); + }); + + expect(markdownParser.querySelector('a')).toBeInTheDocument(); expect(markdownParser).toBeInTheDocument(); }); @@ -227,7 +314,9 @@ describe('Test RichTextEditor Previewer Component', () => { expect(readMoreButton).toBeInTheDocument(); - fireEvent.click(readMoreButton); + act(() => { + fireEvent.click(readMoreButton); + }); expect(markdownParser.querySelector('img')).toBeInTheDocument(); @@ -247,7 +336,9 @@ describe('Test RichTextEditor Previewer Component', () => { expect(readMoreButton).toBeInTheDocument(); - fireEvent.click(readMoreButton); + act(() => { + fireEvent.click(readMoreButton); + }); expect(markdownParser.querySelector('table')).toBeInTheDocument(); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/rich-text-editor/RichTextEditorPreviewer.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/rich-text-editor/RichTextEditorPreviewer.tsx index 809b0f935c1..3cc65d4bfce 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/rich-text-editor/RichTextEditorPreviewer.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/rich-text-editor/RichTextEditorPreviewer.tsx @@ -16,18 +16,20 @@ import { Button } from 'antd'; import classNames from 'classnames'; import { uniqueId } from 'lodash'; import React, { useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { DESCRIPTION_MAX_PREVIEW_CHARACTERS } from '../../../constants/constants'; +import { getTrimmedContent } from '../../../utils/CommonUtils'; import { PreviewerProp } from './RichTextEditor.interface'; import './RichTextEditorPreviewer.less'; -export const MAX_LENGTH = 350; - const RichTextEditorPreviewer = ({ markdown = '', className = '', enableSeeMoreVariant = true, textVariant = 'black', - maxLength = MAX_LENGTH, + maxLength = DESCRIPTION_MAX_PREVIEW_CHARACTERS, }: PreviewerProp) => { + const { t } = useTranslation(); const [content, setContent] = useState(''); const [hideReadMoreText, setHideReadMoreText] = useState( markdown.length <= maxLength @@ -53,7 +55,7 @@ const RichTextEditorPreviewer = ({ initialValue={ hideReadMoreText || !enableSeeMoreVariant ? content - : `${content.slice(0, maxLength)}...` + : `${getTrimmedContent(content, maxLength)}...` } key={uniqueId()} /> @@ -64,7 +66,7 @@ const RichTextEditorPreviewer = ({ data-testid="read-more-button" type="link" onClick={displayMoreHandler}> - {hideReadMoreText ? 'read less' : 'read more'} + {hideReadMoreText ? t('label.read-less') : t('label.read-more')} )} diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts index af71a063537..b50c6115398 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts @@ -45,6 +45,7 @@ export const ADD_USER_CONTAINER_HEIGHT = 250; export const INGESTION_PROGRESS_START_VAL = 20; export const INGESTION_PROGRESS_END_VAL = 80; export const DEPLOYED_PROGRESS_VAL = 100; +export const DESCRIPTION_MAX_PREVIEW_CHARACTERS = 350; export const MAX_CHAR_LIMIT_ENTITY_SUMMARY = 130; export const LOCALSTORAGE_RECENTLY_VIEWED = `recentlyViewedData_${COOKIE_VERSION}`; export const LOCALSTORAGE_RECENTLY_SEARCHED = `recentlySearchedData_${COOKIE_VERSION}`; diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json index 961eb5fec73..c35cd7c88f2 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json @@ -224,6 +224,8 @@ "pipeline": "Pipeline", "function": "Function", "edge-information": "Edge Information", + "read-more": "read more", + "read-less": "read less", "no-owner": "No Owner", "page-views-by-entities": "Page views by datasets", "daily-active-user": "Daily active users on the platform", diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.tsx index 42332f92a5b..5942f7b4fec 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.tsx @@ -1027,3 +1027,27 @@ export const getTagValue = (tag: string | TagLabel): string | TagLabel => { }; } }; + +export const getTrimmedContent = (content: string, limit: number) => { + const lines = content.split('\n'); + // Selecting the content in three lines + const contentInThreeLines = lines.slice(0, 3).join('\n'); + + const slicedContent = contentInThreeLines.slice(0, limit); + + // Logic for eliminating any broken words at the end + // To avoid any URL being cut + const words = slicedContent.split(' '); + const wordsCount = words.length; + + if (wordsCount === 1) { + // In case of only one word (possibly too long URL) + // return the whole word instead of trimming + return content.split(' ')[0]; + } + + // Eliminate word at the end to avoid using broken words + const refinedContent = words.slice(0, wordsCount - 1); + + return refinedContent.join(' '); +};