Skip to content

Commit

Permalink
Merge pull request #2962 from navikt/poc-quill-konvertere-delmalblokk…
Browse files Browse the repository at this point in the history
…-til-fritekst

Konvertere delmalblokk til rikt tekstfelt
  • Loading branch information
charliemidtlyng authored Dec 16, 2024
2 parents 3188f58 + 331db0e commit 49fb9f6
Show file tree
Hide file tree
Showing 9 changed files with 256 additions and 21 deletions.
9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@
"set-node-options": "NODE_OPTIONS='--import=./node_dist/backend/register.js --es-module-specifier-resolution=node'"
},
"lint-staged": {
"src/**/*.{js,jsx,ts,tsx,json,css}": ["prettier --write", "eslint --fix --max-warnings=0"]
"src/**/*.{js,jsx,ts,tsx,json,css}": [
"prettier --write",
"eslint --fix --max-warnings=0"
]
},
"dependencyComments": {
"color-string": "Overrider versjon som brukes av winston, som kommer fra familie-frontend-backend",
Expand Down Expand Up @@ -64,6 +67,7 @@
"passport": "^0.7.0",
"passport-azure-ad": "^4.3.5",
"prom-client": "^15.1.3",
"quill": "^2.0.3",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-pdf": "9.1.1",
Expand Down Expand Up @@ -124,5 +128,6 @@
"resolutions": {
"@types/react": "^18.x",
"@types/react-select": "^5.x"
}
},
"packageManager": "[email protected]+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}
1 change: 1 addition & 0 deletions src/frontend/App/context/toggles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export enum ToggleName {

// Release-toggles
visEndreDokumenttittelKnapp = 'familie.ef.sak.frontend-endre-dokumenttittel',
konvertereDelmalblokkTilHtmlFelt = 'familie.ef.sak.konverter-delmalblokk-til-html-input',

// Midlertidige toggles - kan fjernes etterhvert
}
5 changes: 5 additions & 0 deletions src/frontend/App/hooks/useMellomlagringBrev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
FlettefeltMedVerdi,
Fritekstområder,
MellomlagerRespons,
OverstyrteDelmaler,
ValgteDelmaler,
ValgtFelt,
} from '../../Komponenter/Behandling/Brev/BrevTyper';
Expand All @@ -20,6 +21,7 @@ export interface IBrevverdier {
valgteFeltFraMellomlager: ValgtFelt;
valgteDelmalerFraMellomlager: ValgteDelmaler;
fritekstområderFraMellomlager: Fritekstområder;
overstyrteDelmalerFraMellomlager: OverstyrteDelmaler;
}

