From 094894d492c70e0cbb9becb4fcba3ce6f68809a5 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Wed, 15 May 2024 11:50:27 +0200 Subject: [PATCH] feat: slot for markdown links Fix #957 --- .../shared/HOC/slottableComponent.tsx | 2 + src/components/shared/MDLabel/MDLabel.tsx | 54 +++++++++-------- src/components/shared/MDLabel/MDLabelLink.tsx | 59 ------------------- .../shared/MDLabel/MarkdownLink.tsx | 44 ++++++++++++++ src/stories/utils/orchestrator.jsx | 17 +++--- src/stories/utils/waiting/index.js | 1 - src/stories/utils/waiting/preloader.svg | 1 - src/stories/utils/waiting/waiting.jsx | 21 ------- src/stories/utils/waiting/waiting.scss | 21 ------- 9 files changed, 82 insertions(+), 138 deletions(-) delete mode 100644 src/components/shared/MDLabel/MDLabelLink.tsx create mode 100644 src/components/shared/MDLabel/MarkdownLink.tsx delete mode 100644 src/stories/utils/waiting/index.js delete mode 100644 src/stories/utils/waiting/preloader.svg delete mode 100644 src/stories/utils/waiting/waiting.jsx delete mode 100644 src/stories/utils/waiting/waiting.scss diff --git a/src/components/shared/HOC/slottableComponent.tsx b/src/components/shared/HOC/slottableComponent.tsx index d2f07581a..5a29ad22b 100644 --- a/src/components/shared/HOC/slottableComponent.tsx +++ b/src/components/shared/HOC/slottableComponent.tsx @@ -49,6 +49,7 @@ import type { CustomCheckboxGroup } from '../../CheckboxGroup/CustomCheckboxGrou import type { RouterLink } from '../MDLabel/RouterLink'; import type { SummaryResponses, SummaryTitle } from '../../Summary/Summary'; import type { LunaticComponentProps } from '../../type'; +import type { MarkdownLink } from '../MDLabel/MarkdownLink'; /** * Contains the type of every customizable component @@ -119,6 +120,7 @@ export type LunaticSlotComponents = { ComponentWrapper: ComponentType< PropsWithChildren >; + MarkdownLink: typeof MarkdownLink; }; const empty = {} as Partial | undefined; diff --git a/src/components/shared/MDLabel/MDLabel.tsx b/src/components/shared/MDLabel/MDLabel.tsx index fd7dd3208..b3529fbcc 100644 --- a/src/components/shared/MDLabel/MDLabel.tsx +++ b/src/components/shared/MDLabel/MDLabel.tsx @@ -1,39 +1,41 @@ -import type { ComponentProps } from 'react'; -import Markdown from 'react-markdown'; -import { MDLabelLink } from './MDLabelLink'; +import { + type ComponentProps, + Fragment, + type PropsWithChildren, + type ReactNode, +} from 'react'; +import Markdown, { type Components } from 'react-markdown'; +import { MarkdownLink } from './MarkdownLink'; import remarkBreaks from 'remark-breaks'; import emoji from 'remark-emoji'; type Props = { expression: string }; -export const MDLabel = ({ expression }: Props) => { +export function MDLabel({ expression }: Props) { + const hasParagraphs = /\n\n/.test(expression); + const components = { + p: hasParagraphs ? 'p' : Fragment, + br: 'br', + a: MarkdownA, + } as Partial; return ( {expression} ); -}; - -const renderComponentsFor = ( - expression: string -): ComponentProps['components'] => { - const components = { - p: (props) => <>{props.children}, - br: 'br', - a: (props) => ( - )} /> - ), - } satisfies ComponentProps['components']; - - if (/\n\n/.test(expression)) { - return { - ...components, - p: 'p', - }; - } - - return components; +} +const MarkdownA = ({ + title, + href, + children, +}: PropsWithChildren<{ title?: string; href: string }>) => { + const tooltip = title ? : null; + return ( + + {children} + + ); }; diff --git a/src/components/shared/MDLabel/MDLabelLink.tsx b/src/components/shared/MDLabel/MDLabelLink.tsx deleted file mode 100644 index dda135148..000000000 --- a/src/components/shared/MDLabel/MDLabelLink.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { useState, type PropsWithChildren } from 'react'; -import { Tooltip } from 'react-tooltip'; -import Markdown from 'react-markdown'; -import { RouterLink } from './RouterLink'; - -type Props = PropsWithChildren<{ href: string; title: string }>; - -export const MDLabelLink = (props: Props) => { - const { - href, - children, - title, - // logFunction - } = props; - - // Must be replace by useId when queen move to React 18. - const [id] = useState(() => generateUniqueId()); - - const LinkComponent: React.FC<{ - to?: string; - href?: string; - id: string; - 'data-tooltip-id'?: string; - }> = (() => { - if (href.trim().startsWith('/')) { - return RouterLink; - } else { - return 'a' as any; - } - })(); - - const linkProps = { - ...(href.trim().startsWith('/') - ? { to: href, id } - : { href, target: '_blank', rel: 'noopener noreferrer', id }), - ...(title - ? { 'data-tooltip-id': `tooltip-${id}`, className: 'link-md' } - : {}), - }; - - return ( - <> - {children} - {title && ( - - <>{props.children} }}> - {title} - - - )} - - ); -}; - -export function generateUniqueId(): string { - const timestamp = Date.now().toString(36); - const randomPart = Math.random().toString(36).substring(2, 8); - return `${timestamp}-${randomPart}`; -} diff --git a/src/components/shared/MDLabel/MarkdownLink.tsx b/src/components/shared/MDLabel/MarkdownLink.tsx new file mode 100644 index 000000000..2c4645328 --- /dev/null +++ b/src/components/shared/MDLabel/MarkdownLink.tsx @@ -0,0 +1,44 @@ +import { useState, type PropsWithChildren, type ReactNode } from 'react'; +import { Tooltip } from 'react-tooltip'; +import { RouterLink } from './RouterLink'; +import { slottableComponent } from '../HOC/slottableComponent'; + +type Props = PropsWithChildren<{ href: string; tooltip?: ReactNode }>; + +export const MarkdownLink = slottableComponent( + 'MarkdownLink', + ({ href, children, tooltip }) => { + // Must be replace by useId when queen move to React 18. + const [id] = useState(() => generateUniqueId()); + + const extraProps = tooltip + ? { 'data-tooltip-id': `tooltip-${id}`, className: 'link-md' } + : {}; + const isAbsoluteLink = href.trim().startsWith('/'); + + return ( + <> + {isAbsoluteLink ? ( + + {children} + + ) : ( + + {children} + + )} + {tooltip && ( + + {tooltip} + + )} + + ); + } +); + +export function generateUniqueId(): string { + const timestamp = Date.now().toString(36); + const randomPart = Math.random().toString(36).substring(2, 8); + return `${timestamp}-${randomPart}`; +} diff --git a/src/stories/utils/orchestrator.jsx b/src/stories/utils/orchestrator.jsx index a0a9656b0..c70643746 100644 --- a/src/stories/utils/orchestrator.jsx +++ b/src/stories/utils/orchestrator.jsx @@ -1,13 +1,17 @@ import './custom-lunatic.scss'; import './orchestrator.scss'; -import { useLunatic, Button, components, ModalControls } from '../..'; -import React, { memo, useCallback, useState, useEffect } from 'react'; +import { + Button, + components, + LunaticComponents, + ModalControls, + useLunatic, +} from '../..'; +import React, { memo, useCallback, useEffect, useState } from 'react'; import { Logger } from '../../utils/logger'; import { Overview } from './overview'; -import Waiting from './waiting'; -import { LunaticComponents } from '../..'; import { SchemaValidator } from './SchemaValidator.jsx'; const Input = components.Input; @@ -247,11 +251,6 @@ function OrchestratorForStories({ isCritical={errorsForModal.isCritical} /> )} - -
- Initialisation des données de suggestion... -
-
diff --git a/src/stories/utils/waiting/index.js b/src/stories/utils/waiting/index.js deleted file mode 100644 index 34f03c815..000000000 --- a/src/stories/utils/waiting/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './waiting'; diff --git a/src/stories/utils/waiting/preloader.svg b/src/stories/utils/waiting/preloader.svg deleted file mode 100644 index 5ce3a7d61..000000000 --- a/src/stories/utils/waiting/preloader.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/stories/utils/waiting/waiting.jsx b/src/stories/utils/waiting/waiting.jsx deleted file mode 100644 index ab15a09fc..000000000 --- a/src/stories/utils/waiting/waiting.jsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; -import './waiting.scss'; -import preloader from './preloader.svg'; - -function Waiting({ status, children }) { - if (status) { - return ( -
- loading -
{children}
-
- ); - } - return null; -} - -Waiting.defaultProps = { - status: false, -}; - -export default React.memo(Waiting); diff --git a/src/stories/utils/waiting/waiting.scss b/src/stories/utils/waiting/waiting.scss deleted file mode 100644 index 3f03512b5..000000000 --- a/src/stories/utils/waiting/waiting.scss +++ /dev/null @@ -1,21 +0,0 @@ -.waiting { - display: block; - position: fixed; - height: 100%; - width: 100%; - background-color: rgba(23, 59, 77, 0.8); - top: 0; - img { - display: block; - margin-left: auto; - margin-right: auto; - width: 10%; - margin-top: 120px; - } - .content { - display: block; - width: fit-content; - margin-left: auto; - margin-right: auto; - } -}