Compare commits

..

1 Commits

Author SHA1 Message Date
Dan Ribbens
c02e4df4bc chore: wip sort with 2023-08-29 08:31:59 -04:00
5302 changed files with 169696 additions and 359756 deletions

View File

@@ -19,11 +19,3 @@ indent_size = 2
[*.mdx]
indent_style = space
indent_size = 2
[*.json]
indent_style = space
indent_size = 2
[*.md]
indent_style = space
indent_size = 2

View File

@@ -1,13 +0,0 @@
.tmp
**/.git
**/.hg
**/.pnp.*
**/.svn
**/.yarn/**
**/build
**/dist/**
**/node_modules
**/temp
playwright.config.ts
jest.config.js
test/live-preview/next-app

View File

@@ -1,64 +0,0 @@
/** @type {import('eslint').Linter.Config} */
module.exports = {
extends: ['@payloadcms'],
ignorePatterns: ['README.md', 'packages/**/*.spec.ts'],
overrides: [
{
files: ['packages/**'],
plugins: ['payload'],
rules: {
'payload/no-jsx-import-statements': 'warn',
},
},
{
files: ['scripts/**'],
rules: {
'@typescript-eslint/no-unused-vars': 'off',
'no-console': 'off',
'perfectionist/sort-object-types': 'off',
'perfectionist/sort-objects': 'off',
},
},
{
extends: ['plugin:@typescript-eslint/disable-type-checked'],
files: ['*.js', '*.cjs', '*.json', '*.md', '*.yml', '*.yaml'],
},
{
files: ['packages/eslint-config-payload/**'],
rules: {
'perfectionist/sort-objects': 'off',
},
},
{
files: ['package.json', 'tsconfig.json'],
rules: {
'perfectionist/sort-array-includes': 'off',
'perfectionist/sort-astro-attributes': 'off',
'perfectionist/sort-classes': 'off',
'perfectionist/sort-enums': 'off',
'perfectionist/sort-exports': 'off',
'perfectionist/sort-imports': 'off',
'perfectionist/sort-interfaces': 'off',
'perfectionist/sort-jsx-props': 'off',
'perfectionist/sort-keys': 'off',
'perfectionist/sort-maps': 'off',
'perfectionist/sort-named-exports': 'off',
'perfectionist/sort-named-imports': 'off',
'perfectionist/sort-object-types': 'off',
'perfectionist/sort-objects': 'off',
'perfectionist/sort-svelte-attributes': 'off',
'perfectionist/sort-union-types': 'off',
'perfectionist/sort-vue-attributes': 'off',
},
},
],
parserOptions: {
project: ['./tsconfig.json'],
tsconfigRootDir: __dirname,
EXPERIMENTAL_useSourceOfProjectReferenceRedirect: true,
EXPERIMENTAL_useProjectService: true,
sourceType: 'module',
ecmaVersion: 'latest',
},
root: true,
}

104
.eslintrc.js Normal file
View File

@@ -0,0 +1,104 @@
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module',
ecmaFeatures: {
jsx: true,
},
},
plugins: [
'@typescript-eslint',
],
extends: [
'./eslint-config',
],
settings: {
'import/resolver': {
node: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
},
},
overrides: [
{
files: ['test/**/int.spec.ts'],
rules: {
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/consistent-type-imports': 'warn',
'jest/prefer-strict-equal': 'off',
}
},
{
files: ['test/**/e2e.spec.ts'],
extends: [
'plugin:playwright/playwright-test'
],
rules: {
'jest/consistent-test-it': 'off',
'jest/require-top-level-describe': 'off',
'jest/no-test-callback': 'off',
'jest/prefer-strict-equal': 'off',
'jest/expect-expect': 'off',
'jest-dom/prefer-to-have-attribute': 'off',
}
},
{
files: ['*.ts', '*.tsx'],
parser: '@typescript-eslint/parser',
extends: [
'plugin:@typescript-eslint/recommended',
],
rules: {
'no-shadow': 'off',
'@typescript-eslint/no-shadow': ['error'],
'import/no-unresolved': [
2,
{
ignore: [
'payload-config',
'payload/generated-types',
],
},
],
},
},
{
files: ['*.spec.ts'],
rules: {
'@typescript-eslint/no-use-before-define': 'off',
},
},
{
files: ['*.e2e.ts'],
rules: {
'@typescript-eslint/no-use-before-define': 'off',
'jest/expect-expect': 'off',
},
},
],
rules: {
'import/no-extraneous-dependencies': ['error', { packageDir: './' }],
'react/jsx-filename-extension': [2, { extensions: ['.js', '.jsx', '.ts', '.tsx'] }],
'import/prefer-default-export': 'off',
'react/prop-types': 'off',
'react/require-default-props': 'off',
'react/no-unused-prop-types': 'off',
'no-console': 'warn',
'no-sparse-arrays': 'off',
'no-underscore-dangle': 'off',
'no-use-before-define': 'off',
'arrow-body-style': 0,
'@typescript-eslint/no-use-before-define': 'off',
'import/extensions': [
'error',
'ignorePackages',
{
js: 'never',
jsx: 'never',
ts: 'never',
tsx: 'never',
},
],
},
};

View File

@@ -1,21 +0,0 @@
# lint and format
ae7d6f97d205491390f15850e5104c7abded1550
1fbda85cd04a774cb978778b0f813001664c53dd
# prettier all templates
75a428ddc4672903455998eaba7ae9f9d710bf85
# re-run prettier and eslint everywhere again
cdaa0acd61d3001407609915bd573b78565d5571
# prettier write again
dfac7395fed95fc5d8ebca21b786ce70821942bb
# lint and format plugin-cloud
fb7d1be2f3325d076b7c967b1730afcef37922c2
# lint and format create-payload-app
5fd3d430001efe86515262ded5e26f00c1451181
# 3.0 prettier & lint everywhere
6789e61488a1d3de56f472ac3214faf344030005

41
.github/CODEOWNERS vendored
View File

@@ -1,41 +0,0 @@
# Order matters. The last matching pattern takes precedence.
### Core ###
/packages/payload/src/uploads/ @denolfe
/packages/payload/src/admin/ @jmikrut @jacobsfletch @JarrodMFlesch
### Adapters ###
/packages/db-*/ @denolfe @jmikrut @DanRibbens
/packages/richtext-*/ @denolfe @jmikrut @DanRibbens @AlessioGr
### Plugins ###
/packages/plugin-*/ @denolfe @jmikrut @DanRibbens
/packages/plugin-cloud*/ @denolfe
/packages/plugin-form-builder/ @jacobsfletch
/packages/plugin-live-preview*/ @jacobsfletch
/packages/plugin-nested-docs/ @jacobsfletch
/packages/plugin-redirects/ @jacobsfletch
/packages/plugin-search/ @jacobsfletch
/packages/plugin-sentry/ @JessChowdhury
/packages/plugin-seo/ @jacobsfletch
/packages/plugin-stripe/ @jacobsfletch
### Examples ###
/examples/ @jacobsfletch
/examples/testing/ @JarrodMFlesch
/examples/email/ @JessChowdhury
/examples/whitelabel/ @JessChowdhury
### Templates ###
/templates/ @jacobsfletch @denolfe
### Misc ###
/packages/create-payload-app/ @denolfe
/packages/eslint-config-payload/ @denolfe
/packages/payload-admin-bar/ @jacobsfletch
### Root ###
/package.json @denolfe
/scripts/ @denolfe
/.github/ @denolfe
/.github/CODEOWNERS @denolfe

View File

