Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release/0.59.0 #1681

Merged
merged 7 commits into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,19 @@ All notable changes to this project will be documented in this file. Dates are d

Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).

#### [v0.59.0](https://github.com/isomerpages/isomercms-frontend/compare/v0.58.0...v0.59.0)

- feat(tiptap): allow inserting of embed code using a modal [`#1658`](https://github.com/isomerpages/isomercms-frontend/pull/1658)
- feat(tiptap): rm redundant logos [`#1679`](https://github.com/isomerpages/isomercms-frontend/pull/1679)
- Fix/is 689 reuse image/album tiles [`#1678`](https://github.com/isomerpages/isomercms-frontend/pull/1678)
- Fix/is724-apply-styles [`#1677`](https://github.com/isomerpages/isomercms-frontend/pull/1677)
- fix(editor): refactor to use `tooltip` [`#1669`](https://github.com/isomerpages/isomercms-frontend/pull/1669)
- release/0.58.0 [`#1675`](https://github.com/isomerpages/isomercms-frontend/pull/1675)

#### [v0.58.0](https://github.com/isomerpages/isomercms-frontend/compare/v0.57.0...v0.58.0)

> 14 November 2023

- fix(markdown): update styling to remove overflow [`#1671`](https://github.com/isomerpages/isomercms-frontend/pull/1671)
- feat/addReportingBUtton [`#1674`](https://github.com/isomerpages/isomercms-frontend/pull/1674)
- feat(media): add announcements and feature tour for media enhancements [`#1632`](https://github.com/isomerpages/isomercms-frontend/pull/1632)
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "isomercms-frontend",
"version": "0.58.0",
"version": "0.59.0",
"private": true,
"engines": {
"node": ">=16.0.0"
Expand Down
41 changes: 41 additions & 0 deletions src/components/EditorEmbedModal/EditorEmbedModal.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { useDisclosure } from "@chakra-ui/react"
import { Button } from "@opengovsg/design-system-react"
import type { Meta, StoryFn } from "@storybook/react"

import { useSuccessToast } from "utils"

import { EditorEmbedModal } from "./EditorEmbedModal"

const editorEmbedModalMeta = {
title: "Components/Editor/Embed Modal",
component: EditorEmbedModal,
} as Meta<typeof EditorEmbedModal>

const editorEmbedModalTemplate: StoryFn<typeof EditorEmbedModal> = () => {
const { isOpen, onOpen, onClose } = useDisclosure({ defaultIsOpen: true })
const successToast = useSuccessToast()
const onProceed = () => {
successToast({
id: "storybook-editor-embed-success",
description: "STORYBOOK: Embed has been successfully added",
})
onClose()
}

return (
<>
<Button onClick={onOpen}>Open editor embed modal</Button>
<EditorEmbedModal
isOpen={isOpen}
onClose={onClose}
onProceed={onProceed}
cursorValue=""
/>
</>
)
}

export const Default = editorEmbedModalTemplate.bind({})
Default.args = {}

export default editorEmbedModalMeta
185 changes: 185 additions & 0 deletions src/components/EditorEmbedModal/EditorEmbedModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import {
FormControl,
HStack,
Modal,
ModalBody,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
Textarea,
} from "@chakra-ui/react"
import { yupResolver } from "@hookform/resolvers/yup"
import {
Button,
FormErrorMessage,
FormLabel,
Link,
ModalCloseButton,
} from "@opengovsg/design-system-react"
import * as Cheerio from "cheerio"
import { useEffect } from "react"
import { FormProvider, useForm } from "react-hook-form"
import * as Yup from "yup"

import { useCspHook } from "hooks/settingsHooks"

import { isEmbedCodeValid } from "utils/allowedHTML"

import { EditorEmbedContents } from "types/editPage"

interface EditorEmbedModalProps {
isOpen: boolean
onClose: () => void
onProceed: (embedCode: EditorEmbedContents) => void
cursorValue: string
}

export const EditorEmbedModal = ({
isOpen,
onClose,
onProceed,
cursorValue,
}: EditorEmbedModalProps): JSX.Element => {
const { data: csp } = useCspHook()

const handleSubmit = (embedCode: EditorEmbedContents) => {
const { value } = embedCode
const $ = Cheerio.load(value)

if ($("blockquote").hasClass("instagram-media")) {
const postUrl = $(".instagram-media").attr("data-instgrm-permalink")
const url = document.createElement("a")
url.href = postUrl || ""
const code = `<iframe src="https://${url.host}${url.pathname}embed/" frameborder="0" allowfullscreen="true" width="320" height="440"></iframe>`
onProceed({ value: code })
} else if ($('iframe[src^="https://form.gov.sg"]').length > 0) {
// We only want to keep the <iframe> part, the rest is filled in by Tiptap
onProceed({ value: $('iframe[src^="https://form.gov.sg"]').toString() })
} else {
onProceed(embedCode)
}
}

const methods = useForm<EditorEmbedContents>({
mode: "onTouched",
resolver: yupResolver(
Yup.object().shape({
value: Yup.string()
.required(
"Content to embed cannot be empty. Please enter valid HTML code."
)
.matches(
// Blockquote is to allow for Instagram embeds
/^(<iframe |<blockquote |<div)(.*)$/s,
"Please enter a valid embed code"
)
.test({
name: "isEmbedCodeValid",
message:
"This content is not supported yet, or there is unauthorised Javascript in your code. Ensure that you have valid HTML code and try again.",
test: (value) => isEmbedCodeValid(csp, value),
}),
})
),
defaultValues: {
value: "",
},
})

useEffect(() => {
if (isOpen) {
methods.setValue(
"value",
cursorValue
.replace('<div class="iframe-wrapper">', "")
// Remove the closing div tag
.slice(0, -6)
)
} else {
methods.reset()
}
}, [cursorValue, isOpen, methods])

return (
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />

<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(handleSubmit)}>
<ModalContent>
<ModalCloseButton />
<ModalHeader>
{cursorValue === ""
? "Embed new content"
: "Edit embedded content"}
</ModalHeader>

<ModalBody>
<Text textStyle="body-1" mt="1rem">
You can embed selected external content on your page.{" "}
<Link
target="_blank"
href="https://guide.isomer.gov.sg/guide/integration#embedding"
>
Click here
</Link>{" "}
to learn more about embedding content onto your Isomer site.
</Text>
<FormControl
isRequired
isInvalid={!!methods.formState.errors.value}
>
<FormLabel textStyle="subhead-1" mt="1.5rem" mb="0.5rem">
Code to embed
</FormLabel>

<Textarea
placeholder="Paste your code in here. It might start with a <iframe> or <blockquote> tag."
mt={0}
fontFamily="monospace"
size="md"
height="9rem"
color="green.400"
{...methods.register("value")}
/>

{methods.formState.errors.value ? (
<FormErrorMessage mb={0}>
{methods.formState.errors.value.message}
</FormErrorMessage>
) : (
<Text textStyle="body-2" mt="0.5rem">
Isomer does not support any unauthorised content that
violates the Content Security Policy (CSP).
</Text>
)}
</FormControl>
</ModalBody>

<ModalFooter mt="0.5rem">
<HStack w="100%" spacing={4} justifyContent="flex-end">
{cursorValue !== "" && (
<Button
variant="clear"
colorScheme="neutral"
onClick={() => {
methods.reset()
onClose()
}}
>
Cancel
</Button>
)}
<Button type="submit" isDisabled={!methods.formState.isValid}>
{cursorValue === "" ? "Add to page" : "Save"}
</Button>
</HStack>
</ModalFooter>
</ModalContent>
</form>
</FormProvider>
</Modal>
)
}
1 change: 1 addition & 0 deletions src/components/EditorEmbedModal/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./EditorEmbedModal"
46 changes: 23 additions & 23 deletions src/components/ImagePreviewCard/ImagePreviewCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,29 +60,29 @@ export const ImagePreviewCard = ({
return (
<Box position="relative" h="100%" data-group>
{/* Checkbox overlay over image */}
<Checkbox
position="absolute"
left="0"
top="0"
h="3.25rem"
w="3.25rem"
size="md"
p="1rem"
variant="transparent"
display={isSelected ? "inline-block" : "none"}
_groupHover={{
bg: "transparent",
display: "inline-block",
}}
_focusWithin={{
outline: "none",
}}
zIndex={1}
isChecked={isSelected}
onChange={() => {
if (onCheck) onCheck()
}}
/>
{onCheck && (
<Checkbox
position="absolute"
left="0"
top="0"
h="3.25rem"
w="3.25rem"
size="md"
p="1rem"
variant="transparent"
display={isSelected ? "inline-block" : "none"}
_groupHover={{
bg: "transparent",
display: "inline-block",
}}
_focusWithin={{
outline: "none",
}}
zIndex={1}
isChecked={isSelected}
onChange={onCheck}
/>
)}

<Grid
as={chakra.button}
Expand Down
1 change: 1 addition & 0 deletions src/components/media/MediaModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ const MediaModal = ({ onClose, onProceed, type, showAltTextModal = false }) => {
}
onMediaSelect={onMediaSelect}
onClose={onClose}
mediaType={type}
/>
)
}
Expand Down
Loading
Loading