Skip to content

Commit

Permalink
Merge pull request #1681 from isomerpages/release/0.59.0
Browse files Browse the repository at this point in the history
Release/0.59.0
  • Loading branch information
kishore03109 authored Nov 16, 2023
2 parents 9d3fa88 + 146dd95 commit f047628
Show file tree
Hide file tree
Showing 23 changed files with 579 additions and 274 deletions.
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

0 comments on commit f047628

Please sign in to comment.