@@ -1,6 +1,6 @@
name: Bug Report
description: Create a bug report for Payload
labels: ['[possible-bug]']
labels: ["possible-bug"]
body:
- type: markdown
attributes:
@@ -10,12 +10,7 @@ body:
id: reproduction-link
attributes:
label: Link to reproduction
description: Want us to look into your issue faster? Follow the [reproduction-guide](https://github.com/payloadcms/payload/blob/main/.github/reproduction-guide.md) for more information.
validations:
required: false
- type: textarea
attributes:
label: Describe the Bug
description: Please add a link to a reproduction. See the fork [reproduction-guide](https://github.com/payloadcms/payload/blob/master/.github/reproduction-guide.md) for more information.
validations:
required: true
- type: textarea
@@ -24,6 +19,11 @@ body:
description: Steps to reproduce the behavior, please provide a clear description of how to reproduce the issue, based on the linked minimal reproduction. Screenshots can be provided in the issue body below. If using code blocks, make sure that [syntax highlighting is correct](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlighting) and double check that the rendered preview is not broken.
validations:
required: true
- type: textarea
attributes:
label: Describe the Bug
validations:
required: true
- type: input
id: version
attributes:
@@ -31,11 +31,6 @@ body:
description: What version of Payload are you running?
validations:
required: true
- type: input
id: adapters-plugins
attributes:
label: Adapters and Plugins
description: What adapters and plugins are you using? ie. db-mongodb, db-postgres, bundler-webpack, etc.
- type: markdown
attributes:
value: Before submitting the issue, go through the steps you've written down to make sure the steps provided are detailed and clear.

View File

@@ -2,7 +2,7 @@
<!-- Please include a summary of the pull request and any related issues it fixes. Please also include relevant motivation and context. -->
- [ ] I have read and understand the [CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md) document in this repository.
- [ ] I have read and understand the [CONTRIBUTING.md](../CONTRIBUTING.md) document in this repository.
## Type of change
@@ -12,8 +12,8 @@
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Change to the [templates](https://github.com/payloadcms/payload/tree/main/templates) directory (does not affect core functionality)
- [ ] Change to the [examples](https://github.com/payloadcms/payload/tree/main/examples) directory (does not affect core functionality)
- [ ] Change to the [templates](../templates/) directory (does not affect core functionality)
- [ ] Change to the [examples](../examples/) directory (does not affect core functionality)
- [ ] This change requires a documentation update
## Checklist:

View File

@@ -1,16 +1,14 @@
# Reproduction Guide
1. [Fork](https://github.com/payloadcms/payload/fork) this repo
2. Optionally, create a new branch for your reproduction
3. Run `pnpm install` to install dependencies
4. Open up the `test/_community` directory
5. Add any necessary `collections/globals/fields` in this directory to recreate the issue you are experiencing
6. Run `pnpm dev _community` to start the admin panel
1. [fork](https://github.com/payloadcms/payload/fork) this repo
2. run `yarn` to install dependencies
3. open up the `test/_community` directory
4. add any necessary `collections/globals/fields` in this directory to recreate the issue you are experiencing
5. run `yarn dev _community` to start the admin panel
**NOTE:** The goal is to isolate the problem by reducing the number of `collections/globals/fields` you add to the `test/_community` folder. This folder is _not_ meant for you to copy your project into, but rather recreate the issue you are experiencing with minimal config.
## Example test directory file tree
```text
.
├── config.ts
@@ -22,43 +20,39 @@
- `config.ts` - This is the _granular_ Payload config for testing. It should be as lightweight as possible. Reference existing configs for an example
- `int.spec.ts` [Optional] - This is the test file run by jest. Any test file must have a `*int.spec.ts` suffix.
- `e2e.spec.ts` [Optional] - This is the end-to-end test file that will load up the admin UI using the above config and run Playwright tests.
- `payload-types.ts` - Generated types from `config.ts`. Generate this file by running `pnpm dev:generate-types _community`.
- `payload-types.ts` - Generated types from `config.ts`. Generate this file by running `yarn dev:generate-types _community`.
The directory split up in this way specifically to reduce friction when creating tests and to add the ability to boot up Payload with that specific config. You should modify the files in `test/_community` to get started.
<br />
## Testing is optional but encouraged
An issue does not need to have failing tests — reproduction steps with your forked repo are enough at this point. Some people like to dive deeper and we want to give you the guidance/tools to do so. Read more below:
### Running integration tests (Payload API tests)
First install [Jest Runner for VSVode](https://marketplace.visualstudio.com/items?itemName=firsttris.vscode-jest-runner).
There are a couple ways run integration tests:
- **Granularly** - you can run individual tests in vscode by installing the Jest Runner plugin and using that to run individual tests. Clicking the `debug` button will run the test in debug mode allowing you to set break points.
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/payload/src/admin/assets/images/github/int-debug.png" />
<img src="https://raw.githubusercontent.com/payloadcms/payload/master/src/admin/assets/images/github/int-debug.png" />
- **Manually** - you can run all int tests in the `/test/_community/int.spec.ts` file by running the following command:
```bash
pnpm test:int _community
yarn test:int _community
```
### Running E2E tests (Admin Panel UI tests)
The easiest way to run E2E tests is to install
- [Playwright Test for VSCode](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright)
- [Playwright Runner](https://marketplace.visualstudio.com/items?itemName=ortoni.ortoni)
Once they are installed you can open the `testing` tab in vscode sidebar and drill down to the test you want to run, i.e. `/test/_community/e2e.spec.ts`
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/payload/src/admin/assets/images/github/e2e-debug.png" />
<img src="https://raw.githubusercontent.com/payloadcms/payload/master/src/admin/assets/images/github/e2e-debug.png" />
#### Notes
The default credentials are `dev@payloadcms.com` as email and `test` as password. They can be found in `test/credentials.ts`. By default, these will be autofilled, so no log-in is required.
- It is recommended to add the test credentials (located in `test/credentials.ts`) to your autofill for `localhost:3000/admin` as this will be required on every nodemon restart. The default credentials are `dev@payloadcms.com` as email and `test` as password.

View File

@@ -1,370 +0,0 @@
name: build
on:
pull_request:
types: [opened, reopened, synchronize]
push:
branches: ['main', 'alpha']
jobs:
changes:
runs-on: ubuntu-latest
permissions:
pull-requests: read
outputs:
needs_build: ${{ steps.filter.outputs.needs_build }}
templates: ${{ steps.filter.outputs.templates }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 25
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
needs_build:
- '.github/workflows/**'
- 'packages/**'
- 'test/**'
- 'pnpm-lock.yaml'
- 'package.json'
templates:
- 'templates/**'
- name: Log all filter results
run: |
echo "needs_build: ${{ steps.filter.outputs.needs_build }}"
echo "templates: ${{ steps.filter.outputs.templates }}"
core-build:
needs: changes
if: ${{ needs.changes.outputs.needs_build == 'true' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 25
- name: Use Node.js 18
uses: actions/setup-node@v4
with:
node-version: 18
- name: Install pnpm
uses: pnpm/action-setup@v3
with:
version: 8
run_install: false
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v4
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- run: pnpm install
- run: pnpm run build:core
- name: Cache build
uses: actions/cache@v4
with:
path: ./*
key: ${{ github.sha }}-${{ github.run_number }}
plugins-build:
needs: changes
if: ${{ needs.changes.outputs.needs_build == 'true' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 25
- name: Use Node.js 18
uses: actions/setup-node@v4
with:
node-version: 18
- name: Install pnpm
uses: pnpm/action-setup@v3
with:
version: 8
run_install: false
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v4
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- run: pnpm install
- run: pnpm run build:plugins
tests:
runs-on: ubuntu-latest
needs: core-build
strategy:
fail-fast: false
matrix:
database:
- mongodb
- postgres
- postgres-custom-schema
- postgres-uuid
- supabase
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: payloadtests
AWS_ENDPOINT_URL: http://127.0.0.1:4566
AWS_ACCESS_KEY_ID: localstack
AWS_SECRET_ACCESS_KEY: localstack
AWS_REGION: us-east-1
steps:
- name: Use Node.js 18
uses: actions/setup-node@v4
with:
node-version: 18
- name: Install pnpm
uses: pnpm/action-setup@v3
with:
version: 8
run_install: false
- name: Restore build
uses: actions/cache@v4
with:
path: ./*
key: ${{ github.sha }}-${{ github.run_number }}
- name: Start LocalStack
run: pnpm docker:start
- name: Start PostgreSQL
uses: CasperWA/postgresql-action@v1.2
with:
postgresql version: '14' # See https://hub.docker.com/_/postgres for available versions
postgresql db: ${{ env.POSTGRES_DB }}
postgresql user: ${{ env.POSTGRES_USER }}
postgresql password: ${{ env.POSTGRES_PASSWORD }}
if: startsWith(matrix.database, 'postgres')
- name: Install Supabase CLI
uses: supabase/setup-cli@v1
with:
version: latest
if: matrix.database == 'supabase'
- name: Initialize Supabase
run: |
supabase init
supabase start
if: matrix.database == 'supabase'
- name: Wait for PostgreSQL
run: sleep 30
if: startsWith(matrix.database, 'postgres')
- name: Configure PostgreSQL
run: |
psql "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" -c "CREATE ROLE runner SUPERUSER LOGIN;"
psql "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" -c "SELECT version();"
echo "POSTGRES_URL=postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" >> $GITHUB_ENV
if: startsWith(matrix.database, 'postgres')
- name: Configure PostgreSQL with custom schema
run: |
psql "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" -c "CREATE SCHEMA custom;"
if: matrix.database == 'postgres-custom-schema'
- name: Configure Supabase
run: |
echo "POSTGRES_URL=postgresql://postgres:postgres@127.0.0.1:54322/postgres" >> $GITHUB_ENV
if: matrix.database == 'supabase'
- name: Integration Tests
run: pnpm test:int --testPathIgnorePatterns=test/fields # Ignore fields tests until reworked
env:
NODE_OPTIONS: --max-old-space-size=8096
PAYLOAD_DATABASE: ${{ matrix.database }}
POSTGRES_URL: ${{ env.POSTGRES_URL }}
tests-e2e:
runs-on: ubuntu-latest
needs: core-build
strategy:
fail-fast: false
matrix:
# find test -type f -name 'e2e.spec.ts' | sort | xargs dirname | xargs -I {} basename {}
suite:
- _community
- access-control
# - admin
- auth
# - field-error-states
# - fields-relationship
# - fields
- fields/lexical
- live-preview
# - localization
# - plugin-nested-docs
# - plugin-seo
# - refresh-permissions
# - uploads
# - versions
steps:
- name: Use Node.js 18
uses: actions/setup-node@v4
with:
node-version: 18
- name: Install pnpm
uses: pnpm/action-setup@v3
with:
version: 8
run_install: false
- name: Restore build
uses: actions/cache@v4
with:
path: ./*
key: ${{ github.sha }}-${{ github.run_number }}
- name: Install Playwright
run: pnpm exec playwright install
- name: E2E Tests
uses: nick-fields/retry@v3
with:
retry_on: error
max_attempts: 2
timeout_minutes: 15
command: pnpm test:e2e ${{ matrix.suite }}
- uses: actions/upload-artifact@v4
if: always()
with:
name: test-results
path: test/test-results/
retention-days: 1
tests-type-generation:
if: false # This should be replaced with gen on a real Payload project
runs-on: ubuntu-latest
needs: core-build
steps:
- name: Use Node.js 18
uses: actions/setup-node@v4
with:
node-version: 18
- name: Install pnpm
uses: pnpm/action-setup@v3
with:
version: 8
run_install: false
- name: Restore build
uses: actions/cache@v4
with:
path: ./*
key: ${{ github.sha }}-${{ github.run_number }}
- name: Generate Payload Types
run: pnpm dev:generate-types fields
- name: Generate GraphQL schema file
run: pnpm dev:generate-graphql-schema graphql-schema-gen
plugins:
runs-on: ubuntu-latest
needs: core-build
strategy:
fail-fast: false
matrix:
pkg:
- create-payload-app
- plugin-cloud
- plugin-cloud-storage
- plugin-form-builder
- plugin-nested-docs
- plugin-search
- plugin-sentry
- plugin-seo
steps:
- name: Use Node.js 18
uses: actions/setup-node@v4
with:
node-version: 18
- name: Install pnpm
uses: pnpm/action-setup@v3
with:
version: 8
run_install: false
- name: Restore build
uses: actions/cache@v4
with:
path: ./*
key: ${{ github.sha }}-${{ github.run_number }}
- name: Build ${{ matrix.pkg }}
run: pnpm turbo run build --filter=${{ matrix.pkg }}
- name: Test ${{ matrix.pkg }}
run: pnpm --filter ${{ matrix.pkg }} run test
templates:
needs: changes
if: false # Disable until templates are updated for 3.0
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
template: [blank, website, ecommerce]
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 25
- name: Use Node.js 18
uses: actions/setup-node@v4
with:
node-version: 18
- name: Start MongoDB
uses: supercharge/mongodb-github-action@1.10.0
with:
mongodb-version: 6.0
- name: Build Template
run: |
cd templates/${{ matrix.template }}
cp .env.example .env
yarn install
yarn build
yarn generate:types

88
.github/workflows/tests.yml vendored Normal file
View File

@@ -0,0 +1,88 @@
name: build
on:
pull_request:
types: [opened, reopened, synchronize]
push:
branches: ["master"]
jobs:
build_yarn:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x, 18.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org
scope: "@payloadcms"
always-auth: true
- name: Cache node modules
uses: actions/cache@v1
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- run: yarn
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- run: yarn build
- name: Component Tests
run: yarn test:components
- name: Integration Tests
run: yarn test:int
- name: Generate Payload Types
run: yarn dev:generate-types fields
- name: Generate GraphQL schema file
run: yarn dev:generate-graphql-schema graphql-schema-gen
- name: Install Playwright Browsers
run: npx playwright install --with-deps
- name: E2E Tests
run: yarn test:e2e --bail
- uses: actions/upload-artifact@v3
if: always()
with:
name: test-results
path: test-results/
retention-days: 30
install_npm:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org
scope: "@payloadcms"
always-auth: true
- name: Cache node modules
uses: actions/cache@v1
with:
path: ~/.npm
key: ${{ runner.os }}-node-npm-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-${{ env.cache-name }}-
${{ runner.os }}-npm-
${{ runner.os }}-
- run: npm install
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

223
.gitignore vendored
View File

@@ -1,28 +1,13 @@
coverage
package-lock.json
dist
/.idea/*
!/.idea/runConfigurations
.idea
test-results
.devcontainer
.localstack
/migrations
.localstack
.turbo
.turbo
# Ignore test directory media folder/files
/media
/versions
# Created by https://www.toptal.com/developers/gitignore/api/node,macos,windows,webstorm,sublimetext,visualstudiocode
# Edit at https://www.toptal.com/developers/gitignore?templates=node,macos,windows,webstorm,sublimetext,visualstudiocode
# Created by https://www.gitignore.io/api/node,macos,windows,webstorm,sublimetext,visualstudiocode
### macOS ###
# General
.DS_Store
*.DS_Store
.AppleDouble
.LSOverride
@@ -45,10 +30,6 @@ Network Trash Folder
Temporary Items
.apdisk
### macOS Patch ###
# iCloud generated files
*.icloud
### Node ###
# Logs
logs
@@ -56,11 +37,6 @@ logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
@@ -73,12 +49,11 @@ lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
@@ -87,18 +62,15 @@ bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Typescript v1 declaration files
typings/
# Optional npm cache directory
.npm
@@ -106,15 +78,6 @@ web_modules/
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
@@ -124,87 +87,34 @@ web_modules/
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
# Yarn Berry
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
.pnp.*
### Node Patch ###
# Serverless Webpack directories
.webpack/
# dotenv environment variables file
.env
# Optional stylelint cache
# SvelteKit build / generate output
.svelte-kit
### SublimeText ###
# Cache files for Sublime Text
# cache files for sublime text
*.tmlanguage.cache
*.tmPreferences.cache
*.stTheme.cache
# Workspace files are user-specific
# workspace files are user-specific
*.sublime-workspace
# Project files should be checked into the repository, unless a significant
# proportion of contributors will probably not be using Sublime Text
# project files should be checked into the repository, unless a significant
# proportion of contributors will probably not be using SublimeText
# *.sublime-project
# SFTP configuration file
# sftp configuration file
sftp-config.json
sftp-config-alt*.json
# Package control specific files
Package Control.last-run
@@ -224,53 +134,84 @@ GitHub.sublime-settings
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide
### WebStorm ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff:
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/dictionaries
# Sensitive or high-churn files:
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.xml
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
# Gradle:
.idea/**/gradle.xml
.idea/**/libraries
# CMake
cmake-build-*/
cmake-build-debug/
# File-based project format
# MongoDB Explorer plugin:
.idea/**/mongoSettings.xml
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
out/
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Ruby plugin and RubyMine
/.rakeTasks
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### WebStorm Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr
# Sonarlint plugin
.idea/sonarlint
### Windows ###
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
@@ -278,14 +219,26 @@ $RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
# End of https://www.toptal.com/developers/gitignore/api/node,macos,windows,webstorm,sublimetext,visualstudiocode
# End of https://www.gitignore.io/api/node,macos,windows,webstorm,sublimetext,visualstudiocode
/build
.swc
# Ignore all uploads
demo/upload
demo/media
demo/files
# Ignore build folder
build
# Ignore built components
components/index.js
components/styles.css
# Ignore generated
demo/generated-types.ts
demo/generated-schema.graphql

View File

@@ -1,4 +0,0 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
pnpm run lint-staged --quiet

View File

@@ -1,8 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run Dev Fields" type="NodeJSConfigurationType" application-parameters="--no-deprecation fields" path-to-js-file="test/dev.js" working-dir="$PROJECT_DIR$">
<envs>
<env name="NODE_OPTIONS" value="--no-deprecation" />
</envs>
<method v="2" />
</configuration>
</component>

View File

@@ -1,8 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run Dev _community" type="NodeJSConfigurationType" application-parameters="--no-deprecation _community" path-to-js-file="test/dev.js" working-dir="$PROJECT_DIR$">
<envs>
<env name="NODE_OPTIONS" value="--no-deprecation" />
</envs>
<method v="2" />
</configuration>
</component>

View File

@@ -0,0 +1,935 @@
{
"id": "4b6f2243-b055-45d8-9afa-53cd2b729a12",
"prevId": "00000000-0000-0000-0000-000000000000",
"version": "5",
"dialect": "pg",
"tables": {
"posts_my_array_my_sub_array": {
"name": "posts_my_array_my_sub_array",
"schema": "",
"columns": {
"_order": {
"name": "_order",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"_parent_id": {
"name": "_parent_id",
"type": "varchar",
"primaryKey": false,
"notNull": true
},
"id": {
"name": "id",
"type": "varchar",
"primaryKey": true,
"notNull": true
},
"sub_sub_field": {
"name": "sub_sub_field",
"type": "varchar",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"posts_my_array_my_sub_array__parent_id_posts_my_array_id_fk": {
"name": "posts_my_array_my_sub_array__parent_id_posts_my_array_id_fk",
"tableFrom": "posts_my_array_my_sub_array",
"tableTo": "posts_my_array",
"columnsFrom": [
"_parent_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"posts_my_array": {
"name": "posts_my_array",
"schema": "",
"columns": {
"_order": {
"name": "_order",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"_parent_id": {
"name": "_parent_id",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"id": {
"name": "id",
"type": "varchar",
"primaryKey": true,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"posts_my_array__parent_id_posts_id_fk": {
"name": "posts_my_array__parent_id_posts_id_fk",
"tableFrom": "posts_my_array",
"tableTo": "posts",
"columnsFrom": [
"_parent_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"posts_my_array_locales": {
"name": "posts_my_array_locales",
"schema": "",
"columns": {
"sub_field": {
"name": "sub_field",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"_locale": {
"name": "_locale",
"type": "_locales",
"primaryKey": false,
"notNull": true
},
"_parent_id": {
"name": "_parent_id",
"type": "varchar",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"posts_my_array_locales__parent_id_posts_my_array_id_fk": {
"name": "posts_my_array_locales__parent_id_posts_my_array_id_fk",
"tableFrom": "posts_my_array_locales",
"tableTo": "posts_my_array",
"columnsFrom": [
"_parent_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"posts_block1": {
"name": "posts_block1",
"schema": "",
"columns": {
"_order": {
"name": "_order",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"_path": {
"name": "_path",
"type": "text",
"primaryKey": false,
"notNull": true
},
"_parent_id": {
"name": "_parent_id",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"id": {
"name": "id",
"type": "varchar",
"primaryKey": true,
"notNull": true
},
"non_localized_text": {
"name": "non_localized_text",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"block_name": {
"name": "block_name",
"type": "varchar",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"posts_block1__parent_id_posts_id_fk": {
"name": "posts_block1__parent_id_posts_id_fk",
"tableFrom": "posts_block1",
"tableTo": "posts",
"columnsFrom": [
"_parent_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"posts_block1_locales": {
"name": "posts_block1_locales",
"schema": "",
"columns": {
"localized_text": {
"name": "localized_text",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"_locale": {
"name": "_locale",
"type": "_locales",
"primaryKey": false,
"notNull": true
},
"_parent_id": {
"name": "_parent_id",
"type": "varchar",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"posts_block1_locales__parent_id_posts_block1_id_fk": {
"name": "posts_block1_locales__parent_id_posts_block1_id_fk",
"tableFrom": "posts_block1_locales",
"tableTo": "posts_block1",
"columnsFrom": [
"_parent_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"posts_block2_block_array": {
"name": "posts_block2_block_array",
"schema": "",
"columns": {
"_order": {
"name": "_order",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"_parent_id": {
"name": "_parent_id",
"type": "varchar",
"primaryKey": false,
"notNull": true
},
"id": {
"name": "id",
"type": "varchar",
"primaryKey": true,
"notNull": true
},
"sub_block_array": {
"name": "sub_block_array",
"type": "varchar",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"posts_block2_block_array__parent_id_posts_block2_id_fk": {
"name": "posts_block2_block_array__parent_id_posts_block2_id_fk",
"tableFrom": "posts_block2_block_array",
"tableTo": "posts_block2",
"columnsFrom": [
"_parent_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"posts_block2": {
"name": "posts_block2",
"schema": "",
"columns": {
"_order": {
"name": "_order",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"_path": {
"name": "_path",
"type": "text",
"primaryKey": false,
"notNull": true
},
"_parent_id": {
"name": "_parent_id",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"id": {
"name": "id",
"type": "varchar",
"primaryKey": true,
"notNull": true
},
"number": {
"name": "number",
"type": "numeric",
"primaryKey": false,
"notNull": false
},
"block_name": {
"name": "block_name",
"type": "varchar",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"posts_block2__parent_id_posts_id_fk": {
"name": "posts_block2__parent_id_posts_id_fk",
"tableFrom": "posts_block2",
"tableTo": "posts",
"columnsFrom": [
"_parent_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"posts_my_group_group_array": {
"name": "posts_my_group_group_array",
"schema": "",
"columns": {
"_order": {
"name": "_order",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"_parent_id": {
"name": "_parent_id",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"id": {
"name": "id",
"type": "varchar",
"primaryKey": true,
"notNull": true
},
"group_array_text": {
"name": "group_array_text",
"type": "varchar",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"posts_my_group_group_array__parent_id_posts_id_fk": {
"name": "posts_my_group_group_array__parent_id_posts_id_fk",
"tableFrom": "posts_my_group_group_array",
"tableTo": "posts",
"columnsFrom": [
"_parent_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"posts": {
"name": "posts",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"my_group_sub_field": {
"name": "my_group_sub_field",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"my_group_sub_group_sub_sub_field": {
"name": "my_group_sub_group_sub_sub_field",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"posts_locales": {
"name": "posts_locales",
"schema": "",
"columns": {
"title": {
"name": "title",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"number": {
"name": "number",
"type": "numeric",
"primaryKey": false,
"notNull": false
},
"my_group_sub_field_localized": {
"name": "my_group_sub_field_localized",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"my_group_sub_group_sub_sub_field_localized": {
"name": "my_group_sub_group_sub_sub_field_localized",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"_locale": {
"name": "_locale",
"type": "_locales",
"primaryKey": false,
"notNull": true
},
"_parent_id": {
"name": "_parent_id",
"type": "integer",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"posts_locales__parent_id_posts_id_fk": {
"name": "posts_locales__parent_id_posts_id_fk",
"tableFrom": "posts_locales",
"tableTo": "posts",
"columnsFrom": [
"_parent_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"posts_relationships": {
"name": "posts_relationships",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"parent_id": {
"name": "parent_id",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"path": {
"name": "path",
"type": "varchar",
"primaryKey": false,
"notNull": true
},
"order": {
"name": "order",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"pages_id": {
"name": "pages_id",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"people_id": {
"name": "people_id",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"posts_id": {
"name": "posts_id",
"type": "integer",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"posts_relationships_parent_id_posts_id_fk": {
"name": "posts_relationships_parent_id_posts_id_fk",
"tableFrom": "posts_relationships",
"tableTo": "posts",
"columnsFrom": [
"parent_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
},
"posts_relationships_pages_id_pages_id_fk": {
"name": "posts_relationships_pages_id_pages_id_fk",
"tableFrom": "posts_relationships",
"tableTo": "pages",
"columnsFrom": [
"pages_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
},
"posts_relationships_people_id_people_id_fk": {
"name": "posts_relationships_people_id_people_id_fk",
"tableFrom": "posts_relationships",
"tableTo": "people",
"columnsFrom": [
"people_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
},
"posts_relationships_posts_id_posts_id_fk": {
"name": "posts_relationships_posts_id_posts_id_fk",
"tableFrom": "posts_relationships",
"tableTo": "posts",
"columnsFrom": [
"posts_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"pages": {
"name": "pages",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"slug": {
"name": "slug",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"people": {
"name": "people",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"full_name": {
"name": "full_name",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"users": {
"name": "users",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"email": {
"name": "email",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"reset_password_token": {
"name": "reset_password_token",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"salt": {
"name": "salt",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"hash": {
"name": "hash",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"login_attempts": {
"name": "login_attempts",
"type": "numeric",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {
"email_idx": {
"name": "email_idx",
"columns": [
"email"
],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"payload_preferences": {
"name": "payload_preferences",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"key": {
"name": "key",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"value": {
"name": "value",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"payload_preferences_relationships": {
"name": "payload_preferences_relationships",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"parent_id": {
"name": "parent_id",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"path": {
"name": "path",
"type": "varchar",
"primaryKey": false,
"notNull": true
},
"order": {
"name": "order",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"users_id": {
"name": "users_id",
"type": "integer",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"payload_preferences_relationships_parent_id_payload_preferences_id_fk": {
"name": "payload_preferences_relationships_parent_id_payload_preferences_id_fk",
"tableFrom": "payload_preferences_relationships",
"tableTo": "payload_preferences",
"columnsFrom": [
"parent_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
},
"payload_preferences_relationships_users_id_users_id_fk": {
"name": "payload_preferences_relationships_users_id_users_id_fk",
"tableFrom": "payload_preferences_relationships",
"tableTo": "users",
"columnsFrom": [
"users_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"payload_migrations": {
"name": "payload_migrations",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"name": {
"name": "name",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"batch": {
"name": "batch",
"type": "numeric",
"primaryKey": false,
"notNull": false
},
"schema": {
"name": "schema",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {
"_locales": {
"name": "_locales",
"values": {
"en": "en",
"es": "es"
}
}
},
"schemas": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
}
}

View File

@@ -1 +1 @@
v18.19.1
v16.14.2

2
.npmrc
View File

@@ -1,2 +0,0 @@
symlink=true
node-linker=isolated # due to a typescript bug, isolated mode requires @types/express-serve-static-core, terser and monaco-editor to be installed https://github.com/microsoft/TypeScript/issues/47663#issuecomment-1519138189 along with two other changes in the code which I've marked with (tsbugisolatedmode) in the code

2
.nvmrc
View File

@@ -1 +1 @@
v18.19.1
v16.14.2

View File

@@ -1,12 +0,0 @@
.tmp
**/.git
**/.hg
**/.pnp.*
**/.svn
**/.yarn/**
**/build
**/dist/**
**/node_modules
**/temp
**/docs/**
tsconfig.json

View File

@@ -1,6 +0,0 @@
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100,
"semi": false
}

22
.release-it.json Normal file
View File

@@ -0,0 +1,22 @@
{
"verbose": true,
"git": {
"commitMessage": "chore(release): v${version}",
"requireCleanWorkingDir": true
},
"github": {
"release": true
},
"npm": {
"skipChecks": true
},
"hooks": {
"before:init": ["yarn", "yarn clean", "yarn test"]
},
"plugins": {
"@release-it/conventional-changelog": {
"preset": "angular",
"infile": "CHANGELOG.md"
}
}
}

25
.release-it.pre.json Normal file
View File

@@ -0,0 +1,25 @@
{
"verbose": true,
"git": {
"requireCleanWorkingDir": false,
"commit": false,
"push": false,
"tag": false
},
"github": {
"release": true
},
"npm": {
"skipChecks": true,
"tag": "canary"
},
"hooks": {
"before:init": ["yarn", "yarn clean", "yarn test"]
},
"plugins": {
"@release-it/conventional-changelog": {
"preset": "angular",
"infile": "CHANGELOG.md"
}
}
}

15
.swcrc
View File

@@ -1,15 +0,0 @@
{
"$schema": "https://json.schemastore.org/swcrc",
"sourceMaps": "inline",
"jsc": {
"target": "esnext",
"parser": {
"syntax": "typescript",
"tsx": true,
"dts": true
}
},
"module": {
"type": "es6"
}
}

View File

@@ -1,8 +0,0 @@
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"firsttris.vscode-jest-runner",
"ms-playwright.playwright"
]
}

199
.vscode/launch.json vendored
View File

@@ -1,187 +1,112 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"command": "pnpm generate:types",
"name": "Generate Types CLI",
"command": "yarn run dev _community",
"name": "Run Dev Community",
"request": "launch",
"type": "node-terminal",
"cwd": "${workspaceFolder}"
},
{
"command": "node --no-deprecation test/dev.js fields",
"cwd": "${workspaceFolder}",
"name": "Run Dev Community",
"request": "launch",
"type": "node-terminal"
},
{
"command": "pnpm run dev live-preview -- --no-turbo",
"cwd": "${workspaceFolder}",
"name": "Run Dev Live Preview",
"request": "launch",
"type": "node-terminal"
},
{
"command": "pnpm run dev plugin-cloud-storage",
"cwd": "${workspaceFolder}",
"name": "Run Dev - plugin-cloud-storage",
"request": "launch",
"type": "node-terminal",
"env": {
"PAYLOAD_PUBLIC_CLOUD_STORAGE_ADAPTER": "s3"
}
},
{
"command": "pnpm run dev fields",
"cwd": "${workspaceFolder}",
"command": "yarn run dev fields",
"name": "Run Dev Fields",
"request": "launch",
"type": "node-terminal"
"type": "node-terminal",
"cwd": "${workspaceFolder}"
},
{
"command": "pnpm run dev:postgres versions",
"cwd": "${workspaceFolder}",
"command": "yarn run dev:postgres postgres -- -I", // Allow input
"name": "Run Dev Postgres",
"request": "launch",
"type": "node-terminal"
"type": "node-terminal",
"cwd": "${workspaceFolder}"
},
{
"command": "pnpm run dev versions",
"cwd": "${workspaceFolder}",
"command": "yarn run dev versions",
"name": "Run Dev Versions",
"request": "launch",
"type": "node-terminal"
},
{
"command": "pnpm run dev localization",
"cwd": "${workspaceFolder}",
"name": "Run Dev Localization",
"request": "launch",
"type": "node-terminal"
},
{
"command": "pnpm run dev uploads",
"cwd": "${workspaceFolder}",
"name": "Run Dev Uploads",
"request": "launch",
"type": "node-terminal"
},
{
"command": "PAYLOAD_BUNDLER=vite pnpm run dev fields",
"cwd": "${workspaceFolder}",
"name": "Run Dev Fields (Vite)",
"request": "launch",
"type": "node-terminal",
"env": {
"NODE_ENV": "production"
}
"cwd": "${workspaceFolder}"
},
{
"command": "pnpm run test:int live-preview",
"cwd": "${workspaceFolder}",
"name": "Live Preview Int Tests",
"type": "node-terminal",
"command": "ts-node src/bin/migrate.ts migrate",
"request": "launch",
"type": "node-terminal"
},
{
"command": "pnpm run test:int plugin-search",
"cwd": "${workspaceFolder}",
"name": "Search Plugin Int Tests",
"request": "launch",
"type": "node-terminal"
},
{
"command": "ts-node ./packages/payload/src/bin/index.ts build",
"env": {
"PAYLOAD_CONFIG_PATH": "test/fields/config.ts",
"PAYLOAD_BUNDLER": "vite",
"DISABLE_SWC": "true" // SWC messes up debugging the bin scripts
},
"name": "Build CLI - Vite",
"outputCapture": "std",
"request": "launch",
"type": "node-terminal"
},
{
"command": "ts-node ./packages/payload/src/bin/index.ts build",
"env": {
"PAYLOAD_CONFIG_PATH": "test/fields/config.ts",
"PAYLOAD_ANALYZE_BUNDLE": "true",
"DISABLE_SWC": "true" // SWC messes up debugging the bin scripts
},
"name": "Build CLI - Webpack",
"outputCapture": "std",
"request": "launch",
"type": "node-terminal"
},
{
"command": "ts-node ./packages/payload/src/bin/index.ts migrate:status",
"name": "Migrate CLI - migrate",
"env": {
"PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
"PAYLOAD_DATABASE": "postgres",
"DISABLE_SWC": "true" // SWC messes up debugging the bin scripts
// "PAYLOAD_DROP_DATABASE": "true",
},
"outputCapture": "std",
},
{
"type": "node-terminal",
"command": "ts-node src/bin/migrate.ts migrate:status",
"request": "launch",
"name": "Migrate CLI - status",
"outputCapture": "std",
"request": "launch",
"type": "node-terminal"
},
{
"command": "ts-node ./packages/payload/src/bin/index.ts migrate:create yass",
"env": {
"PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
"PAYLOAD_DATABASE": "postgres",
"DISABLE_SWC": "true" // SWC messes up debugging the bin scripts
// "PAYLOAD_DROP_DATABASE": "true",
},
"outputCapture": "std",
},
{
"type": "node-terminal",
"command": "ts-node src/bin/migrate.ts migrate:create yass",
"request": "launch",
"name": "Migrate CLI - create",
"outputCapture": "std",
"request": "launch",
"type": "node-terminal"
},
{
"command": "ts-node ./packages/payload/src/bin/index.ts migrate:down",
"env": {
"PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
"PAYLOAD_DATABASE": "mongoose",
"DISABLE_SWC": "true" // SWC messes up debugging the bin scripts
// "PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
"PAYLOAD_CONFIG_PATH": "test/postgres/config.ts",
"PAYLOAD_DATABASE": "postgres",
// "PAYLOAD_DROP_DATABASE": "true",
},
"outputCapture": "std",
},
{
"type": "node-terminal",
"command": "ts-node src/bin/migrate.ts migrate:down",
"request": "launch",
"name": "Migrate CLI - down",
"outputCapture": "std",
"request": "launch",
"type": "node-terminal"
},
{
"command": "ts-node ./packages/payload/src/bin/index.ts migrate:reset",
"env": {
"PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
"PAYLOAD_DATABASE": "mongoose",
"DISABLE_SWC": "true" // SWC messes up debugging the bin scripts
// "PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
"PAYLOAD_CONFIG_PATH": "test/postgres/config.ts",
"PAYLOAD_DATABASE": "postgres",
// "PAYLOAD_DROP_DATABASE": "true",
},
"outputCapture": "std",
},
{
"type": "node-terminal",
"command": "ts-node src/bin/migrate.ts migrate:reset",
"request": "launch",
"name": "Migrate CLI - reset",
"outputCapture": "std",
"request": "launch",
"type": "node-terminal"
},
{
"command": "ts-node ./packages/payload/src/bin/index.ts migrate:refresh",
"env": {
"PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
"PAYLOAD_DATABASE": "mongoose",
"DISABLE_SWC": "true" // SWC messes up debugging the bin scripts
// "PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
"PAYLOAD_CONFIG_PATH": "test/postgres/config.ts",
"PAYLOAD_DATABASE": "postgres",
// "PAYLOAD_DROP_DATABASE": "true",
},
"name": "Migrate CLI - refresh",
"outputCapture": "std",
},
{
"type": "node-terminal",
"command": "ts-node src/bin/migrate.ts migrate:refresh",
"request": "launch",
"type": "node-terminal"
}
],
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0"
"name": "Migrate CLI - refresh",
"env": {
// "PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
"PAYLOAD_CONFIG_PATH": "test/postgres/config.ts",
"PAYLOAD_DATABASE": "postgres",
// "PAYLOAD_DROP_DATABASE": "true",
},
"outputCapture": "std",
},
]
}

44
.vscode/settings.json vendored
View File

@@ -1,44 +0,0 @@
{
"npm.packageManager": "pnpm",
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"editor.formatOnSaveMode": "file",
// All ESLint rules to 'warn' to differentate from TypeScript's 'error' level
"eslint.rules.customizations": [{ "rule": "*", "severity": "warn" }],
"typescript.tsdk": "node_modules/typescript/lib",
// Load .git-blame-ignore-revs file
"gitlens.advanced.blame.customArguments": ["--ignore-revs-file", ".git-blame-ignore-revs"],
"[javascript][typescript][typescriptreact]": {
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -14,7 +14,7 @@ If you find a vulnerability within the core Payload repository, and we determine
## Documentation edits
Payload documentation can be found directly within its codebase, and you can feel free to make changes / improvements to any of it through opening a PR. We utilize these files directly in our website and will periodically deploy documentation updates as necessary.
Payload documentation can be found directly within its codebase and you can feel free to make changes / improvements to any of it through opening a PR. We utilize these files directly in our website and will periodically deploy documentation updates as necessary.
## Building additional features
@@ -30,17 +30,9 @@ Our design review ensures that proposed changes fit seamlessly with other compon
To help us work on new features, you can create a new feature request post in [GitHub Discussion](https://github.com/payloadcms/payload/discussions) or discuss it in our [Discord](https://discord.com/invite/payload). New functionality often has large implications across the entire Payload repo, so it is best to discuss the architecture and approach before starting work on a pull request.
### Installation & Requirements
Payload is structured as a Monorepo, encompassing not only the core Payload platform but also various plugins and packages. To install all required dependencies, you have to run `pnpm install` once in the root directory. **PNPM IS REQUIRED!** Yarn or npm will not work - you will have to use pnpm to develop in the core repository. In most systems, the easiest way to install pnpm is to run `corepack enable` in your terminal.
If you're coming from a very outdated version of payload, it is recommended to nuke the node_modules folder before running pnpm install. On UNIX systems, you can easily do that using the `pnpm clean:unix` command, which will delete all node_modules folders and build artefacts.
It is also recommended to use at least Node v18 or higher. You can check your current node version by typing `node --version` in your terminal. The easiest way to switch between different node versions is to use [nvm](https://github.com/nvm-sh/nvm#intro).
### Code
Most new functionality should keep testing in mind. All top-level directories within the `test/` directory are for testing a specific category: `fields`, `collections`, etc.
Most new functionality should keep testing in mind. With 1.0, testability of new features has been vastly improved. All top-level directories within the `test/` directory are for testing a specific category: `fields`, `collections`, etc.
If it makes sense to add your feature to an existing test directory, please do so.
@@ -57,35 +49,21 @@ A typical directory with `test/` will be structured like this:
- `config.ts` - This is the _granular_ Payload config for testing. It should be as lightweight as possible. Reference existing configs for an example
- `int.spec.ts` - This is the test file run by jest. Any test file must have a `*int.spec.ts` suffix.
- `e2e.spec.ts` - This is the end-to-end test file that will load up the admin UI using the above config and run Playwright tests. These tests are typically only needed if a large change is being made to the Admin UI.
- `payload-types.ts` - Generated types from `config.ts`. Generate this file by running `pnpm dev:generate-types my-test-dir`. Replace `my-test-dir` with the name of your testing directory.
- `payload-types.ts` - Generated types from `config.ts`. Generate this file by running `yarn dev:generate-types my-test-dir`.
Each test directory is split up in this way specifically to reduce friction when creating tests and to add the ability to boot up Payload with that specific config.
The directory split up in this way specifically to reduce friction when creating tests and to add the ability to boot up Payload with that specific config.
The following command will start Payload with your config: `pnpm dev my-test-dir`. Example: `pnpm dev fields` for the test/`fields` test suite. This command will start up Payload using your config and refresh a test database on every restart. If you're using VS Code, the most common run configs are automatically added to your editor - you should be able to find them in your VS Code launch tab.
The following command will start Payload with your config: `yarn dev my-test-dir`. This command will start up Payload using your config and refresh a test database on every restart.
By default, payload will [automatically log you in](https://payloadcms.com/docs/authentication/config#admin-autologin) with the default credentials. To disable that, you can either pass in the --no-auto-login flag (example: `pnpm dev my-test-dir --no-auto-login`) or set the `PAYLOAD_PUBLIC_DISABLE_AUTO_LOGIN` environment variable to `false`.
By default, it will automatically log you in with the default credentials. To disable that, you can either pass in the --no-auto-login flag (example: `yarn dev my-test-dir --no-auto-login`) or set the `PAYLOAD_PUBLIC_DISABLE_AUTO_LOGIN` environment variable to `false`.
The default credentials are `dev@payloadcms.com` as E-Mail and `test` as password. These are used in the auto-login.
### Testing with your own MongoDB database
If you wish to use your own MongoDB database for the `test` directory instead of using the in memory database, all you need to do is add the following env vars to the `test/dev.ts` file:
If you wish to use to your own Mongo database for the `test` directory instead of using the in memory database, all you need to do is add the following env vars to the `test/dev.ts` file:
- `process.env.NODE_ENV`
- `process.env.PAYLOAD_TEST_MONGO_URL`
- Simply set `process.env.NODE_ENV` to `test` and set `process.env.PAYLOAD_TEST_MONGO_URL` to your MongoDB URL e.g. `mongodb://127.0.0.1/your-test-db`.
- Simply set `process.env.NODE_ENV` to `test` and set `process.env.PAYLOAD_TEST_MONGO_URL` to your mongo url e.g. `mongodb://127.0.0.1/your-test-db`.
### Using Postgres
If you have postgres installed on your system, you can also run the test suites using postgres. By default, mongodb is used.
To do that, simply set the `PAYLOAD_DATABASE` environment variable to `postgres`.
### Running the e2e and int tests
You can run the entire test suite using `pnpm test`. If you wish to only run e2e tests, you can use `pnpm test:e2e`. If you wish to only run int tests, you can use `pnpm test:int`.
By default, `pnpm test:int` will only run int test against MongoDB. To run int tests against postgres, you can use `pnpm test:int:postgres`. You will have to have postgres installed on your system for this to work.
NOTE: It is recommended to add the test credentials (located in `test/credentials.ts`) to your autofill for `localhost:3000/admin` as this will be required on every nodemon restart. The default credentials are `dev@payloadcms.com` as E-Mail and `test` as password.
### Commits
@@ -111,14 +89,3 @@ If you are committing to [templates](./templates) or [examples](./examples), use
## Pull Requests
For all Pull Requests, you should be extremely descriptive about both your problem and proposed solution. If there are any affected open or closed issues, please leave the issue number in your PR message.
## Previewing docs
This is how you can preview changes you made locally to the docs:
1. Clone our [website repository](https://github.com/payloadcms/website)
2. Run `yarn install`
3. Duplicate the `.env.example` file and rename it to `.env`
4. Add a `DOCS_DIR` environment variable to the `.env` file which points to the absolute path of your modified docs folder. For example `DOCS_DIR=/Users/yourname/Documents/GitHub/payload/docs`
5. Run `yarn run fetchDocs:local`. If this was successful, you should see no error messages and the following output: *Docs successfully written to /.../website/src/app/docs.json*. There could be error messages if you have incorrect markdown in your local docs folder. In this case, it will tell you how you can fix it
6. You're done! Now you can start the website locally using `yarn run dev` and preview the docs under [http://localhost:3000/docs/](http://localhost:3000/docs/)

View File

@@ -6,10 +6,9 @@ To report an issue, please follow the steps below:
2. Add necessary collections/globals/fields to the `test/_community` directory to recreate the issue you are experiencing
3. Create an issue and add a link to your forked repo
**The goal is to isolate the problem by reducing the number of fields/collections you add to the test/\_community folder. This folder is not meant for you to copy your project into, but to recreate the issue you are experiencing with minimal config.**
**The goal is to isolate the problem by reducing the number of fields/collections you add to the test/_community folder. This folder is not meant for you to copy your project into, but to recreate the issue you are experiencing with minimal config.**
## Test directory file tree explanation
```text
.
├── config.ts
@@ -21,49 +20,45 @@ To report an issue, please follow the steps below:
- `config.ts` - This is the _granular_ Payload config for testing. It should be as lightweight as possible. Reference existing configs for an example
- `int.spec.ts` [Optional] - This is the test file run by jest. Any test file must have a `*int.spec.ts` suffix.
- `e2e.spec.ts` [Optional] - This is the end-to-end test file that will load up the admin UI using the above config and run Playwright tests.
- `payload-types.ts` - Generated types from `config.ts`. Generate this file by running `pnpm dev:generate-types _community`.
- `payload-types.ts` - Generated types from `config.ts`. Generate this file by running `yarn dev:generate-types _community`.
The directory split up in this way specifically to reduce friction when creating tests and to add the ability to boot up Payload with that specific config. You should modify the files in `test/_community` to get started.
## How to start test collection admin UI
To start the admin panel so you can manually recreate your issue, you can run the following command:
```bash
# This command will start up Payload using your config
# NOTE: it will wipe the test database on restart
pnpm dev _community
```
```bash
# This command will start up Payload using your config
# NOTE: it will wipe the test database on restart
yarn dev _community
```
## Testing is optional but encouraged
An issue does not need to have failing tests — reproduction steps with your forked repo are enough at this point. Some people like to dive deeper and we want to give you the guidance/tools to do so. Read more below.
### How to run integration tests (Payload API tests)
There are a couple ways to do this:
- **Granularly** - you can run individual tests in vscode by installing the Jest Runner plugin and using that to run individual tests. Clicking the `debug` button will run the test in debug mode allowing you to set break points.
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/src/admin/assets/images/github/int-debug.png" />
<img src="https://raw.githubusercontent.com/payloadcms/payload/master/src/admin/assets/images/github/int-debug.png" />
- **Manually** - you can run all int tests in the `/test/_community/int.spec.ts` file by running the following command:
```bash
pnpm test:int _community
yarn test:int _community
```
### How to run E2E tests (Admin Panel UI tests)
The easiest way to run E2E tests is to install
- [Playwright Test for VSCode](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright)
- [Playwright Runner](https://marketplace.visualstudio.com/items?itemName=ortoni.ortoni)
Once they are installed you can open the `testing` tab in vscode sidebar and drill down to the test you want to run, i.e. `/test/_community/e2e.spec.ts`
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/src/admin/assets/images/github/e2e-debug.png" />
<img src="https://raw.githubusercontent.com/payloadcms/payload/master/src/admin/assets/images/github/e2e-debug.png" />
#### Notes
- It is recommended to add the test credentials (located in `test/credentials.ts`) to your autofill for `localhost:3000/admin` as this will be required on every nodemon restart. The default credentials are `dev@payloadcms.com` as email and `test` as password.

View File

@@ -1,24 +1,30 @@
<a href="https://payloadcms.com"><img width="100%" src="https://github.com/payloadcms/payload/blob/main/packages/payload/src/admin/assets/images/github-banner-alt.jpg?raw=true" alt="Payload headless CMS Admin panel built with React" /></a>
<a href="https://payloadcms.com">
<img width="100%" src="src/admin/assets/images/github-banner-alt.jpg" alt="Payload headless CMS Admin panel built with React" />
</a>
<br />
<br />
<p align="left">
<a href="https://github.com/payloadcms/payload/actions"><img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/payloadcms/payload/main.yml?style=flat-square"></a>
<a href="https://github.com/payloadcms/payload/actions">
<img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/payloadcms/payload/tests.yml?style=flat-square">
</a>
&nbsp;
<a href="https://discord.gg/payload"><img alt="Discord" src="https://img.shields.io/discord/967097582721572934?label=Discord&color=7289da&style=flat-square" /></a>
<a href="https://discord.gg/payload">
<img alt="Discord" src="https://img.shields.io/discord/967097582721572934?label=Discord&color=7289da&style=flat-square" />
</a>
&nbsp;
<a href="https://www.npmjs.com/package/payload"><img alt="npm" src="https://img.shields.io/npm/v/payload?style=flat-square" /></a>
<a href="https://www.npmjs.com/package/payload">
<img alt="npm" src="https://img.shields.io/npm/v/payload?style=flat-square" />
</a>
&nbsp;
<a href="https://twitter.com/payloadcms"><img src="https://img.shields.io/badge/follow-payloadcms-1DA1F2?logo=twitter&style=flat-square" alt="Payload Twitter" /></a>
<a href="https://twitter.com/payloadcms">
<img src="https://img.shields.io/badge/follow-payloadcms-1DA1F2?logo=twitter&style=flat-square" alt="Payload Twitter" />
</a>
</p>
<hr/>
<h4>
<a target="_blank" href="https://payloadcms.com/docs/getting-started/what-is-payload" rel="dofollow"><strong>Explore the Docs</strong></a>&nbsp;·&nbsp;<a target="_blank" href="https://payloadcms.com/community-help" rel="dofollow"><strong>Community Help</strong></a>&nbsp;·&nbsp;<a target="_blank" href="https://demo.payloadcms.com/" rel="dofollow"><strong>Try Live Demo</strong></a>&nbsp;·&nbsp;<a target="_blank" href="https://github.com/payloadcms/payload/discussions/1539" rel="dofollow"><strong>Roadmap</strong></a>&nbsp;·&nbsp;<a target="_blank" href="https://www.g2.com/products/payload-cms/reviews#reviews" rel="dofollow"><strong>View G2 Reviews</strong></a>
</h4>
<hr/>
> [!IMPORTANT]
> 🎉 <strong>Payload 2.0 is now available!</strong> Read more in the <a target="_blank" href="https://payloadcms.com/blog/payload-2-0" rel="dofollow"><strong>announcement post</strong></a>.
<h3>Benefits over a regular CMS</h3>
<ul>
<li>Dont hit some third-party SaaS API, hit your own API</li>
@@ -33,7 +39,6 @@
</ul>
## ☁️ Deploy instantly with Payload Cloud.
Create a cloud account, connect your GitHub, and [deploy in minutes](https://payloadcms.com/new).
## 🚀 Get started by self-hosting completely free, forever.
@@ -41,27 +46,18 @@ Create a cloud account, connect your GitHub, and [deploy in minutes](https://pay
Before beginning to work with Payload, make sure you have all of the [required software](https://payloadcms.com/docs/getting-started/installation).
```text
npx create-payload-app@latest
npx create-payload-app
```
Alternatively, it only takes about five minutes to [create an app from scratch](https://payloadcms.com/docs/getting-started/installation#from-scratch).
## 🖱️ One-click templates
### 🛒 [E-Commerce](https://github.com/payloadcms/payload/tree/master/templates/ecommerce)
Eliminate the need to combine Shopify and a CMS, and instead do it all with Payload + Stripe. Best of all, you can extend it as much as you need.
Jumpstart your next project by starting with a pre-made template. These are production-ready, end-to-end solutions designed to get you to market as fast as possible.
[All Official Templates](https://github.com/orgs/payloadcms/repositories?q=topic%3Apayload-template)&nbsp;·&nbsp;[Community Templates](https://github.com/topics/payload-template)
### [🛒 E-Commerce](https://github.com/payloadcms/payload/tree/main/templates/ecommerce)
Eliminate the need to combine Shopify and a CMS, and instead do it all with Payload + Stripe. Comes with a beautiful, fully functional front-end complete with shopping cart, checkout, orders, and much more.
### [🌐 Website](https://github.com/payloadcms/payload/tree/main/templates/website)
Build any kind of website, blog, or portfolio from small to enterprise. Comes with a beautiful, fully functional front-end complete with posts, projects, comments, and much more.
We're constantly adding more templates to our [Templates Directory](https://github.com/payloadcms/payload/tree/main/templates). If you maintain your own template, consider adding the `payload-template` topic to your GitHub repository for others to find.
- [Official Templates](https://github.com/payloadcms/payload/tree/main/templates)
- [Community Templates](https://github.com/topics/payload-template)
**If you maintain your own template, consider adding the `payload-template` topic to your GitHub repository for others to find.**
## ✨ Features
@@ -89,19 +85,15 @@ We're constantly adding more templates to our [Templates Directory](https://gith
Check out the [Payload website](https://payloadcms.com/docs/getting-started/what-is-payload) to find in-depth documentation for everything that Payload offers.
Migrating from v1 to v2? Check out the [2.0 Release Notes](https://github.com/payloadcms/payload/releases/tag/v2.0.0) on how to do it.
## 🙋 Contributing
If you want to add contributions to this repository, please follow the instructions in [contributing.md](./CONTRIBUTING.md).
If you want to add contributions to this repository, please follow the instructions in [contributing.md](./contributing.md).
## 📚 Examples
The [Examples Directory](./examples) is a great resource for learning how to setup Payload in a variety of different ways, but you can also find great examples in our blog and throughout our social media.
The examples directory is a great resource for learning how to setup Payload in a variety of different ways.
- [Examples Directory](./examples)
- [Payload Blog](https://payloadcms.com/blog)
- [Payload YouTube](https://www.youtube.com/@payloadcms)
[Examples Directory](./examples)
## 🔌 Plugins

View File

@@ -1,17 +0,0 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
import config from '@payload-config'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import { NotFoundView } from '@payloadcms/next/views/NotFound/index.js'
type Args = {
params: {
segments: string[]
}
searchParams: {
[key: string]: string | string[]
}
}
const NotFound = ({ params, searchParams }: Args) => NotFoundView({ config, params, searchParams })
export default NotFound

View File

@@ -1,22 +0,0 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
import type { Metadata } from 'next'
import config from '@payload-config'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import { RootPage, generatePageMetadata } from '@payloadcms/next/views/Root/index.js'
type Args = {
params: {
segments: string[]
}
searchParams: {
[key: string]: string | string[]
}
}
export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>
generatePageMetadata({ config, params, searchParams })
const Page = ({ params, searchParams }: Args) => RootPage({ config, params, searchParams })
export default Page

View File

@@ -1,9 +0,0 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
import config from '@payload-config'
import { REST_DELETE, REST_GET, REST_PATCH, REST_POST } from '@payloadcms/next/routes/index.js'
export const GET = REST_GET(config)
export const POST = REST_POST(config)
export const DELETE = REST_DELETE(config)
export const PATCH = REST_PATCH(config)

View File

@@ -1,6 +0,0 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
import config from '@payload-config'
import { GRAPHQL_PLAYGROUND_GET } from '@payloadcms/next/routes/index.js'
export const GET = GRAPHQL_PLAYGROUND_GET(config)

View File

@@ -1,6 +0,0 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
import config from '@payload-config'
import { GRAPHQL_POST } from '@payloadcms/next/routes/index.js'
export const POST = GRAPHQL_POST(config)

View File

@@ -1,15 +0,0 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
import configPromise from '@payload-config'
import { RootLayout } from '@payloadcms/next/layouts/Root/index.js'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import React from 'react'
import './custom.scss'
type Args = {
children: React.ReactNode
}
const Layout = ({ children }: Args) => <RootLayout config={configPromise}>{children}</RootLayout>
export default Layout

View File

@@ -1,43 +0,0 @@
'use client'
import { useLivePreview } from '@payloadcms/live-preview-react'
import React from 'react'
import type { Page as PageType } from '../../../../test/live-preview/payload-types.js'
import { PAYLOAD_SERVER_URL } from '../../_api/serverURL.js'
import { Blocks } from '../../_components/Blocks/index.js'
import { Gutter } from '../../_components/Gutter/index.js'
import { Hero } from '../../_components/Hero/index.js'
export const PageClient: React.FC<{
page: PageType
}> = ({ page: initialPage }) => {
const { data } = useLivePreview<PageType>({
depth: 2,
initialData: initialPage,
serverURL: PAYLOAD_SERVER_URL,
})
return (
<React.Fragment>
<Gutter>
<h1 id="page-title">{data.title}</h1>
</Gutter>
<Hero {...data?.hero} />
<Blocks
blocks={[
...(data?.layout ?? []),
{
blockName: 'Relationships',
blockType: 'relationships',
data,
},
]}
disableTopPadding={
!data?.hero || data?.hero?.type === 'none' || data?.hero?.type === 'lowImpact'
}
/>
</React.Fragment>
)
}

View File

@@ -1,37 +0,0 @@
import { notFound } from 'next/navigation.js'
import React from 'react'
import type { Page } from '../../../../test/live-preview/payload-types.js'
import { fetchDoc } from '../../_api/fetchDoc.js'
import { fetchDocs } from '../../_api/fetchDocs.js'
import { PageClient } from './page.client.js'
// eslint-disable-next-line no-restricted-exports
export default async function Page({ params: { slug = 'home' } }) {
let page: Page | null = null
try {
page = await fetchDoc<Page>({
slug,
collection: 'pages',
})
} catch (error) {
console.error(error)
}
if (!page) {
return notFound()
}
return <PageClient page={page} />
}
export async function generateStaticParams() {
try {
const pages = await fetchDocs<Page>('pages')
return pages?.map(({ slug }) => slug)
} catch (error) {
return []
}
}

View File

@@ -1,68 +0,0 @@
'use client'
import { useLivePreview } from '@payloadcms/live-preview-react'
import React from 'react'
import type { Post as PostType } from '../../../../../test/live-preview/payload-types.js'
import { PAYLOAD_SERVER_URL } from '../../../_api/serverURL.js'
import { Blocks } from '../../../_components/Blocks/index.js'
import { PostHero } from '../../../_heros/PostHero/index.js'
export const PostClient: React.FC<{
post: PostType
}> = ({ post: initialPost }) => {
const { data } = useLivePreview<PostType>({
depth: 2,
initialData: initialPost,
serverURL: PAYLOAD_SERVER_URL,
})
return (
<React.Fragment>
<PostHero post={data} />
<Blocks blocks={data?.layout} />
<Blocks
blocks={[
{
blockName: 'Related Posts',
blockType: 'relatedPosts',
docs: data?.relatedPosts,
introContent: [
{
type: 'h4',
children: [
{
text: 'Related posts',
},
],
},
{
type: 'p',
children: [
{
text: 'The posts displayed here are individually selected for this page. Admins can select any number of related posts to display here and the layout will adjust accordingly. Alternatively, you could swap this out for the "Archive" block to automatically populate posts by category complete with pagination. To manage related posts, ',
},
{
type: 'link',
children: [
{
text: 'navigate to the admin dashboard',
},
],
url: `/admin/collections/posts/${data?.id}`,
},
{
text: '.',
},
],
},
],
relationTo: 'posts',
},
]}
disableTopPadding
/>
</React.Fragment>
)
}

View File

@@ -1,36 +0,0 @@
import { notFound } from 'next/navigation.js'
import React from 'react'
import type { Post } from '../../../../../test/live-preview/payload-types.js'
import { fetchDoc } from '../../../_api/fetchDoc.js'
import { fetchDocs } from '../../../_api/fetchDocs.js'
import { PostClient } from './page.client.js'
export default async function Post({ params: { slug = '' } }) {
let post: Post | null = null
try {
post = await fetchDoc<Post>({
slug,
collection: 'posts',
})
} catch (error) {
console.error(error) // eslint-disable-line no-console
}
if (!post) {
notFound()
}
return <PostClient post={post} />
}
export async function generateStaticParams() {
try {
const posts = await fetchDocs<Post>('posts')
return posts?.map(({ slug }) => slug)
} catch (error) {
return []
}
}

View File

@@ -1,35 +0,0 @@
import QueryString from 'qs'
import { PAYLOAD_SERVER_URL } from './serverURL.js'
export const fetchDoc = async <T>(args: {
collection: string
depth?: number
id?: string
slug?: string
}): Promise<T> => {
const { id, slug, collection, depth = 2 } = args || {}
const queryString = QueryString.stringify(
{
...(slug ? { 'where[slug][equals]': slug } : {}),
...(depth ? { depth } : {}),
},
{ addQueryPrefix: true },
)
const doc: T = await fetch(`${PAYLOAD_SERVER_URL}/api/${collection}${queryString}`, {
cache: 'no-store',
headers: {
'Content-Type': 'application/json',
},
method: 'GET',
})
?.then((res) => res.json())
?.then((res) => {
if (res.errors) throw new Error(res?.errors?.[0]?.message ?? 'Error fetching doc')
return res?.docs?.[0]
})
return doc
}

View File

@@ -1,19 +0,0 @@
import { PAYLOAD_SERVER_URL } from './serverURL.js'
export const fetchDocs = async <T>(collection: string): Promise<T[]> => {
const docs: T[] = await fetch(`${PAYLOAD_SERVER_URL}/api/${collection}?depth=0&limit=100`, {
cache: 'no-store',
headers: {
'Content-Type': 'application/json',
},
method: 'GET',
})
?.then((res) => res.json())
?.then((res) => {
if (res.errors) throw new Error(res?.errors?.[0]?.message ?? 'Error fetching docs')
return res?.docs
})
return docs
}

View File

@@ -1,25 +0,0 @@
import type { Footer } from '../../../test/live-preview/payload-types.js'
import { PAYLOAD_SERVER_URL } from './serverURL.js'
export async function fetchFooter(): Promise<Footer> {
if (!PAYLOAD_SERVER_URL) throw new Error('PAYLOAD_SERVER_URL not found')
const footer = await fetch(`${PAYLOAD_SERVER_URL}/api/globals/footer`, {
cache: 'no-store',
headers: {
'Content-Type': 'application/json',
},
method: 'GET',
})
.then((res) => {
if (!res.ok) throw new Error('Error fetching doc')
return res.json()
})
?.then((res) => {
if (res?.errors) throw new Error(res?.errors[0]?.message || 'Error fetching footer')
return res
})
return footer
}

View File

@@ -1,25 +0,0 @@
import type { Header } from '../../../test/live-preview/payload-types.js'
import { PAYLOAD_SERVER_URL } from './serverURL.js'
export async function fetchHeader(): Promise<Header> {
if (!PAYLOAD_SERVER_URL) throw new Error('PAYLOAD_SERVER_URL not found')
const header = await fetch(`${PAYLOAD_SERVER_URL}/api/globals/header`, {
cache: 'no-store',
headers: {
'Content-Type': 'application/json',
},
method: 'GET',
})
?.then((res) => {
if (!res.ok) throw new Error('Error fetching doc')
return res.json()
})
?.then((res) => {
if (res?.errors) throw new Error(res?.errors[0]?.message || 'Error fetching header')
return res
})
return header
}

View File

@@ -1 +0,0 @@
export const PAYLOAD_SERVER_URL = 'http://localhost:3000'

View File

@@ -1,13 +0,0 @@
@import '../../_css/common';
.archiveBlock {
position: relative;
}
.introContent {
margin-bottom: calc(var(--base) * 2);
@include mid-break {
margin-bottom: calc(var(--base) * 2);
}
}

View File

@@ -1,46 +0,0 @@
import React from 'react'
import type { ArchiveBlockProps } from './types.js'
import { CollectionArchive } from '../../_components/CollectionArchive/index.js'
import { Gutter } from '../../_components/Gutter/index.js'
import RichText from '../../_components/RichText/index.js'
import classes from './index.module.scss'
export const ArchiveBlock: React.FC<
ArchiveBlockProps & {
id?: string
}
> = (props) => {
const {
id,
categories,
introContent,
limit,
populateBy,
populatedDocs,
populatedDocsTotal,
relationTo,
selectedDocs,
} = props
return (
<div className={classes.archiveBlock} id={`block-${id}`}>
{introContent && (
<Gutter className={classes.introContent}>
<RichText content={introContent} />
</Gutter>
)}
<CollectionArchive
categories={categories}
limit={limit}
populateBy={populateBy}
populatedDocs={populatedDocs}
populatedDocsTotal={populatedDocsTotal}
relationTo={relationTo}
selectedDocs={selectedDocs}
sort="-publishedDate"
/>
</div>
)
}

View File

@@ -1,6 +0,0 @@
import type { Page } from '../../../../test/live-preview/payload-types.js'
export type ArchiveBlockProps = Extract<
Exclude<Page['layout'], undefined>[0],
{ blockType: 'archive' }
>

View File

@@ -1,47 +0,0 @@
@use '../../_css/queries.scss' as *;
$spacer-h: calc(var(--block-padding) / 2);
.callToAction {
padding-left: $spacer-h;
padding-right: $spacer-h;
position: relative;
background-color: var(--color-base-100);
color: var(--color-base-1000);
}
.invert {
background-color: var(--color-base-1000);
color: var(--color-base-0);
}
.wrap {
display: flex;
gap: $spacer-h;
align-items: center;
@include mid-break {
flex-direction: column;
align-items: flex-start;
}
}
.content {
flex-grow: 1;
}
.linkGroup {
display: flex;
flex-direction: column;
justify-content: center;
height: 100%;
flex-shrink: 0;
> * {
margin-bottom: calc(var(--base) / 2);
&:last-child {
margin-bottom: 0;
}
}
}

View File

@@ -1,38 +0,0 @@
import React from 'react'
import type { Page } from '../../../../test/live-preview/payload-types.js'
import { Gutter } from '../../_components/Gutter/index.js'
import { CMSLink } from '../../_components/Link/index.js'
import RichText from '../../_components/RichText/index.js'
import { VerticalPadding } from '../../_components/VerticalPadding/index.js'
import classes from './index.module.scss'
type Props = Extract<Exclude<Page['layout'], undefined>[0], { blockType: 'cta' }>
export const CallToActionBlock: React.FC<
Props & {
id?: string
}
> = ({ invertBackground, links, richText }) => {
return (
<Gutter>
<VerticalPadding
className={[classes.callToAction, invertBackground && classes.invert]
.filter(Boolean)
.join(' ')}
>
<div className={classes.wrap}>
<div className={classes.content}>
<RichText className={classes.richText} content={richText} />
</div>
<div className={classes.linkGroup}>
{(links || []).map(({ link }, i) => {
return <CMSLink key={i} {...link} invert={invertBackground} />
})}
</div>
</div>
</VerticalPadding>
</Gutter>
)
}

View File

@@ -1,42 +0,0 @@
@import '../../_css/common';
.grid {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: var(--base) calc(var(--base) * 2);
@include mid-break {
grid-template-columns: repeat(6, 1fr);
gap: var(--base) var(--base);
}
}
.column--oneThird {
grid-column-end: span 4;
}
.column--half {
grid-column-end: span 6;
}
.column--twoThirds {
grid-column-end: span 8;
}
.column--full {
grid-column-end: span 12;
}
.column {
@include mid-break {
grid-column-end: span 6;
}
@include small-break {
grid-column-end: span 6;
}
}
.link {
margin-top: var(--base);
}

View File

@@ -1,39 +0,0 @@
import React, { Fragment } from 'react'
import type { Page } from '../../../../test/live-preview/payload-types.js'
import { Gutter } from '../../_components/Gutter/index.js'
import { CMSLink } from '../../_components/Link/index.js'
import RichText from '../../_components/RichText/index.js'
import classes from './index.module.scss'
type Props = Extract<Exclude<Page['layout'], undefined>[0], { blockType: 'content' }>
export const ContentBlock: React.FC<
Props & {
id?: string
}
> = (props) => {
const { columns } = props
return (
<Gutter className={classes.content}>
<div className={classes.grid}>
{columns && columns.length > 0 ? (
<Fragment>
{columns.map((col, index) => {
const { enableLink, link, richText, size } = col
return (
<div className={[classes.column, classes[`column--${size}`]].join(' ')} key={index}>
<RichText content={richText} />
{enableLink && <CMSLink className={classes.link} {...link} />}
</div>
)
})}
</Fragment>
) : null}
</div>
</Gutter>
)
}

View File

@@ -1,8 +0,0 @@
.mediaBlock {
position: relative;
}
.caption {
color: var(--color-base-500);
margin-top: var(--base);
}

View File

@@ -1,42 +0,0 @@
import type { StaticImageData } from 'next/image.js'
import React from 'react'
import type { Page } from '../../../../test/live-preview/payload-types.js'
import { Gutter } from '../../_components/Gutter/index.js'
import { Media } from '../../_components/Media/index.js'
import RichText from '../../_components/RichText/index.js'
import classes from './index.module.scss'
type Props = Extract<Exclude<Page['layout'], undefined>[0], { blockType: 'mediaBlock' }> & {
id?: string
staticImage?: StaticImageData
}
export const MediaBlock: React.FC<Props> = (props) => {
const { media, position = 'default', staticImage } = props
let caption
if (media && typeof media === 'object') caption = media.caption
return (
<div className={classes.mediaBlock}>
{position === 'fullscreen' && (
<div className={classes.fullscreen}>
<Media resource={media} src={staticImage} />
</div>
)}
{position === 'default' && (
<Gutter>
<Media resource={media} src={staticImage} />
</Gutter>
)}
{caption && (
<Gutter className={classes.caption}>
<RichText content={caption} />
</Gutter>
)}
</div>
)
}

View File

@@ -1,58 +0,0 @@
@import '../../_css/common';
.introContent {
position: relative;
margin-bottom: calc(var(--base) * 2);
@include mid-break {
margin-bottom: var(--base);
}
}
.grid {
display: grid;
grid-template-columns: repeat(12, 1fr);
width: 100%;
gap: var(--base) 40px;
@include mid-break {
grid-template-columns: repeat(6, 1fr);
gap: calc(var(--base) / 2) var(--base);
}
}
.column {
grid-column-end: span 12;
@include mid-break {
grid-column-end: span 6;
}
@include small-break {
grid-column-end: span 6;
}
}
.cols-half {
grid-column-end: span 6;
@include mid-break {
grid-column-end: span 6;
}
@include small-break {
grid-column-end: span 6;
}
}
.cols-thirds {
grid-column-end: span 3;
@include mid-break {
grid-column-end: span 6;
}
@include small-break {
grid-column-end: span 6;
}
}

View File

@@ -1,52 +0,0 @@
import React from 'react'
import type { Post } from '../../../../test/live-preview/payload-types.js'
import { Card } from '../../_components/Card/index.js'
import { Gutter } from '../../_components/Gutter/index.js'
import RichText from '../../_components/RichText/index.js'
import classes from './index.module.scss'
export type RelatedPostsProps = {
blockName: string
blockType: 'relatedPosts'
docs?: (Post | string)[] | null
introContent?: any
relationTo: 'posts'
}
export const RelatedPosts: React.FC<RelatedPostsProps> = (props) => {
const { docs, introContent, relationTo } = props
return (
<div className={classes.relatedPosts}>
{introContent && (
<Gutter className={classes.introContent}>
<RichText content={introContent} />
</Gutter>
)}
<Gutter>
<div className={classes.grid}>
{docs?.map((doc, index) => {
if (typeof doc === 'string') return null
return (
<div
className={[
classes.column,
docs.length === 2 && classes['cols-half'],
docs.length >= 3 && classes['cols-thirds'],
]
.filter(Boolean)
.join(' ')}
key={index}
>
<Card doc={doc} relationTo={relationTo} showCategories />
</div>
)
})}
</div>
</Gutter>
</div>
)
}

View File

@@ -1,17 +0,0 @@
@import '../../_css/common';
.relationshipsBlock {
}
.array {
border: 1px solid var(--color-base-100);
padding: var(--base);
& > *:first-child {
margin-top: 0;
}
& > *:last-child {
margin-bottom: 0;
}
}

View File

@@ -1,196 +0,0 @@
import React, { Fragment } from 'react'
import type { Page } from '../../../../test/live-preview/payload-types.js'
import { Gutter } from '../../_components/Gutter/index.js'
import RichText from '../../_components/RichText/index.js'
import classes from './index.module.scss'
export type RelationshipsBlockProps = {
blockName: string
blockType: 'relationships'
data: Page
}
export const RelationshipsBlock: React.FC<RelationshipsBlockProps> = (props) => {
const { data } = props
return (
<div className={classes.relationshipsBlock}>
<Gutter>
<p>
This block is for testing purposes only. It renders every possible type of relationship.
</p>
<p>
<b>Rich Text Slate:</b>
</p>
{data?.richTextSlate && <RichText content={data.richTextSlate} renderUploadFilenameOnly />}
<p>
<b>Rich Text Lexical:</b>
</p>
{data?.richTextLexical && (
<RichText content={data.richTextLexical} renderUploadFilenameOnly serializer="lexical" />
)}
<p>
<b>Upload:</b>
</p>
{data?.relationshipAsUpload ? (
<div>
{typeof data?.relationshipAsUpload === 'string'
? data?.relationshipAsUpload
: data?.relationshipAsUpload.filename}
</div>
) : (
<div>None</div>
)}
<p>
<b>Monomorphic Has One:</b>
</p>
{data?.relationshipMonoHasOne ? (
<div>
{typeof data?.relationshipMonoHasOne === 'string'
? data?.relationshipMonoHasOne
: data?.relationshipMonoHasOne.title}
</div>
) : (
<div>None</div>
)}
<p>
<b>Monomorphic Has Many:</b>
</p>
{data?.relationshipMonoHasMany ? (
<Fragment>
{data?.relationshipMonoHasMany.length
? data?.relationshipMonoHasMany?.map((item, index) =>
item ? (
<div key={index}>{typeof item === 'string' ? item : item.title}</div>
) : (
'null'
),
)
: 'None'}
</Fragment>
) : (
<div>None</div>
)}
<p>
<b>Polymorphic Has One:</b>
</p>
{data?.relationshipPolyHasOne ? (
<div>
{typeof data?.relationshipPolyHasOne.value === 'string'
? data?.relationshipPolyHasOne.value
: data?.relationshipPolyHasOne.value.title}
</div>
) : (
<div>None</div>
)}
<p>
<b>Polymorphic Has Many:</b>
</p>
{data?.relationshipPolyHasMany ? (
<Fragment>
{data?.relationshipPolyHasMany.length
? data?.relationshipPolyHasMany?.map((item, index) =>
item.value ? (
<div key={index}>
{typeof item.value === 'string' ? item.value : item.value.title}
</div>
) : (
'null'
),
)
: 'None'}
</Fragment>
) : (
<div>None</div>
)}
<p>
<b>Array of Relationships:</b>
</p>
{data?.arrayOfRelationships?.map((item, index) => (
<div className={classes.array} key={index}>
<p>
<b>Rich Text:</b>
</p>
{item?.richTextInArray && <RichText content={item.richTextInArray} />}
<p>
<b>Upload:</b>
</p>
{item?.uploadInArray ? (
<div>
{typeof item?.uploadInArray === 'string'
? item?.uploadInArray
: item?.uploadInArray.filename}
</div>
) : (
<div>None</div>
)}
<p>
<b>Monomorphic Has One:</b>
</p>
{item?.relationshipInArrayMonoHasOne ? (
<div>
{typeof item?.relationshipInArrayMonoHasOne === 'string'
? item?.relationshipInArrayMonoHasOne
: item?.relationshipInArrayMonoHasOne.title}
</div>
) : (
<div>None</div>
)}
<p>
<b>Monomorphic Has Many:</b>
</p>
{item?.relationshipInArrayMonoHasMany ? (
<Fragment>
{item?.relationshipInArrayMonoHasMany.length
? item?.relationshipInArrayMonoHasMany?.map((rel, relIndex) =>
rel ? (
<div key={relIndex}>{typeof rel === 'string' ? rel : rel.title}</div>
) : (
'null'
),
)
: 'None'}
</Fragment>
) : (
<div>None</div>
)}
<p>
<b>Polymorphic Has One:</b>
</p>
{item?.relationshipInArrayPolyHasOne ? (
<div>
{typeof item?.relationshipInArrayPolyHasOne.value === 'string'
? item?.relationshipInArrayPolyHasOne.value
: item?.relationshipInArrayPolyHasOne.value.title}
</div>
) : (
<div>None</div>
)}
<p>
<b>Polymorphic Has Many:</b>
</p>
{item?.relationshipInArrayPolyHasMany ? (
<Fragment>
{item?.relationshipInArrayPolyHasMany.length
? item?.relationshipInArrayPolyHasMany?.map((rel, relIndex) =>
rel.value ? (
<div key={relIndex}>
{typeof rel.value === 'string' ? rel.value : rel.value.title}
</div>
) : (
'null'
),
)
: 'None'}
</Fragment>
) : (
<div>None</div>
)}
</div>
))}
</Gutter>
</div>
)
}

View File

@@ -1,3 +0,0 @@
.invert {
background-color: var(--color-base-750);
}

View File

@@ -1,20 +0,0 @@
import React from 'react'
import classes from './index.module.scss'
type Props = {
children?: React.ReactNode
className?: string
id?: string
invert?: boolean | null
}
export const BackgroundColor: React.FC<Props> = (props) => {
const { id, children, className, invert } = props
return (
<div className={[invert && classes.invert, className].filter(Boolean).join(' ')} id={id}>
{children}
</div>
)
}

View File

@@ -1,88 +0,0 @@
import React, { Fragment } from 'react'
import type { Page } from '../../../../test/live-preview/payload-types.js'
import type { RelationshipsBlockProps } from '../../_blocks/Relationships/index.js'
import type { VerticalPaddingOptions } from '../VerticalPadding/index.js'
import { ArchiveBlock } from '../../_blocks/ArchiveBlock/index.js'
import { CallToActionBlock } from '../../_blocks/CallToAction/index.js'
import { ContentBlock } from '../../_blocks/Content/index.js'
import { MediaBlock } from '../../_blocks/MediaBlock/index.js'
import { RelatedPosts, type RelatedPostsProps } from '../../_blocks/RelatedPosts/index.js'
import { RelationshipsBlock } from '../../_blocks/Relationships/index.js'
import { toKebabCase } from '../../_utilities/toKebabCase.js'
import { BackgroundColor } from '../BackgroundColor/index.js'
import { VerticalPadding } from '../VerticalPadding/index.js'
const blockComponents = {
archive: ArchiveBlock,
content: ContentBlock,
cta: CallToActionBlock,
mediaBlock: MediaBlock,
relatedPosts: RelatedPosts,
relationships: RelationshipsBlock,
}
type Block = NonNullable<Page['layout']>[number]
export const Blocks: React.FC<{
blocks?: (Block | RelatedPostsProps | RelationshipsBlockProps)[] | null
disableTopPadding?: boolean
}> = (props) => {
const { blocks, disableTopPadding } = props
const hasBlocks = blocks && Array.isArray(blocks) && blocks.length > 0
if (hasBlocks) {
return (
<Fragment>
{blocks.map((block, index) => {
const { blockName, blockType } = block
if (blockType && blockType in blockComponents) {
const Block = blockComponents[blockType]
// the cta block is containerized, so we don't consider it to be inverted at the block-level
const blockIsInverted =
'invertBackground' in block && blockType !== 'cta' ? block.invertBackground : false
const prevBlock = blocks[index - 1]
const prevBlockInverted =
prevBlock && 'invertBackground' in prevBlock && prevBlock?.invertBackground
const isPrevSame = Boolean(blockIsInverted) === Boolean(prevBlockInverted)
let paddingTop: VerticalPaddingOptions = 'large'
let paddingBottom: VerticalPaddingOptions = 'large'
if (prevBlock && isPrevSame) {
paddingTop = 'none'
}
if (index === blocks.length - 1) {
paddingBottom = 'large'
}
if (disableTopPadding && index === 0) {
paddingTop = 'none'
}
if (Block) {
return (
<BackgroundColor invert={blockIsInverted} key={index}>
<VerticalPadding bottom={paddingBottom} top={paddingTop}>
{/* @ts-expect-error */}
<Block id={toKebabCase(blockName)} {...block} />
</VerticalPadding>
</BackgroundColor>
)
}
}
return null
})}
</Fragment>
)
}
return null
}

View File

@@ -1,72 +0,0 @@
@import '../../_css/type.scss';
.button {
border: none;
cursor: pointer;
display: inline-flex;
justify-content: center;
background-color: transparent;
text-decoration: none;
display: inline-flex;
padding: 12px 24px;
font-family: inherit;
line-height: inherit;
font-size: inherit;
}
.content {
display: flex;
align-items: center;
justify-content: space-around;
svg {
margin-right: calc(var(--base) / 2);
width: var(--base);
height: var(--base);
}
}
.label {
@extend %label;
text-align: center;
display: flex;
align-items: center;
}
.appearance--primary {
background-color: var(--color-base-1000);
color: var(--color-base-0);
}
.appearance--secondary {
background-color: transparent;
box-shadow: inset 0 0 0 1px var(--color-base-1000);
}
.primary--invert {
background-color: var(--color-base-0);
color: var(--color-base-1000);
}
.secondary--invert {
background-color: var(--color-base-1000);
box-shadow: inset 0 0 0 1px var(--color-base-0);
}
.appearance--default {
padding: 0;
color: var(--theme-text);
}
.appearance--none {
padding: 0;
color: var(--theme-text);
&:local() {
.label {
text-transform: none;
line-height: inherit;
font-size: inherit;
}
}
}

View File

@@ -1,80 +0,0 @@
'use client'
import type { ElementType } from 'react'
import LinkWithDefault from 'next/link.js'
import React from 'react'
import classes from './index.module.scss'
const Link = (LinkWithDefault.default || LinkWithDefault) as typeof LinkWithDefault.default
export type Props = {
appearance?: 'default' | 'none' | 'primary' | 'secondary'
className?: string
disabled?: boolean
el?: 'a' | 'button' | 'link'
href?: string
invert?: boolean
label?: string
newTab?: boolean
onClick?: () => void
type?: 'button' | 'submit'
}
export const Button: React.FC<Props> = ({
type = 'button',
appearance,
className: classNameFromProps,
disabled,
el: elFromProps = 'link',
href,
invert,
label,
newTab,
onClick,
}) => {
let el = elFromProps
const newTabProps = newTab ? { rel: 'noopener noreferrer', target: '_blank' } : {}
const className = [
classes.button,
classNameFromProps,
classes[`appearance--${appearance}`],
invert && classes[`${appearance}--invert`],
]
.filter(Boolean)
.join(' ')
const content = (
<div className={classes.content}>
<span className={classes.label}>{label}</span>
</div>
)
if (onClick || type === 'submit') el = 'button'
if (el === 'link') {
return (
<Link className={className} href={href || ''} {...newTabProps} onClick={onClick}>
{content}
</Link>
)
}
const Element: ElementType = el
return (
<Element
className={className}
href={href}
type={type}
{...newTabProps}
disabled={disabled}
onClick={onClick}
>
{content}
</Element>
)
}

View File

@@ -1,106 +0,0 @@
@import '../../_css/common';
.card {
border: 1px var(--color-base-200) solid;
border-radius: 4px;
height: 100%;
display: flex;
flex-direction: column;
}
.vertical {
flex-direction: column;
}
.horizontal {
flex-direction: row;
&:local() {
.mediaWrapper {
width: 150px;
@include mid-break {
width: 100%;
}
}
}
@include mid-break {
flex-direction: column;
}
}
.content {
padding: var(--base);
flex-grow: 1;
display: flex;
flex-direction: column;
gap: calc(var(--base) / 2);
@include small-break {
padding: calc(var(--base) / 2);
gap: calc(var(--base) / 4);
}
}
.title {
margin: 0;
}
.titleLink {
text-decoration: none;
}
.centerAlign {
align-items: center;
}
.body {
flex-grow: 1;
}
.leader {
@extend %label;
display: flex;
gap: var(--base);
}
.description {
margin: 0;
}
.hideImageOnMobile {
@include mid-break {
display: none;
}
}
.mediaWrapper {
text-decoration: none;
display: block;
position: relative;
aspect-ratio: 16 / 9;
}
.image {
object-fit: cover;
}
.placeholder {
background-color: var(--color-base-50);
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.actions {
display: flex;
align-items: center;
@include mid-break {
flex-direction: column;
align-items: flex-start;
}
}

View File

@@ -1,88 +0,0 @@
import LinkWithDefault from 'next/link.js'
import React, { Fragment } from 'react'
import type { Post } from '../../../../test/live-preview/payload-types.js'
import { Media } from '../Media/index.js'
import classes from './index.module.scss'
const Link = (LinkWithDefault.default || LinkWithDefault) as typeof LinkWithDefault.default
export const Card: React.FC<{
alignItems?: 'center'
className?: string
doc?: Post
hideImagesOnMobile?: boolean
orientation?: 'horizontal' | 'vertical'
relationTo?: 'posts'
showCategories?: boolean
title?: string
}> = (props) => {
const {
className,
doc,
orientation = 'vertical',
relationTo,
showCategories,
title: titleFromProps,
} = props
const { slug, categories, meta, title } = doc || {}
const { description, image: metaImage } = meta || {}
const hasCategories = categories && Array.isArray(categories) && categories.length > 0
const titleToUse = titleFromProps || title
const sanitizedDescription = description?.replace(/\s/g, ' ') // replace non-breaking space with white space
const href = `/live-preview/${relationTo}/${slug}`
return (
<div
className={[classes.card, className, orientation && classes[orientation]]
.filter(Boolean)
.join(' ')}
>
<Link className={classes.mediaWrapper} href={href}>
{!metaImage && <div className={classes.placeholder}>No image</div>}
{metaImage && typeof metaImage !== 'string' && (
<Media fill imgClassName={classes.image} resource={metaImage} />
)}
</Link>
<div className={classes.content}>
{showCategories && hasCategories && (
<div className={classes.leader}>
{showCategories && hasCategories && (
<div>
{categories?.map((category, index) => {
const titleFromCategory = typeof category === 'string' ? category : category.title
const categoryTitle = titleFromCategory || 'Untitled category'
const isLast = index === categories.length - 1
return (
<Fragment key={index}>
{categoryTitle}
{!isLast && <Fragment>, &nbsp;</Fragment>}
</Fragment>
)
})}
</div>
)}
</div>
)}
{titleToUse && (
<h4 className={classes.title}>
<Link className={classes.titleLink} href={href}>
{titleToUse}
</Link>
</h4>
)}
{description && (
<div className={classes.body}>
{description && <p className={classes.description}>{sanitizedDescription}</p>}
</div>
)}
</div>
</div>
)
}

View File

@@ -1,26 +0,0 @@
import React from 'react'
export const Chevron: React.FC<{
className?: string
rotate?: number
}> = ({ className, rotate }) => {
return (
<svg
className={className}
height="100%"
style={{
transform: typeof rotate === 'number' ? `rotate(${rotate || 0}deg)` : undefined,
}}
viewBox="0 0 24 24"
width="100%"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M23.245 4l-11.245 14.374-11.219-14.374-.781.619 12 15.381 12-15.391-.755-.609z"
fill="none"
stroke="currentColor"
vectorEffect="non-scaling-stroke"
/>
</svg>
)
}

View File

@@ -1,73 +0,0 @@
@import '../../../_css/common';
// this is to make up for the space taken by the fixed header, since the scroll method does not accept an offset parameter
.scrollRef {
position: absolute;
left: 0;
top: calc(var(--base) * -5);
@include mid-break {
top: calc(var(--base) * -2);
}
}
.introContent {
position: relative;
margin-bottom: calc(var(--base) * 2);
@include mid-break {
margin-bottom: var(--base);
}
}
.resultCountWrapper {
display: flex;
margin-bottom: calc(var(--base) * 2);
@include mid-break {
margin-bottom: var(--base);
}
}
.pageRange {
margin-bottom: var(--base);
@include mid-break {
margin-bottom: var(--base);
}
}
.list {
position: relative;
}
.grid {
display: grid;
grid-template-columns: repeat(12, 1fr);
width: 100%;
gap: var(--base) 40px;
@include mid-break {
grid-template-columns: repeat(6, 1fr);
gap: calc(var(--base) / 2) var(--base);
}
}
.column {
grid-column-end: span 4;
@include mid-break {
grid-column-end: span 6;
}
@include small-break {
grid-column-end: span 6;
}
}
.pagination {
margin-top: calc(var(--base) * 2);
@include mid-break {
margin-top: var(--base);
}
}

View File

@@ -1,186 +0,0 @@
'use client'
import qs from 'qs'
import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react'
import type { Post } from '../../../../../test/live-preview/payload-types.js'
import type { ArchiveBlockProps } from '../../../_blocks/ArchiveBlock/types.js'
import { PAYLOAD_SERVER_URL } from '../../../_api/serverURL.js'
import { Card } from '../../Card/index.js'
import { Gutter } from '../../Gutter/index.js'
import { PageRange } from '../../PageRange/index.js'
import { Pagination } from '../../Pagination/index.js'
import classes from './index.module.scss'
type Result = {
docs: (Post | string)[]
hasNextPage: boolean
hasPrevPage: boolean
nextPage: number
page: number
prevPage: number
totalDocs: number
totalPages: number
}
export type Props = Omit<ArchiveBlockProps, 'blockType'> & {
className?: string
onResultChange?: (result: Result) => void // eslint-disable-line no-unused-vars
showPageRange?: boolean
sort?: string
}
export const CollectionArchiveByCollection: React.FC<Props> = (props) => {
const {
categories: catsFromProps,
className,
limit = 10,
onResultChange,
populatedDocs,
populatedDocsTotal,
relationTo,
showPageRange,
sort = '-createdAt',
} = props
const [results, setResults] = useState<Result>({
docs: populatedDocs?.map((doc) => doc.value) || [],
hasNextPage: false,
hasPrevPage: false,
nextPage: 1,
page: 1,
prevPage: 1,
totalDocs: typeof populatedDocsTotal === 'number' ? populatedDocsTotal : 0,
totalPages: 1,
})
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState<string | undefined>(undefined)
const scrollRef = useRef<HTMLDivElement>(null)
const hasHydrated = useRef(false)
const [page, setPage] = useState(1)
const scrollToRef = useCallback(() => {
const { current } = scrollRef
if (current) {
// current.scrollIntoView({
// behavior: 'smooth',
// })
}
}, [])
useEffect(() => {
if (!isLoading && typeof results.page !== 'undefined') {
// scrollToRef()
}
}, [isLoading, scrollToRef, results])
useEffect(() => {
// hydrate the block with fresh content after first render
// don't show loader unless the request takes longer than x ms
// and don't show it during initial hydration
const timer = setTimeout(() => {
if (hasHydrated) {
setIsLoading(true)
}
}, 500)
const searchQuery = qs.stringify(
{
depth: 1,
limit,
page,
sort,
where: {
...(catsFromProps && catsFromProps?.length > 0
? {
categories: {
in:
typeof catsFromProps === 'string'
? [catsFromProps]
: catsFromProps
.map((cat) => (typeof cat === 'object' && cat !== null ? cat.id : cat))
.join(','),
},
}
: {}),
},
},
{ encode: false },
)
const makeRequest = async () => {
try {
const req = await fetch(`${PAYLOAD_SERVER_URL}/api/${relationTo}?${searchQuery}`)
const json = await req.json()
clearTimeout(timer)
hasHydrated.current = true
const { docs } = json as { docs: Post[] }
if (docs && Array.isArray(docs)) {
setResults(json)
setIsLoading(false)
if (typeof onResultChange === 'function') {
onResultChange(json)
}
}
} catch (err) {
console.warn(err) // eslint-disable-line no-console
setIsLoading(false)
setError(`Unable to load "${relationTo} archive" data at this time.`)
}
}
void makeRequest()
return () => {
if (timer) clearTimeout(timer)
}
}, [page, catsFromProps, relationTo, onResultChange, sort, limit])
return (
<div className={[classes.collectionArchive, className].filter(Boolean).join(' ')}>
<div className={classes.scrollRef} ref={scrollRef} />
{!isLoading && error && <Gutter>{error}</Gutter>}
<Fragment>
{showPageRange !== false && (
<Gutter>
<div className={classes.pageRange}>
<PageRange
collection={relationTo}
currentPage={results.page}
limit={limit}
totalDocs={results.totalDocs}
/>
</div>
</Gutter>
)}
<Gutter>
<div className={classes.grid}>
{results.docs?.map((result, index) => {
if (typeof result === 'string') {
return null
}
return (
<div className={classes.column} key={index}>
<Card doc={result} relationTo="posts" showCategories />
</div>
)
})}
</div>
{results.totalPages > 1 && (
<Pagination
className={classes.pagination}
onClick={setPage}
page={results.page}
totalPages={results.totalPages}
/>
)}
</Gutter>
</Fragment>
</div>
)
}

View File

@@ -1,25 +0,0 @@
@import '../../../_css/common';
.grid {
display: grid;
grid-template-columns: repeat(12, 1fr);
width: 100%;
gap: var(--base) 40px;
@include mid-break {
grid-template-columns: repeat(6, 1fr);
gap: calc(var(--base) / 2) var(--base);
}
}
.column {
grid-column-end: span 4;
@include mid-break {
grid-column-end: span 6;
}
@include small-break {
grid-column-end: span 6;
}
}

View File

@@ -1,42 +0,0 @@
'use client'
import React, { Fragment } from 'react'
import type { ArchiveBlockProps } from '../../../_blocks/ArchiveBlock/types.js'
import { Card } from '../../Card/index.js'
import { Gutter } from '../../Gutter/index.js'
import classes from './index.module.scss'
export type Props = {
className?: string
selectedDocs?: ArchiveBlockProps['selectedDocs']
}
export const CollectionArchiveBySelection: React.FC<Props> = (props) => {
const { className, selectedDocs } = props
const result = selectedDocs?.map((doc) => doc.value)
return (
<div className={[classes.collectionArchive, className].filter(Boolean).join(' ')}>
<Fragment>
<Gutter>
<div className={classes.grid}>
{result?.map((result, index) => {
if (typeof result === 'string') {
return null
}
return (
<div className={classes.column} key={index}>
<Card doc={result} relationTo="posts" showCategories />
</div>
)
})}
</div>
</Gutter>
</Fragment>
</div>
)
}

View File

@@ -1,25 +0,0 @@
import React from 'react'
import type { ArchiveBlockProps } from '../../_blocks/ArchiveBlock/types.js'
import { CollectionArchiveByCollection } from './PopulateByCollection/index.js'
import { CollectionArchiveBySelection } from './PopulateBySelection/index.js'
export type Props = Omit<ArchiveBlockProps, 'blockType'> & {
className?: string
sort?: string
}
export const CollectionArchive: React.FC<Props> = (props) => {
const { className, populateBy, selectedDocs } = props
if (populateBy === 'selection') {
return <CollectionArchiveBySelection className={className} selectedDocs={selectedDocs} />
}
if (populateBy === 'collection') {
return <CollectionArchiveByCollection {...props} className={className} />
}
return null
}

View File

@@ -1,36 +0,0 @@
@use '../../_css/queries.scss' as *;
.footer {
padding: calc(var(--base) * 4) 0;
background-color: var(--color-base-1000);
color: var(--color-base-0);
@include small-break {
padding: calc(var(--base) * 2) 0;
}
}
.wrap {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
gap: calc(var(--base) / 2) var(--base);
}
.logo {
width: 150px;
}
.nav {
display: flex;
gap: calc(var(--base) / 4) var(--base);
align-items: center;
flex-wrap: wrap;
opacity: 1;
transition: opacity 100ms linear;
visibility: visible;
> * {
text-decoration: none;
}
}

View File

@@ -1,41 +0,0 @@
import LinkWithDefault from 'next/link.js'
import React from 'react'
import { fetchFooter } from '../../_api/fetchFooter.js'
import { Gutter } from '../Gutter/index.js'
import { CMSLink } from '../Link/index.js'
import classes from './index.module.scss'
const Link = (LinkWithDefault.default || LinkWithDefault) as typeof LinkWithDefault.default
export async function Footer() {
const footer = await fetchFooter()
const navItems = footer?.navItems || []
return (
<footer className={classes.footer}>
<Gutter className={classes.wrap}>
<Link href="/">
<picture>
<img
alt="Payload Logo"
className={classes.logo}
src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/payload/src/admin/assets/images/payload-logo-light.svg"
/>
</picture>
</Link>
<nav className={classes.nav}>
{navItems.map(({ link }, i) => {
return <CMSLink key={i} {...link} />
})}
<Link href="/admin">Admin</Link>
<Link href="https://github.com/payloadcms/payload/tree/main/templates/ecommerce">
Source Code
</Link>
<Link href="https://github.com/payloadcms/payload">Payload</Link>
</nav>
</Gutter>
</footer>
)
}

View File

@@ -1,13 +0,0 @@
.gutter {
max-width: 1920px;
margin-left: auto;
margin-right: auto;
}
.gutterLeft {
padding-left: var(--gutter-h);
}
.gutterRight {
padding-right: var(--gutter-h);
}

View File

@@ -1,35 +0,0 @@
import type { Ref } from 'react'
import React, { forwardRef } from 'react'
import classes from './index.module.scss'
type Props = {
children: React.ReactNode
className?: string
left?: boolean
ref?: Ref<HTMLDivElement>
right?: boolean
}
export const Gutter: React.FC<Props> = forwardRef<HTMLDivElement, Props>((props, ref) => {
const { children, className, left = true, right = true } = props
return (
<div
className={[
classes.gutter,
left && classes.gutterLeft,
right && classes.gutterRight,
className,
]
.filter(Boolean)
.join(' ')}
ref={ref}
>
{children}
</div>
)
})
Gutter.displayName = 'Gutter'

View File

@@ -1,12 +0,0 @@
@use '../../../_css/queries.scss' as *;
.nav {
display: flex;
gap: calc(var(--base) / 4) var(--base);
align-items: center;
flex-wrap: wrap;
> * {
text-decoration: none;
}
}

View File

@@ -1,20 +0,0 @@
'use client'
import React from 'react'
import type { Header as HeaderType } from '../../../../../test/live-preview/payload-types.js'
import { CMSLink } from '../../Link/index.js'
import classes from './index.module.scss'
export const HeaderNav: React.FC<{ header: HeaderType }> = ({ header }) => {
const navItems = header?.navItems || []
return (
<nav className={classes.nav}>
{navItems.map(({ link }, i) => {
return <CMSLink key={i} {...link} />
})}
</nav>
)
}

View File

@@ -1,16 +0,0 @@
@use '../../_css/queries.scss' as *;
.header {
padding: var(--base) 0;
}
.wrap {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
gap: calc(var(--base) / 2) var(--base);
}
.logo {
width: 150px;
}

View File

@@ -1,28 +0,0 @@
import LinkWithDefault from 'next/link.js'
import React from 'react'
import { fetchHeader } from '../../_api/fetchHeader.js'
import { Gutter } from '../Gutter/index.js'
import { HeaderNav } from './Nav/index.js'
import classes from './index.module.scss'
const Link = (LinkWithDefault.default || LinkWithDefault) as typeof LinkWithDefault.default
export async function Header() {
const header = await fetchHeader()
return (
<header className={classes.header}>
<Gutter className={classes.wrap}>
<Link href="/live-preview">
<img
alt="Payload Logo"
className={classes.logo}
src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/payload/src/admin/assets/images/payload-logo-dark.svg"
/>
</Link>
<HeaderNav header={header} />
</Gutter>
</header>
)
}

View File

@@ -1,23 +0,0 @@
import React from 'react'
import type { Page } from '../../../../test/live-preview/payload-types.js'
import { HighImpactHero } from '../../_heros/HighImpact/index.js'
import { LowImpactHero } from '../../_heros/LowImpact/index.js'
const heroes = {
highImpact: HighImpactHero,
lowImpact: LowImpactHero,
}
export const Hero: React.FC<Page['hero']> = (props) => {
const { type } = props || {}
if (!type || type === 'none') return null
const HeroToRender = heroes[type]
if (!HeroToRender) return null
return <HeroToRender {...props} />
}

View File

@@ -1,67 +0,0 @@
import LinkWithDefault from 'next/link.js'
import React from 'react'
import type { Page, Post } from '../../../../test/live-preview/payload-types.js'
import type { Props as ButtonProps } from '../Button/index.js'
import { Button } from '../Button/index.js'
const Link = (LinkWithDefault.default || LinkWithDefault) as typeof LinkWithDefault.default
type CMSLinkType = {
appearance?: ButtonProps['appearance']
children?: React.ReactNode
className?: string
invert?: ButtonProps['invert']
label?: string
newTab?: boolean
reference?: {
relationTo: 'pages' | 'posts'
value: Page | Post | string
}
type?: 'custom' | 'reference'
url?: string
}
export const CMSLink: React.FC<CMSLinkType> = ({
type,
appearance,
children,
className,
invert,
label,
newTab,
reference,
url,
}) => {
const href =
type === 'reference' && typeof reference?.value === 'object' && reference.value.slug
? `/${reference.value.slug}`
: url
if (!href) return null
if (!appearance) {
const newTabProps = newTab ? { rel: 'noopener noreferrer', target: '_blank' } : {}
if (href || url) {
return (
<Link {...newTabProps} className={className} href={href || url || ''}>
{label && label}
{children && children}
</Link>
)
}
}
return (
<Button
appearance={appearance}
className={className}
href={href}
invert={invert}
label={label}
newTab={newTab}
/>
)
}

View File

@@ -1,7 +0,0 @@
.placeholder-color-light {
background-color: rgba(0, 0, 0, 0.05);
}
.placeholder {
background-color: var(--color-base-50);
}

View File

@@ -1,81 +0,0 @@
'use client'
import type { StaticImageData } from 'next/image.js'
import NextImageWithDefault from 'next/image.js'
import React from 'react'
import type { Props as MediaProps } from '../types.js'
import { PAYLOAD_SERVER_URL } from '../../../_api/serverURL.js'
import cssVariables from '../../../cssVariables.js'
import classes from './index.module.scss'
const { breakpoints } = cssVariables
const NextImage = (NextImageWithDefault.default ||
NextImageWithDefault) as typeof NextImageWithDefault.default
export const Image: React.FC<MediaProps> = (props) => {
const {
alt: altFromProps,
fill,
imgClassName,
onClick,
onLoad: onLoadFromProps,
priority,
resource,
src: srcFromProps,
} = props
const [isLoading, setIsLoading] = React.useState(true)
let width: number | undefined
let height: number | undefined
let alt = altFromProps
let src: StaticImageData | string = srcFromProps || ''
if (!src && resource && typeof resource !== 'string') {
const {
alt: altFromResource,
filename: fullFilename,
height: fullHeight,
width: fullWidth,
} = resource
width = fullWidth || undefined
height = fullHeight || undefined
alt = altFromResource
const filename = fullFilename
src = `${PAYLOAD_SERVER_URL}/api/media/file/${filename}`
}
// NOTE: this is used by the browser to determine which image to download at different screen sizes
const sizes = Object.entries(breakpoints)
.map(([, value]) => `(max-width: ${value}px) ${value}px`)
.join(', ')
return (
<NextImage
alt={alt || ''}
className={[isLoading && classes.placeholder, classes.image, imgClassName]
.filter(Boolean)
.join(' ')}
fill={fill}
height={!fill ? height : undefined}
onClick={onClick}
onLoad={() => {
setIsLoading(false)
if (typeof onLoadFromProps === 'function') {
onLoadFromProps()
}
}}
priority={priority}
sizes={sizes}
src={src}
width={!fill ? width : undefined}
/>
)
}

View File

@@ -1,11 +0,0 @@
.video {
max-width: 100%;
width: 100%;
background-color: var(--color-base-50);
}
.cover {
object-fit: cover;
width: 100%;
height: 100%;
}

View File

@@ -1,46 +0,0 @@
'use client'
import React, { useEffect, useRef } from 'react'
import type { Props as MediaProps } from '../types.js'
import { PAYLOAD_SERVER_URL } from '../../../_api/serverURL.js'
import classes from './index.module.scss'
export const Video: React.FC<MediaProps> = (props) => {
const { onClick, resource, videoClassName } = props
const videoRef = useRef<HTMLVideoElement>(null)
// const [showFallback] = useState<boolean>()
useEffect(() => {
const { current: video } = videoRef
if (video) {
video.addEventListener('suspend', () => {
// setShowFallback(true);
// console.warn('Video was suspended, rendering fallback image.')
})
}
}, [])
if (resource && typeof resource !== 'string') {
const { filename } = resource
return (
<video
autoPlay
className={[classes.video, videoClassName].filter(Boolean).join(' ')}
controls={false}
loop
muted
onClick={onClick}
playsInline
ref={videoRef}
>
<source src={`${PAYLOAD_SERVER_URL}/media/${filename}`} />
</video>
)
}
return null
}

View File

@@ -1,31 +0,0 @@
import type { ElementType } from 'react'
import React, { Fragment } from 'react'
import type { Props } from './types.js'
import { Image } from './Image/index.js'
import { Video } from './Video/index.js'
export const Media: React.FC<Props> = (props) => {
const { className, htmlElement = 'div', resource } = props
const isVideo = typeof resource !== 'string' && resource?.mimeType?.includes('video')
const Tag = (htmlElement as ElementType) || Fragment
return (
<Tag
{...(htmlElement !== null
? {
className,
}
: {})}
>
{isVideo ? (
<Video {...props} />
) : (
<Image {...props} /> // eslint-disable-line
)}
</Tag>
)
}

View File

@@ -1,20 +0,0 @@
import type { StaticImageData } from 'next/image'
import type { ElementType, Ref } from 'react'
import type { Media as MediaType } from '../../../payload-types'
export interface Props {
alt?: string
className?: string
fill?: boolean // for NextImage only
htmlElement?: ElementType | null
imgClassName?: string
onClick?: () => void
onLoad?: () => void
priority?: boolean // for NextImage only
ref?: Ref<HTMLImageElement | HTMLVideoElement | null>
resource?: MediaType | string // for Payload media
size?: string // for NextImage only
src?: StaticImageData // for static media
videoClassName?: string
}

View File

@@ -1,21 +0,0 @@
@import '../../_css/common';
.pageRange {
display: flex;
align-items: center;
font-weight: 600;
}
.content {
display: flex;
align-items: center;
margin: 0 var(--base(0.5));
}
.divider {
margin: 0 2px;
}
.hyperlink {
display: flex;
}

View File

@@ -1,52 +0,0 @@
import React from 'react'
import classes from './index.module.scss'
const defaultLabels = {
plural: 'Docs',
singular: 'Doc',
}
const defaultCollectionLabels = {
products: {
plural: 'Products',
singular: 'Product',
},
}
export const PageRange: React.FC<{
className?: string
collection?: string
collectionLabels?: {
plural?: string
singular?: string
}
currentPage?: number
limit?: number
totalDocs?: number
}> = (props) => {
const {
className,
collection,
collectionLabels: collectionLabelsFromProps,
currentPage,
limit,
totalDocs,
} = props
const indexStart = (currentPage ? currentPage - 1 : 1) * (limit || 1) + 1
let indexEnd = (currentPage || 1) * (limit || 1)
if (totalDocs && indexEnd > totalDocs) indexEnd = totalDocs
const { plural, singular } =
collectionLabelsFromProps || defaultCollectionLabels[collection || ''] || defaultLabels || {}
return (
<div className={[className, classes.pageRange].filter(Boolean).join(' ')}>
{(typeof totalDocs === 'undefined' || totalDocs === 0) && 'Search produced no results.'}
{typeof totalDocs !== 'undefined' &&
totalDocs > 0 &&
`Showing ${indexStart} - ${indexEnd} of ${totalDocs} ${totalDocs > 1 ? plural : singular}`}
</div>
)
}

View File

@@ -1,29 +0,0 @@
@import '../../_css/type.scss';
.pagination {
@extend %label;
display: flex;
align-items: center;
gap: calc(var(--base) / 2);
}
.button {
all: unset;
cursor: pointer;
position: relative;
display: flex;
padding: calc(var(--base) / 2);
color: var(--color-base-500);
border: 1px solid var(--color-base-200);
&:disabled {
cursor: not-allowed;
color: var(--color-base-200);
border-color: var(--color-base-150);
}
}
.icon {
width: calc(var(--base) / 2);
height: calc(var(--base) / 2);
}

View File

@@ -1,45 +0,0 @@
import React from 'react'
import { Chevron } from '../Chevron/index.js'
import classes from './index.module.scss'
export const Pagination: React.FC<{
className?: string
onClick: (page: number) => void
page: number
totalPages: number
}> = (props) => {
const { className, onClick, page, totalPages } = props
const hasNextPage = page < totalPages
const hasPrevPage = page > 1
return (
<div className={[classes.pagination, className].filter(Boolean).join(' ')}>
<button
className={classes.button}
disabled={!hasPrevPage}
onClick={() => {
onClick(page - 1)
}}
type="button"
>
<Chevron className={classes.icon} rotate={90} />
</button>
<div className={classes.pageRange}>
<span className={classes.pageRangeLabel}>
Page {page} of {totalPages}
</span>
</div>
<button
className={classes.button}
disabled={!hasNextPage}
onClick={() => {
onClick(page + 1)
}}
type="button"
>
<Chevron className={classes.icon} rotate={-90} />
</button>
</div>
)
}

View File

@@ -1,9 +0,0 @@
.richText {
> *:first-child {
margin-top: 0;
}
> *:last-child {
margin-bottom: 0;
}
}

View File

@@ -1,26 +0,0 @@
import React from 'react'
import classes from './index.module.scss'
import serializeLexical from './serializeLexical.js'
import serializeSlate from './serializeSlate.js'
const RichText: React.FC<{
className?: string
content: any
renderUploadFilenameOnly?: boolean
serializer?: 'lexical' | 'slate'
}> = ({ className, content, renderUploadFilenameOnly, serializer = 'slate' }) => {
if (!content) {
return null
}
return (
<div className={[classes.richText, className].filter(Boolean).join(' ')}>
{serializer === 'slate'
? serializeSlate(content, renderUploadFilenameOnly)
: serializeLexical(content, renderUploadFilenameOnly)}
</div>
)
}
export default RichText

View File

@@ -1,92 +0,0 @@
import type { SerializedEditorState } from 'lexical'
import React from 'react'
import { CMSLink } from '../Link/index.js'
import { Media } from '../Media/index.js'
const serializer = (
content?: SerializedEditorState['root']['children'],
renderUploadFilenameOnly?: boolean,
): React.ReactNode | React.ReactNode[] =>
content?.map((node, i) => {
switch (node.type) {
case 'h1':
return <h1 key={i}>{serializeLexical(node?.children, renderUploadFilenameOnly)}</h1>
case 'h2':
return <h2 key={i}>{serializeLexical(node?.children, renderUploadFilenameOnly)}</h2>
case 'h3':
return <h3 key={i}>{serializeLexical(node?.children, renderUploadFilenameOnly)}</h3>
case 'h4':
return <h4 key={i}>{serializeLexical(node?.children, renderUploadFilenameOnly)}</h4>
case 'h5':
return <h5 key={i}>{serializeLexical(node?.children, renderUploadFilenameOnly)}</h5>
case 'h6':
return <h6 key={i}>{serializeLexical(node?.children, renderUploadFilenameOnly)}</h6>
case 'quote':
return (
<blockquote key={i}>
{serializeLexical(node?.children, renderUploadFilenameOnly)}
</blockquote>
)
case 'ul':
return <ul key={i}>{serializeLexical(node?.children, renderUploadFilenameOnly)}</ul>
case 'ol':
return <ol key={i}>{serializeLexical(node.children, renderUploadFilenameOnly)}</ol>
case 'li':
return <li key={i}>{serializeLexical(node.children, renderUploadFilenameOnly)}</li>
case 'relationship':
return (
<span key={i}>
{node.value && typeof node.value === 'object'
? node.value.title || node.value.id
: node.value}
</span>
)
case 'link':
return (
<CMSLink
key={i}
newTab={Boolean(node?.newTab)}
reference={node.doc as any}
type={node.linkType === 'internal' ? 'reference' : 'custom'}
url={node.url}
>
{serializer(node?.children, renderUploadFilenameOnly)}
</CMSLink>
)
case 'upload':
if (renderUploadFilenameOnly) {
return <span key={i}>{node.value.filename}</span>
}
return <Media key={i} resource={node?.value} />
case 'paragraph':
return <p key={i}>{serializer(node?.children, renderUploadFilenameOnly)}</p>
case 'text':
return <span key={i}>{node.text}</span>
}
})
const serializeLexical = (
content?: SerializedEditorState,
renderUploadFilenameOnly?: boolean,
): React.ReactNode | React.ReactNode[] => {
return serializer(content?.root?.children, renderUploadFilenameOnly)
}
export default serializeLexical

View File

@@ -1,131 +0,0 @@
import escapeHTML from 'escape-html'
import React, { Fragment } from 'react'
import { Text } from 'slate'
import { CMSLink } from '../Link/index.js'
import { Media } from '../Media/index.js'
// eslint-disable-next-line no-use-before-define
type Children = Leaf[]
type Leaf = {
[key: string]: unknown
children?: Children
type: string
url?: string
value?: any
}
const serializeSlate = (
children?: Children,
renderUploadFilenameOnly?: boolean,
): React.ReactNode[] =>
children?.map((node, i) => {
if (Text.isText(node)) {
let text = <span dangerouslySetInnerHTML={{ __html: escapeHTML(node.text) }} />
if (node.bold) {
text = <strong key={i}>{text}</strong>
}
if (node.code) {
text = <code key={i}>{text}</code>
}
if (node.italic) {
text = <em key={i}>{text}</em>
}
if (node.underline) {
text = (
<span key={i} style={{ textDecoration: 'underline' }}>
{text}
</span>
)
}
if (node.strikethrough) {
text = (
<span key={i} style={{ textDecoration: 'line-through' }}>
{text}
</span>
)
}
return <Fragment key={i}>{text}</Fragment>
}
if (!node) {
return null
}
switch (node.type) {
case 'h1':
return <h1 key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</h1>
case 'h2':
return <h2 key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</h2>
case 'h3':
return <h3 key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</h3>
case 'h4':
return <h4 key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</h4>
case 'h5':
return <h5 key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</h5>
case 'h6':
return <h6 key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</h6>
case 'quote':
return (
<blockquote key={i}>
{serializeSlate(node?.children, renderUploadFilenameOnly)}
</blockquote>
)
case 'ul':
return <ul key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</ul>
case 'ol':
return <ol key={i}>{serializeSlate(node.children, renderUploadFilenameOnly)}</ol>
case 'li':
return <li key={i}>{serializeSlate(node.children, renderUploadFilenameOnly)}</li>
case 'relationship':
return (
<span key={i}>
{node.value && typeof node.value === 'object'
? node.value.title || node.value.id
: node.value}
</span>
)
case 'link':
return (
<CMSLink
key={i}
newTab={Boolean(node?.newTab)}
reference={node.doc as any}
type={node.linkType === 'internal' ? 'reference' : 'custom'}
url={node.url}
>
{serializeSlate(node?.children, renderUploadFilenameOnly)}
</CMSLink>
)
case 'upload':
if (renderUploadFilenameOnly) {
return <span key={i}>{node.value.filename}</span>
}
return <Media key={i} resource={node?.value} />
default:
return <p key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</p>
}
}) || []
export default serializeSlate

View File

@@ -1,15 +0,0 @@
.top-large {
padding-top: var(--block-padding);
}
.top-medium {
padding-top: calc(var(--block-padding) / 2);
}
.bottom-large {
padding-bottom: var(--block-padding);
}
.bottom-medium {
padding-bottom: calc(var(--block-padding) / 2);
}

View File

@@ -1,29 +0,0 @@
import React from 'react'
import classes from './index.module.scss'
export type VerticalPaddingOptions = 'large' | 'medium' | 'none'
type Props = {
bottom?: VerticalPaddingOptions
children: React.ReactNode
className?: string
top?: VerticalPaddingOptions
}
export const VerticalPadding: React.FC<Props> = ({
bottom = 'medium',
children,
className,
top = 'medium',
}) => {
return (
<div
className={[className, classes[`top-${top}`], classes[`bottom-${bottom}`]]
.filter(Boolean)
.join(' ')}
>
{children}
</div>
)
}

View File

@@ -1,121 +0,0 @@
@use './queries.scss' as *;
@use './colors.scss' as *;
@use './type.scss' as *;
:root {
--base: 24px;
--font-body: system-ui;
--font-mono: 'Roboto Mono', monospace;
--gutter-h: 180px;
--block-padding: 120px;
--theme-text: var(--color-base-750);
@include large-break {
--gutter-h: 144px;
--block-padding: 96px;
}
@include mid-break {
--gutter-h: 24px;
--block-padding: 60px;
}
}
* {
box-sizing: border-box;
}
html {
@extend %body;
-webkit-font-smoothing: antialiased;
}
html,
body,
#app {
height: 100%;
}
body {
font-family: var(--font-body);
margin: 0;
color: var(--theme-text);
}
::selection {
background: var(--color-success-500);
color: var(--color-base-800);
}
::-moz-selection {
background: var(--color-success-500);
color: var(--color-base-800);
}
img {
max-width: 100%;
height: auto;
display: block;
}
h1 {
@extend %h1;
}
h2 {
@extend %h2;
}
h3 {
@extend %h3;
}
h4 {
@extend %h4;
}
h5 {
@extend %h5;
}
h6 {
@extend %h6;
}
p {
margin: var(--base) 0;
@include mid-break {
margin: calc(var(--base) * 0.75) 0;
}
}
#page-title {
@extend %h6;
}
ul,
ol {
padding-left: var(--base);
margin: 0 0 var(--base);
}
a {
color: currentColor;
&:focus {
opacity: 0.8;
outline: none;
}
&:active {
opacity: 0.7;
outline: none;
}
}
svg {
vertical-align: middle;
}

View File

@@ -1,85 +0,0 @@
// Keep these in sync with the colors exported in '../cssVariables.js'
:root {
--color-base-0: rgb(255, 255, 255);
--color-base-50: rgb(245, 245, 245);
--color-base-100: rgb(235, 235, 235);
--color-base-150: rgb(221, 221, 221);
--color-base-200: rgb(208, 208, 208);
--color-base-250: rgb(195, 195, 195);
--color-base-300: rgb(181, 181, 181);
--color-base-350: rgb(168, 168, 168);
--color-base-400: rgb(154, 154, 154);
--color-base-450: rgb(141, 141, 141);
--color-base-500: rgb(128, 128, 128);
--color-base-550: rgb(114, 114, 114);
--color-base-600: rgb(101, 101, 101);
--color-base-650: rgb(87, 87, 87);
--color-base-700: rgb(74, 74, 74);
--color-base-750: rgb(60, 60, 60);
--color-base-800: rgb(47, 47, 47);
--color-base-850: rgb(34, 34, 34);
--color-base-900: rgb(20, 20, 20);
--color-base-950: rgb(7, 7, 7);
--color-base-1000: rgb(0, 0, 0);
--color-success-50: rgb(247, 255, 251);
--color-success-100: rgb(240, 255, 247);
--color-success-150: rgb(232, 255, 243);
--color-success-200: rgb(224, 255, 239);
--color-success-250: rgb(217, 255, 235);
--color-success-300: rgb(209, 255, 230);
--color-success-350: rgb(201, 255, 226);
--color-success-400: rgb(193, 255, 222);
--color-success-450: rgb(186, 255, 218);
--color-success-500: rgb(178, 255, 214);
--color-success-550: rgb(160, 230, 193);
--color-success-600: rgb(142, 204, 171);
--color-success-650: rgb(125, 179, 150);
--color-success-700: rgb(107, 153, 128);
--color-success-750: rgb(89, 128, 107);
--color-success-800: rgb(71, 102, 86);
--color-success-850: rgb(53, 77, 64);
--color-success-900: rgb(36, 51, 43);
--color-success-950: rgb(18, 25, 21);
--color-warning-50: rgb(255, 255, 246);
--color-warning-100: rgb(255, 255, 237);
--color-warning-150: rgb(254, 255, 228);
--color-warning-200: rgb(254, 255, 219);
--color-warning-250: rgb(254, 255, 210);
--color-warning-300: rgb(254, 255, 200);
--color-warning-350: rgb(254, 255, 191);
--color-warning-400: rgb(253, 255, 182);
--color-warning-450: rgb(253, 255, 173);
--color-warning-500: rgb(253, 255, 164);
--color-warning-550: rgb(228, 230, 148);
--color-warning-600: rgb(202, 204, 131);
--color-warning-650: rgb(177, 179, 115);
--color-warning-700: rgb(152, 153, 98);
--color-warning-750: rgb(127, 128, 82);
--color-warning-800: rgb(101, 102, 66);
--color-warning-850: rgb(76, 77, 49);
--color-warning-900: rgb(51, 51, 33);
--color-warning-950: rgb(25, 25, 16);
--color-error-50: rgb(255, 241, 241);
--color-error-100: rgb(255, 226, 228);
--color-error-150: rgb(255, 212, 214);
--color-error-200: rgb(255, 197, 200);
--color-error-250: rgb(255, 183, 187);
--color-error-300: rgb(255, 169, 173);
--color-error-350: rgb(255, 154, 159);
--color-error-400: rgb(255, 140, 145);
--color-error-450: rgb(255, 125, 132);
--color-error-500: rgb(255, 111, 118);
--color-error-550: rgb(230, 100, 106);
--color-error-600: rgb(204, 89, 94);
--color-error-650: rgb(179, 78, 83);
--color-error-700: rgb(153, 67, 71);
--color-error-750: rgb(128, 56, 59);
--color-error-800: rgb(102, 44, 47);
--color-error-850: rgb(77, 33, 35);
--color-error-900: rgb(51, 22, 24);
--color-error-950: rgb(25, 11, 12);
}

Some files were not shown because too many files have changed in this diff Show More