Skip to content

Commit

Permalink
Merge branch 'main' into 69-graphql-reorg
Browse files Browse the repository at this point in the history
* main:
  simpler eslint config (no custom babel) (#185)
  chore: node minor dep upgrades (#188)
  docs: env variable with graphcdn
  fix bug in postheader loading wrong scss file
  fix env.sample domain for graphcdn
  fix: meta not respecting social variables, customized title (#176)
  feat: log wp admin users into front-end preview mode (#173)
  fix: Get stylelint working again (#182)
  feat: Next 12 upgrade (#180)
  feat: bumping husky to version 7 (#179)
  resolve bug where clicking 'view' in wp-admin leads to a broken redirect (#165)
  feat: new convenience plugins in wordpress admin (#166)
  feat: theme mod instead of acf options for preview mode url (#163)
  • Loading branch information
ccorda committed Dec 28, 2021
2 parents 77ec44e + 76e4a6d commit ab9a650
Show file tree
Hide file tree
Showing 45 changed files with 1,249 additions and 1,783 deletions.
18 changes: 2 additions & 16 deletions .vscode/settings.json
Expand Up @@ -2,22 +2,8 @@
// Global default is to use VS Code prettier plugin
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
// For our JS and SCSS Files, we want project defined versions
// "[javascript]": {
// "editor.formatOnSave": false
// },
// "[javascriptreact]": {
// "editor.formatOnSave": false
// },
// "[typescript]": {
// "editor.formatOnSave": false
// },
// "[typescriptreact]": {
// "editor.formatOnSave": false
// },
"[scss]": {
"editor.formatOnSave": false
},
// As of version 14, stylelint needs to be told to validate SCSS
"stylelint.validate": ["css", "scss"],
// Turn on auto-fixing for project defined linters.
"editor.codeActionsOnSave": {
// For ESLint
Expand Down
25 changes: 21 additions & 4 deletions docs/preview-mode.md
Expand Up @@ -20,14 +20,31 @@ Our goal with implementing this was to use built in Next.js and WordPress functi

# Setup Instructions

1. On your production/staging WordPress (which isn't versioned and on WP Engine requires SFTP/SSH) open `wp-config.php`, scroll to the bottom, and add two variables which help configure preview mode. These are used to help secure the authentication tokens therefore need to be randomly generated for each project. [You can get a strong random key from GRC's passwords page.](https://www.grc.com/passwords.htm) Make sure both values are different.
## Production Instance

1. On your production/staging WordPress `wp-config.php`, scroll to the bottom, and add two variables which help configure preview mode. These are used to help secure the authentication tokens therefore need to be randomly generated for each project. [You can get a strong random key from GRC's passwords page.](https://www.grc.com/passwords.htm) Make sure both values are different.

```
define('HEADLESS_AUTH_SECRET', 'bubs-next-wp-auth-secret-key');
define('HEADLESS_API_SECRET', 'bubs-next-headless-secret-key');
```

2. Save `HEADLESS_API_SECRET` to Vercel as an environment variable, this is needed to authenticate API calls to WordPress to securely generate the logged in user's access token to activate preview mode. `HEADLESS_AUTH_SECRET` only lives inside WordPress to encrypt the access token, DO NOT copy/use this value outside of the setting in `wp-config.php`. Set `WORDPRESS_DOMAIN` to the root URL of the Wordpress instance (without /graphql)
_On WP Engine, these values aren't version controlled so you'll need to SFTP or SSH in to work on them_

2. Save `HEADLESS_API_SECRET` to Vercel as an environment variable, this is needed to authenticate API calls to WordPress to securely generate the logged in user's access token to activate preview mode. `HEADLESS_AUTH_SECRET` only lives inside WordPress to encrypt the access token, DO NOT copy/use this value outside of the setting in `wp-config.php`.

3. Set `WORDPRESS_DOMAIN` to the root URL of the Wordpress instance (without /graphql)

4. Open `wordpress/wp-content/headless/functions.php` and edit the values for `$preview_domain` for production.

5. Preview mode should now work. You can test by logging into Wordpress admin, creating a post (but don't publish!) and then click "Preview". If it all works, you should be able to view your post with a black bar on the top of the page indicating preview mode is enabled.

## Preview Instances

By default, we tend to configure preview branches to use production graphql, so they can run against live data. This works great for previewing front-end changes.

Sometimes we want a staging WordPress where we can test changes there.

3. Open `wordpress/wp-content/headless/functions.php` and edit the values for `$preview_domain` for staging and production. The staging URL can be also be set from within the Wordpress dashboard under `Headless Settings` menu item, but make sure a generic preview URL is selected as a fallback.
Vercel supports per branch env variables, which allows us to setup a combination of WP backend and Next front-end for a specific branch. You'll want to set the `WORDPRESS_DOMAIN` and `WORDPRESS_API_URL` variables to point to your staging WordPress. The `HEADLESS_AUTH_SECRET` and `HEADLESS_API_SECRET` can be the same values on both production and staging.

4. Preview mode should now work. You can test by logging into Wordpress admin, creating a post (but don't publish!) and then click "Preview". If it all works, you should be able to view your post with a black bar on the top of the page indicating preview mode is enabled.
To tie together your staging WordPress with your preview frontend, you need to set one variable in WordPress. Inside of WordPress, go to Appearance > Customization section in the theme. Under the "Headless" options heading, you can set the URL for your preview instance.
32 changes: 32 additions & 0 deletions docs/seo.md
@@ -0,0 +1,32 @@
# SEO

## Web performance

Optimizing websites for speed is one of our core reasons for pursuing a headless approach with WordPress. Since 2018, [page speed has been a factor for Google](https://developers.google.com/web/updates/2018/07/search-ads-speed) in ranking both paid search and ads. In 2021, it took on increased prominence in organic results with an increased emphasis on [Core Web Vitals and page experience](https://developers.google.com/search/blog/2021/04/more-details-page-experience).

Next.js does much of this for us out of the box, but there are a few things to be aware of with our implementation.

- Using [next/image](https://nextjs.org/docs/api-reference/next/image) to generate responsive images. For more, read our image handling docs.
- Using [next/link](https://nextjs.org/docs/api-reference/next/link) to handle transitions between pages.

## Per page meta tags

The primary way we tackle this is to combine the [WordPress Yoast SEO plugin](https://yoast.com/wordpress/plugins/seo/) with [next-seo](https://github.com/garmeeh/next-seo) to allow editors to populate each page with unique meta tags optimized for both search and social networks.

Inside of our [Meta component](../website/src/components/Meta.js), we merge together defaults defined in `lib/constants.js` and merge in passed in yoast data pulled from graphql via globals context.

Inside of the constants file, you'll want to set per site values like site name and favicons. An important variable to set is the `META.url` to the production domain. This is used to rewrite relative images and relative links from sitemaps and feeds so that those are only indexed from your production URL.

## Sitemaps and RSS Feeds

One approach to sitemaps is to [generate them in Next](https://www.npmjs.com/package/next-sitemap). You can similarly create a feed using [some custom code](https://ashleemboyer.com/how-i-added-an-rss-feed-to-my-nextjs-site) and the feed npm package.

We find however that WordPress already does a great job at this, including specific support for features like Google News indexing through a variety of well tested plugins. We've therefore implemented a lightweight proxy that grabs sitemap files from WordPress, serving them on your domain direct to search engines. Inside of [next.config.js](../website/next.config.js) we set a couple rewrites that point any requests to `*sitemap.xml` or `feed*` and retrieve them from the WordPress origin. We rewrite any absolute links so that they point to your next.js public domain.

## Further reading

- [Lighthouse SEO audits](https://web.dev/lighthouse-seo/)
- [Next.js introduction to SEO](https://nextjs.org/learn/seo/introduction-to-seo/importance-of-seo)
- Next.js 12 [bot aware ISR fallback](https://nextjs.org/blog/next-12#bot-aware-isr-fallback)
- [Core web vitals report](https://support.google.com/webmasters/answer/9205520?hl=en)
- [Vercel Analytics](https://vercel.com/analytics)
10 changes: 6 additions & 4 deletions website/.env.sample
Expand Up @@ -5,15 +5,17 @@
# NEXT_PUBLIC_GTM_ID=GTM-123456

# Configure the primary Wordpress backend domain
# This is used as the default graphQL domain and for
# This is used as the default graphQL domain and for
# other API calls involving preview mode
#WORDPRESS_DOMAIN=localhost:8000
WORDPRESS_DOMAIN=bubsnext.wpengine.com

# When GraphCDN is used, it'll be a different URL
# When GraphCDN is used, it'll be a different URL
# than the Wordpress backend. Define it specifically here
# Including the preceding HTTPS
#WORDPRESS_API_URL=https://bubsnext.graphcdn.io
# WORDPRESS_API_URL=http://localhost:8000/graphql
# WORDPRESS_API_URL=https://bubsnext.wpengine.com/graphq
WORDPRESS_API_URL=https://bubsnext.graphcdn.app

# When GraphCDN is used, cache purging can be enabled
# whenever a Wordpress post is created or updated. Grab
Expand All @@ -22,7 +24,7 @@ WORDPRESS_DOMAIN=bubsnext.wpengine.com
#GRAPHCDN_PURGE_API_URL=https://admin.graphcdn.io/bubsnext

# Preview secret for localhosting only
HEADLESS_API_SECRET=bubs-next-headless-secret-key
# HEADLESS_API_SECRET=bubs-next-headless-secret-key

# Enable bundle analyzer
# ANALYZE=true
15 changes: 5 additions & 10 deletions website/.eslintrc.json
Expand Up @@ -7,20 +7,12 @@
},
"plugins": ["react", "import"],
"extends": [
"next",
"eslint:recommended",
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"plugin:@next/next/recommended",
"prettier"
],
"parser": "babel-eslint",
"parserOptions": {
"ecmaFeatures": {
"jsx": true // enable linting for jsx files
},
"ecmaVersion": 11,
"sourceType": "module"
},
"settings": {
"react": {
"version": "detect"
Expand Down Expand Up @@ -66,6 +58,9 @@
"react/display-name": 1,

// React Hooks
"react-hooks/exhaustive-deps": "off"
"react-hooks/exhaustive-deps": "off",

// Next
"@next/next/no-page-custom-font": "off"
}
}
4 changes: 1 addition & 3 deletions website/.githooks/post-merge.sh → website/.husky/post-merge 100644 → 100755
@@ -1,4 +1,5 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

function changed {
git diff --name-only HEAD@{1} HEAD | grep "^$1" > /dev/null 2>&1
Expand All @@ -7,6 +8,3 @@ function changed {
if changed 'yarn.lock'; then
echo "📦 yarn.lock changed. Run yarn install to bring your dependencies up to date."
fi

# todo: check for changes to acf-json folder
chmod -R 777 ../wordpress/wp-content/themes/headless/acf-json
5 changes: 5 additions & 0 deletions website/.husky/pre-commit
@@ -0,0 +1,5 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

cd website
yarn lint-staged
12 changes: 10 additions & 2 deletions website/.stylelintrc.json
Expand Up @@ -2,16 +2,24 @@
"extends": [
"stylelint-config-idiomatic-order",
"stylelint-config-standard",
"stylelint-config-standard-scss",
"stylelint-prettier/recommended"
],
"fix": true,
"formatter": "verbose",
"plugins": ["stylelint-scss"],
"rules": {
"at-rule-no-unknown": null,
"scss/at-rule-no-unknown": true,
"selector-pseudo-class-no-unknown": null,
"max-nesting-depth": 3,
"scss/comment-no-empty": null,
"selector-class-pattern": null,
"selector-id-pattern": null,
"scss/at-import-no-partial-leading-underscore": null,
"scss/dollar-variable-pattern": null,
"scss/no-global-function-names": null,
"scss/at-extend-no-missing-placeholder": null,
"declaration-block-no-redundant-longhand-properties": null,
"color-function-notation": "legacy",
"at-rule-empty-line-before": [
"always",
{
Expand Down
15 changes: 4 additions & 11 deletions website/next.config.js
Expand Up @@ -21,6 +21,7 @@ module.exports = withBundleAnalyzer({

async rewrites() {
return [
// these two rules are used to locally serve (and rewrite urls) from WP
{
source: '/(.*)sitemap.xml',
destination: '/api/upstream-proxy',
Expand All @@ -29,19 +30,11 @@ module.exports = withBundleAnalyzer({
source: '/feed',
destination: '/api/upstream-proxy',
},
];
},

// For some projects, /wp-content upload paths still need to resolve
// Proxy the resource up to Wordpress. Uncomment to enable.
/*async redirects() {
return [
// resolve relative links to WP upload assets
{
source: '/wp-content/uploads/:path*',
destination:
'https://bubsnext.wpengine.com/wp-content/uploads/:path*',
permanent: true,
destination: `https://${process.env.WORDPRESS_DOMAIN}/wp-content/uploads/:path*`,
},
];
},*/
},
});
53 changes: 24 additions & 29 deletions website/package.json
Expand Up @@ -15,50 +15,45 @@
"stylelint:fix": "npx stylelint '**/*.scss' --fix",
"eslint": "npx eslint '**/*.{js,jsx,ts,tsx}'",
"eslint:fix": "npx eslint '**/*.{js,jsx,ts,tsx}' --fix",
"analyze": "cross-env ANALYZE=true yarn build"
"analyze": "cross-env ANALYZE=true yarn build",
"prepare": "cd .. && husky install website/.husky && chmod ug+x website/.husky/* || true"
},
"author": "Patronage.org",
"license": "private",
"dependencies": {
"@popperjs/core": "^2.9.3",
"bootstrap": "^5.1.0",
"@popperjs/core": "^2.11.0",
"bootstrap": "^5.1.3",
"classnames": "^2.3.1",
"dayjs": "^1.10.6",
"dayjs": "^1.10.7",
"isomorphic-unfetch": "^3.1.0",
"next": "^11.1.0",
"next-seo": "^4.26.0",
"next": "^12.0.7",
"next-seo": "^4.28.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-icons": "^4.2.0",
"sass": "^1.38.1",
"slugify": "^1.6.0",
"react-icons": "^4.3.1",
"sass": "^1.45.1",
"slugify": "^1.6.4",
"widont": "^0.3.3"
},
"devDependencies": {
"@next/bundle-analyzer": "^11.1.0",
"@next/bundle-analyzer": "^12.0.7",
"babel-eslint": "^10.1.0",
"cross-env": "^7.0.3",
"eslint": "^7.32.0",
"eslint-config-next": "^11.1.0",
"eslint": "^8.5.0",
"eslint-config-next": "^12.0.7",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.24.2",
"eslint-plugin-react": "^7.24.0",
"eslint-plugin-react-hooks": "^4.2.0",
"husky": "^4.3.8",
"lint-staged": "^11.1.2",
"prettier": "^2.3.2",
"stylelint": "^13.13.1",
"eslint-plugin-import": "^2.25.3",
"eslint-plugin-react": "^7.28.0",
"eslint-plugin-react-hooks": "^4.3.0",
"husky": "^7.0.4",
"lint-staged": "^12.1.4",
"prettier": "^2.5.1",
"stylelint": "^14.2.0",
"stylelint-config-idiomatic-order": "^8.1.0",
"stylelint-config-prettier": "^8.0.2",
"stylelint-config-standard": "^22.0.0",
"stylelint-prettier": "^1.2.0",
"stylelint-scss": "^3.20.1"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged",
"post-merge": "./.githooks/post-merge.sh"
}
"stylelint-config-prettier": "^9.0.3",
"stylelint-config-standard-scss": "^3.0.0",
"stylelint-prettier": "^2.0.0",
"stylelint-scss": "^4.1.0"
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": "eslint --fix",
Expand Down
2 changes: 1 addition & 1 deletion website/src/components/Footer.module.scss
@@ -1,4 +1,4 @@
@import 'styles/_scaffold';
@import 'styles/scaffold';

.footer {
border-top: 4px solid $primary;
Expand Down
3 changes: 2 additions & 1 deletion website/src/components/Header.module.scss
@@ -1,4 +1,4 @@
@import 'styles/_scaffold';
@import 'styles/scaffold';

.header {
padding-top: 30px;
Expand Down Expand Up @@ -47,6 +47,7 @@
background: rgba($primary, 1);
color: $white;
opacity: 0;

// transition: all 0.5s ease-in-out;
// transition: opacity 1.35s ease-in-out;
transition: opacity 1s ease-in-out;
Expand Down

0 comments on commit ab9a650

Please sign in to comment.