Skip to content

Commit

Permalink
Merge pull request #4286 from dfe-analytical-services/EES-4512
Browse files Browse the repository at this point in the history
EES-4512 allow links in footnotes
  • Loading branch information
amyb-hiveit authored Sep 20, 2023
2 parents a179611 + 79b682a commit 434ed83
Show file tree
Hide file tree
Showing 21 changed files with 265 additions and 83 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
Editor as EditorType,
Element,
GlossaryPlugin,
PluginName,
ToolbarOption,
} from '@admin/types/ckeditor';
import { defaultAllowedHeadings } from '@admin/config/ckEditorConfig';
import useCKEditorConfig from '@admin/hooks/useCKEditorConfig';
Expand Down Expand Up @@ -43,9 +45,10 @@ export interface FormEditorProps {
hideLabel?: boolean;
hint?: string;
id: string;
includePlugins?: ReadonlySet<PluginName> | Set<PluginName>;
label: string;
testId?: string;
toolbarConfig?: string[];
toolbarConfig?: ReadonlyArray<ToolbarOption> | Array<ToolbarOption>;
value: string;
onAutoSave?: (values: string) => void;
onBlur?: () => void;
Expand All @@ -66,6 +69,7 @@ const FormEditor = ({
hideLabel,
hint,
id,
includePlugins,
label,
testId,
toolbarConfig,
Expand All @@ -91,6 +95,7 @@ const FormEditor = ({
allowComments,
allowedHeadings,
editorInstance,
includePlugins,
toolbarConfig,
onAutoSave,
onCancelComment,
Expand Down Expand Up @@ -208,8 +213,14 @@ const FormEditor = ({
);

editorInstance.current = editor;
commentsPlugin.current = editor.plugins.get<CommentsPlugin>('Comments');
glossaryPlugin.current = editor.plugins.get<GlossaryPlugin>('Glossary');

if (editor.plugins.has<CommentsPlugin>('Comments')) {
commentsPlugin.current = editor.plugins.get<CommentsPlugin>('Comments');
}

if (editor.plugins.has<GlossaryPlugin>('Glossary')) {
glossaryPlugin.current = editor.plugins.get<GlossaryPlugin>('Glossary');
}

setMarkersOrder(getMarkersOrder([...editor.model.markers]));
},
Expand Down
84 changes: 48 additions & 36 deletions src/explore-education-statistics-admin/src/config/ckEditorConfig.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,57 @@
import {
AlignmentConfig,
HeadingOption,
PluginName,
ResizeOption,
ToolbarOption,
} from '@admin/types/ckeditor';
import { Dictionary } from '@admin/types';

export const toolbarConfigs: Dictionary<string[]> = {
full: [
'heading',
'|',
'bold',
'italic',
'link',
'|',
'bulletedList',
'numberedList',
'|',
'blockQuote',
'insertTable',
'toggleTableCaption',
'imageUpload',
'alignment',
'|',
'redo',
'undo',
'|',
'comment',
'glossary',
],
simple: [
'bold',
'italic',
'link',
'|',
'bulletedList',
'numberedList',
'|',
'redo',
'undo',
],
};
export const toolbarConfigFull: ReadonlyArray<ToolbarOption> = [
'heading',
'|',
'bold',
'italic',
'link',
'|',
'bulletedList',
'numberedList',
'|',
'blockQuote',
'insertTable',
'toggleTableCaption',
'imageUpload',
'alignment',
'|',
'redo',
'undo',
'|',
'comment',
'glossary',
];
export const toolbarConfigSimple: ReadonlyArray<ToolbarOption> = [
'bold',
'italic',
'link',
'|',
'bulletedList',
'numberedList',
'|',
'redo',
'undo',
];
export const toolbarConfigLinkOnly: ReadonlyArray<ToolbarOption> = ['link'];

export const corePlugins: ReadonlySet<PluginName> = new Set<PluginName>([
'Essentials',
'Paragraph',
]);

export const pluginsConfigLinksOnly: ReadonlySet<PluginName> =
new Set<PluginName>(['Link']);

export const pluginsConfigSimple: ReadonlySet<PluginName> = new Set<PluginName>(
['Bold', 'Italic', 'Link', 'List'],
);

export const defaultAllowedHeadings: string[] = ['h3', 'h4', 'h5'];

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import {
alignmentOptions,
corePlugins,
headingOptions,
imageToolbar,
resizeOptions,
tableContentToolbar,
toolbarConfigs,
toolbarConfigFull,
} from '@admin/config/ckEditorConfig';
import { Editor, EditorConfig } from '@admin/types/ckeditor';
import {
Editor as EditorType,
EditorConfig,
PluginName,
ToolbarOption,
} from '@admin/types/ckeditor';
import Editor from 'explore-education-statistics-ckeditor';
import { useCommentsContext } from '@admin/contexts/CommentsContext';
import {
ImageUploadCancelHandler,
Expand All @@ -19,7 +26,8 @@ const useCKEditorConfig = ({
allowComments,
allowedHeadings,
editorInstance,
toolbarConfig = toolbarConfigs.full,
includePlugins,
toolbarConfig = toolbarConfigFull,
onAutoSave,
onCancelComment,
onClickAddComment,
Expand All @@ -29,13 +37,14 @@ const useCKEditorConfig = ({
}: {
allowComments?: boolean;
allowedHeadings?: string[];
editorInstance?: MutableRefObject<Editor | undefined>;
editorInstance?: MutableRefObject<EditorType | undefined>;
glossaryItems?: {
title: string;
slug: string;
body: string;
}[];
toolbarConfig?: string[];
includePlugins?: ReadonlySet<PluginName> | Set<PluginName>;
toolbarConfig?: ReadonlyArray<ToolbarOption> | Array<ToolbarOption>;
onAutoSave?: (content: string) => void;
onCancelComment?: () => void;
onClickAddComment?: () => void;
Expand Down Expand Up @@ -94,6 +103,13 @@ const useCKEditorConfig = ({
},
}
: undefined,
removePlugins: includePlugins
? Editor.builtinPlugins?.filter(
plugin =>
!corePlugins.has(plugin.pluginName) &&
!includePlugins.has(plugin.pluginName),
)
: undefined,
table: {
contentToolbar: tableContentToolbar,
},
Expand Down Expand Up @@ -196,6 +212,7 @@ const useCKEditorConfig = ({
allowedHeadings,
editorInstance,
hasImageUpload,
includePlugins,
onAutoSave,
onCancelComment,
onClickAddComment,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import FormFieldEditor from '@admin/components/form/FormFieldEditor';
import { toolbarConfigs } from '@admin/config/ckEditorConfig';
import { toolbarConfigSimple } from '@admin/config/ckEditorConfig';
import toHtml from '@admin/utils/markdown/toHtml';
import toMarkdown from '@admin/utils/markdown/toMarkdown';
import Button from '@common/components/Button';
Expand Down Expand Up @@ -85,7 +85,7 @@ const EditableKeyStatDataBlockForm = ({

<FormFieldEditor<KeyStatDataBlockFormValues>
name="guidanceText"
toolbarConfig={toolbarConfigs.simple}
toolbarConfig={toolbarConfigSimple}
label="Guidance text"
/>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import FormFieldEditor from '@admin/components/form/FormFieldEditor';
import { toolbarConfigs } from '@admin/config/ckEditorConfig';
import {
pluginsConfigSimple,
toolbarConfigSimple,
} from '@admin/config/ckEditorConfig';
import toHtml from '@admin/utils/markdown/toHtml';
import toMarkdown from '@admin/utils/markdown/toMarkdown';
import Button from '@common/components/Button';
Expand Down Expand Up @@ -108,7 +111,8 @@ export default function EditableKeyStatTextForm({

<FormFieldEditor<KeyStatTextFormValues>
name="guidanceText"
toolbarConfig={toolbarConfigs.simple}
includePlugins={pluginsConfigSimple}
toolbarConfig={toolbarConfigSimple}
label="Guidance text"
/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import dataReplacementService from '@admin/services/dataReplacementService';
import LoadingSpinner from '@common/components/LoadingSpinner';
import WarningMessage from '@common/components/WarningMessage';
import useAsyncRetry from '@common/hooks/useAsyncRetry';
import sanitizeHtml from '@common/utils/sanitizeHtml';
import React from 'react';
import { generatePath, RouteComponentProps } from 'react-router';

Expand Down Expand Up @@ -102,7 +103,7 @@ const ReleaseDataFileReplacementCompletePage = ({
},
)}
>
{footnote.content}
{sanitizeHtml(footnote.content, { allowedTags: [] })}
</Link>
</li>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import useToggle from '@common/hooks/useToggle';
import useMountedRef from '@common/hooks/useMountedRef';
import React, { useMemo, useState } from 'react';
import { generatePath } from 'react-router';
import sanitizeHtml from '@common/utils/sanitizeHtml';

interface Props {
publicationId: string;
Expand Down Expand Up @@ -280,7 +281,7 @@ const DataFileReplacementPlan = ({
return (
<Details
key={footnote.id}
summary={footnote.content}
summary={sanitizeHtml(footnote.content, { allowedTags: [] })}
summaryAfter={
<Tag
className="govuk-!-margin-left-2"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { toolbarConfigs } from '@admin/config/ckEditorConfig';
import {
pluginsConfigSimple,
toolbarConfigSimple,
} from '@admin/config/ckEditorConfig';
import FormFieldEditor from '@admin/components/form/FormFieldEditor';
import releaseDataGuidanceService from '@admin/services/releaseDataGuidanceService';
import Accordion from '@common/components/Accordion';
Expand Down Expand Up @@ -182,7 +185,8 @@ const ReleaseDataGuidanceSection = ({ releaseId, canUpdateRelease }: Props) => {
renderContent={() =>
isEditing ? (
<FormFieldEditor<DataGuidanceFormValues>
toolbarConfig={toolbarConfigs.simple}
includePlugins={pluginsConfigSimple}
toolbarConfig={toolbarConfigSimple}
name={`subjects[${index}].content`}
label="File guidance content"
testId="fileGuidanceContent"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,15 @@ import {
SubjectSelectionType,
} from '@admin/services/footnoteService';
import footnoteToFlatFootnote from '@admin/services/utils/footnote/footnoteToFlatFootnote';
import {
pluginsConfigLinksOnly,
toolbarConfigLinkOnly,
} from '@admin/config/ckEditorConfig';
import Button from '@common/components/Button';
import ButtonGroup from '@common/components/ButtonGroup';
import { Form, FormFieldRadioGroup } from '@common/components/form';
import FormFieldTextArea from '@common/components/form/FormFieldTextArea';
import sanitizeHtml from '@common/utils/sanitizeHtml';
import FormFieldEditor from '@admin/components/form/FormFieldEditor';
import Yup from '@common/validation/yup';
import { Formik } from 'formik';
import deepmerge from 'deepmerge';
Expand Down Expand Up @@ -106,8 +111,12 @@ const FootnoteForm = ({
'At least one Subject, Indicator or Filter must be selected',
);
}
const sanitizedValues = {
...values,
content: sanitizeHtml(values.content, { allowedTags: ['a'] }),
};

await onSubmit(values);
await onSubmit(sanitizedValues);
}}
>
{form => (
Expand All @@ -124,7 +133,12 @@ const FootnoteForm = ({
to.
</p>

<FormFieldTextArea<BaseFootnote> name="content" label="Footnote" />
<FormFieldEditor<BaseFootnote>
name="content"
label="Footnote"
includePlugins={pluginsConfigLinksOnly}
toolbarConfig={toolbarConfigLinkOnly}
/>

{orderBy(
Object.values(footnoteMeta.subjects),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import classNames from 'classnames';
import React, { useMemo, useState } from 'react';
import { generatePath } from 'react-router';
import { DragDropContext, Droppable } from 'react-beautiful-dnd';
import ContentHtml from '@common/components/ContentHtml';

interface Props {
publicationId: string;
Expand Down Expand Up @@ -87,7 +88,10 @@ const FootnotesList = ({
testId={`Footnote - ${content}`}
>
<div className={styles.row}>
<div className={styles.rowContent}>{content}</div>
<ContentHtml
className={styles.rowContent}
html={content}
/>

{!isReordering && canUpdateRelease && (
<ButtonGroup className={styles.rowActions}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,15 @@
// we can actually import) should go here.

declare module 'explore-education-statistics-ckeditor' {
import { EditorClass, EditorConfig } from '@admin/types/ckeditor';
import {
EditorClass,
EditorConfig,
PluginClass,
} from '@admin/types/ckeditor';

// https://ckeditor.com/docs/ckeditor5/latest/api/module_editor-classic_classiceditor-ClassicEditor.html
interface CustomEditor extends EditorClass {
builtinPlugins?: PluginClass[];
new (
element: string | HTMLElement,
config: EditorConfig,
Expand Down
Loading

0 comments on commit 434ed83

Please sign in to comment.