This repository has been archived by the owner on Dec 18, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add editable texts & news banner
- Loading branch information
1 parent
8fc0c7a
commit e1221c7
Showing
17 changed files
with
646 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
import "react-quill/dist/quill.snow.css"; | ||
|
||
import { Box, Button, Container, styled } from "@mui/material"; | ||
import dynamic from "next/dynamic"; | ||
import { useRouter } from "next/router"; | ||
import React, { useRef, useState } from "react"; | ||
import ReactQuill from "react-quill"; | ||
|
||
import { put } from "api/api"; | ||
import { EditorProps } from "components/editableText/EditableTextElement"; | ||
import { EditableTextRenderer } from "components/editableText/EditableTextRenderer"; | ||
import BL_CONFIG from "utils/bl-config"; | ||
import useExitInterceptor from "utils/useExitInterceptor"; | ||
|
||
const Quill = styled( | ||
dynamic<ReactQuill.ReactQuillProps>(import("react-quill"), { ssr: false }), | ||
)({}); | ||
|
||
export const EditableTextEditor = ({ editableText }: EditorProps) => { | ||
const initialValue = editableText.text ?? ""; | ||
|
||
const editorState = useRef<string>(initialValue); | ||
const editorRef = useRef<HTMLDivElement>(null); | ||
|
||
const [readOnly, setReadOnly] = useState(true); | ||
|
||
const router = useRouter(); | ||
useExitInterceptor(!readOnly); | ||
|
||
const onEdit = () => { | ||
setReadOnly(false); | ||
}; | ||
|
||
const onEditorSave = async () => { | ||
if (!(await router.replace(router.asPath))) { | ||
throw new Error("Unable to refresh"); | ||
} | ||
}; | ||
|
||
const onSave = () => { | ||
setReadOnly(true); | ||
if (editorRef.current?.innerText.trim().length === 0) { | ||
editorState.current = ""; | ||
} | ||
put(`${BL_CONFIG.collection.editableText}/${editableText.id}/`, { | ||
...editableText, | ||
text: editorState.current, | ||
}) | ||
.then(async () => { | ||
await onEditorSave(); | ||
return; | ||
}) | ||
.catch((error) => { | ||
throw new Error("Failed to save editable text", { cause: error }); | ||
}); | ||
}; | ||
|
||
const onCancel = () => { | ||
editorState.current = initialValue; | ||
setReadOnly(true); | ||
}; | ||
|
||
return ( | ||
<Box | ||
sx={{ | ||
display: "flex", | ||
flexDirection: "column", | ||
alignItems: "center", | ||
gap: 1, | ||
padding: 0, | ||
}} | ||
data-testid="editor" | ||
> | ||
{readOnly ? ( | ||
<Button data-testid="edit-button" onClick={onEdit}> | ||
Rediger | ||
</Button> | ||
) : ( | ||
<Container | ||
sx={{ | ||
display: "flex", | ||
flexDirection: "row", | ||
gap: 1, | ||
justifyContent: "center", | ||
}} | ||
> | ||
<Button | ||
data-testid="cancel-button" | ||
onClick={onCancel} | ||
color="warning" | ||
variant="outlined" | ||
> | ||
Avbryt | ||
</Button> | ||
<Button | ||
data-testid="save-button" | ||
onClick={onSave} | ||
variant="contained" | ||
> | ||
Lagre | ||
</Button> | ||
</Container> | ||
)} | ||
<Container | ||
ref={editorRef} | ||
sx={{ | ||
display: readOnly ? "none" : undefined, | ||
}} | ||
> | ||
<Quill | ||
formats={quillFormats} | ||
modules={quillModules} | ||
placeholder="Rediger meg ..." | ||
defaultValue={initialValue} | ||
onChange={(changedState) => { | ||
editorState.current = changedState; | ||
}} | ||
sx={{ | ||
width: "100%", | ||
"& .ql-editor": { | ||
minHeight: "10em", | ||
}, | ||
}} | ||
/> | ||
</Container> | ||
{readOnly && <EditableTextRenderer editableText={editableText} />} | ||
</Box> | ||
); | ||
}; | ||
|
||
const quillModules = { | ||
toolbar: [ | ||
[{ header: "1" }, { header: "2" }, { header: "3" }], | ||
// Disabled until bl-web no longer needs to be supported | ||
// [{ size: [] }], | ||
["bold", "italic", "underline", "strike", "blockquote"], | ||
[ | ||
{ list: "ordered" }, | ||
{ list: "bullet" }, | ||
{ indent: "-1" }, | ||
{ indent: "+1" }, | ||
], | ||
["link"], | ||
// Disabled until bl-web no longer needs to be supported | ||
// [{ align: [] }], | ||
["clean"], | ||
], | ||
clipboard: { | ||
// toggle to add extra line breaks when pasting HTML: | ||
matchVisual: false, | ||
}, | ||
}; | ||
|
||
/* | ||
* Quill editor formats | ||
* See https://quilljs.com/docs/formats/ | ||
*/ | ||
const quillFormats = [ | ||
"header", | ||
"size", | ||
"bold", | ||
"italic", | ||
"underline", | ||
"strike", | ||
"blockquote", | ||
"list", | ||
"bullet", | ||
"indent", | ||
"align", | ||
"link", | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import React from "react"; | ||
|
||
import "react-draft-wysiwyg/dist/react-draft-wysiwyg.css"; | ||
|
||
import { isAdmin } from "api/auth"; | ||
import { EditableTextEditor } from "components/editableText/EditableTextEditor"; | ||
import { EditableTextRenderer } from "components/editableText/EditableTextRenderer"; | ||
import { MaybeEmptyEditableText } from "utils/types"; | ||
import useIsHydrated from "utils/useIsHydrated"; | ||
|
||
export interface EditorProps { | ||
editableText: MaybeEmptyEditableText; | ||
} | ||
|
||
const EditableTextElement = ({ editableText }: EditorProps) => { | ||
const hydrated = useIsHydrated(); | ||
|
||
if (hydrated && isAdmin()) { | ||
return <EditableTextEditor editableText={editableText} />; | ||
} | ||
|
||
return <EditableTextRenderer editableText={editableText} />; | ||
}; | ||
|
||
export default EditableTextElement; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import "react-quill/dist/quill.core.css"; | ||
import { Box } from "@mui/material"; | ||
import React from "react"; | ||
|
||
import { sanitizeQuillHtml } from "utils/sanitizeHtml"; | ||
import { MaybeEmptyEditableText } from "utils/types"; | ||
|
||
export const EditableTextRenderer = ({ | ||
editableText, | ||
}: { | ||
editableText: MaybeEmptyEditableText; | ||
}) => { | ||
if (!editableText.text) { | ||
return null; | ||
} | ||
const content = sanitizeQuillHtml(editableText.text); | ||
return ( | ||
<Box | ||
className="ql-editor" | ||
sx={{ | ||
width: "fit-content", | ||
}} | ||
dangerouslySetInnerHTML={{ __html: content }} | ||
/> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { Box } from "@mui/material"; | ||
import { ComponentProps } from "react"; | ||
|
||
import EditableTextElement from "components/editableText/EditableTextElement"; | ||
import theme from "utils/theme"; | ||
|
||
const NewsBanner = (props: ComponentProps<typeof EditableTextElement>) => { | ||
if ( | ||
props.editableText.text === null || | ||
props.editableText.text.length === 0 | ||
) { | ||
return <EditableTextElement {...props} />; | ||
} | ||
return ( | ||
<Box | ||
sx={{ | ||
borderColor: theme.palette.warning.main, | ||
borderWidth: 2, | ||
borderStyle: "solid", | ||
borderRadius: 1, | ||
backgroundColor: theme.palette.warning.light, | ||
color: theme.palette.warning.contrastText, | ||
padding: 1, | ||
my: 5, | ||
mx: "auto", | ||
width: "fit-content", | ||
}} | ||
> | ||
<EditableTextElement {...props} /> | ||
</Box> | ||
); | ||
}; | ||
|
||
export default NewsBanner; |
Oops, something went wrong.