+ {author} is a passionate content writer with a flair for turning
+ ideas into engaging stories. When she’s not writing, Jane enjoys
+ cozy afternoons with a good book, exploring new coffee spots, and
+ finding inspiration in everyday moments.
+
+ {author} is a passionate content writer with a flair for turning
+ ideas into engaging stories. When she’s not writing, Jane enjoys
+ cozy afternoons with a good book, exploring new coffee spots, and
+ finding inspiration in everyday moments.
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export async function getServerSideProps({
+ query: { author },
+}: {
+ query: { author: string };
+}) {
+ const {
+ data: articles,
+ totalCount,
+ cursor,
+ } = await PCCConvenienceFunctions.getPaginatedArticles({
+ pageSize: PAGE_SIZE,
+ metadataFilters: {
+ author,
+ },
+ });
+
+ return {
+ props: {
+ articles,
+ cursor,
+ totalCount,
+ author,
+ },
+ };
+}
diff --git a/starters/nextjs-starter-ts/playwright-tests/basic-pages.spec.ts b/starters/nextjs-starter-ts/playwright-tests/basic-pages.spec.ts
new file mode 100644
index 00000000..6562fa59
--- /dev/null
+++ b/starters/nextjs-starter-ts/playwright-tests/basic-pages.spec.ts
@@ -0,0 +1,17 @@
+import { expect, test } from "@playwright/test";
+
+// This assumes that the first article to appear in the list will have an author.
+test("basic navigation", async ({ page }) => {
+ await page.goto("http://localhost:3001");
+
+ (await page.$("section .group button")).click();
+ await page.waitForURL("**/articles/**", {
+ waitUntil: "networkidle",
+ });
+ await page.getByTestId("author").click();
+ await page.waitForURL("**/authors/**", {
+ waitUntil: "networkidle",
+ });
+
+ await expect(await page.getByTestId("author-header")).toBeInViewport();
+});
diff --git a/starters/nextjs-starter-ts/playwright.config.ts b/starters/nextjs-starter-ts/playwright.config.ts
new file mode 100644
index 00000000..56218cda
--- /dev/null
+++ b/starters/nextjs-starter-ts/playwright.config.ts
@@ -0,0 +1,79 @@
+import { defineConfig, devices } from '@playwright/test';
+
+/**
+ * Read environment variables from file.
+ * https://github.com/motdotla/dotenv
+ */
+// import dotenv from 'dotenv';
+// import path from 'path';
+// dotenv.config({ path: path.resolve(__dirname, '.env') });
+
+/**
+ * See https://playwright.dev/docs/test-configuration.
+ */
+export default defineConfig({
+ testDir: './playwright-tests',
+ /* Run tests in files in parallel */
+ fullyParallel: true,
+ /* Fail the build on CI if you accidentally left test.only in the source code. */
+ forbidOnly: !!process.env.CI,
+ /* Retry on CI only */
+ retries: process.env.CI ? 2 : 0,
+ /* Opt out of parallel tests on CI. */
+ workers: process.env.CI ? 1 : undefined,
+ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
+ reporter: 'html',
+ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
+ use: {
+ /* Base URL to use in actions like `await page.goto('/')`. */
+ // baseURL: 'http://127.0.0.1:3000',
+
+ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
+ trace: 'on-first-retry',
+ },
+
+ /* Configure projects for major browsers */
+ projects: [
+ {
+ name: 'chromium',
+ use: { ...devices['Desktop Chrome'] },
+ },
+
+ {
+ name: 'firefox',
+ use: { ...devices['Desktop Firefox'] },
+ },
+
+ {
+ name: 'webkit',
+ use: { ...devices['Desktop Safari'] },
+ },
+
+ /* Test against mobile viewports. */
+ // {
+ // name: 'Mobile Chrome',
+ // use: { ...devices['Pixel 5'] },
+ // },
+ // {
+ // name: 'Mobile Safari',
+ // use: { ...devices['iPhone 12'] },
+ // },
+
+ /* Test against branded browsers. */
+ // {
+ // name: 'Microsoft Edge',
+ // use: { ...devices['Desktop Edge'], channel: 'msedge' },
+ // },
+ // {
+ // name: 'Google Chrome',
+ // use: { ...devices['Desktop Chrome'], channel: 'chrome' },
+ // },
+ ],
+
+ /* Run your local dev server before starting the tests */
+ // webServer: {
+ // command: 'npm run start',
+ // url: 'http://127.0.0.1:3000',
+ // reuseExistingServer: !process.env.CI,
+ // },
+});
diff --git a/starters/nextjs-starter-ts/public/images/no-avatar.png b/starters/nextjs-starter-ts/public/images/no-avatar.png
new file mode 100644
index 00000000..ef404ce2
Binary files /dev/null and b/starters/nextjs-starter-ts/public/images/no-avatar.png differ
diff --git a/starters/nextjs-starter-ts/vite.config.js b/starters/nextjs-starter-ts/vite.config.js
index 1de88e54..761ffe98 100644
--- a/starters/nextjs-starter-ts/vite.config.js
+++ b/starters/nextjs-starter-ts/vite.config.js
@@ -1,5 +1,5 @@
-import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react";
+import { configDefaults, defineConfig } from "vitest/config";
export default defineConfig(({ mode }) => {
return {
@@ -9,6 +9,7 @@ export default defineConfig(({ mode }) => {
reportsDirectory: `./coverage`,
},
setupFiles: ["./setupVitest.js"],
+ exclude: [...configDefaults.exclude, "./playwright-tests/*"],
},
plugins: [react()],
};
diff --git a/starters/nextjs-starter/.gitignore b/starters/nextjs-starter/.gitignore
index 4c39b439..f6334b8e 100644
--- a/starters/nextjs-starter/.gitignore
+++ b/starters/nextjs-starter/.gitignore
@@ -49,3 +49,9 @@ out
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
+
+# playwright tests
+/test-results/
+/playwright-report/
+/blob-report/
+/playwright/.cache/
diff --git a/starters/nextjs-starter/components/article-view.jsx b/starters/nextjs-starter/components/article-view.jsx
index 22a6294e..540b96c7 100644
--- a/starters/nextjs-starter/components/article-view.jsx
+++ b/starters/nextjs-starter/components/article-view.jsx
@@ -3,7 +3,10 @@ import {
ArticleRenderer,
useArticleTitle,
} from "@pantheon-systems/pcc-react-sdk/components";
-import React, { useMemo } from "react";
+import Image from "next/image";
+import Link from "next/link";
+import React from "react";
+import { getSeoMetadata } from "../lib/utils";
import { clientSmartComponentMap } from "./smart-components";
const ELEMENT_STYLES_TO_OVERRIDE = [
@@ -14,81 +17,133 @@ const ELEMENT_STYLES_TO_OVERRIDE = [
/lineHeight/,
/height/,
];
+
const overrideElementStyles = (tag) => {
function resultFunc({ children, id, style, ...attrs }) {
const newStyles = { ...style };
+
ELEMENT_STYLES_TO_OVERRIDE.forEach((s) => {
Object.keys(newStyles).forEach((key) => {
if (s.test(key)) delete newStyles[key];
});
});
+
return React.createElement(
tag,
{ id, style: newStyles, ...attrs },
children,
);
}
+
return resultFunc;
};
-export default function ArticleView({ article, onlyContent }) {
- const { data } = useArticle(
- article.id,
- {
- publishingLevel: article.publishingLevel,
- contentType: "TREE_PANTHEON_V2",
- },
- {
- skip: article.publishingLevel !== "REALTIME",
- },
- );
-
- const hydratedArticle = useMemo(
- () => data?.article ?? article,
- [data, article],
- );
-
- return (
-
- );
-}
-
-export function StaticArticleView({ article, onlyContent }) {
- const articleTitle = useArticleTitle(article);
+const componentOverrideMap = {
+ h1: overrideElementStyles("h1"),
+ h2: overrideElementStyles("h2"),
+ h3: overrideElementStyles("h3"),
+ h4: overrideElementStyles("h4"),
+ h5: overrideElementStyles("h5"),
+ h6: overrideElementStyles("h6"),
+ p: overrideElementStyles("p"),
+ span: overrideElementStyles("span"),
+};
+const ArticleHeader = ({ article, articleTitle, seoMetadata }) => {
return (
- <>
-
+ {author} is a passionate content writer with a flair for turning
+ ideas into engaging stories. When she’s not writing, Jane enjoys
+ cozy afternoons with a good book, exploring new coffee spots, and
+ finding inspiration in everyday moments.
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export async function getServerSideProps({ query: { author } }) {
+ const {
+ data: articles,
+ totalCount,
+ cursor,
+ } = await PCCConvenienceFunctions.getPaginatedArticles({
+ pageSize: PAGE_SIZE,
+ metadataFilters: {
+ author,
+ },
+ });
+
+ return {
+ props: {
+ articles,
+ cursor,
+ totalCount,
+ author,
+ },
+ };
+}
diff --git a/starters/nextjs-starter/playwright-tests/basic-pages.spec.js b/starters/nextjs-starter/playwright-tests/basic-pages.spec.js
new file mode 100644
index 00000000..e3d152ae
--- /dev/null
+++ b/starters/nextjs-starter/playwright-tests/basic-pages.spec.js
@@ -0,0 +1,17 @@
+import { expect, test } from "@playwright/test";
+
+// This assumes that the first article to appear in the list will have an author.
+test("basic navigation", async ({ page }) => {
+ await page.goto("http://localhost:3000");
+
+ (await page.$("section .group button")).click();
+ await page.waitForURL("**/articles/**", {
+ waitUntil: "networkidle",
+ });
+ await page.getByTestId("author").click();
+ await page.waitForURL("**/authors/**", {
+ waitUntil: "networkidle",
+ });
+
+ await expect(await page.getByTestId("author-header")).toBeInViewport();
+});
diff --git a/starters/nextjs-starter/playwright.config.js b/starters/nextjs-starter/playwright.config.js
new file mode 100644
index 00000000..26605466
--- /dev/null
+++ b/starters/nextjs-starter/playwright.config.js
@@ -0,0 +1,79 @@
+import { defineConfig, devices } from "@playwright/test";
+
+/**
+ * Read environment variables from file.
+ * https://github.com/motdotla/dotenv
+ */
+// import dotenv from 'dotenv';
+// import path from 'path';
+// dotenv.config({ path: path.resolve(__dirname, '.env') });
+
+/**
+ * See https://playwright.dev/docs/test-configuration.
+ */
+export default defineConfig({
+ testDir: "./playwright-tests",
+ /* Run tests in files in parallel */
+ fullyParallel: true,
+ /* Fail the build on CI if you accidentally left test.only in the source code. */
+ forbidOnly: !!process.env.CI,
+ /* Retry on CI only */
+ retries: process.env.CI ? 2 : 0,
+ /* Opt out of parallel tests on CI. */
+ workers: process.env.CI ? 1 : undefined,
+ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
+ reporter: "html",
+ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
+ use: {
+ /* Base URL to use in actions like `await page.goto('/')`. */
+ // baseURL: 'http://127.0.0.1:3000',
+
+ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
+ trace: "on-first-retry",
+ },
+
+ /* Configure projects for major browsers */
+ projects: [
+ {
+ name: "chromium",
+ use: { ...devices["Desktop Chrome"] },
+ },
+
+ {
+ name: "firefox",
+ use: { ...devices["Desktop Firefox"] },
+ },
+
+ {
+ name: "webkit",
+ use: { ...devices["Desktop Safari"] },
+ },
+
+ /* Test against mobile viewports. */
+ // {
+ // name: 'Mobile Chrome',
+ // use: { ...devices['Pixel 5'] },
+ // },
+ // {
+ // name: 'Mobile Safari',
+ // use: { ...devices['iPhone 12'] },
+ // },
+
+ /* Test against branded browsers. */
+ // {
+ // name: 'Microsoft Edge',
+ // use: { ...devices['Desktop Edge'], channel: 'msedge' },
+ // },
+ // {
+ // name: 'Google Chrome',
+ // use: { ...devices['Desktop Chrome'], channel: 'chrome' },
+ // },
+ ],
+
+ /* Run your local dev server before starting the tests */
+ // webServer: {
+ // command: 'npm run start',
+ // url: 'http://127.0.0.1:3000',
+ // reuseExistingServer: !process.env.CI,
+ // },
+});
diff --git a/starters/nextjs-starter/public/images/no-avatar.png b/starters/nextjs-starter/public/images/no-avatar.png
new file mode 100644
index 00000000..ef404ce2
Binary files /dev/null and b/starters/nextjs-starter/public/images/no-avatar.png differ
diff --git a/starters/nextjs-starter/vite.config.js b/starters/nextjs-starter/vite.config.js
index 1de88e54..761ffe98 100644
--- a/starters/nextjs-starter/vite.config.js
+++ b/starters/nextjs-starter/vite.config.js
@@ -1,5 +1,5 @@
-import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react";
+import { configDefaults, defineConfig } from "vitest/config";
export default defineConfig(({ mode }) => {
return {
@@ -9,6 +9,7 @@ export default defineConfig(({ mode }) => {
reportsDirectory: `./coverage`,
},
setupFiles: ["./setupVitest.js"],
+ exclude: [...configDefaults.exclude, "./playwright-tests/*"],
},
plugins: [react()],
};