From fb64af6b76c3f5dc44e77eb2617e5d21c6cc1bc3 Mon Sep 17 00:00:00 2001 From: Tanner Heffner Date: Mon, 18 Dec 2023 12:18:52 -0800 Subject: [PATCH] VACMS-16287 additional preview indicator (#280) --- README.md | 16 ++++++ src/data/queries/newsStory.ts | 1 + src/data/queries/storyListing.ts | 1 + .../__snapshots__/newsStory.test.tsx.snap | 1 + src/pages/[[...slug]].tsx | 24 +++----- src/templates/common/preview/index.test.tsx | 55 +++++++++++++++++++ src/templates/common/preview/index.tsx | 53 ++++++++++++++++++ .../common/preview/preview.stories.ts | 17 ++++++ .../preview/unpublishedPreview.stories.ts | 35 ++++++++++++ src/templates/globals/wrapper/index.tsx | 8 +++ 10 files changed, 195 insertions(+), 16 deletions(-) create mode 100644 src/templates/common/preview/index.test.tsx create mode 100644 src/templates/common/preview/index.tsx create mode 100644 src/templates/common/preview/preview.stories.ts create mode 100644 src/templates/common/preview/unpublishedPreview.stories.ts diff --git a/README.md b/README.md index 07221c89e..e8d8f3f5f 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,22 @@ NEXT_IMAGE_DOMAIN=https://va-gov-cms.ddev.site Now you can run `yarn dev` and data will be coming from your local CMS environment instead. +### Local CMS Preview + +To test the preview API route locally, you will also need to add public and private OAuth keys to your local clone of the va.gov-cms root directory at `public.key` and `private.key` respectively. These files are gitignored in the va.gov-cms repo. + +``` +-----BEGIN PUBLIC KEY----- +Retrieve this value from AWS SSM @ /cms/staging/drupal_api_users/next_build_api/public.key +-----END PUBLIC KEY----- +``` + +``` +-----BEGIN RSA PRIVATE KEY----- +Retrieve this value from AWS SSM @ /cms/staging/drupal_api_users/next_build_api/private.key +-----END RSA PRIVATE KEY----- +``` + ## Generating the static site To generate the static pages for https://va.gov, run `yarn export`. This command will generate static pages for all paths that next-build is aware of. diff --git a/src/data/queries/newsStory.ts b/src/data/queries/newsStory.ts index 24f18d03c..eac01b7ed 100644 --- a/src/data/queries/newsStory.ts +++ b/src/data/queries/newsStory.ts @@ -58,6 +58,7 @@ export const formatter: QueryFormatter = ( entityPath: entity.path.alias, type: entity.type, published: entity.status, + moderationState: entity.moderation_state, title: entity.title, metatags: entity.metatag, image: queries.formatData('media--image', { diff --git a/src/data/queries/storyListing.ts b/src/data/queries/storyListing.ts index e70a3059e..2362d47c5 100644 --- a/src/data/queries/storyListing.ts +++ b/src/data/queries/storyListing.ts @@ -123,6 +123,7 @@ export const formatter: QueryFormatter = ({ entityPath: entity.path.alias, type: entity.type, published: entity.status, + moderationState: entity.moderation_state, title: entity.title, metatags: entity.metatag, introText: entity.field_intro_text, diff --git a/src/data/queries/tests/__snapshots__/newsStory.test.tsx.snap b/src/data/queries/tests/__snapshots__/newsStory.test.tsx.snap index f3b30036e..91fc61f87 100644 --- a/src/data/queries/tests/__snapshots__/newsStory.test.tsx.snap +++ b/src/data/queries/tests/__snapshots__/newsStory.test.tsx.snap @@ -463,6 +463,7 @@ exports[`node--news_story formatData outputs formatted data 1`] = ` "tag": "meta", }, ], + "moderationState": "published", "published": true, "socialLinks": { "path": "/pittsburgh-health-care/stories/we-honor-outstanding-doctors", diff --git a/src/pages/[[...slug]].tsx b/src/pages/[[...slug]].tsx index 8c75a6625..89d743034 100644 --- a/src/pages/[[...slug]].tsx +++ b/src/pages/[[...slug]].tsx @@ -25,6 +25,7 @@ import { NewsStory as FormattedNewsStory } from '@/types/dataTypes/formatted/new import { StoryListing as FormattedStoryListing } from '@/types/dataTypes/formatted/storyListing' import { Event as FormattedEvent } from '@/types/dataTypes/formatted/event' import { Meta } from '@/templates/globals/meta' +import { PreviewCrumb } from '@/templates/common/preview' const RESOURCE_TYPES_TO_BUILD = [ RESOURCE_TYPES.STORY_LISTING, @@ -56,25 +57,16 @@ export default function ResourcePage({ ` return ( - + - {preview && ( - - )} + {preview && } { + test('renders PreviewCrumb component', () => { + render() + + expect(screen.getByRole('link')).toHaveAttribute( + 'href', + `${process.env.NEXT_PUBLIC_DRUPAL_BASE_URL}/node/500/edit` + ) + }) + + test('UnpublishedBanner renders with draft content', () => { + render() + + expect(screen.getByRole('link')).toHaveAttribute( + 'href', + `${process.env.NEXT_PUBLIC_DRUPAL_BASE_URL}/node/500/edit` + ) + }) + + test('UnpublishedBanner renders with archived content', () => { + const modResource = { + ...resource, + moderationState: 'archived', + } + render() + + expect(screen.getByRole('link')).toHaveAttribute( + 'href', + `${process.env.NEXT_PUBLIC_DRUPAL_BASE_URL}/node/500/edit` + ) + }) + + test('UnpublishedBanner does not render with a published revision', () => { + const modResource = { + ...resource, + published: true, + } + render() + + expect( + screen.queryByText('You are viewing a draft revision') + ).not.toBeInTheDocument() + }) +}) diff --git a/src/templates/common/preview/index.tsx b/src/templates/common/preview/index.tsx new file mode 100644 index 000000000..26737b41c --- /dev/null +++ b/src/templates/common/preview/index.tsx @@ -0,0 +1,53 @@ +/* These two components appear when viewing a page through the /api/preview route + * using the Drupal CMS preview. + */ + +// In preview mode, this appears just above the page's breadcrumbs. +export const PreviewCrumb = ({ entityId }) => { + return ( + + ) +} + +// In preview mode, this appears as a small banner at the very top of the page when viewing draft or archived revisions. +export const UnpublishedBanner = ({ resource }) => { + if (resource.published) return null + + let modState + switch (resource.moderationState) { + case 'archived': + modState = 'an archived' + break + case 'draft': + case 'review': + default: + modState = 'a draft' + } + + return ( +
+
+ You are viewing {modState} revision of {resource?.entityPath}. + + Edit this page in the CMS. + +
+
+ ) +} diff --git a/src/templates/common/preview/preview.stories.ts b/src/templates/common/preview/preview.stories.ts new file mode 100644 index 000000000..943f2b80d --- /dev/null +++ b/src/templates/common/preview/preview.stories.ts @@ -0,0 +1,17 @@ +import { Meta, StoryObj } from '@storybook/react' + +import { PreviewCrumb } from './index' + +const meta: Meta = { + title: 'Common/Preview', + component: PreviewCrumb, +} +export default meta + +type Story = StoryObj + +export const PreviewBreadcrumbLink: Story = { + args: { + entityId: 500, + }, +} diff --git a/src/templates/common/preview/unpublishedPreview.stories.ts b/src/templates/common/preview/unpublishedPreview.stories.ts new file mode 100644 index 000000000..300b0960a --- /dev/null +++ b/src/templates/common/preview/unpublishedPreview.stories.ts @@ -0,0 +1,35 @@ +import { Meta, StoryObj } from '@storybook/react' + +import { UnpublishedBanner } from './index' + +const meta: Meta = { + title: 'Common/Preview', + component: UnpublishedBanner, +} +export default meta + +type Story = StoryObj + +export const Draft: Story = { + args: { + resource: { + published: false, + moderationState: 'draft', + entityPath: + '/oklahoma-city-health-care/stories/el-reno-high-school-continues-78-year-tradition-of-giving-gifts-to-veterans', + entityId: '500', + }, + }, +} + +export const Archived: Story = { + args: { + resource: { + published: false, + moderationState: 'archived', + entityPath: + '/oklahoma-city-health-care/stories/el-reno-high-school-continues-78-year-tradition-of-giving-gifts-to-veterans', + entityId: '500', + }, + }, +} diff --git a/src/templates/globals/wrapper/index.tsx b/src/templates/globals/wrapper/index.tsx index a2ad4d72b..262e315c8 100644 --- a/src/templates/globals/wrapper/index.tsx +++ b/src/templates/globals/wrapper/index.tsx @@ -15,6 +15,9 @@ import { BannerDisplayType, BannerTypeMapping } from '@/data/queries/banners' import { Header } from '../header' import { Footer } from '../footer/index' import { handleSkipLink } from '@/lib/utils/handleSkipLink' +import { UnpublishedBanner } from '@/templates/common/preview' +import { StaticPropsResource } from '@/lib/drupal/staticProps' +import { FormattedResource } from '@/data/queries' // Allows additions to window object without overwriting global type interface customWindow extends Window { @@ -33,6 +36,8 @@ export interface LayoutProps { | FormattedFacilityBanner > headerFooterData?: HeaderFooterData + preview?: boolean + resource?: StaticPropsResource } export const formatBannerType = (bannerData) => { @@ -51,6 +56,8 @@ export const formatBannerType = (bannerData) => { export function Wrapper({ bannerData, headerFooterData, + preview, + resource, children, }: LayoutProps) { const [showBanners, setShowBanners] = useState(false) @@ -74,6 +81,7 @@ export function Wrapper({ Skip to Content + {preview ? : null}
{showBanners ? banners : null} {children}