export interface IMellomlagreBrevRequest {
Expand All @@ -39,6 +41,7 @@ export type MellomlagreSanitybrev = (
valgteFelt: ValgtFelt,
valgteDelmaler: ValgteDelmaler,
fritekstområder: Fritekstområder,
overstyrteDelmaler: OverstyrteDelmaler,
brevmal: string
) => void;

Expand All @@ -59,6 +62,7 @@ export const useMellomlagringBrev = (
valgteFelt: ValgtFelt,
valgteDelmaler: ValgteDelmaler,
fritekstområder: Fritekstområder,
overstyrteDelmaler: OverstyrteDelmaler,
brevmal: string
): void => {
axiosRequest<string, IMellomlagretBrev>({
Expand All @@ -70,6 +74,7 @@ export const useMellomlagringBrev = (
valgteFeltFraMellomlager: valgteFelt,
valgteDelmalerFraMellomlager: valgteDelmaler,
fritekstområderFraMellomlager: fritekstområder,
overstyrteDelmalerFraMellomlager: overstyrteDelmaler,
}),
brevmal,
versjon: sanityVersjon,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
FlettefeltMedVerdi,
Fritekstområder,
MellomlagerRespons,
OverstyrteDelmaler,
ValgteDelmaler,
ValgtFelt,
} from '../../Komponenter/Behandling/Brev/BrevTyper';
Expand All @@ -28,6 +29,7 @@ export const useMellomlagringFrittståendeSanitybrev = (
valgteFelt: ValgtFelt,
valgteDelmaler: ValgteDelmaler,
fritekstområder: Fritekstområder,
overstyrteDelmaler: OverstyrteDelmaler,
brevmal: string
): void => {
axiosRequest<string, IMellomlagretBrev>({
Expand All @@ -39,6 +41,7 @@ export const useMellomlagringFrittståendeSanitybrev = (
valgteFeltFraMellomlager: valgteFelt,
valgteDelmalerFraMellomlager: valgteDelmaler,
fritekstområderFraMellomlager: fritekstområder,
overstyrteDelmalerFraMellomlager: overstyrteDelmaler,
}),
brevmal,
versjon: sanityVersjon,
Expand Down
83 changes: 83 additions & 0 deletions src/frontend/Felles/HtmlEditor/HtmlEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import Quill from 'quill';
import React, { forwardRef, useEffect, useLayoutEffect, useRef, useState } from 'react';
import 'quill/dist/quill.snow.css';
import { BlockBlot } from 'parchment';
import { AlertError } from '../Visningskomponenter/Alerts';

type Props = {
defaultValue?: string;
onTextChange: (html: string, renTekst: string) => void;
};

/**
* Overstyre default tag fra `p` til `div` ettersom `p` gir noen uheldige sideeffekter med mellomrom i tekstene våre.
*/
const Block = Quill.import('blots/block') as BlockBlot;
// @ts-expect-error Utypet kode - usikkert hvordan vi får dette til
Block.tagName = 'div';
// @ts-expect-error Utypet kode - usikkert hvordan vi får dette til
Quill.register(Block);

export const HtmlEditor = forwardRef(({ defaultValue, onTextChange }: Props, ref) => {
const defaultValueRef = useRef(defaultValue);
const containerRef = useRef<HTMLDivElement>(null);
const onTextChangeRef = useRef(onTextChange);
const [feilVedOpprettelse, settFeilVedOpprettelse] = useState<boolean>(false);
useLayoutEffect(() => {
onTextChangeRef.current = onTextChange;
});

useEffect(() => {
const container = containerRef.current;

if (!container) {
settFeilVedOpprettelse(true);
return;
}

settFeilVedOpprettelse(false);
const editorContainer = container.appendChild(container.ownerDocument.createElement('div'));

const verktøySomSkalMed = [[{ list: 'bullet' }], ['clean']];

const quill = new Quill(editorContainer, {
theme: 'snow',
modules: {
toolbar: verktøySomSkalMed,
},
});

if (ref && typeof ref === 'function') {
ref(quill);
} else if (ref && 'current' in ref) {
(ref as React.MutableRefObject<Quill | null>).current = quill;
}

if (defaultValueRef.current) {
const htmlAsDelta = quill.clipboard.convert({ html: defaultValueRef.current });
quill.setContents(htmlAsDelta);
}

quill.on('text-change', () => {
const html = quill.root.innerHTML;
const text = quill.getText();

onTextChangeRef.current(html, text);
});

return () => {
if (ref && 'current' in ref) {
(ref as React.MutableRefObject<Quill | null>).current = null;
}
container.innerHTML = '';
};
}, [ref]);

return feilVedOpprettelse ? (
<AlertError>En uventet feil oppstod ved opprettelse av tekstfelt.</AlertError>
) : (
<div ref={containerRef}></div>
);
});

HtmlEditor.displayName = 'HtmlEditor';
85 changes: 68 additions & 17 deletions src/frontend/Komponenter/Behandling/Brev/BrevMenyDelmal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@ import {
Delmal,
FlettefeltMedVerdi,
Flettefeltreferanse,
OverstyrtDelmal,
OverstyrteDelmaler,
ValgtFelt,
} from './BrevTyper';
import React, { Dispatch, SetStateAction, useState } from 'react';
import { ValgfeltSelect } from './ValgfeltSelect';
import { Flettefelt } from './Flettefelt';
import styled from 'styled-components';
import { Accordion, Checkbox } from '@navikt/ds-react';
import { Accordion, Button, Checkbox } from '@navikt/ds-react';
import { ABorderRadiusMedium, ABorderStrong } from '@navikt/ds-tokens/dist/tokens';
import { HtmlEditor } from '../../../Felles/HtmlEditor/HtmlEditor';
import { useToggles } from '../../../App/context/TogglesContext';
import { ToggleName } from '../../../App/context/toggles';

const DelmalValg = styled.div`
display: flex;
Expand Down Expand Up @@ -38,6 +43,12 @@ interface Props {
settBrevOppdatert: (kanSendeTilBeslutter: boolean) => void;
valgt: boolean;
skjul: boolean;
overstyring: {
konverterTilHtml: (delmal: Delmal) => void;
konverterTilDelmalblokk: (delmal: Delmal) => void;
settOverstyrteDelmaler: Dispatch<SetStateAction<OverstyrteDelmaler>>;
overstyrtDelmal?: OverstyrtDelmal;
};
}

export const BrevMenyDelmal: React.FC<Props> = ({
Expand All @@ -51,8 +62,13 @@ export const BrevMenyDelmal: React.FC<Props> = ({
settBrevOppdatert,
valgt,
skjul,
overstyring,
}) => {
const { delmalValgfelt, delmalFlettefelter } = delmal;
const { toggles } = useToggles();

const skalKunneKonvertereDelmalblokk = toggles[ToggleName.konvertereDelmalblokkTilHtmlFelt];

const [ekspanderbartPanelÅpen, settEkspanderbartPanelÅpen] = useState(false);

const handleFlettefeltInput = (verdi: string, flettefelt: Flettefeltreferanse) => {
Expand All @@ -62,6 +78,13 @@ export const BrevMenyDelmal: React.FC<Props> = ({
settBrevOppdatert(false);
};

const oppdaterOverstyrtInnhold = (delmal: Delmal, htmlInnhold: string) => {
overstyring.settOverstyrteDelmaler((prevState) => ({
...prevState,
[delmal.delmalApiNavn]: { htmlInnhold: htmlInnhold, skalOverstyre: true },
}));
};

const håndterToggleDelmal = (e: React.ChangeEvent<HTMLInputElement>) => {
settValgteDelmaler((prevState) => ({
...prevState,
Expand All @@ -79,6 +102,8 @@ export const BrevMenyDelmal: React.FC<Props> = ({
return null;
}

const erDelmalblokk = overstyring.overstyrtDelmal?.skalOverstyre !== true;

return (
<DelmalValg>
<Checkbox hideLabel onChange={håndterToggleDelmal} checked={valgt}>
Expand All @@ -103,7 +128,8 @@ export const BrevMenyDelmal: React.FC<Props> = ({
</Accordion.Header>
{ekspanderbartPanelÅpen && (
<AccordionInnhold>
{delmalValgfelt &&
{erDelmalblokk &&
delmalValgfelt &&
delmalValgfelt.map((valgFelt, index) => (
<ValgfeltSelect
valgFelt={valgFelt}
Expand All @@ -118,22 +144,47 @@ export const BrevMenyDelmal: React.FC<Props> = ({
settKanSendeTilBeslutter={settBrevOppdatert}
/>
))}
{delmalFlettefelter
.flatMap((f) => f.flettefelt)
.filter(
(felt, index, self) =>
self.findIndex((t) => t._ref === felt._ref) === index
)
.map((flettefelt) => (
<Flettefelt
fetLabel={true}
flettefelt={flettefelt}
dokument={dokument}
flettefelter={flettefelter}
handleFlettefeltInput={handleFlettefeltInput}
key={flettefelt._ref}
{erDelmalblokk &&
delmalFlettefelter
.flatMap((f) => f.flettefelt)
.filter(
(felt, index, self) =>
self.findIndex((t) => t._ref === felt._ref) === index
)
.map((flettefelt) => (
<Flettefelt
fetLabel={true}
flettefelt={flettefelt}
dokument={dokument}
flettefelter={flettefelter}
handleFlettefeltInput={handleFlettefeltInput}
key={flettefelt._ref}
/>
))}
{erDelmalblokk && skalKunneKonvertereDelmalblokk && (
<Button
onClick={() => overstyring.konverterTilHtml(delmal)}
size={'small'}
>
Konverter til tekstfelt
</Button>
)}
{overstyring.overstyrtDelmal?.skalOverstyre && (
<>
<HtmlEditor
defaultValue={overstyring.overstyrtDelmal.htmlInnhold}
onTextChange={(nyttInnhold) => {
oppdaterOverstyrtInnhold(delmal, nyttInnhold);
}}
/>
))}
<Button
onClick={() => overstyring.konverterTilDelmalblokk(delmal)}
size={'small'}
>
Konverter til brevbygger
</Button>
</>
)}
</AccordionInnhold>
)}
</Accordion.Item>
Expand Down
4 changes: 4 additions & 0 deletions src/frontend/Komponenter/Behandling/Brev/BrevTyper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { IBrevmottakere } from '../Brevmottakere/typer';

export type ValgtFelt = { [valgFeltKategori: string]: Valgmulighet };
export type ValgteDelmaler = { [delmalNavn: string]: boolean };
export type OverstyrteDelmaler = {
[delmalNavn: string]: OverstyrtDelmal;
};
export type OverstyrtDelmal = { htmlInnhold: string; skalOverstyre: boolean };

export interface BrevStruktur {
dokument: DokumentMal;
Expand Down
Loading

0 comments on commit 49fb9f6

Please sign in to comment.