diff --git a/.changeset/plenty-rice-cheer.md b/.changeset/plenty-rice-cheer.md
new file mode 100644
index 00000000..0583a1f5
--- /dev/null
+++ b/.changeset/plenty-rice-cheer.md
@@ -0,0 +1,6 @@
+---
+"@pantheon-systems/pcc-react-sdk": patch
+---
+
+The injected preview bar is deprecated in favor of the integrated preview bar in
+the dashboard preview interface
diff --git a/packages/react-sdk/package.json b/packages/react-sdk/package.json
index 4030a628..7b841a13 100644
--- a/packages/react-sdk/package.json
+++ b/packages/react-sdk/package.json
@@ -103,12 +103,8 @@
"dependencies": {
"@apollo/client": "^3.10.3",
"@pantheon-systems/pcc-sdk-core": "workspace:*",
- "framer-motion": "^10.18.0",
- "goober": "^2.1.14",
"graphql": "^16.8.1",
"lodash": "^4.17.21",
- "query-string": "^8.2.0",
- "react-laag": "^2.0.5",
"react-markdown": "^8.0.7",
"rehype-raw": "^6.1.1",
"remark-heading-id": "^1.0.1",
diff --git a/packages/react-sdk/src/components/ArticleRenderer/index.tsx b/packages/react-sdk/src/components/ArticleRenderer/index.tsx
index b5df3c97..9da2444f 100644
--- a/packages/react-sdk/src/components/ArticleRenderer/index.tsx
+++ b/packages/react-sdk/src/components/ArticleRenderer/index.tsx
@@ -6,9 +6,7 @@ import {
} from "@pantheon-systems/pcc-sdk-core/types";
import { Element } from "hast";
import React, { useEffect } from "react";
-import { createPortal } from "react-dom";
import { getTextContent } from "../../utils/react-element";
-import { PreviewBar, PreviewBarExternalProps } from "../Preview/Preview";
import MarkdownRenderer from "./Markdown";
import PantheonTreeRenderer from "./PantheonTreeRenderer";
import PantheonTreeV2Renderer from "./PantheonTreeV2Renderer";
@@ -52,7 +50,6 @@ interface Props {
content: string,
) => React.ReactNode;
smartComponentMap?: SmartComponentMap;
- previewBarProps?: PreviewBarExternalProps;
componentMap?: ComponentMap;
renderBody?: (bodyElement: React.ReactElement) => React.ReactNode;
__experimentalFlags?: {
@@ -63,22 +60,6 @@ interface Props {
};
}
-function getOrCreatePortalTarget(
- targetOverride: globalThis.Element | null | undefined,
-) {
- const pccGeneratedPortalTargetKey = "__pcc-portal-target__";
- let portalTarget =
- targetOverride || document.getElementById(pccGeneratedPortalTargetKey);
-
- if (!portalTarget) {
- portalTarget = document.createElement("div");
- portalTarget.id = pccGeneratedPortalTargetKey;
- document.body.prepend(portalTarget);
- }
-
- return portalTarget;
-}
-
const ArticleRenderer = ({
article,
bodyClassName,
@@ -86,13 +67,10 @@ const ArticleRenderer = ({
headerClassName,
renderTitle,
smartComponentMap,
- previewBarProps,
componentMap,
renderBody,
__experimentalFlags,
}: Props) => {
- const [renderCSR, setRenderCSR] = React.useState(false);
-
useEffect(() => {
if (__experimentalFlags?.useUnintrusiveTitleRendering !== true) {
console.warn(
@@ -101,33 +79,15 @@ const ArticleRenderer = ({
}
}, [__experimentalFlags]);
- useEffect(() => {
- setRenderCSR(true);
- }, []);
-
if (!article?.content) {
return null;
}
- const portalTarget =
- renderCSR && typeof document !== "undefined"
- ? getOrCreatePortalTarget(previewBarProps?.portalTarget)
- : null;
const contentType = article?.contentType;
if (contentType === "TEXT_MARKDOWN") {
return (
- {renderCSR &&
- article != null &&
- portalTarget != null &&
- article.publishingLevel === "REALTIME"
- ? createPortal(
-
,
- portalTarget,
- )
- : null}
-
{article?.content ? (
- {renderCSR &&
- article != null &&
- portalTarget != null &&
- article.publishingLevel === "REALTIME"
- ? createPortal(
- ,
- portalTarget,
- )
- : null}
-
{titleElement != null ? (
{renderTitle
diff --git a/packages/react-sdk/src/components/Preview/LivePreviewIndicator.tsx b/packages/react-sdk/src/components/Preview/LivePreviewIndicator.tsx
deleted file mode 100644
index a92a991b..00000000
--- a/packages/react-sdk/src/components/Preview/LivePreviewIndicator.tsx
+++ /dev/null
@@ -1,106 +0,0 @@
-/* eslint-disable jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */
-import { setup, styled } from "goober";
-import React, { ComponentProps, useState } from "react";
-import { Arrow, useLayer } from "react-laag";
-import { IconDot } from "../Icons/IconDot";
-import { IconInfo } from "../Icons/IconInfo";
-
-interface Props {
- isLive: boolean;
-}
-
-setup(React.createElement);
-
-export const LivePreviewIndicator = ({ isLive }: Props) => {
- const [isOpen, setIsOpen] = useState(false);
-
- const { renderLayer, triggerProps, layerProps, arrowProps } = useLayer({
- isOpen,
- triggerOffset: 24,
- placement: "bottom-center",
- onOutsideClick: () => setIsOpen(false),
- });
-
- return (
-
-
- {isLive ? "Page preview: On" : "Page preview: Off"}
- {!isLive ? (
- setIsOpen(!isOpen)} {...triggerProps}>
-
-
- ) : null}
- {isOpen && !isLive
- ? renderLayer(
-
-
-
- This preview page is no longer connected to the document
- (updates to the document will not be displayed until this is
- reconnected).
-
-
-
-
- To reconnect, navigate to the document and select the
- 'Preview' button in the Content Cloud add-on.
-
-
-
)}
- size={9}
- roundness={0}
- backgroundColor="#23232D"
- />
- ,
- )
- : null}
-
- );
-};
-
-const Container = styled("div")`
- border: 1px solid #cfcfd3;
- font-weight: 600;
- display: flex;
- align-items: center;
- border-radius: 100px;
- color: #6d6d78;
- padding: 0 8px;
- font-size: 12px;
- flex-direction: row;
- width: fit-content;
- height: 24px;
-
- > span {
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- margin-left: 8px;
- }
-`;
-
-const InfoButton = styled("button", React.forwardRef)`
- margin-left: 8px;
-`;
diff --git a/packages/react-sdk/src/components/Preview/Preview.tsx b/packages/react-sdk/src/components/Preview/Preview.tsx
deleted file mode 100644
index 9438895e..00000000
--- a/packages/react-sdk/src/components/Preview/Preview.tsx
+++ /dev/null
@@ -1,240 +0,0 @@
-import "../../index.css";
-import { Article } from "@pantheon-systems/pcc-sdk-core/types";
-import { AnimatePresence, motion } from "framer-motion";
-import { setup, styled } from "goober";
-import queryString from "query-string";
-import React, { useEffect } from "react";
-import { getCookie } from "../../utils/cookies";
-import { IconDocs } from "../Icons/IconDocs";
-import { IconExport } from "../Icons/IconExport";
-import { IconShare } from "../Icons/IconShare";
-import { LivePreviewIndicator } from "./LivePreviewIndicator";
-
-setup(React.createElement);
-
-export interface PreviewBarExternalProps {
- previewBarOverride?: React.ReactElement | undefined | null;
- openedPreviewBarProps?: React.HTMLAttributes
;
- portalTarget?: globalThis.Element | null | undefined;
-}
-
-interface PreviewBarInternalProps {
- article: Article;
-}
-
-const pccGrant = getCookie("PCC-GRANT");
-const maxDocTitleLength = 51;
-
-export const PreviewBar = ({
- article,
- previewBarOverride,
- openedPreviewBarProps,
-}: PreviewBarInternalProps & PreviewBarExternalProps) => {
- const [isHidden, setIsHidden] = React.useState(true);
- const [isLive, setIsLive] = React.useState(false);
- const [hasCopied, setHasCopied] = React.useState(false);
- const [copyResetTimeoutId, setCopyResetTimeoutId] =
- React.useState(null);
-
- const truncatedDocTitle =
- article?.title && article?.title?.length >= maxDocTitleLength
- ? `${article?.title.substring(0, maxDocTitleLength)}...`
- : article?.title;
-
- useEffect(() => {
- // If no preview is active, then we can leave isLive as the default false.
- if (!article.previewActiveUntil) return;
-
- const livePreviewTimeRemaining = article.previewActiveUntil - Date.now();
- if (livePreviewTimeRemaining >= 100) {
- setIsLive(true);
- setTimeout(() => {
- setIsLive(false);
- }, livePreviewTimeRemaining);
- }
- }, [article]);
-
- React.useEffect(() => {
- if (typeof window !== "undefined" && typeof location !== "undefined") {
- const parsed = queryString.parse(location.search);
-
- if (parsed.publishingLevel?.toString().toLowerCase() === "realtime") {
- setIsHidden(false);
- }
- }
- }, []);
-
- if (isHidden) {
- return null;
- }
-
- if (previewBarOverride != null) {
- return React.cloneElement(previewBarOverride, {
- article,
- });
- }
-
- return (
-
-
-
-
-
- {truncatedDocTitle}
-
-
-
-
-
-
-
-
- {
- if (copyResetTimeoutId) {
- clearTimeout(copyResetTimeoutId);
- setCopyResetTimeoutId(null);
- }
-
- const parsedUrl = queryString.parseUrl(
- window.location.href,
- {
- parseFragmentIdentifier: true,
- },
- );
-
- const query = {
- ...(parsedUrl.query || {}),
- pccGrant,
- };
-
- navigator.clipboard.writeText(
- `${parsedUrl.url}?${queryString.stringify(query)}${
- parsedUrl.fragmentIdentifier
- ? `#${parsedUrl.fragmentIdentifier}`
- : ""
- }`,
- );
- setHasCopied(true);
-
- // Reset the copied state after 2 seconds
- const timeoutId = setTimeout(() => {
- setHasCopied(false);
- }, 2000);
- setCopyResetTimeoutId(timeoutId);
- }}
- >
-
- {hasCopied ? "Copied URL" : "Share preview URL"}
-
-
-
-
-
-
-
- );
-};
-
-const Wrapper = styled("div")`
- font-family: Poppins, sans-serif;
- z-index: 5;
- position: relative;
- top: 0;
- width: 100%;
- transition: all 0.2s ease-in-out;
- display: flex;
- justify-content: flex-end;
-
- box-shadow:
- 0px 0px 0px 1px rgba(0, 0, 0, 0.08),
- 0px 8px 8px -8px rgba(0, 0, 0, 0.04);
- padding: 15px 0;
-`;
-
-const Container = styled(motion.div)`
- padding: 32px;
- padding-block: 8px;
- background: white;
- display: grid;
- gap: 1em;
- width: 100%;
-
- @media (min-width: 768px) {
- padding-block: 0;
- grid-auto-flow: column;
- grid-template-columns: 0.45fr 0.55fr;
- }
-`;
-
-const TitleSection = styled("a")`
- display: flex;
- flex-direction: row;
- column-gap: 10px;
- align-items: center;
- font-size: 0.875rem;
- font-weight: 600;
- color: #23232d;
- min-width: 0;
-
- > span {
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
-
- > svg {
- flex-shrink: 0;
- }
-`;
-
-const LivePreviewIndicatorContainer = styled("div")`
- display: flex;
- justify-content: center;
- align-items: center;
-`;
-
-const EndBlock = styled("div")`
- display: flex;
- flex-direction: row;
- column-gap: 4px;
- align-items: center;
- justify-content: flex-end;
-`;
-
-const CopyUrlButtonContainer = styled("div")`
- height: 32px;
-`;
-
-const CopyUrlButton = styled("button")`
- height: 100%;
- padding: 0 13px;
- background-color: #3017a1;
- color: white;
- border-radius: 3px;
- font-size: 0.875rem;
- font-weight: 600;
- column-gap: 5px;
-
- display: flex;
- align-items: center;
-
- &:hover {
- opacity: 0.8;
- }
-`;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ad34f926..e8b1c2a8 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -395,24 +395,12 @@ importers:
'@pantheon-systems/pcc-sdk-core':
specifier: workspace:*
version: link:../core
- framer-motion:
- specifier: ^10.18.0
- version: 10.18.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
- goober:
- specifier: ^2.1.14
- version: 2.1.14(csstype@3.1.3)
graphql:
specifier: ^16.8.1
version: 16.8.1
lodash:
specifier: ^4.17.21
version: 4.17.21
- query-string:
- specifier: ^8.2.0
- version: 8.2.0
- react-laag:
- specifier: ^2.0.5
- version: 2.0.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react-markdown:
specifier: ^8.0.7
version: 8.0.7(@types/react@18.3.3)(react@18.3.1)
@@ -2287,12 +2275,6 @@ packages:
'@emnapi/runtime@1.2.0':
resolution: {integrity: sha512-bV21/9LQmcQeCPEg3BDFtvwL6cwiTMksYNWQQ4KOxCZikEGalWtenoZ0wCiukJINlGCIi2KXx01g4FoH/LxpzQ==}
- '@emotion/is-prop-valid@0.8.8':
- resolution: {integrity: sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==}
-
- '@emotion/memoize@0.7.4':
- resolution: {integrity: sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==}
-
'@emotion/use-insertion-effect-with-fallbacks@1.0.1':
resolution: {integrity: sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==}
peerDependencies:
@@ -6148,6 +6130,7 @@ packages:
acorn-import-assertions@1.9.0:
resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==}
+ deprecated: package has been renamed to acorn-import-attributes
peerDependencies:
acorn: ^8
@@ -8606,17 +8589,6 @@ packages:
fraction.js@4.3.7:
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
- framer-motion@10.18.0:
- resolution: {integrity: sha512-oGlDh1Q1XqYPksuTD/usb0I70hq95OUzmL9+6Zd+Hs4XV0oaISBa/UUMSjYiq6m8EUF32132mOJ8xVZS+I0S6w==}
- peerDependencies:
- react: ^18.0.0
- react-dom: ^18.0.0
- peerDependenciesMeta:
- react:
- optional: true
- react-dom:
- optional: true
-
fresh@0.5.2:
resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
engines: {node: '>= 0.6'}
@@ -8941,11 +8913,6 @@ packages:
resolution: {integrity: sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ==}
engines: {node: '>=18'}
- goober@2.1.14:
- resolution: {integrity: sha512-4UpC0NdGyAFqLNPnhCT2iHpza2q+RAY3GV85a/mRPdzyPQMsj0KmMMuetdIkzWRbJ+Hgau1EZztq8ImmiMGhsg==}
- peerDependencies:
- csstype: ^3.0.10
-
google-auth-library@9.13.0:
resolution: {integrity: sha512-p9Y03Uzp/Igcs36zAaB0XTSwZ8Y0/tpYiz5KIde5By+H9DCVUSYtDWZu6aFXsWTqENMb8BD/pDT3hR8NVrPkfA==}
engines: {node: '>=14'}
@@ -12315,12 +12282,6 @@ packages:
react-is@18.3.1:
resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
- react-laag@2.0.5:
- resolution: {integrity: sha512-RCvublJhdcgGRHU1wMYJ8kRtnYsKUgYusLvVhMuftg65POnnOB4+fwXvnETm6adc0cMnc1spujlrK6bGIz6aug==}
- peerDependencies:
- react: ^16.0.0 || ^17.0.0 || ^18.0.0
- react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0
-
react-loading-skeleton@3.4.0:
resolution: {integrity: sha512-1oJEBc9+wn7BbkQQk7YodlYEIjgeR+GrRjD+QXkVjwZN7LGIcAFHrx4NhT7UHGBxNY1+zax3c+Fo6XQM4R7CgA==}
peerDependencies:
@@ -13531,9 +13492,6 @@ packages:
tiny-invariant@1.3.3:
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
- tiny-warning@1.0.3:
- resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==}
-
tinybench@2.6.0:
resolution: {integrity: sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==}
@@ -17160,14 +17118,6 @@ snapshots:
tslib: 2.6.2
optional: true
- '@emotion/is-prop-valid@0.8.8':
- dependencies:
- '@emotion/memoize': 0.7.4
- optional: true
-
- '@emotion/memoize@0.7.4':
- optional: true
-
'@emotion/use-insertion-effect-with-fallbacks@1.0.1(react@18.3.1)':
dependencies:
react: 18.3.1
@@ -25709,14 +25659,6 @@ snapshots:
fraction.js@4.3.7: {}
- framer-motion@10.18.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
- dependencies:
- tslib: 2.6.2
- optionalDependencies:
- '@emotion/is-prop-valid': 0.8.8
- react: 18.3.1
- react-dom: 18.3.1(react@18.3.1)
-
fresh@0.5.2: {}
fs-constants@1.0.0: {}
@@ -26714,10 +26656,6 @@ snapshots:
slash: 5.1.0
unicorn-magic: 0.1.0
- goober@2.1.14(csstype@3.1.3):
- dependencies:
- csstype: 3.1.3
-
google-auth-library@9.13.0(encoding@0.1.13):
dependencies:
base64-js: 1.5.1
@@ -31266,12 +31204,6 @@ snapshots:
react-is@18.3.1: {}
- react-laag@2.0.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
- dependencies:
- react: 18.3.1
- react-dom: 18.3.1(react@18.3.1)
- tiny-warning: 1.0.3
-
react-loading-skeleton@3.4.0(react@18.3.1):
dependencies:
react: 18.3.1
@@ -32808,8 +32740,6 @@ snapshots:
tiny-invariant@1.3.3: {}
- tiny-warning@1.0.3: {}
-
tinybench@2.6.0: {}
tinypool@0.8.2: {}