Skip to content

Commit b747278

Browse files
committed
Make links always to open in new window
1 parent c562702 commit b747278

5 files changed

Lines changed: 48 additions & 24 deletions

File tree

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import React from 'react';
2+
import ReactMarkdown from 'react-markdown';
3+
import rehypeExternalLinks from 'rehype-external-links';
4+
import remarkBreaks from 'remark-breaks';
5+
6+
interface Props {
7+
children: string;
8+
}
9+
10+
export default function MarkdownViewer({ children }: Props) {
11+
return (
12+
<ReactMarkdown
13+
rehypePlugins={[
14+
() =>
15+
rehypeExternalLinks({
16+
target: '_blank',
17+
rel: 'noopener noreferrer',
18+
}),
19+
]}
20+
remarkPlugins={[remarkBreaks]}
21+
>
22+
{children}
23+
</ReactMarkdown>
24+
);
25+
}

client/src/components/RichTextEditor.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,17 @@ interface Props {
9696
* @returns Draft.js editor state
9797
*/
9898
function markdownToEditorState(markdown: string) {
99-
const rawData = markdownToDraft(markdown);
99+
const rawData = markdownToDraft(markdown, {
100+
remarkableOptions: { html: true },
101+
});
102+
// Set target="_blank" for all link entities
103+
if (rawData.entityMap) {
104+
Object.values(rawData.entityMap).forEach((entity: any) => {
105+
if (entity.type === 'LINK') {
106+
entity.data.targetOption = '_blank';
107+
}
108+
});
109+
}
100110
const contentState = convertFromRaw(rawData);
101111
return EditorState.createWithContent(contentState);
102112
}

client/src/components/SectionInfo.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ import {
1212
} from '@mui/material';
1313
import { useTranslations } from '@src/stores/TranslationContext';
1414
import React, { forwardRef, useId, useImperativeHandle, useState } from 'react';
15-
import ReactMarkdown from 'react-markdown';
16-
import rehypeExternalLinks from 'rehype-external-links';
15+
import MarkdownViewer from './MarkdownViewer';
1716

1817
interface Props {
1918
subject: string;
@@ -56,9 +55,7 @@ export default forwardRef(function SectionInfo(
5655
open={infoDialogOpen}
5756
>
5857
<DialogContent id={`${dialogId}-dialog-content`}>
59-
<ReactMarkdown rehypePlugins={[rehypeExternalLinks]}>
60-
{infoText}
61-
</ReactMarkdown>
58+
<MarkdownViewer>{infoText}</MarkdownViewer>
6259
</DialogContent>
6360
<DialogActions>
6461
<Button

client/src/components/SurveyThanksPage.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
import { Survey } from '@interfaces/survey';
22
import {
3+
Box,
34
Link,
4-
Typography,
5+
Stack,
56
Theme,
6-
Box,
7+
Typography,
78
useMediaQuery,
8-
Stack,
99
} from '@mui/material';
1010
import { useTranslations } from '@src/stores/TranslationContext';
11+
import { useImageHeaderQuery } from '@src/utils/useImageHeaderQuery';
1112
import React, { useEffect, useState } from 'react';
12-
import ReactMarkdown from 'react-markdown';
13-
import rehypeExternalLinks from 'rehype-external-links';
1413
import Footer from './Footer';
15-
import { useImageHeaderQuery } from '@src/utils/useImageHeaderQuery';
14+
import MarkdownViewer from './MarkdownViewer';
1615

1716
const styles = (theme: Theme) => ({
1817
testSurveyHeader: {
@@ -128,9 +127,9 @@ export default function SurveyThanksPage({ survey, isTestSurvey }: Props) {
128127
<Typography variant="h5" component="h1">
129128
{survey.thanksPage.title?.[surveyLanguage]}
130129
</Typography>
131-
<ReactMarkdown rehypePlugins={[rehypeExternalLinks]}>
130+
<MarkdownViewer>
132131
{survey.thanksPage.text?.[surveyLanguage]}
133-
</ReactMarkdown>
132+
</MarkdownViewer>
134133
</div>
135134
{survey.thanksPage.imageName && (
136135
<Box

client/src/components/TextSection.tsx

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
import { SurveyTextSection } from '@interfaces/survey';
2+
import { Typography } from '@mui/material';
23
import { useSurveyAnswers } from '@src/stores/SurveyAnswerContext';
34
import { useTranslations } from '@src/stores/TranslationContext';
45
import React from 'react';
5-
import ReactMarkdown from 'react-markdown';
6-
import rehypeExternalLinks from 'rehype-external-links';
7-
import remarkBreaks from 'remark-breaks';
6+
import MarkdownViewer from './MarkdownViewer';
87
import SectionInfo from './SectionInfo';
9-
import { Typography } from '@mui/material';
108

119
interface Props {
1210
section: SurveyTextSection;
@@ -42,12 +40,7 @@ export default function TextSection({ section, isFollowUp = false }: Props) {
4240
)}
4341
</div>
4442
<div style={{ color: section.bodyColor ?? '#000000' }}>
45-
<ReactMarkdown
46-
rehypePlugins={[rehypeExternalLinks]}
47-
remarkPlugins={[remarkBreaks]}
48-
>
49-
{section.body?.[surveyLanguage]}
50-
</ReactMarkdown>
43+
<MarkdownViewer>{section.body?.[surveyLanguage]}</MarkdownViewer>
5144
</div>
5245
</>
5346
);

0 commit comments

Comments
 (0)