Compare commits

..

213 Commits

Author SHA1 Message Date
James
21708bcc1c chore(release): v1.0.5 2022-07-19 01:34:03 -07:00
James
23d605c5aa Merge branch 'master' of github.com:payloadcms/payload 2022-07-19 01:32:36 -07:00
James
b4ffa22885 feat: adds initAsync 2022-07-19 01:32:25 -07:00
Elliot DeNolf
d2bc4b72a0 chore: update onInit type 2022-07-19 01:05:49 -07:00
Elliot DeNolf
27c352c2ff chore: update changelog 2022-07-19 00:07:13 -07:00
James
19a354863f chore(release): v1.0.4 2022-07-18 23:54:41 -07:00
James
33f4a1322d chore: 1.0.3 2022-07-18 23:53:22 -07:00
Elliot DeNolf
563f98e2fb feat: merge 1.0 #762 2022-07-18 23:22:02 -07:00
Dan Ribbens
1e4b94c508 Merge branch 'feat/1.0' of github.com:payloadcms/payload into feat/1.0 2022-07-19 02:03:24 -04:00
Dan Ribbens
cd8d1c7ace docs: dark mode 2022-07-19 02:02:30 -04:00
Dan Ribbens
2ea545f0cf test: list cell data 2022-07-19 02:02:06 -04:00
Elliot DeNolf
e8a1cdafba test: misc e2e fixes 2022-07-18 22:59:48 -07:00
Elliot DeNolf
1f8b5d8e1a test: make auth e2e a todo 2022-07-18 22:53:30 -07:00
Dan Ribbens
793dfe96b9 test: promise hooks 2022-07-19 01:32:31 -04:00
Elliot DeNolf
6533af9c0e chore: add scss breaking change to changelog 2022-07-18 22:23:26 -07:00
Dan Ribbens
87ead2f89b Merge branch 'feat/1.0' of github.com:payloadcms/payload into feat/1.0 2022-07-18 23:50:57 -04:00
Dan Ribbens
28e3928094 test: array fields 2022-07-18 23:50:28 -04:00
Elliot DeNolf
dc41372acb test: test sibling data in access control 2022-07-18 20:33:36 -07:00
Elliot DeNolf
6a9ea638d5 test: add access-control int tests 2022-07-18 18:50:41 -07:00
Elliot DeNolf
2a7651eb9c test: fix odd behavior 2022-07-18 15:27:20 -07:00
James
9021d5352d Merge branch 'feat/1.0' of github.com:payloadcms/payload into feat/1.0 2022-07-18 13:07:25 -07:00
James
dc5c9ee3a0 chore: better fix to rich text upload 2022-07-18 13:07:20 -07:00
James
079623f40a fix: responsive improvements to rich text upload 2022-07-18 12:50:20 -07:00
Dan Ribbens
b59ba230de Merge branch 'feat/1.0' of github.com:payloadcms/payload into feat/1.0 2022-07-18 14:26:09 -04:00
Dan Ribbens
6826e44072 test: field default values 2022-07-18 14:25:18 -04:00
Elliot DeNolf
878fd4ab92 test: custom id crud 2022-07-18 10:29:33 -07:00
Elliot DeNolf
abfb1868ab test: custom id number relation test 2022-07-18 10:08:01 -07:00
Elliot DeNolf
7319a8e90d test: rest client update now can return errors 2022-07-18 10:07:25 -07:00
Elliot DeNolf
d2be893a5a test: port relationships spec 2022-07-18 09:55:59 -07:00
Dan Ribbens
cc8b636248 test: relationship populates with locale 2022-07-18 12:19:35 -04:00
Elliot DeNolf
a103665bd9 chore: typo 2022-07-17 16:38:24 -07:00
Elliot DeNolf
0d1603b467 test: fix more stuff 2022-07-17 16:22:20 -07:00
Elliot DeNolf
d6a7b8c319 test: remove demo and rework node scripts 2022-07-17 16:18:48 -07:00
Elliot DeNolf
f5ad7a163a test: remove all old tests 2022-07-17 16:08:07 -07:00
James
d433351dbd chore: typo in changelog 2022-07-17 15:12:55 -07:00
James
3601ef90bc Merge branch 'feat/1.0' of github.com:payloadcms/payload into feat/1.0 2022-07-17 15:12:36 -07:00
James
76b6c736e1 fix: success banner color rendering incorrectly 2022-07-17 15:12:27 -07:00
Dan Ribbens
a194a64845 Merge branch 'feat/1.0' of github.com:payloadcms/payload into feat/1.0 2022-07-17 18:02:49 -04:00
Dan Ribbens
b00d8584f3 feat: test admin globals 2022-07-17 18:02:41 -04:00
James
39d075b999 fix: ensures passing partial data to update works when fields are localized 2022-07-17 15:00:38 -07:00
James
e57a5741e5 chore: reduces lorem ipsum amount in rich text 2022-07-17 13:34:06 -07:00
James
a6a9ab15b7 feat: merges select isClearable 2022-07-17 13:29:18 -07:00
James
4280a59c77 Merge branch 'master' of github.com:payloadcms/payload into feat/1.0 2022-07-17 13:23:04 -07:00
James
3259e34e27 chore: merges master 2022-07-17 13:23:00 -07:00
Arick
3132d35e27 feat: allow clear select value (#735) 2022-07-17 13:22:49 -07:00
James
7376246592 fix: ensures success pills are properly colored 2022-07-17 13:22:01 -07:00
James
69ce6d78da Merge branch 'feat/1.0' of github.com:payloadcms/payload into feat/1.0 2022-07-17 13:11:47 -07:00
James
b43e8c5db0 tests: ports versions tests to new infra 2022-07-17 13:11:38 -07:00
Dan Ribbens
e348bdcc21 Merge branch 'feat/1.0' of github.com:payloadcms/payload into feat/1.0 2022-07-17 15:58:47 -04:00
Dan Ribbens
039c3ec110 test: int test globals and endpoints 2022-07-17 15:58:32 -04:00
Elliot DeNolf
f98ef62ad3 test: port auth tests to int 2022-07-17 10:46:07 -07:00
Elliot DeNolf
17ebcc6925 chore: remove .prettierignore, unused 2022-07-17 10:45:46 -07:00
James
6347a2febf fix: ensures column selector renders active columns properly 2022-07-17 10:06:58 -07:00
James
2ef8a1e35a fix: ensures point field doesn't interrupt version creation 2022-07-17 10:03:22 -07:00
James
df11478905 Merge branch 'feat/1.0' of github.com:payloadcms/payload into feat/1.0 2022-07-17 09:51:55 -07:00
James
e7d2bdb45a fix: styles empty version diff row for dark mode 2022-07-17 09:51:42 -07:00
Dan Ribbens
1564fcaeb3 fix test collections rest endpoints 2022-07-17 12:44:39 -04:00
Dan Ribbens
cf38e8d520 feat: test collections rest endpoints 2022-07-17 12:41:47 -04:00
Dan Ribbens
bb9f28fb08 feat: remove files test helper 2022-07-17 12:39:45 -04:00
James
d8a28acfa8 feat: expands rich text field config for better coverage 2022-07-17 09:26:43 -07:00
Elliot DeNolf
b0eccdd12e chore: revert prettier
This reverts commit bc9de859c4.
2022-07-17 09:06:37 -07:00
James
1427fb4078 Merge branch 'feat/1.0' of github.com:payloadcms/payload into feat/1.0 2022-07-17 08:32:23 -07:00
James
1812be2e16 feat: adds uploads collection for testing to /fields tests 2022-07-17 08:32:15 -07:00
Elliot DeNolf
db87179576 chore: fix .prettierignore 2022-07-17 08:32:06 -07:00
Elliot DeNolf
0d8d41ce7e chore: format demo dir 2022-07-17 08:29:53 -07:00
Elliot DeNolf
a2f95f6cea test: add .gitkeep to uploads/media 2022-07-17 08:23:26 -07:00
Elliot DeNolf
fa9bd6191c chore: run prettier on entire test dir 2022-07-17 08:20:01 -07:00
Elliot DeNolf
bc9de859c4 chore: add prettier config, test dir only 2022-07-17 08:19:07 -07:00
James
73d04262ad chore: version bump 2022-07-16 18:12:18 -07:00
James
6e7d547da5 Merge branch 'feat/1.0' of github.com:payloadcms/payload into feat/1.0 2022-07-16 18:08:46 -07:00
James
6060e4cb27 fix: bug with version diff screen when arrays have no labels 2022-07-16 18:08:37 -07:00
Elliot DeNolf
b5ce54c7ae test: localization relationship tests 2022-07-16 18:01:13 -07:00
Dan Ribbens
492a1308cc Merge branch 'feat/1.0' of github.com:payloadcms/payload into feat/1.0 2022-07-16 21:00:35 -04:00
Dan Ribbens
147a4f8624 hooks tests 2022-07-16 21:00:18 -04:00
James
eac982398e feat: avoids forcing fonts to be inline within webpack builds 2022-07-16 17:59:24 -07:00
Dan Ribbens
9afd51b7dd Merge branch 'feat/1.0' into feat/test-hooks 2022-07-16 20:42:13 -04:00
Dan Ribbens
dd810a3593 fix: tabs top margin 2022-07-16 19:09:36 -04:00
James
75a4c52071 chore: reduces padding in eyebrow on mobile 2022-07-16 16:03:22 -07:00
James
2707af1d45 fix: ensures blocks can be nested within tabs / rows / collapsibles 2022-07-16 15:51:05 -07:00
James
b7d49db536 Merge branch 'feat/1.0' of github.com:payloadcms/payload into feat/1.0 2022-07-16 15:45:20 -07:00
James
3a98c6ad53 feat: ensures groups within blocks render properly 2022-07-16 15:45:15 -07:00
Elliot DeNolf
79f3de8042 test: add localization int tests 2022-07-16 15:01:31 -07:00
Elliot DeNolf
81024e4753 test: extra desc block 2022-07-16 15:00:44 -07:00
James
e2c333ac02 Merge branch 'feat/1.0' of github.com:payloadcms/payload into feat/1.0 2022-07-16 13:57:44 -07:00
James
1bdacb9bda chore: removes unused file 2022-07-16 13:57:32 -07:00
James
2fa680cdf8 feat: refactors z index methodology 2022-07-16 13:56:49 -07:00
James
3a17a8a001 feat: styles nested groups and adds gutter back to richText 2022-07-16 12:45:15 -07:00
Dan Ribbens
afbb014cbd Merge branch 'feat/1.0' of github.com:payloadcms/payload into feat/1.0 2022-07-16 14:40:46 -04:00
Dan Ribbens
c75c67ec86 feat: point field int test 2022-07-16 14:40:02 -04:00
James
e8b72f186f chore: restores deprecated features for ease of uploading 2022-07-16 11:27:28 -07:00
James
a777032894 chore: only imports get-port if dev 2022-07-16 11:11:14 -07:00
James
624e25c077 chore: adds pretest 2022-07-16 11:06:06 -07:00
James
34d0b89376 chore: increases jest timeout 2022-07-16 11:01:09 -07:00
James
fcd3b0d2cb chore: updates version 2022-07-16 10:59:25 -07:00
James
5512022a10 chore(release): v0.20.1 2022-07-16 10:58:46 -07:00
James
28e31fee72 chore: patch 0.20.0 2022-07-16 10:56:53 -07:00
James
779a60a42c chore: removes unused dependencies 2022-07-16 10:41:54 -07:00
Dan Ribbens
ba29898d97 Merge branch 'feat/1.0' of github.com:payloadcms/payload into feat/1.0 2022-07-16 13:40:12 -04:00
Dan Ribbens
a636c65e34 chore: do not track images in media 2022-07-16 13:38:33 -04:00
Dan Ribbens
ff3e137c78 chore: media test dir 2022-07-16 13:37:56 -04:00
James
8c4e0fa7b2 feat: updates styling of group field 2022-07-15 22:24:52 -07:00
James
3ae1c26a07 fix: ensures duplicated rows have properly created ids 2022-07-15 22:04:31 -07:00
James
7a6f2392fa feat: restores built-in fields into table column builder 2022-07-15 21:08:49 -07:00
James
07949525ef Merge branch 'feat/1.0' of github.com:payloadcms/payload into feat/1.0 2022-07-15 21:06:22 -07:00
James
3b9fdf3ffd feat: ensures nested fields not affecting data can be traversed properly 2022-07-15 21:06:12 -07:00
Dan Ribbens
2694c0d5bd * feat: point field e2e test (#760) 2022-07-15 23:36:07 -04:00
Elliot DeNolf
4f8b5b9f85 test(int): collections-graphl 2022-07-15 20:32:40 -07:00
Dan Ribbens
e75cca4512 feat: upload integration tests (#759) 2022-07-15 22:48:39 -04:00
James
68e7c41fdc feat: finishes tabs field 2022-07-15 18:40:31 -07:00
James
735e385537 feat: only renders arrays and blocks once they are initialized 2022-07-15 17:26:05 -07:00
James
c995f797fe feat: initializes useField internalValue with incoming field value 2022-07-15 17:20:32 -07:00
James
54162b52cc feat: ensures tabs and collapsibles render in version diff 2022-07-15 17:18:33 -07:00
James
66fc06895a Merge branch 'feat/1.0' of github.com:payloadcms/payload into feat/tabs 2022-07-15 16:24:59 -07:00
James
85b7a490eb feat: scaffolds tabs field 2022-07-15 16:24:54 -07:00
Elliot DeNolf
4b0a257246 test: fix all test scripts 2022-07-15 14:36:09 -07:00
Elliot DeNolf
29f1af7eb4 chore: remove console logs 2022-07-15 12:53:07 -07:00
Elliot DeNolf
ba79b4446c test: refactor all test directories into one 2022-07-15 12:51:43 -07:00
Dan Ribbens
40b6afff2d feat: remove duplication of config in tests 2022-07-15 14:11:16 -04:00
Dan Ribbens
a664b627f6 fix: upload e2e file path 2022-07-15 13:39:12 -04:00
Elliot DeNolf
3a4657e368 test: fix bad file ref and update locators 2022-07-15 10:02:09 -07:00
James
4cb565f1cc Merge branch 'feat/1.0' of github.com:payloadcms/payload into feat/1.0 2022-07-14 16:35:58 -07:00
James
31ca363982 feat: revises e2e field testing 2022-07-14 16:35:35 -07:00
Elliot DeNolf
4a3588e965 test: collections and new test ids and classes 2022-07-14 16:16:16 -07:00
Dan Ribbens
11600930b7 feat: e2e test upload (#755) 2022-07-14 18:43:19 -04:00
James
4bb0d3994f fix: bug with base colors 2022-07-14 15:27:17 -07:00
James
5fc4f3adad feat: more maintainable colors 2022-07-14 15:20:34 -07:00
James
41a0ba5780 feat: finishes block field 2022-07-14 14:46:42 -07:00
Elliot DeNolf
f7a81d70ac test(access-control): restricted and read-only (#754) 2022-07-14 14:33:54 -07:00
Elliot DeNolf
baf4664073 feat: add card ids 2022-07-14 14:09:59 -07:00
Dan Ribbens
48700a93e3 * fix: css selectors 2022-07-14 17:00:34 -04:00
James
49d09a349f feat: updates block field to use new collapsible 2022-07-14 14:00:14 -07:00
Dan Ribbens
31bc4c6532 * feat: optimize collection list relationship queries (#749)
* feat: optimize collection list relationship queries
2022-07-14 16:51:41 -04:00
James
aa89251a3b feat: retrofits array to new collapsible component 2022-07-13 20:09:50 -07:00
Elliot DeNolf
057846e250 test: collections-rest and dev type generation (#750) 2022-07-13 17:46:59 -07:00
Dan Ribbens
5a5e3b589c chore: set playwright timeout to 10m 2022-07-13 20:20:25 -04:00
James
270dd22f08 docs: updates fields overview to include collapsible 2022-07-13 15:41:41 -07:00
James
beec04a1bc docs: collapsible 2022-07-13 15:40:59 -07:00
James
60bfb1c3b8 feat: finishes collapsible field 2022-07-13 15:29:49 -07:00
James
98676bea69 feat: adds collapsible field type 2022-07-13 14:45:10 -07:00
James
8589fdefda feat: reorganizes mongo connection 2022-07-13 13:17:00 -07:00
James
8ba25e8602 chore: renames admin url util conventions 2022-07-13 12:47:53 -07:00
James
de43e21ebc chore: stubs out fields e2e 2022-07-13 12:40:29 -07:00
Elliot DeNolf
90ba15f9bd feat: testing refactor (e2e/int) (#748)
Co-authored-by: James <james@trbl.design>
Co-authored-by: Dan Ribbens <dan.ribbens@gmail.com>
2022-07-13 11:00:10 -07:00
James Mikrut
b9f9f15d77 feat/adds dark mode and evolves admin ui
* feat: builds color palette for light and dark mode, removes all conflicting rgba sass

* chore: replaces colors with css vars

* chore: adapts blur-bg to be more performant and stable

* chore: getting ready for dark mode

* chore: removes unused file

* chore: reverts input bg

* chore: reverses selection in dark mode

* feat: builds theme toggler

* feat: adds auto mode for theme

* feat: establishes light / dark css pattern, updates account and list

* chore: migrates more views to gutter component

* chore: adapts global, adjusts popups

* chore: finishes retrofitting views

* feat: moves to medium instead of semi-bold for headlines

* feat: introduces new font for rte

* feat: updates rich text styles

* feat: styles react select in dark mode

* chore: styles datepicker, misc refinements

* chore: updates style of UploadCard

* chore: fixes code styles

* chore: styles PerPage

* chore: improves styling of column / where selector

* feat: improves field errors in dark mode

* chore: styles built-in rich text elements

* chore: improves styling of rte elements

* chore: tweaks
2022-07-13 10:36:25 -07:00
James
cac5266c79 chore: changelog 2022-07-11 11:53:12 -07:00
James
420afc6838 chore(release): v0.19.2 2022-07-11 11:50:25 -07:00
James
4e3c4e9c0c Merge branch 'master' of github.com:payloadcms/payload into feat/extensible-auth-strategies 2022-07-11 11:45:48 -07:00
James
2c27812ba1 chore: removes cookie-parser 2022-07-11 11:45:44 -07:00
James Mikrut
2f57a983cd Merge pull request #739 from payloadcms/feat/extensible-auth-strategies
Feat/extensible auth strategies
2022-07-11 11:44:32 -07:00
James
91c4ef226b fix: ensures router switch only contains top-level route components 2022-07-11 08:31:43 -07:00
James
38b52bf67b feat: better types useAuth and custom provider components 2022-07-09 22:23:37 -07:00
James
281985970d Merge branch 'feat/extensible-auth-strategies' of github.com:payloadcms/payload into feat/extensible-auth-strategies 2022-07-09 17:39:19 -07:00
James
03f28a4804 feat: ensures auth component doesn't render if disabled 2022-07-09 17:16:19 -07:00
Dan Ribbens
c0acba94c6 Merge branch 'feat/extensible-auth-strategies' of github.com:payloadcms/payload into feat/extensible-auth-strategies 2022-07-09 19:58:19 -04:00
Dan Ribbens
8d550d411e docs: update collection hooks 2022-07-09 19:57:58 -04:00
Elliot DeNolf
e8064371b0 feat: add preMiddleware and postMiddleware, deprecate middleware 2022-07-09 16:47:23 -07:00
Dan Ribbens
166bd31506 feat: add res to token hooks 2022-07-09 19:38:24 -04:00
Dan Ribbens
4055908bc8 feat: add afterMe afterLogout and afterRefresh 2022-07-09 19:29:00 -04:00
James
d68bb8c292 fix: removes reliance on auth email 2022-07-09 14:40:31 -07:00
James
c8be171f24 chore: further disabling of local auth strategy 2022-07-09 12:04:21 -07:00
Dan Ribbens
43eafd4b9f chore(release): v0.19.1 2022-07-09 12:04:17 -04:00
Dan Ribbens
ce1c99b01c fix: ensures duplicative relationship options are not present (#732)
Co-authored-by: James <james@trbl.design>
2022-07-09 11:58:03 -04:00
James
4b2bc36f89 chore: reduces unnecessary strategy complexity 2022-07-09 08:40:13 -07:00
James
58587525e5 feat: adds cookie-parser 2022-07-08 18:36:12 -07:00
James
df76f60e7f fix: ensures anonymous passport strategy is loaded last 2022-07-08 17:56:39 -07:00
James
2c66ad8689 feat: ensures only plain objects are merged within incoming configs 2022-07-08 17:37:43 -07:00
James
6016e2346c feat: extends strategies with more properties 2022-07-08 15:16:30 -07:00
James
56cdd943fd feat: only adds email if local strategy enabled 2022-07-08 14:50:04 -07:00
James
6d02f7d3ac feat: begins extensible auth strategies 2022-07-08 13:46:12 -07:00
Dan Ribbens
18f8790062 chore: breaking changes 0.19.0 2022-07-08 14:08:04 -04:00
Dan Ribbens
854b63ec34 chore(release): v0.19.0 2022-07-08 14:07:28 -04:00
Dan Ribbens
beccbbd3e8 chore: package script quotes windows compatibility 2022-07-08 14:00:23 -04:00
Dan Ribbens
91d0d84c65 docs: update graphql schema 2022-07-08 13:53:40 -04:00
chris-schra
7dd67a8d39 chore: ignore Yarn Berry files in .gitignore (#713) 2022-07-08 13:51:42 -04:00
Dan Ribbens
ad43cbc808 feat: graphql schema output (#730)
* chore: refactor graphql initialization

* feat: generate graphql schema script

* feat: script commands are case insenstive
2022-07-08 13:50:46 -04:00
James Mikrut
2b2a562d83 Merge pull request #731 from payloadcms/fix/auth-me-gql-relationships
Fix/auth me gql relationships
2022-07-08 10:48:09 -07:00
James
d9e7696384 chore: strips trailing slashes from jwt strategy 2022-07-08 10:41:10 -07:00
James
01bc1fef1e fix: ensures auth/me relations in gql can be queried 2022-07-08 10:25:00 -07:00
Dan Ribbens
567d8c19bf fix: allow passing of autoIndex mongoose connectionOptions (#722) 2022-07-06 15:57:02 -04:00
James Mikrut
b722bed24f Merge pull request #720 from payloadcms/fix/#692
fix: ensures old data from arrays is not persisted
2022-07-06 14:36:00 -04:00
James
0af66d68a7 chore: tests array functionality 2022-07-06 11:34:22 -07:00
Henri Tuan
f3b7dcff57 feat: File argument in create/update operation (#708)
Co-authored-by: Dan Ribbens <dan.ribbens@gmail.com>
2022-07-06 11:26:04 -07:00
chris-schra
67331eb975 fix: copyfiles cross platform (#712) 2022-07-06 13:57:18 -04:00
James
d9ef803d20 fix: ensures old data from arrays is not persisted 2022-07-06 10:52:52 -07:00
Andrzej Kłapeć
9fd171b26d feat: allow clearing DatePicker value (#641)
Co-authored-by: Elliot DeNolf <denolfe@gmail.com>
2022-07-06 09:38:45 -07:00
Dan Ribbens
91e33d1c1c fix: relationship field disabled from access control in related collections (#644)
* fix: disabled relationship field from access control in related collections

* fix: ids can be read from relationships regardless of access of related document
2022-07-03 07:01:45 -04:00
Akansh Sirohi
601e69ab0d Fixes payloadcms/payload#650 (#706)
* fix: min/max number validation of decimal values
2022-07-03 06:25:03 -04:00
Andres Kalle
81aa74bbbe docs: fixed example URL (#705) 2022-07-02 07:42:12 -04:00
Dan Ribbens
e84b7a9c58 chore(release): v0.18.5 2022-06-29 14:14:47 -04:00
Dan Ribbens
b6b0ffb674 fix: empty cell data renders in list (#699) 2022-06-29 14:07:21 -04:00
Dan Ribbens
62bd2db5f9 chore: update json-schema-to-typescript to v11 (#700) 2022-06-29 14:03:30 -04:00
Steven Roh
74342a4dea Avoid app crashing when code (data) is undefined (#681)
* fix: code field ui errors when data is undefined
2022-06-29 12:12:33 -04:00
Min Somai
c78d77446a fix: icon appears above select field's option list (#685)
* fix: icon of the date field appears above the option list
2022-06-29 12:09:53 -04:00
Dan Ribbens
6eb4fc04b1 Fix/validate imagesizes with mimetypes (#688)
* fix: mimetype validation with image sizes

* fix: unique filename error on image sizes
2022-06-29 12:05:57 -04:00
Dan Ribbens
f1b00e85fc docs: update links 2022-06-29 11:19:34 -04:00
gonza moiguer
7c73f2c1d9 Corrected Radio field doc, 'option' for 'label' (#690) 2022-06-27 13:03:41 -04:00
James
704e543c7e chore(release): v0.18.4 2022-06-24 17:53:35 -04:00
Lachlan Heywood
fcaa1454dc Update load.ts (#679) 2022-06-24 17:52:06 -04:00
James
9d388f7a89 chore(release): v0.18.3 2022-06-24 17:51:01 -04:00
James
918062de2f fix: #670, max tokenExpiration 2022-06-24 17:49:36 -04:00
James
375738d99c chore(release): v0.18.2 2022-06-24 17:34:07 -04:00
Dan Ribbens
1c37ec3902 feat: telemetry
* feat: add telemetry to payload config

wip: more telemetry

* feat: send telemetry events

* chore: update node ci versions

* chore: cleanup console log

* chore: updates ts due to dependency update

* chore: remove unused deps

* chore: fix origin and casing

* docs: telemetry

* feat: uses oneWayHash within telemetry

* chore: sends hashed domain in telemetry

* feat: improves reliability of telemetry projectID

* chore: revises telemetry docs

Co-authored-by: Elliot DeNolf <denolfe@gmail.com>
Co-authored-by: James <james@trbl.design>
2022-06-24 17:32:09 -04:00
Kent Warren
7eb804daf9 docs: typo (#683)
Changes "you" to "your"
2022-06-24 16:52:57 -04:00
James
d99a67ca95 chore(release): v0.18.1 2022-06-21 15:45:20 -04:00
James
3d5ed93fce fix: #671, password reset broken 2022-06-21 15:43:12 -04:00
Ahmet Kilinc
b80006be45 Change "ist" to "it" (#668)
docs: Small typo on middleware page
2022-06-20 10:45:34 -04:00
Dan Ribbens
70edb0ba38 chore(release): v0.18.0 2022-06-14 08:53:17 -04:00
Dan Ribbens
a1fe17d05d fix: me auth route breaks with query params (#648) 2022-06-13 11:49:04 -04:00
James Mikrut
7083225abd Feat/remove this bindings (#629)
* feat: removes this bindings for cleaner, more maintainable code

Co-authored-by: Elliot DeNolf <denolfe@users.noreply.github.com>
Co-authored-by: Dan Ribbens <dan.ribbens@gmail.com>
2022-06-08 14:44:34 -04:00
Jarrod Flesch
af6479bf34 feat: adds timestamps to generated collection types if enabled (#604)
* feat: adds timestamps to generated collection types if enabled
2022-06-08 11:41:09 -04:00
Dan Ribbens
44c12325b4 feat: enable webpack filesystem cache in dev (#621) 2022-06-08 11:38:07 -04:00
Dan Ribbens
f2bf2399fa fix: custom fields values resetting in ui (#626) 2022-06-08 11:37:31 -04:00
Dan Ribbens
7fe2fece6c chore: remove node-sass (#605) 2022-06-08 11:36:33 -04:00
699 changed files with 17992 additions and 16747 deletions

View File

@@ -21,6 +21,27 @@ module.exports = {
},
},
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',
}
},
{
files: ['*.ts', '*.tsx'],
parser: '@typescript-eslint/parser',
@@ -66,7 +87,7 @@ module.exports = {
'no-underscore-dangle': 'off',
'no-use-before-define': 'off',
'arrow-body-style': 0,
'@typescript-eslint/no-use-before-define': ['error'],
'@typescript-eslint/no-use-before-define': 'off',
'import/extensions': [
'error',
'ignorePackages',

View File

@@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x, 14.x, 16.x]
node-version: [14.x, 16.x, 18.x]
steps:
- uses: actions/checkout@v2
@@ -31,11 +31,25 @@ jobs:
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- run: yarn build
- run: yarn test:client
- run: yarn test:int # In-memory db + api tests
- run: yarn demo:generate:types
env:
CI: true
- 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: Install Playwright Browsers
# run: npx playwright install --with-deps
# - name: E2E Tests
# run: yarn test:e2e
# - uses: actions/upload-artifact@v2
# if: always()
# with:
# name: playwright-report
# path: playwright-report/
# retention-days: 30
install_npm:
runs-on: ubuntu-latest
strategy:

12
.gitignore vendored
View File

@@ -2,8 +2,6 @@ coverage
package-lock.json
dist
.idea
cypress/videos
cypress/screenshots
# Created by https://www.gitignore.io/api/node,macos,windows,webstorm,sublimetext,visualstudiocode
@@ -88,6 +86,15 @@ typings/
# Yarn Integrity file
.yarn-integrity
# Yarn Berry
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
.pnp.*
# dotenv environment variables file
.env
@@ -233,3 +240,4 @@ components/styles.css
# Ignore generated
demo/generated-types.ts
demo/generated-schema.graphql

44
.vscode/launch.json vendored
View File

@@ -29,55 +29,17 @@
"request": "launch",
"name": "Launch Program",
"env": {
"PAYLOAD_CONFIG_PATH": "demo/payload.config.ts",
"BABEL_ENV": "development"
},
"program": "${workspaceFolder}/demo/index.js",
"program": "${workspaceFolder}/test/dev.js",
"skipFiles": [
"<node_internals>/**"
],
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/babel-node",
"runtimeArgs": [
"--nolazy"
],
},
{
"type": "node",
"request": "launch",
"name": "Launch Program - Production",
"env": {
"PAYLOAD_CONFIG_PATH": "demo/payload.config.ts",
"NODE_ENV": "production",
"BABEL_ENV": "development"
},
"program": "${workspaceFolder}/demo/index.js",
"skipFiles": [
"<node_internals>/**"
],
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/babel-node",
"outputCapture": "std",
"runtimeArgs": [
"--nolazy"
],
},
{
"type": "chrome",
"request": "launch",
"name": "Launch Chrome against Localhost",
"url": "http://localhost:3000/admin",
"webRoot": "${workspaceFolder}"
},
{
"type": "node",
"request": "launch",
"name": "Debug Payload Generate Types",
"program": "${workspaceFolder}/src/bin/generateTypes.ts",
"env": {
"PAYLOAD_CONFIG_PATH": "demo/payload.config.ts",
},
"outFiles": [
"${workspaceFolder}/dist/**/*.js",
"!**/node_modules/**"
"args": [
"fields"
]
},
]

File diff suppressed because it is too large Load Diff

View File

@@ -2,3 +2,4 @@ export { default as Button } from '../dist/admin/components/elements/Button';
export { default as Card } from '../dist/admin/components/elements/Card';
export { default as Eyebrow } from '../dist/admin/components/elements/Eyebrow';
export { default as Nav } from '../dist/admin/components/elements/Nav';
export { default as Gutter } from '../dist/admin/components/elements/Gutter';

View File

@@ -1,8 +0,0 @@
{
"$schema": "https://on.cypress.io/cypress.schema.json",
"testFiles": "**/*e2e.ts",
"ignoreTestFiles": "**/examples/*spec.js",
"viewportWidth": 1440,
"viewportHeight": 900,
"baseUrl": "http://localhost:3000"
}

16
cypress/cypress.d.ts vendored
View File

@@ -1,16 +0,0 @@
export { };
declare global {
namespace Cypress {
interface Chainable<Subject> {
visitAdmin(): Chainable<any>
/**
* Login
*
* Creates user if not exists
*/
login(): Chainable<any>
apiLogin(): Chainable<any>
}
}
}

View File

@@ -1,5 +0,0 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}

View File

@@ -1,43 +0,0 @@
import { adminURL } from './common/constants';
import { credentials } from './common/credentials';
describe('Collections', () => {
const collectionName = 'Admins';
before(() => {
cy.apiLogin();
});
beforeEach(() => {
cy.visitAdmin();
});
it('can view collection', () => {
cy.contains(collectionName).click();
cy.get('.collection-list__wrap')
.should('be.visible');
cy.get('.collection-list__header')
.contains(collectionName)
.should('be.visible');
cy.get('.table')
.contains(credentials.email)
.should('be.visible');
});
it('can create new', () => {
cy.contains(collectionName).click();
cy.contains('Create New').click();
cy.url().should('contain', `${adminURL}/collections/${collectionName.toLowerCase()}/create`);
});
it('can create new - plus button', () => {
cy.contains(collectionName)
.get('.card__actions')
.first()
.click();
cy.url().should('contain', `${adminURL}/collections/${collectionName.toLowerCase()}/create`);
});
});

View File

@@ -1 +0,0 @@
export const adminURL = 'http://localhost:3000/admin';

View File

@@ -1,5 +0,0 @@
export const credentials = {
email: 'test@test.com',
password: 'test123',
roles: ['admin'],
};

View File

@@ -1,76 +0,0 @@
describe('Fields', () => {
before(() => {
cy.apiLogin();
});
describe('Array', () => {
beforeEach(() => {
cy.visit('/admin/collections/all-fields/create');
});
it('can add and remove rows', () => {
cy.contains('Add Array').click();
cy.contains('Array Text 1')
.should('be.visible');
cy.get('.action-panel__add-row').first().click();
cy.get('.field-type.array')
.filter(':contains("Editable Array")')
.should('contain', '02');
cy.get('.action-panel__remove-row').first().click();
cy.get('.field-type.array')
.filter(':contains("Editable Array")')
.should('not.contain', '02')
.should('contain', '01');
});
it('can be readOnly', () => {
cy.get('.field-type.array')
.filter(':contains("readOnly Array")')
.should('not.contain', 'Add Row');
cy.get('.field-type.array')
.filter(':contains("readOnly Array")')
.children('.action-panel__add-row')
.should('not.exist');
cy.get('.field-type.array')
.filter(':contains("readOnly Array")')
.children('.position-panel__move-backward')
.should('not.exist');
cy.get('.field-type.array')
.filter(':contains("readOnly Array")')
.children('.position-panel__move-forward')
.should('not.exist');
});
});
describe('Admin', () => {
describe('Conditions', () => {
beforeEach(() => {
cy.visit('/admin/collections/conditions/create');
});
it('can see conditional fields', () => {
cy.get('#simpleCondition')
.should('not.exist');
cy.get('#customComponent')
.should('not.exist');
cy.contains('Enable Test').click();
cy.get('#simpleCondition')
.should('be.visible');
cy.get('#customComponent')
.should('be.visible');
});
});
});
});

View File

@@ -1,99 +0,0 @@
/* eslint-disable jest/expect-expect */
import { adminURL } from './common/constants';
import { credentials } from './common/credentials';
// running login more than one time is not working
const viewportSizes: Cypress.ViewportPreset[] = [
'macbook-15',
// 'iphone-x',
// 'ipad-2',
];
// intermittent failure
describe.skip('Payload Login', () => {
beforeEach(() => {
cy.clearCookies();
});
after(() => {
cy.apiLogin();
});
viewportSizes.forEach((viewportSize) => {
describe(`Login (${viewportSize})`, () => {
beforeEach(() => {
cy.visit(adminURL);
});
it('success', () => {
cy.viewport(viewportSize);
cy.url().should('include', '/admin/login');
cy.get('.field-type.email input').type(credentials.email);
cy.get('.field-type.password input').type(credentials.password);
cy.get('form')
.contains('form', 'Login')
.should('be.visible')
.submit();
cy.get('.template-default')
.find('h3.dashboard__label')
.should('have.length', 2); // TODO: Should assert label content
cy.url().should('eq', adminURL);
});
// skip due to issue with cookies not being reset between tests
it.skip('bad Password', () => {
cy.viewport(viewportSize);
cy.visit(adminURL);
cy.get('#email').type(credentials.email);
cy.get('#password').type('badpassword');
cy.get('form')
.contains('form', 'Login')
.should('be.visible')
.submit();
cy.get('.Toastify')
.contains('The email or password provided is incorrect.')
.should('be.visible');
});
// skip due to issue with cookies not being reset between tests
it.skip('bad Password - Retry Success', () => {
cy.viewport(viewportSize);
cy.visit(adminURL);
cy.get('#email').type(credentials.email);
cy.get('#password').type('badpassword');
cy.get('form')
.contains('form', 'Login')
.should('be.visible')
.submit();
cy.get('.Toastify')
.contains('The email or password provided is incorrect.')
.should('be.visible');
// Dismiss notification
cy.wait(500);
cy.get('.Toastify__toast-body').click();
cy.wait(200);
cy.get('.Toastify__toast-body').should('not.be.visible');
cy.url().should('eq', `${adminURL}/login`);
cy.get('#password').clear().type(credentials.password);
cy.get('form')
.contains('form', 'Login')
.should('be.visible')
.submit();
cy.get('.template-default')
.find('h3.dashboard__label')
.should('have.length', 2); // TODO: Should assert label content
cy.url().should('eq', adminURL);
});
});
});
});

View File

@@ -1,17 +0,0 @@
require('isomorphic-fetch');
const { credentials } = require('../integration/common/credentials');
/**
* @type {Cypress.PluginConfig}
*/
module.exports = (on) => {
on('before:run', () => {
return fetch('http://localhost:3000/api/admins/first-register', {
body: JSON.stringify(credentials),
headers: {
'Content-Type': 'application/json',
},
method: 'post',
});
});
};

View File

@@ -1,54 +0,0 @@
import { adminURL } from '../integration/common/constants';
import { credentials } from '../integration/common/credentials';
Cypress.Commands.add('visitAdmin', () => {
cy.visit(adminURL);
});
Cypress.Commands.add('login', () => {
cy.clearCookies();
cy.visit(adminURL);
cy.get('#email').type(credentials.email);
cy.get('#password').type(credentials.password);
cy.get('body')
.then((body) => {
if (body.find('.dashboard__card-list').length) {
cy.get('.dashboard__card-list')
.should('be.visible');
}
if (body.find('#confirm-password').length) {
cy.get('#confirm-password').type(credentials.password);
cy.get('.rs__indicators').first()
.click();
cy.get('.rs__menu').first().contains('admin')
.click();
cy.get('form')
.contains('form', 'Create')
.should('be.visible')
.submit();
}
if (body.find('form').length) {
cy.get('form')
.contains('form', 'Login')
.should('be.visible')
.submit();
}
cy.get('.dashboard__card-list')
.should('be.visible');
});
});
Cypress.Commands.add('apiLogin', () => {
cy.api({
url: '/api/admins/login',
method: 'POST',
body: credentials,
failOnStatusCode: true,
}).should(({ status }) => {
cy.wrap(status).should('equal', 200);
});
});

View File

@@ -1,25 +0,0 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
import '@bahmutov/cy-api';
// Import commands.js using ES2015 syntax:
import './commands';
// Alternatively you can use CommonJS syntax:
// require('./commands')
Cypress.Cookies.defaults({
preserve: 'payload-token',
});

View File

@@ -1,15 +0,0 @@
{
"compilerOptions": {
"target": "es5",
"lib": [
"es5",
"dom"
],
"types": [
"cypress"
]
},
"include": [
"**/*.ts"
]
}

View File

@@ -1,17 +0,0 @@
/**
* authorize a request by comparing the current user with one or more roles
* @param allRoles
* @param user
* @returns {Function}
*/
const checkRole = (allRoles, user) => {
if (user) {
if (allRoles.some((role) => user.roles && user.roles.some((individualRole) => individualRole === role))) {
return true;
}
}
return false;
};
export default checkRole;

View File

@@ -1,7 +0,0 @@
export default [
'admin',
'editor',
'moderator',
'user',
'viewer',
];

View File

@@ -1,26 +0,0 @@
import { Block } from '../../src/fields/config/types';
const CTA: Block = {
slug: 'cta',
labels: {
singular: 'Call to Action',
plural: 'Calls to Action',
},
fields: [
{
name: 'label',
label: 'Label',
type: 'text',
maxLength: 100,
required: true,
},
{
name: 'url',
label: 'URL',
type: 'text',
required: true,
},
],
};
export default CTA;

View File

@@ -1,19 +0,0 @@
import { Block } from '../../src/fields/config/types';
const Email: Block = {
slug: 'email',
labels: {
singular: 'Email',
plural: 'Emails',
},
fields: [
{
name: 'testEmail',
label: 'Test Email Field',
type: 'email',
required: true,
},
],
};
export default Email;

View File

@@ -1,20 +0,0 @@
import { Block } from '../../src/fields/config/types';
const NumberBlock: Block = {
slug: 'number',
labels: {
singular: 'Number',
plural: 'Numbers',
},
fields: [
{
name: 'testNumber',
label: 'Test Number Field',
type: 'number',
max: 100,
required: true,
},
],
};
export default NumberBlock;

View File

@@ -1,27 +0,0 @@
import { Block } from '../../src/fields/config/types';
const Quote: Block = {
imageURL: '/static/assets/images/generic-block-image.svg',
slug: 'quote',
labels: {
singular: 'Quote',
plural: 'Quotes',
},
fields: [
{
name: 'quote',
label: 'Quote',
type: 'textarea',
required: true,
},
{
name: 'color',
label: 'Color',
type: 'text',
maxLength: 7,
required: true,
},
],
};
export default Quote;

View File

@@ -1,16 +0,0 @@
// As this is the demo folder, we import Payload SCSS functions relatively.
@import '../../../../scss';
// In your own projects, you'd import as follows:
// @import '~payload/scss';
.after-dashboard {
margin-top: base(2);
&__cards {
list-style: none;
margin: 0;
padding: 0;
}
}

View File

@@ -1,41 +0,0 @@
import React from 'react';
import { useHistory } from 'react-router-dom';
import { useConfig } from '../../../../src/admin/components/utilities/Config';
// As this is the demo project, we import our dependencies from the `src` directory.
import Card from '../../../../src/admin/components/elements/Card';
// In your projects, you can import as follows:
// import { Card } from 'payload/components/elements';
import './index.scss';
const baseClass = 'after-dashboard';
const AfterDashboard: React.FC = () => {
const history = useHistory();
const { routes: { admin: adminRoute } } = useConfig();
return (
<div className={baseClass}>
<h3>Custom Routes &amp; Dashboard Components</h3>
<p>This is a custom component that is rendered within the built-in dashboard component after its contents are rendered. Below, there are two cards that link to custom routes.</p>
<ul className="dashboard__card-list">
<li>
<Card
title="Default Template"
onClick={() => history.push(`${adminRoute}/custom-default-route`)}
/>
</li>
<li>
<Card
title="Minimal Template"
onClick={() => history.push(`${adminRoute}/custom-minimal-route`)}
/>
</li>
</ul>
</div>
);
};
export default AfterDashboard;

View File

@@ -1,40 +0,0 @@
import React from 'react';
import { NavLink } from 'react-router-dom';
import { useConfig } from '../../../../src/admin/components/utilities/Config';
// As this is the demo project, we import our dependencies from the `src` directory.
import Chevron from '../../../../src/admin/components/icons/Chevron';
// In your projects, you can import as follows:
// import { Chevron } from 'payload/components/icons';
const baseClass = 'after-nav-links';
const AfterNavLinks: React.FC = () => {
const { routes: { admin: adminRoute } } = useConfig();
return (
<div className={baseClass}>
<span className="nav__label">Custom Routes</span>
<nav>
<NavLink
activeClassName="active"
to={`${adminRoute}/custom-default-route`}
>
<Chevron />
Default Template
</NavLink>
<NavLink
activeClassName="active"
to={`${adminRoute}/custom-minimal-route`}
>
<Chevron />
Minimal Template
</NavLink>
</nav>
</div>
);
};
export default AfterNavLinks;

View File

@@ -1,24 +0,0 @@
import React from 'react';
const BeforeLogin: React.FC = () => {
return (
<div>
<h3>Welcome</h3>
<p>
This demo is a set up to configure Payload for the develop and testing of features. To see a product demo of a Payload project
please visit:
{' '}
<a
href="https://demo.payloadcms.com"
target="_blank"
rel="noreferrer"
>
demo.payloadcms.com
</a>
.
</p>
</div>
);
};
export default BeforeLogin;

View File

@@ -1,29 +0,0 @@
import React, { createContext, useState, useContext } from 'react';
type CustomContext = {
getCustom
setCustom
}
const Context = createContext({} as CustomContext);
const CustomProvider: React.FC = ({ children }) => {
const [getCustom, setCustom] = useState({});
const value = {
getCustom,
setCustom,
};
console.log('custom provider called');
return (
<Context.Provider value={value}>
{children}
</Context.Provider>
);
};
export default CustomProvider;
export const useCustom = () => useContext(Context);

View File

@@ -1,7 +0,0 @@
import React from 'react';
const DemoUIFieldCell: React.FC = () => (
<p>Demo UI Field Cell</p>
);
export default DemoUIFieldCell;

View File

@@ -1,7 +0,0 @@
import React from 'react';
const DemoUIField: React.FC = () => (
<p>Demo UI Field</p>
);
export default DemoUIField;

View File

@@ -1,33 +0,0 @@
@import '../../../../../../../src/admin/scss/styles.scss';
.button-rich-text-button {
.btn {
margin-right: base(1);
}
&__modal {
display: flex;
align-items: center;
height: 100%;
&.payload__modal-item--enterDone {
@include blur-bg;
}
}
&__header {
width: 100%;
margin-bottom: $baseline;
display: flex;
justify-content: space-between;
h3 {
margin: 0;
}
svg {
width: base(1.5);
height: base(1.5);
}
}
}

View File

@@ -1,129 +0,0 @@
import React, { Fragment, useCallback } from 'react';
import { Modal, useModal } from '@faceless-ui/modal';
import { Transforms } from 'slate';
import { useSlate, ReactEditor } from 'slate-react';
import MinimalTemplate from '../../../../../../../src/admin/components/templates/Minimal';
import ElementButton from '../../../../../../../src/admin/components/forms/field-types/RichText/elements/Button';
import X from '../../../../../../../src/admin/components/icons/X';
import Button from '../../../../../../../src/admin/components/elements/Button';
import Form from '../../../../../../../src/admin/components/forms/Form';
import Submit from '../../../../../../../src/admin/components/forms/Submit';
import reduceFieldsToValues from '../../../../../../../src/admin/components/forms/Form/reduceFieldsToValues';
import Text from '../../../../../../../src/admin/components/forms/field-types/Text';
import Checkbox from '../../../../../../../src/admin/components/forms/field-types/Checkbox';
import Select from '../../../../../../../src/admin/components/forms/field-types/Select';
import './index.scss';
const baseClass = 'button-rich-text-button';
const initialFormData = {
style: 'primary',
};
const insertButton = (editor, { href, label, style, newTab = false }: any) => {
const text = { text: ' ' };
const button = {
type: 'button',
href,
style,
newTab,
label,
children: [
text,
],
};
const nodes = [button, { children: [{ text: '' }] }];
if (editor.blurSelection) {
Transforms.select(editor, editor.blurSelection);
}
Transforms.insertNodes(editor, nodes);
const currentPath = editor.selection.anchor.path[0];
const newSelection = { anchor: { path: [currentPath + 1, 0], offset: 0 }, focus: { path: [currentPath + 1, 0], offset: 0 } };
Transforms.select(editor, newSelection);
ReactEditor.focus(editor);
};
const ToolbarButton: React.FC<{path: string}> = ({ path }) => {
const { open, closeAll } = useModal();
const editor = useSlate();
const handleAddButton = useCallback((fields) => {
const data = reduceFieldsToValues(fields);
insertButton(editor, data);
closeAll();
}, [editor, closeAll]);
const modalSlug = `${path}-add-button`;
return (
<Fragment>
<ElementButton
className={baseClass}
format="button"
onClick={() => open(modalSlug)}
>
Button
</ElementButton>
<Modal
slug={modalSlug}
className={`${baseClass}__modal`}
>
<MinimalTemplate>
<header className={`${baseClass}__header`}>
<h3>Add button</h3>
<Button
buttonStyle="none"
onClick={closeAll}
>
<X />
</Button>
</header>
<Form
onSubmit={handleAddButton}
initialData={initialFormData}
>
<Text
label="Label"
name="label"
required
/>
<Text
label="URL"
name="href"
required
/>
<Select
label="Style"
name="style"
options={[
{
label: 'Primary',
value: 'primary',
},
{
label: 'Secondary',
value: 'secondary',
},
]}
/>
<Checkbox
label="Open in new tab"
name="newTab"
/>
<Submit>
Add button
</Submit>
</Form>
</MinimalTemplate>
</Modal>
</Fragment>
);
};
export default ToolbarButton;

View File

@@ -1,19 +0,0 @@
@import '../../../../../../../src/admin/scss/styles.scss';
.rich-text-button {
margin: $baseline 0;
}
.rich-text-button__button {
padding: base(.5) base(1.5);
border-radius: $style-radius-s;
&--primary {
background-color: $color-dark-gray;
color: white;
}
&--secondary {
background-color: $color-light-gray;
}
}

View File

@@ -1,29 +0,0 @@
import React from 'react';
import './index.scss';
const baseClass = 'rich-text-button';
const ButtonElement: React.FC = ({ attributes, children, element }) => {
const { style = 'primary', label } = element;
return (
<div
className={baseClass}
contentEditable={false}
>
<span
{...attributes}
className={[
`${baseClass}__button`,
`${baseClass}__button--${style}`,
].join(' ')}
>
{label}
{children}
</span>
</div>
);
};
export default ButtonElement;

View File

@@ -1,15 +0,0 @@
import { RichTextCustomElement } from '../../../../../../src/fields/config/types';
import Button from './Button';
import Element from './Element';
import plugin from './plugin';
const button: RichTextCustomElement = {
name: 'button',
Button,
Element,
plugins: [
plugin,
],
};
export default button;

View File

@@ -1,12 +0,0 @@
import { Editor } from 'slate';
const withButton = (incomingEditor: Editor): Editor => {
const editor = incomingEditor;
const { isVoid } = editor;
editor.isVoid = (element) => (element.type === 'button' ? true : isVoid(element));
return editor;
};
export default withButton;

View File

@@ -1,10 +0,0 @@
import React from 'react';
import LeafButton from '../../../../../../../src/admin/components/forms/field-types/RichText/leaves/Button';
const Button = () => (
<LeafButton format="purple-background">
Purple Background
</LeafButton>
);
export default Button;

View File

@@ -1,12 +0,0 @@
import React from 'react';
const PurpleBackground: React.FC<any> = ({ attributes, children }) => (
<span
{...attributes}
style={{ backgroundColor: 'purple' }}
>
{children}
</span>
);
export default PurpleBackground;

View File

@@ -1,8 +0,0 @@
import Button from './Button';
import Leaf from './Leaf';
export default {
name: 'purple-background',
Button,
Leaf,
};

View File

@@ -1,64 +0,0 @@
import React, { useEffect } from 'react';
import { Redirect } from 'react-router-dom';
import { useConfig } from '../../../../../src/admin/components/utilities/Config';
// As this is the demo project, we import our dependencies from the `src` directory.
import DefaultTemplate from '../../../../../src/admin/components/templates/Default';
import Button from '../../../../../src/admin/components/elements/Button';
import Eyebrow from '../../../../../src/admin/components/elements/Eyebrow';
import { AdminView } from '../../../../../src/config/types';
import { useStepNav } from '../../../../../src/admin/components/elements/StepNav';
import Meta from '../../../../../src/admin/components/utilities/Meta';
// In your projects, you can import as follows:
// import { DefaultTemplate } from 'payload/components/templates';
// import { Button, Eyebrow } from 'payload/components/elements';
// import { AdminView } from 'payload/config';
// import { useStepNav } from 'payload/components/hooks';
// import { Meta } from 'payload/components/utilities';
const CustomDefaultRoute: AdminView = ({ user, canAccessAdmin }) => {
const { routes: { admin: adminRoute } } = useConfig();
const { setStepNav } = useStepNav();
// This effect will only run one time and will allow us
// to set the step nav to display our custom route name
useEffect(() => {
setStepNav([
{
label: 'Custom Route with Default Template',
},
]);
}, [setStepNav]);
// If an unauthorized user tries to navigate straight to this page,
// Boot 'em out
if (!user || (user && !canAccessAdmin)) {
return (
<Redirect to={`${adminRoute}/unauthorized`} />
);
}
return (
<DefaultTemplate>
<Meta
title="Custom Route with Default Template"
description="Building custom routes into Payload is easy."
keywords="Custom React Components, Payload, CMS"
/>
<Eyebrow />
<h1>Custom Route</h1>
<p>Here is a custom route that was added in the Payload config. It uses the Default Template, so the sidebar is rendered.</p>
<Button
el="link"
to={`${adminRoute}`}
buttonStyle="secondary"
>
Go to Dashboard
</Button>
</DefaultTemplate>
);
};
export default CustomDefaultRoute;

View File

@@ -1,12 +0,0 @@
// As this is the demo folder, we import Payload SCSS functions relatively.
@import '../../../../../scss';
// In your own projects, you'd import as follows:
// @import '~payload/scss';
.custom-minimal-route {
&__login-btn {
margin-right: base(.5);
}
}

View File

@@ -1,42 +0,0 @@
import React from 'react';
// As this is the demo project, we import our dependencies from the `src` directory.
import MinimalTemplate from '../../../../../src/admin/components/templates/Minimal';
import Button from '../../../../../src/admin/components/elements/Button';
import { useConfig } from '../../../../../src/admin/components/utilities/Config';
// In your projects, you can import as follows:
// import { MinimalTemplate } from 'payload/components/templates';
// import { Button } from 'payload/components/elements';
// import { useConfig } from 'payload/components/utilities';
import './index.scss';
const baseClass = 'custom-minimal-route';
const CustomMinimalRoute: React.FC = () => {
const { routes: { admin: adminRoute } } = useConfig();
return (
<MinimalTemplate className={baseClass}>
<h1>Custom Route</h1>
<p>Here is a custom route that was added in the Payload config.</p>
<Button
className={`${baseClass}__login-btn`}
el="link"
to={`${adminRoute}/login`}
>
Go to Login
</Button>
<Button
el="link"
to={`${adminRoute}`}
buttonStyle="secondary"
>
Go to Dashboard
</Button>
</MinimalTemplate>
);
};
export default CustomMinimalRoute;

View File

@@ -1,14 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<div id="app"></div>
<div id="portal"></div>
</body>
</html>

View File

@@ -1,9 +0,0 @@
<svg width="82" height="53" viewBox="0 0 82 53" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect x="0.713013" width="80.574" height="52.7791" fill="url(#pattern0)"/>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0" transform="scale(0.00387597 0.00591716)"/>
</pattern>
<image id="image0" width="258" height="169" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQIAAACpCAYAAADA4zPJAAALpklEQVR4Ae2dSY/UOhSF+///DCSGHQuEgAVI7BBITAI2zCCGZuhmpqEBP53W8yOvK1UdO3Zyr/NFilJdXalyTo4/X984zlZgQQEUWLwCW4tXAAFQAAUCIMAEKIACgAAPoAAKBECACVAABQABHkABFAiAABOgAAoAAjyAAiggBbhqgA9QAAUAAR5AARQgIsADKIACdA3wAAqggBQgR4APUAAFAAEeQAEUICLAAyiAAnQN8AAKoIAUIEeAD1AABQABHkABFCAiwAMogAJ0DfAACqCAFCBHgA9QAAUAAR5AARQgIsADKIACdA3wAAqggBQgR4APUAAFAAEeQAEUICLAAyiAAnQN8AAKoIAUIEeAD1AABQABHkABFCAiwAMogAJ0DfAACqCAFCBHgA9QAAUAAR5AARQgIsADKIACdA3wAAqggBQgR4APUAAFAAEeQAEUICLAAyiAAnQN8AAKoIAUIEeAD1AABQABHkABFCAiwAMLVmB/fz98+/YtfPr0KXz48CHs7u6GnZ2dg+3Hjx/D169fw48fP8KfP3+aV4muQfOnmAPsKqDKr4r/9u3bsL29PXgVKPb29pqFAiDouoTXzSrw8+fPg5Y+pfL3ffbdu3fh+/fvzekECJo7pRxQVwGF9YoA+ir1mPfUhVB00coCCFo5kxzHigKqqGrBx1T4Tfu+efOmmegAEKzYhzdaUEBdAVXUTRW51P+UVPS+AALvZ5DyryigSGAqCESYeIcBIFixEW94VuDXr1/JVwRiZR671VUFrwsg8HrmKPeKAkoMKok3tkLn7q8oRCDyuAACj2eNMvcqoMFBuZW41H4alORxAQQezxplXlHg9+/fk+cF1sHDYxcBEKxYijc8KvDly5fZo4EIhvfv37uTEBC4O2UU+LACyg2kDhmOlbbWVpcvPS2AwNPZoqy9CigUr1Whc79Xoxk9LYDA09mirL0K1BhCnAuAuJ8iFE8LIPB0tihrrwI1hxHHip2z9XQpERD0Wos3vSig/EBOJZ1iH09XDwCBF8dTzl4FNJx4ikqd8xu6kuFlAQRezhTl7FVAMwjlVNIp9vn8+XNvmS2+aR4E6md56mtZPMktl8niFYMIGU9XDkyDQCdZ47e1eupvtVzxrB0bEUGZM2ISBEoAafLISNa4FWH1PxYUiApo4E70h7UtOYJ4ljK2OrGbLgfpfy1NEZUhEbt0FNA9BtYAEMvjaW5DMxGBWnpN7hBFPGrrfSKIjpd5OVIBa8OLo3c9DTM2AQJRXbdvRgGHbrWP9mVZtgKaanyoZ6b6nPJanrqxs4MgJgRzTxCJxGVDQEevEDzXP7X2E5w8LbOBQLQsOUZ8TCLx9evX4fHjx+HRo0esTjW4efNmKL3eu3cvyBs5sPB2lWsWENSaZlr3gacmEi9fvhyOHTvGiga9Hjh79mwyDJSz8NQtUOQyOQhSEoI5JNY+mrJqyPLy5cvekw8YAGPXA7du3UqKCob6b4hHp/rMZCDITQjmwmBIIvH+/fuAgEjgSA9cuXJlMAh0edtbNCDYTAICjf6a4xKPflO/vW4BBLT83ZZ/3esUEHi6ZNitF1VBIDLqxovcVr3UfipDH6UBASBYV/m77w8FgaeRhF0I6HU1EChpp+Rdqco89nv6EomAABB0K/y610NAoCHxfY3N4Qpn9e8qILAwv3wfODTmoJvIef78+ZH9w3Xm4P3lQOTGjRsbGzSNGfAMgeIRgRKCFkd5HYaCyqiyaj1//jwwIGG41gOnT58Ourp02EPx7zHjVyxFB8UigrkSgvGEpG5jIlEw0ECiO3fusDaowe3bt8PVq1eDwvvU9fr162sh0NqI1tEgsJIQTAVB/Py6RKIlWlOWcQrIo+oSlrpypShADUhLyygQaOYgSwnBWLlTtzoGZkFqydb9xxKBkONZQUSNRqs+yQaBbvRQeJRa6ax+Xsfi6f7xfqvz7lAFVKEVJSjbLzCookc/a6uBQcol6ZKgxgZ4TwYepUsyCBQSeUgI5gJHxmgt7DvKBPwfBZJAIDKW6mflVtQp9tMxeh0hhqVRIEeBQSBQWKQQaYpKaOk3dMyth4Q5pmGf9hQ4EgTqS+3s7CwOAhFIOvZWE0Tt2ZkjylVgIwhaSwjGyp26JZGYay/286JALwiULOubTjy1ArX2ee/jyb2YknJOr8AKCJQk06WT1ipxqeORNiQSpzcqv1hXgf9AsNSEYC4gSCTWNSbfPq0CByBYekIwFwYkEqc1K79WT4GtsdOJ51aiVvZTItHbjLX17MQ3e1Vgq5UKOfdxtHI7ajSy7iYlFxLVaH8LCLa3iyVGlUhMnU7dmsWUK+pOL6dZpxlUZe0slS8PICgIghiV6F4M3ct+6dIlV+vFixcPJmo5d+5c6K4XLlwI+p/l47l27RpdtBF8AAQVQHDmzJm1M94wxVm9Kc6kOzeM5dEAEBQGgR6dRmWvV9mP0lbdM5Z0BQBBYRA8ePAAEMw4B6KeVciSrgAgAARNgQsQpENAewACQAAI8upOU3sBgsIgePjwYVMV66g+ubX/ExHk8QkQFAaB5sA/ceIEMJghT3D8+HHmnczjAF2DeO2/5Pbu3bvh1KlTwGBCGJw8eTI8efIksxqwGxFB4YggAuXVq1fh2bNn4enTp0euL168OJj/QaP4Sq26O1JzLw75/Rqf0czApY5lyPcwfmAczABBJRBEIAzd6ual7nMZx5xWK3eTxkfLjTkW9p1GAUBgBAQRGGMrj7W7SRWV6AYmFtsKAAJjIBAQciqPbgyyPL0cj5YDBMXu7out5lK2QyuPl+nleLScXRgQERiMCLqg2zQLkqIAJdK6n7f+mhmhbcIAEBgHgSp2X+VRlnx3d9cVBLqQGpsLsVmd/JYKEDgAQaxA8bmM1hKCsXypWx4tZwccgMARCFTRFB2kVjjrn2dG6PmBAAicgcB6pc4t36ZcyPzVpP0SAAJAYCbC6MuFtF8FbRwhIAAEZkAQowkeLTc9HAABIDAHAgGBR8tNCwNAAAhMgiBGByQSpwECIAAEpkEgIJBIrA8DQAAIzINAMFAikUfL1QMCIAAELkAQuwqtPVquXtVO+2ZAAAhcgUBAaOHRcmnVtP6nAQEgcAeCGB3ohiuWMgoAAkDgFgQCgm68Ypqy8TAABIDANQgEAxKJgMC9iWOYy3b84+lJJOYDgYiAiKApmGoWpP39/fwasdA9AQEgaAoEMbIqNSP0UrgACABBkyAQEEgkDscYIAAEzYJAMMiZEXp49Wnnk4AAEDQNgthVGDojdDtVO+1IAAEgWAQIBAQSievhAAgAwWJAIBhozAGJxFUgAAJAsCgQxK4C06n/HwaAABAsEgQCAonEvzAABIBgsSCI0QGJxBAAASBYPAgEhKU/lxEQAAJA8K8HljydOiAABIDgkAfio+X+9qDbfwUIDpkg9hvZjr8b0LOGS3suIyAABEQEGzywlOnUAcEGE3hu0Sh7uYhmCdOpAwJAQEQwwAOtJxIBwQAT0LqWa129a9nqcxkBASAgIkj0QIvPZQQEiSbw3qJR/nLRTUuJREAACIgIRniglUQiIBhhAlrXcq2rZy1bmE4dEAACIoJCHvA8nTogKGQCzy0aZS8X2Xh9LiMgAAREBBU84O25jICgggloYcu1sJ619DSdOiAABEQEFT3gJZEICCqawHNrRtnLRjXWE4mAABAQEUzkAcvTqQOCiUxAC1u2hfWsp8Xp1AEBICAimMED1hKJgGAGE3huzSh7ucjG0nTqgAAQEBHM7AEL06kDgplNQAtbroX1rOXciURAAAiICIx4QGMO5kokAgIjJvDcmlH2slHNHM9lBASAgIjAoAemTiQCAoMmoIUt28J61nOqRCIgAAREBMY9MMUsSIDAuAk8t2aUvVxkU3s6dUAACIgIHHmg1nMZAYEjE9DClmthPWtZYzp1QAAIiAiceqDkdOqAwKkJPLdolL1cZFMqkbilkUysaIAH/Hpgb28vjF3+AbSb48mcXO9tAAAAAElFTkSuQmCC"/>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 4.4 KiB

View File

@@ -1,67 +0,0 @@
import { CollectionConfig } from '../../src/collections/config/types';
import roles from '../access/roles';
import checkRole from '../access/checkRole';
const access = ({ req: { user } }) => {
const result = checkRole(['admin'], user);
return result;
};
const Admin: CollectionConfig = {
slug: 'admins',
labels: {
singular: 'Admin',
plural: 'Admins',
},
access: {
create: access,
read: access,
update: access,
delete: access,
admin: () => true,
},
auth: {
tokenExpiration: 7200, // 2 hours
verify: false,
maxLoginAttempts: 5,
lockTime: 600 * 1000, // lock time in ms
useAPIKey: true,
depth: 0,
cookies: {
secure: false,
sameSite: 'lax',
domain: undefined,
},
},
fields: [
{
name: 'roles',
label: 'Role',
type: 'select',
options: roles,
defaultValue: 'user',
required: true,
saveToJWT: true,
hasMany: true,
},
{
name: 'publicUser',
type: 'relationship',
hasMany: true,
relationTo: 'public-users',
},
{
name: 'apiKey',
type: 'text',
access: {
read: ({ req: { user } }) => checkRole(['admin'], user),
},
},
],
timestamps: true,
admin: {
useAsTitle: 'email',
},
};
export default Admin;

View File

@@ -1,363 +0,0 @@
import { CollectionConfig } from '../../src/collections/config/types';
import checkRole from '../access/checkRole';
import Email from '../blocks/Email';
import Quote from '../blocks/Quote';
import NumberBlock from '../blocks/Number';
import CallToAction from '../blocks/CallToAction';
import CollectionDescription from '../customComponents/CollectionDescription';
import DemoUIField from '../client/components/DemoUIField/Field';
import DemoUIFieldCell from '../client/components/DemoUIField/Cell';
const AllFields: CollectionConfig = {
slug: 'all-fields',
labels: {
singular: 'All Fields',
plural: 'All Fields',
},
admin: {
defaultColumns: ['text', 'demo', 'createdAt'],
useAsTitle: 'text',
preview: (doc, { token }) => {
const { text } = doc;
if (doc && text) {
return `http://localhost:3000/previewable-posts/${text}?preview=true&token=${token}`;
}
return null;
},
description: CollectionDescription,
},
versions: {
maxPerDoc: 20,
retainDeleted: true,
drafts: false,
},
access: {
read: () => true,
},
fields: [
{
name: 'text',
type: 'text',
label: 'Text',
required: true,
defaultValue: 'Default Value',
unique: true,
access: {
create: ({ req: { user } }) => checkRole(['admin'], user),
update: ({ req: { user } }) => checkRole(['admin'], user),
read: ({ req: { user } }) => Boolean(user),
},
},
{
name: 'descriptionText',
type: 'text',
label: 'Text with text description',
defaultValue: 'Default Value',
admin: {
description: 'This text describes the field',
},
},
{
name: 'descriptionFunction',
type: 'text',
label: 'Text with function description',
defaultValue: 'Default Value',
maxLength: 20,
admin: {
description: ({ value }) => (typeof value === 'string' ? `${20 - value.length} characters left` : ''),
},
},
{
name: 'image',
type: 'upload',
label: 'Image',
relationTo: 'media',
admin: {
description: 'No selfies',
},
},
{
name: 'select',
label: 'Select',
type: 'select',
options: [{
value: 'option-1',
label: 'Option 1 Label',
}, {
value: 'option-2',
label: 'Option 2 Label',
}, {
value: 'option-3',
label: 'Option 3 Label',
}, {
value: 'option-4',
label: 'Option 4 Label',
}],
defaultValue: 'option-1',
required: true,
},
{
name: 'selectMany',
label: 'Select w/ hasMany',
type: 'select',
options: [{
value: 'option-1',
label: 'Option 1 Label',
}, {
value: 'option-2',
label: 'Option 2 Label',
}, {
value: 'option-3',
label: 'Option 3 Label',
}, {
value: 'option-4',
label: 'Option 4 Label',
}],
defaultValue: 'option-1',
required: true,
hasMany: true,
},
{
name: 'dayOnlyDateFieldExample',
label: 'Day Only',
type: 'date',
required: true,
admin: {
date: {
pickerAppearance: 'dayOnly',
monthsToShow: 2,
},
},
},
{
name: 'timeOnlyDateFieldExample',
label: 'Time Only',
type: 'date',
admin: {
date: {
pickerAppearance: 'timeOnly',
},
},
},
{
name: 'point',
label: 'Point Field (GeoJSON)',
type: 'point',
},
{
name: 'radioGroupExample',
label: 'Radio Group Example',
type: 'radio',
options: [{
value: 'option-1',
label: 'Options 1 Label',
}, {
value: 'option-2',
label: 'Option 2 Label',
}, {
value: 'option-3',
label: 'Option 3 Label',
}],
defaultValue: 'option-2',
required: true,
admin: {
readOnly: true,
},
},
{
type: 'row',
fields: [
{
name: 'email',
label: 'Email',
type: 'email',
}, {
name: 'number',
label: 'Number',
type: 'number',
},
],
},
{
type: 'group',
label: 'Group',
name: 'group',
fields: [
{
type: 'row',
fields: [
{
name: 'nestedText1',
label: 'Nested Text 1',
type: 'text',
}, {
name: 'nestedText2',
label: 'Nested Text 2',
type: 'text',
},
],
},
],
},
{
type: 'array',
label: 'Editable Array',
name: 'array',
minRows: 2,
maxRows: 4,
fields: [
{
type: 'row',
fields: [
{
name: 'arrayText1',
label: 'Array Text 1',
type: 'text',
required: true,
admin: {
width: '50%',
},
},
{
name: 'arrayText2',
label: 'Array Text 2',
type: 'text',
required: true,
admin: {
width: '50%',
},
access: {
read: ({ req: { user } }) => Boolean(user),
update: ({ req: { user } }) => checkRole(['admin'], user),
},
},
],
},
{
type: 'text',
name: 'arrayText3',
label: 'Array Text 3',
admin: {
readOnly: true,
},
},
{
name: 'checkbox',
label: 'Checkbox',
type: 'checkbox',
},
],
},
{
type: 'array',
name: 'readOnlyArray',
label: 'readOnly Array',
admin: {
readOnly: true,
},
defaultValue: [{
text: 'text in readOnly array one',
},
{
text: 'text in readOnly array two',
}],
fields: [
{
type: 'text',
name: 'text',
},
],
},
{
type: 'blocks',
label: 'Blocks Content',
name: 'blocks',
minRows: 2,
blocks: [Email, NumberBlock, Quote, CallToAction],
localized: true,
required: true,
},
{
type: 'relationship',
label: 'Relationship to One Collection',
name: 'relationship',
relationTo: 'conditions',
admin: {
description: 'Relates to description',
},
},
{
type: 'relationship',
label: 'Relationship hasMany',
name: 'relationshipHasMany',
relationTo: 'localized-posts',
hasMany: true,
},
{
type: 'relationship',
label: 'Relationship to Multiple Collections',
name: 'relationshipMultipleCollections',
relationTo: ['localized-posts', 'conditions'],
},
{
type: 'textarea',
label: 'Textarea',
name: 'textarea',
admin: {
description: 'Hello textarea description',
},
},
{
name: 'richText',
type: 'richText',
label: 'Rich Text',
required: true,
},
{
type: 'ui',
name: 'demo',
admin: {
position: 'sidebar',
components: {
Field: DemoUIField,
Cell: DemoUIFieldCell,
},
},
},
{
name: 'slug',
type: 'text',
label: 'Slug',
admin: {
position: 'sidebar',
},
localized: true,
unique: true,
required: true,
},
{
name: 'checkbox',
type: 'checkbox',
label: 'Checkbox',
admin: {
position: 'sidebar',
},
},
{
name: 'dateFieldExample',
label: 'Day and Time',
type: 'date',
admin: {
position: 'sidebar',
date: {
timeIntervals: 30,
},
},
},
],
timestamps: true,
};
export default AllFields;

View File

@@ -1,102 +0,0 @@
import { CollectionConfig } from '../../src/collections/config/types';
const AutoLabel: CollectionConfig = {
slug: 'auto-label',
admin: {
useAsTitle: 'autoLabelField',
enableRichTextRelationship: true,
},
fields: [
{
name: 'autoLabelField',
type: 'text',
},
{
name: 'noLabel',
type: 'text',
label: false,
},
{
name: 'labelOverride',
type: 'text',
label: 'Custom Label',
},
{
name: 'testRelationship',
type: 'relationship',
relationTo: 'all-fields',
},
{
name: 'specialBlock',
type: 'blocks',
minRows: 1,
maxRows: 20,
// Will auto-label
// labels: {
// singular: 'Special Block',
// plural: 'Special Blocks',
// },
blocks: [
{
slug: 'number',
// Will auto-label
// labels: {
// singular: 'Number',
// plural: 'Numbers',
// },
fields: [
{
name: 'testNumber',
type: 'number',
},
],
},
],
},
{
name: 'noLabelBlock',
type: 'blocks',
label: false,
minRows: 1,
maxRows: 20,
blocks: [
{
slug: 'number',
// labels: {
// singular: 'Number',
// plural: 'Numbers',
// },
fields: [
{
name: 'testNumber',
type: 'number',
},
],
},
],
},
{
name: 'items',
type: 'array',
fields: [
{
name: 'itemName',
type: 'text',
},
],
},
{
name: 'noLabelArray',
type: 'array',
label: false,
fields: [
{
type: 'text',
name: 'textField',
},
],
},
],
};
export default AutoLabel;

View File

@@ -1,46 +0,0 @@
import { CollectionConfig } from '../../src/collections/config/types';
import Email from '../blocks/Email';
import Quote from '../blocks/Quote';
import NumberBlock from '../blocks/Number';
import CallToAction from '../blocks/CallToAction';
const Blocks: CollectionConfig = {
slug: 'blocks',
labels: {
singular: 'Blocks',
plural: 'Blocks',
},
access: {
read: () => true,
},
versions: {
drafts: true,
},
fields: [
{
name: 'layout',
label: 'Layout Blocks',
labels: {
singular: 'Block',
plural: 'Blocks',
},
type: 'blocks',
blocks: [Email, NumberBlock, Quote, CallToAction],
localized: true,
required: true,
},
{
name: 'nonLocalizedLayout',
label: 'Non Localized Layout',
labels: {
singular: 'Layout',
plural: 'Layouts',
},
type: 'blocks',
blocks: [Email, NumberBlock, Quote, CallToAction],
required: true,
},
],
};
export default Blocks;

View File

@@ -1,23 +0,0 @@
import { CollectionConfig } from '../../src/collections/config/types';
const Code: CollectionConfig = {
slug: 'code',
labels: {
singular: 'Code',
plural: 'Codes',
},
fields: [
{
name: 'code',
type: 'code',
label: 'Code',
required: true,
admin: {
language: 'js',
description: 'javascript example',
},
},
],
};
export default Code;

View File

@@ -1,86 +0,0 @@
import { CollectionConfig } from '../../src/collections/config/types';
import Email from '../blocks/Email';
import Quote from '../blocks/Quote';
import NumberBlock from '../blocks/Number';
import CallToAction from '../blocks/CallToAction';
import Text from './CustomComponents/components/fields/Text/Field';
const Conditions: CollectionConfig = {
slug: 'conditions',
labels: {
singular: 'Conditions',
plural: 'Conditions',
},
fields: [
{
name: 'title',
type: 'text',
label: 'Title',
required: true,
},
{
name: 'enableTest',
type: 'checkbox',
label: 'Enable Test',
},
{
name: 'number',
type: 'number',
label: 'Number Field',
},
{
name: 'simpleCondition',
type: 'text',
label: 'Enable Test is checked',
required: true,
admin: {
condition: (_, siblings) => siblings.enableTest === true,
},
},
{
name: 'orCondition',
type: 'text',
label: 'Number is greater than 20 OR enableTest is checked',
required: true,
admin: {
condition: (_, siblings) => siblings.number > 20 || siblings.enableTest === true,
},
},
{
name: 'nestedConditions',
type: 'text',
label: 'Number is either greater than 20 AND enableTest is checked, OR number is less than 20 and enableTest is NOT checked',
admin: {
condition: (_, siblings) => (siblings.number > 20 && siblings.enableTest === true) || (siblings.number < 20 && siblings.enableTest === false),
},
},
{
name: 'blocks',
label: 'Blocks',
labels: {
singular: 'Block',
plural: 'Blocks',
},
type: 'blocks',
blocks: [Email, NumberBlock, Quote, CallToAction],
required: true,
admin: {
condition: (_, siblings) => siblings?.enableTest === true,
},
},
{
name: 'customComponent',
type: 'text',
label: 'Custom Component with Enable Test is checked',
required: true,
admin: {
condition: (_, siblings) => siblings?.enableTest === true,
components: {
Field: Text,
},
},
},
],
};
export default Conditions;

View File

@@ -1,5 +0,0 @@
import React from 'react';
const Cell: React.FC = () => <div className="description">fake description cell</div>;
export default Cell;

View File

@@ -1,5 +0,0 @@
import React from 'react';
const Description: React.FC = () => <div className="description">fake description field</div>;
export default Description;

View File

@@ -1,3 +0,0 @@
.custom-description-filter {
background: lightgray;
}

View File

@@ -1,15 +0,0 @@
import React from 'react';
import { Props } from './types';
import './index.scss';
const Filter: React.FC<Props> = ({ onChange, value }) => (
<input
className="custom-description-filter"
type="text"
onChange={(e) => onChange(e.target.value)}
value={value}
/>
);
export default Filter;

View File

@@ -1,4 +0,0 @@
export type Props = {
value?: string
onChange?: (value: string) => void
}

View File

@@ -1,19 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Group } from '../../../../../../../components/forms';
const CustomGroup: React.FC = (props) => (
<div className="custom-group">
<Group {...props} />
</div>
);
CustomGroup.defaultProps = {
value: '',
};
CustomGroup.propTypes = {
value: PropTypes.string,
};
export default CustomGroup;

View File

@@ -1,5 +0,0 @@
import React from 'react';
const NestedArrayCustomField = () => <div className="nested-array-custom-field">Nested array custom field</div>;
export default NestedArrayCustomField;

View File

@@ -1,5 +0,0 @@
import React from 'react';
const NestedGroupCustomField = () => <div className="nested-group-custom-field">Nested group custom field</div>;
export default NestedGroupCustomField;

View File

@@ -1,5 +0,0 @@
import React from 'react';
const NestedText1 = () => <div className="nested-text-1">Nested Text 1</div>;
export default NestedText1;

View File

@@ -1,61 +0,0 @@
import React, { useCallback } from 'react';
import SelectInput from '../../../../../../../src/admin/components/forms/field-types/Select/Input';
import { Props as SelectFieldType } from '../../../../../../../src/admin/components/forms/field-types/Select/types';
import useField from '../../../../../../../src/admin/components/forms/useField';
const Select: React.FC<SelectFieldType> = (props) => {
const {
path,
name,
label,
options,
} = props;
const {
showError,
value,
setValue,
} = useField({
path,
});
const onChange = useCallback((incomingOption) => {
const { value: incomingValue } = incomingOption;
const sendToCRM = async () => {
try {
const req = await fetch('https://fake-crm.com', {
method: 'post',
body: JSON.stringify({
someKey: incomingValue,
}),
});
const res = await req.json();
if (res.ok) {
console.log('Successfully synced to CRM.'); // eslint-disable-line no-console
}
} catch (e) {
console.error(e);
}
};
sendToCRM();
setValue(incomingValue);
}, [
setValue,
]);
return (
<SelectInput
name={name}
label={label}
options={options}
value={value as string}
onChange={onChange}
showError={showError}
/>
);
};
export default Select;

View File

@@ -1,44 +0,0 @@
import React, { useCallback } from 'react';
import TextInput from '../../../../../../../src/admin/components/forms/field-types/Text/Input';
import { Props as TextFieldType } from '../../../../../../../src/admin/components/forms/field-types/Text/types';
import useField from '../../../../../../../src/admin/components/forms/useField';
const Text: React.FC<TextFieldType> = (props) => {
const {
path,
name,
label,
} = props;
const field = useField({
path,
enableDebouncedValue: true,
});
const {
showError,
value,
setValue,
} = field;
const onChange = useCallback((e) => {
const { value: incomingValue } = e.target;
const valueWithoutSpaces = incomingValue.replace(/\s/g, '');
setValue(valueWithoutSpaces);
}, [
setValue,
]);
return (
<TextInput
path={path}
name={name}
value={value as string || ''}
label={label}
onChange={onChange}
showError={showError}
/>
);
};
export default Text;

View File

@@ -1,41 +0,0 @@
import React, { useCallback } from 'react';
import TextAreaInput from '../../../../../../../src/admin/components/forms/field-types/Textarea/Input';
import { Props as TextFieldType } from '../../../../../../../src/admin/components/forms/field-types/Text/types';
import useField from '../../../../../../../src/admin/components/forms/useField';
const TextArea: React.FC<TextFieldType> = (props) => {
const {
path,
name,
label,
} = props;
const field = useField({
path,
});
const {
showError,
value,
setValue,
} = field;
const onChange = useCallback((e) => {
const { value: incomingValue } = e.target;
setValue(incomingValue);
}, [
setValue,
]);
return (
<TextAreaInput
name={name}
value={value as string || ''}
label={label}
onChange={onChange}
showError={showError}
/>
);
};
export default TextArea;

View File

@@ -1,50 +0,0 @@
import React, { useCallback } from 'react';
import TextInput from '../../../../../../../src/admin/components/forms/field-types/Text';
import { UIField as UIFieldType } from '../../../../../../../src/fields/config/types';
import SelectInput from '../../../../../../../src/admin/components/forms/field-types/Select';
const UIField: React.FC<UIFieldType> = () => {
const [textValue, setTextValue] = React.useState('');
const [selectValue, setSelectValue] = React.useState('');
const onTextChange = useCallback((incomingValue) => {
setTextValue(incomingValue);
}, [])
const onSelectChange = useCallback((incomingValue) => {
setSelectValue(incomingValue);
}, [])
return (
<div>
<TextInput
name="ui-text"
label="Presentation-only text field (does not submit)"
value={textValue as string}
onChange={onTextChange}
/>
<SelectInput
name="ui-select"
label="Presentation-only select field (does not submit)"
options={[
{
label: 'Option 1',
value: 'option-1'
},
{
label: 'Option 2',
value: 'option-2'
},
{
label: 'Option 3',
value: 'option-4'
}
]}
value={selectValue as string}
onChange={onSelectChange}
/>
</div>
)
};
export default UIField;

View File

@@ -1,57 +0,0 @@
import React, { useCallback } from 'react';
import UploadInput from '../../../../../../../src/admin/components/forms/field-types/Upload/Input';
import { Props as UploadFieldType } from '../../../../../../../src/admin/components/forms/field-types/Upload/types';
import useField from '../../../../../../../src/admin/components/forms/useField';
import { SanitizedCollectionConfig } from '../../../../../../../src/collections/config/types';
import { useConfig } from '../../../../../../../src/admin/components/utilities/Config';
const Text: React.FC<UploadFieldType> = (props) => {
const {
path,
name,
label,
relationTo,
fieldTypes,
} = props;
const {
value,
setValue,
showError,
} = useField({
path,
});
const onChange = useCallback((incomingValue) => {
const incomingID = incomingValue?.id || incomingValue;
setValue(incomingID);
}, [setValue]);
const {
collections,
serverURL,
routes: {
api,
},
} = useConfig();
const collection = collections.find((coll) => coll.slug === relationTo) || undefined;
return (
<UploadInput
path={path}
relationTo={relationTo}
fieldTypes={fieldTypes}
name={name}
label={label}
value={value as string}
onChange={onChange}
showError={showError}
collection={collection as SanitizedCollectionConfig}
serverURL={serverURL}
api={api}
/>
);
};
export default Text;

View File

@@ -1,5 +0,0 @@
$color: purple;
.custom-list {
color: $color;
}

View File

@@ -1,14 +0,0 @@
import React from 'react';
import DefaultList from '../../../../../../src/admin/components/views/collections/List/Default';
import './index.scss';
const CustomListView: React.FC = (props) => (
<div className="custom-list">
<p>This is a custom Pages list view</p>
<p>Sup</p>
<DefaultList {...props} />
</div>
);
export default CustomListView;

View File

@@ -1,224 +0,0 @@
import { CollectionConfig } from '../../../src/collections/config/types';
import DescriptionField from './components/fields/Description/Field';
import TextField from './components/fields/Text/Field';
import SelectField from './components/fields/Select/Field';
import TextAreaField from './components/fields/TextArea/Field';
import UploadField from './components/fields/Upload/Field';
import DescriptionCell from './components/fields/Description/Cell';
import DescriptionFilter from './components/fields/Description/Filter';
import NestedArrayField from './components/fields/NestedArrayCustomField/Field';
import GroupField from './components/fields/Group/Field';
import NestedGroupField from './components/fields/NestedGroupCustomField/Field';
import NestedText1Field from './components/fields/NestedText1/Field';
import UIField from './components/fields/UI/Field';
import ListView from './components/views/List';
import CustomDescriptionComponent from '../../customComponents/Description';
const CustomComponents: CollectionConfig = {
slug: 'custom-components',
labels: {
singular: 'Custom Component',
plural: 'Custom Components',
},
fields: [
{
name: 'title',
label: 'Title',
type: 'text',
maxLength: 100,
required: true,
unique: true,
},
{
name: 'normalText',
label: 'Normal text field',
type: 'text',
// required: true,
},
{
name: 'customText',
label: 'Custom text field (removes whitespace)',
type: 'text',
// required: true,
admin: {
components: {
Field: TextField,
},
},
},
{
name: 'normalSelect',
label: 'Normal select field',
type: 'select',
options: [
{
label: 'Option 1',
value: '1',
},
{
label: 'Option 2',
value: '2',
},
{
label: 'Option 3',
value: '3',
},
],
},
{
name: 'customSelect',
label: 'Custom select field (syncs value with crm)',
type: 'select',
options: [
{
label: 'Option 1',
value: '1',
},
{
label: 'Option 2',
value: '2',
},
{
label: 'Option 3',
value: '3',
},
],
admin: {
components: {
Field: SelectField,
},
},
},
{
name: 'normalTextarea',
label: 'Normal textarea field',
type: 'textarea',
},
{
name: 'customTextarea',
label: 'Custom textarea field',
type: 'textarea',
admin: {
components: {
Field: TextAreaField,
},
},
},
{
name: 'ui',
label: 'UI',
type: 'ui',
admin: {
components: {
Field: UIField,
},
},
},
{
name: 'normalUpload',
label: 'Normal upload field',
type: 'upload',
relationTo: 'media',
},
{
name: 'customUpload',
label: 'Custom upload field',
type: 'upload',
relationTo: 'media',
admin: {
components: {
Field: UploadField,
},
},
},
{
name: 'description',
label: 'Description',
type: 'textarea',
admin: {
components: {
Field: DescriptionField,
Cell: DescriptionCell,
Filter: DescriptionFilter,
},
},
},
{
name: 'componentDescription',
label: 'Component ViewDescription',
type: 'text',
admin: {
description: CustomDescriptionComponent,
},
},
{
name: 'array',
label: 'Array',
type: 'array',
fields: [
{
type: 'text',
name: 'nestedArrayCustomField',
label: 'Nested Array Custom Field',
admin: {
components: {
Field: NestedArrayField,
},
},
},
],
},
{
name: 'group',
label: 'Group',
type: 'group',
admin: {
components: {
Field: GroupField,
},
},
fields: [
{
type: 'text',
name: 'nestedGroupCustomField',
label: 'Nested Group Custom Field',
admin: {
components: {
Field: NestedGroupField,
},
},
},
],
},
{
type: 'row',
fields: [
{
name: 'nestedText1',
label: 'Nested Text 1',
type: 'text',
admin: {
components: {
Field: NestedText1Field,
},
},
}, {
name: 'nestedText2',
label: 'Nested Text 2',
type: 'text',
},
],
},
],
timestamps: true,
admin: {
useAsTitle: 'title',
components: {
views: {
List: ListView,
},
},
},
};
export default CustomComponents;

View File

@@ -1,22 +0,0 @@
import { CollectionConfig } from '../../src/collections/config/types';
const CustomID: CollectionConfig = {
slug: 'custom-id',
labels: {
singular: 'CustomID',
plural: 'CustomIDs',
},
fields: [
{
name: 'id',
type: 'number',
},
{
name: 'name',
type: 'text',
required: true,
},
],
};
export default CustomID;

View File

@@ -1,329 +0,0 @@
import { CollectionConfig } from '../../src/collections/config/types';
import checkRole from '../access/checkRole';
import Email from '../blocks/Email';
import Quote from '../blocks/Quote';
import NumberBlock from '../blocks/Number';
import CallToAction from '../blocks/CallToAction';
const DefaultValues: CollectionConfig = {
slug: 'default-values',
labels: {
singular: 'Default Value Test',
plural: 'Default Value Tests',
},
admin: {
useAsTitle: 'text',
},
access: {
read: () => true,
},
fields: [
{
name: 'text',
type: 'text',
label: 'Text',
defaultValue: 'Default Value',
unique: true,
access: {
create: ({ req: { user } }) => checkRole(['admin'], user),
update: ({ req: { user } }) => checkRole(['admin'], user),
read: ({ req: { user } }) => Boolean(user),
},
},
{
name: 'image',
type: 'upload',
label: 'Image',
relationTo: 'media',
},
{
name: 'select',
label: 'Select',
type: 'select',
options: [{
value: 'option-1',
label: 'Option 1 Label',
}, {
value: 'option-2',
label: 'Option 2 Label',
}, {
value: 'option-3',
label: 'Option 3 Label',
}, {
value: 'option-4',
label: 'Option 4 Label',
}],
defaultValue: 'option-1',
},
{
name: 'selectMany',
label: 'Select w/ hasMany',
type: 'select',
options: [{
value: 'option-1',
label: 'Option 1 Label',
}, {
value: 'option-2',
label: 'Option 2 Label',
}, {
value: 'option-3',
label: 'Option 3 Label',
}, {
value: 'option-4',
label: 'Option 4 Label',
}],
defaultValue: ['option-1', 'option-4'],
hasMany: true,
},
{
name: 'radioGroupExample',
label: 'Radio Group Example',
type: 'radio',
options: [{
value: 'option-1',
label: 'Options 1 Label',
}, {
value: 'option-2',
label: 'Option 2 Label',
}, {
value: 'option-3',
label: 'Option 3 Label',
}],
defaultValue: 'option-2',
},
{
type: 'row',
fields: [
{
name: 'email',
label: 'Email',
type: 'email',
defaultValue: 'some@email.com',
}, {
name: 'number',
label: 'Number',
type: 'number',
defaultValue: 5,
},
],
},
{
type: 'group',
label: 'Group',
name: 'group',
defaultValue: {
nestedText2: 'nested default text 2',
nestedText3: 'neat',
},
fields: [
{
type: 'row',
fields: [
{
name: 'nestedText1',
label: 'Nested Text 1',
type: 'text',
defaultValue: 'this should take priority',
},
{
name: 'nestedText2',
label: 'Nested Text 2',
type: 'text',
},
{
name: 'nestedText3',
type: 'text',
},
],
},
],
},
{
type: 'array',
label: 'Array',
name: 'array',
admin: {
readOnly: true,
},
defaultValue: [
{
arrayText1: 'Get out',
arrayText2: 'Get in',
},
],
fields: [
{
type: 'row',
fields: [
{
name: 'arrayText1',
label: 'Array Text 1',
type: 'text',
admin: {
width: '50%',
},
defaultValue: 'default array text',
},
{
name: 'arrayText2',
label: 'Array Text 2',
type: 'text',
admin: {
width: '50%',
},
access: {
read: ({ req: { user } }) => Boolean(user),
update: ({ req: { user } }) => checkRole(['admin'], user),
},
},
],
},
{
type: 'text',
name: 'arrayText3',
label: 'Array Text 3',
admin: {
readOnly: true,
},
},
{
name: 'checkbox',
label: 'Checkbox',
type: 'checkbox',
defaultValue: true,
},
],
},
{
type: 'blocks',
label: 'Blocks Content',
name: 'blocks',
labels: {
singular: 'Block',
plural: 'Blocks',
},
blocks: [Email, NumberBlock, Quote, CallToAction],
localized: true,
admin: {
readOnly: true,
},
defaultValue: [
{
blockType: 'email',
testEmail: 'dev@payloadcms.com',
},
],
},
{
type: 'relationship',
label: 'Relationship to One Collection',
name: 'relationship',
relationTo: 'conditions',
},
{
type: 'relationship',
label: 'Relationship hasMany',
name: 'relationshipHasMany',
relationTo: 'localized-posts',
hasMany: true,
},
{
type: 'relationship',
label: 'Relationship to Multiple Collections',
name: 'relationshipMultipleCollections',
relationTo: ['localized-posts', 'conditions'],
},
{
type: 'textarea',
label: 'Textarea',
name: 'textarea',
defaultValue: 'my textarea text',
},
{
name: 'slug',
type: 'text',
label: 'Slug',
admin: {
position: 'sidebar',
},
localized: true,
unique: true,
defaultValue: 'my-slug',
},
{
name: 'checkbox',
type: 'checkbox',
label: 'Checkbox',
admin: {
position: 'sidebar',
},
defaultValue: true,
},
{
name: 'richText',
type: 'richText',
label: 'Rich Text',
admin: {
elements: [
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'blockquote',
'ul',
'ol',
'link',
],
leaves: [
'bold',
'italic',
'underline',
'strikethrough',
],
},
defaultValue: [{
children: [{ text: 'Cookin now' }],
}],
},
{
type: 'array',
name: 'asyncArray',
defaultValue: () => {
return [{ child: 'ok' }];
},
fields: [
{
name: 'child',
type: 'text',
defaultValue: () => {
return 'async child';
},
},
],
},
{
name: 'asyncText',
type: 'text',
defaultValue: async (): Promise<string> => {
return new Promise((resolve) => setTimeout(() => {
resolve('asyncFunction');
}, 50));
},
},
{
name: 'function',
type: 'text',
defaultValue: (args): string => {
const { locale } = args;
if (locale === 'en') {
return 'function';
}
return '';
},
},
],
timestamps: true,
};
export default DefaultValues;

View File

@@ -1,70 +0,0 @@
import { CollectionConfig } from '../../src/collections/config/types';
import checkRole from '../access/checkRole';
const access = ({ req: { user } }) => {
const isAdmin = checkRole(['admin'], user);
if (isAdmin) {
return true;
}
if (user) {
return {
owner: {
equals: user.id,
},
};
}
return false;
};
const Files: CollectionConfig = {
slug: 'files',
labels: {
singular: 'File',
plural: 'Files',
},
upload: {
staticURL: '/files',
staticDir: './files',
},
access: {
create: () => true,
read: access,
update: access,
delete: access,
},
fields: [
{
name: 'type',
label: 'Type',
type: 'select',
options: [{
value: 'Type 1',
label: 'Type 1 Label',
}, {
value: 'Type 2',
label: 'Type 2 Label',
}, {
value: 'Type 3',
label: 'Type 3 Label',
}],
defaultValue: 'Type 1',
required: true,
},
{
name: 'owner',
label: 'Owner',
type: 'relationship',
relationTo: 'admins',
required: true,
},
],
timestamps: true,
admin: {
useAsTitle: 'filename',
},
};
export default Files;

View File

@@ -1,43 +0,0 @@
import { CollectionConfig } from '../../src/collections/config/types';
const HiddenFields: CollectionConfig = {
slug: 'hidden-fields',
labels: {
singular: 'Hidden Fields',
plural: 'Hidden Fields',
},
fields: [
{
name: 'title',
type: 'text',
label: 'Title - Not Hidden',
required: true,
},
{
name: 'hiddenAdmin',
type: 'text',
label: 'Hidden on Admin',
admin: {
hidden: true,
},
required: true,
defaultValue: 'should be hidden from admin, visible in API',
},
{
name: 'hiddenAPI',
type: 'text',
label: 'Hidden on API',
hidden: true,
required: true,
hooks: {
beforeValidate: [
({ value }) => {
return value || 'should be hidden from API';
},
],
},
},
],
};
export default HiddenFields;

View File

@@ -1,98 +0,0 @@
/* eslint-disable no-param-reassign, no-console */
// If importing outside of demo project, should import CollectionAfterReadHook, CollectionBeforeChangeHook, etc
import { AfterChangeHook, AfterDeleteHook, AfterReadHook, BeforeChangeHook, BeforeDeleteHook, BeforeReadHook, CollectionConfig } from '../../src/collections/config/types';
import { FieldHook } from '../../src/fields/config/types';
import { Hook } from '../payload-types';
const Hooks: CollectionConfig = {
slug: 'hooks',
labels: {
singular: 'Hook',
plural: 'Hooks',
},
admin: {
useAsTitle: 'title',
},
access: {
create: () => true,
read: () => true,
update: () => true,
delete: () => true,
},
hooks: {
beforeRead: [
((operation) => {
if (operation.req.headers.hook === 'beforeRead') {
console.log('before reading Hooks document');
}
}) as BeforeReadHook<Hook>,
],
beforeChange: [
((operation) => {
if (operation.req.headers.hook === 'beforeChange') {
operation.data.description += '-beforeChangeSuffix';
}
return operation.data;
}) as BeforeChangeHook<Hook>,
],
beforeDelete: [
((operation) => {
if (operation.req.headers.hook === 'beforeDelete') {
// TODO: Find a better hook operation to assert against in tests
operation.req.headers.hook = 'afterDelete';
}
}) as BeforeDeleteHook,
],
afterRead: [
((operation) => {
const { doc, findMany } = operation;
doc.afterReadHook = true;
doc.findMany = findMany;
return doc;
}) as AfterReadHook<Hook & { afterReadHook: boolean, findMany: boolean }>,
],
afterChange: [
((operation) => {
if (operation.req.headers.hook === 'afterChange') {
operation.doc.afterChangeHook = true;
}
return operation.doc;
}) as AfterChangeHook<Hook & { afterChangeHook: boolean }>,
],
afterDelete: [
((operation) => {
if (operation.req.headers.hook === 'afterDelete') {
operation.doc.afterDeleteHook = true;
}
return operation.doc;
}) as AfterDeleteHook,
],
},
fields: [
{
name: 'title',
label: 'Title',
type: 'text',
maxLength: 100,
required: true,
unique: true,
localized: true,
hooks: {
afterRead: [
({ value }) => (value ? value.toUpperCase() : null) as FieldHook<Hook, 'title'>,
],
},
},
{
name: 'description',
label: 'Description',
type: 'textarea',
required: true,
localized: true,
},
],
timestamps: true,
};
export default Hooks;

View File

@@ -1,22 +0,0 @@
import { CollectionConfig } from '../../src/collections/config/types';
const Images: CollectionConfig = {
slug: 'images',
admin: {
description: 'Used to test upload relationship queries',
},
labels: {
singular: 'Image',
plural: 'Images',
},
fields: [
{
name: 'upload',
type: 'upload',
relationTo: 'media',
},
],
timestamps: true,
};
export default Images;

View File

@@ -1,38 +0,0 @@
import { CollectionConfig } from '../../src/collections/config/types';
const LocalOperations: CollectionConfig = {
slug: 'local-operations',
labels: {
singular: 'Local Operation',
plural: 'Local Operations',
},
hooks: {
afterRead: [
async ({ req, doc }) => {
const formattedData = { ...doc };
const localizedPosts = await req.payload.find({
collection: 'localized-posts',
});
const blocksGlobal = await req.payload.findGlobal({
slug: 'blocks-global',
});
formattedData.localizedPosts = localizedPosts;
formattedData.blocksGlobal = blocksGlobal;
return formattedData;
},
],
},
fields: [
{
name: 'title',
type: 'text',
label: 'title',
required: true,
},
],
};
export default LocalOperations;

View File

@@ -1,146 +0,0 @@
import { CollectionConfig } from '../../src/collections/config/types';
import { PayloadRequest } from '../../src/express/types';
import { Block } from '../../src/fields/config/types';
const validateLocalizationTransform = (hook: string, value, req: PayloadRequest) => {
if (req.locale !== 'all' && value !== undefined && typeof value !== 'string' && value !== null) {
console.error(hook, value);
throw new Error('Locale transformation should happen before hook is called');
}
return value;
};
const RichTextBlock: Block = {
slug: 'richTextBlock',
labels: {
singular: 'Rich Text Block',
plural: 'Rich Text Blocks',
},
fields: [
{
name: 'content',
localized: true,
type: 'richText',
admin: {
hideGutter: true,
},
},
],
};
const LocalizedPosts: CollectionConfig = {
slug: 'localized-posts',
labels: {
singular: 'Localized Post',
plural: 'Localized Posts',
},
admin: {
useAsTitle: 'title',
defaultColumns: [
'title',
'priority',
'createdAt',
],
enableRichTextRelationship: true,
},
access: {
read: () => true,
},
fields: [
{
name: 'title',
label: 'Title',
type: 'text',
maxLength: 100,
required: true,
unique: true,
localized: true,
hooks: {
beforeValidate: [({ value, req }) => validateLocalizationTransform('beforeValidate', value, req)],
beforeChange: [({ value, req }) => validateLocalizationTransform('beforeChange', value, req)],
afterChange: [({ value, req }) => validateLocalizationTransform('afterChange', value, req)],
afterRead: [({ value, req }) => validateLocalizationTransform('afterRead', value, req)],
},
},
{
name: 'summary',
label: 'Summary',
type: 'text',
index: true,
},
{
name: 'description',
label: 'Description',
type: 'textarea',
required: true,
localized: true,
},
{
type: 'richText',
name: 'richText',
label: 'Rich Text',
},
{
name: 'priority',
label: 'Priority',
type: 'number',
localized: true,
},
{
name: 'localizedGroup',
label: 'Localized Group',
type: 'group',
localized: true,
fields: [
{
type: 'text',
name: 'text',
label: 'Text',
},
{
type: 'text',
name: 'demoHiddenField',
hidden: true,
},
],
},
{
name: 'nonLocalizedGroup',
label: 'Non-Localized Group',
type: 'group',
fields: [
{
type: 'text',
name: 'text',
label: 'Text',
localized: true,
},
],
},
{
type: 'array',
label: 'Non-Localized Array',
name: 'nonLocalizedArray',
maxRows: 3,
fields: [
{
type: 'text',
name: 'localizedEmbeddedText',
label: 'Localized Embedded Text',
localized: true,
},
],
},
{
label: 'Blocks',
name: 'richTextBlocks',
type: 'blocks',
blocks: [
RichTextBlock,
],
},
],
timestamps: true,
};
export default LocalizedPosts;

View File

@@ -1,79 +0,0 @@
import { CollectionConfig } from '../../src/collections/config/types';
import { FieldAccess } from '../../src/fields/config/types';
import checkRole from '../access/checkRole';
const PublicReadabilityAccess: FieldAccess = ({ req: { user }, siblingData }) => {
if (checkRole(['admin'], user)) {
return true;
}
if (siblingData?.allowPublicReadability) return true;
return false;
};
const LocalizedArrays: CollectionConfig = {
slug: 'localized-arrays',
labels: {
singular: 'Localized Array',
plural: 'Localized Arrays',
},
access: {
read: () => true,
},
fields: [
{
type: 'array',
label: false,
name: 'array',
localized: true,
required: true,
minRows: 2,
maxRows: 4,
fields: [
{
type: 'row',
fields: [
{
name: 'allowPublicReadability',
label: 'Allow Public Readability',
type: 'checkbox',
},
{
name: 'arrayText1',
label: 'Array Text 1',
type: 'text',
required: true,
admin: {
width: '50%',
},
access: {
read: PublicReadabilityAccess,
},
},
{
name: 'arrayText2',
label: 'Array Text 2',
type: 'text',
required: true,
admin: {
width: '50%',
},
},
],
},
{
type: 'text',
name: 'arrayText3',
label: 'Array Text 3',
admin: {
readOnly: true,
},
},
],
},
],
timestamps: true,
};
export default LocalizedArrays;

View File

@@ -1,81 +0,0 @@
import { CollectionConfig, BeforeChangeHook } from '../../src/collections/config/types';
const checkForUploadSizesHook: BeforeChangeHook = ({ req: { payloadUploadSizes }, data }) => {
if (typeof payloadUploadSizes === 'object') {
return {
...data,
foundUploadSizes: true,
};
}
return data;
};
const Media: CollectionConfig = {
slug: 'media',
labels: {
singular: 'Media',
plural: 'Media',
},
access: {
read: () => true,
},
admin: {
enableRichTextRelationship: true,
description: 'No selfies please',
},
hooks: {
beforeChange: [
checkForUploadSizesHook,
],
},
upload: {
staticURL: '/media',
staticDir: './media',
adminThumbnail: ({ doc }) => `/media/${doc.filename}`,
imageSizes: [
{
name: 'maintainedAspectRatio',
width: 1024,
height: null,
crop: 'center',
},
{
name: 'tablet',
width: 640,
height: 480,
crop: 'left top',
},
{
name: 'mobile',
width: 320,
height: 240,
crop: 'left top',
},
{
name: 'icon',
width: 16,
height: 16,
},
],
staticOptions: {
maxAge: 21600000, // 6 hours in milliseconds
},
},
fields: [
{
name: 'alt',
label: 'Alt Text',
type: 'text',
required: true,
localized: true,
},
{
name: 'foundUploadSizes',
type: 'checkbox',
},
],
timestamps: true,
};
export default Media;

View File

@@ -1,71 +0,0 @@
import { CollectionConfig } from '../../src/collections/config/types';
const NestedArray: CollectionConfig = {
slug: 'nested-arrays',
labels: {
singular: 'Nested Array',
plural: 'Nested Arrays',
},
access: {
read: () => true,
},
fields: [
{
type: 'array',
label: 'Array',
name: 'array',
labels: {
singular: 'Parent Row',
plural: 'Parent Rows',
},
required: true,
minRows: 2,
maxRows: 4,
fields: [
{
name: 'parentIdentifier',
label: 'Parent Identifier',
defaultValue: ' ',
type: 'text',
required: true,
},
{
type: 'array',
name: 'nestedArray',
labels: {
singular: 'Child Row',
plural: 'Child Rows',
},
required: true,
fields: [
{
name: 'childIdentifier',
label: 'Child Identifier',
type: 'text',
required: true,
},
{
type: 'array',
name: 'deeplyNestedArray',
labels: {
singular: 'Grandchild Row',
plural: 'Grandchild Rows',
},
required: true,
fields: [
{
name: 'grandchildIdentifier',
label: 'Grandchild Identifier',
type: 'text',
},
],
},
],
},
],
},
],
timestamps: true,
};
export default NestedArray;

View File

@@ -1,37 +0,0 @@
import { CollectionConfig } from '../../src/collections/config/types';
const Preview: CollectionConfig = {
slug: 'previewable-post',
labels: {
singular: 'Previewable Post',
plural: 'Previewable Posts',
},
admin: {
useAsTitle: 'title',
preview: async (doc, { token }) => {
const { title } = doc;
if (title) {
const mockAsyncReq = await fetch(`http://localhost:3000/api/previewable-post?depth=0`)
const mockJSON = await mockAsyncReq.json();
const mockParam = mockJSON?.docs?.[0]?.title || '';
return `http://localhost:3000/previewable-posts/${title}?preview=true&token=${token}&mockParam=${mockParam}`;
}
return null;
},
},
fields: [
{
name: 'title',
label: 'Title',
type: 'text',
maxLength: 100,
required: true,
unique: true,
localized: true,
},
],
timestamps: true,
};
export default Preview;

View File

@@ -1,61 +0,0 @@
import checkRole from '../access/checkRole';
import { CollectionConfig } from '../../src/collections/config/types';
const access = ({ req: { user } }) => checkRole(['admin'], user);
const PublicUsers: CollectionConfig = {
slug: 'public-users',
labels: {
singular: 'Public User',
plural: 'Public Users',
},
admin: {
useAsTitle: 'email',
},
access: {
admin: () => false,
create: () => true,
read: () => true,
update: ({ req: { user } }) => {
if (checkRole(['admin'], user)) {
return true;
}
if (user) {
return {
id: user.id,
};
}
return false;
},
delete: ({ req: { user } }) => checkRole(['admin'], user),
},
auth: {
tokenExpiration: 300,
verify: true,
maxLoginAttempts: 5,
lockTime: 600 * 1000, // lock time in ms
cookies: {
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
domain: undefined,
},
},
fields: [
{
name: 'adminOnly',
label: 'This field should only be readable and editable by Admins with "admin" role',
type: 'text',
defaultValue: 'test',
access: {
create: access,
read: access,
update: access,
},
},
],
timestamps: true,
};
export default PublicUsers;

View File

@@ -1,86 +0,0 @@
import { CollectionConfig } from '../../src/collections/config/types';
const RelationshipA: CollectionConfig = {
slug: 'relationship-a',
access: {
read: () => true,
},
labels: {
singular: 'Relationship A',
plural: 'Relationship A',
},
fields: [
{
name: 'post',
label: 'Post',
type: 'relationship',
relationTo: 'relationship-b',
},
{
name: 'LocalizedPost',
label: 'Localized Post',
type: 'relationship',
relationTo: 'localized-posts',
hasMany: true,
localized: true,
},
{
name: 'postLocalizedMultiple',
label: 'Localized Post Multiple',
type: 'relationship',
relationTo: ['localized-posts', 'all-fields', 'custom-id'],
hasMany: true,
localized: true,
},
{
name: 'postManyRelationships',
label: 'Post Many Relationships',
type: 'relationship',
relationTo: ['relationship-b'],
localized: true,
hasMany: false,
},
{
name: 'postMaxDepth',
maxDepth: 0,
label: 'Post With MaxDepth',
type: 'relationship',
relationTo: 'relationship-b',
hasMany: false,
},
{
name: 'customID',
label: 'CustomID Relation',
type: 'relationship',
relationTo: 'custom-id',
hasMany: true,
localized: true,
},
{
name: 'filterRelationship',
type: 'relationship',
relationTo: 'relationship-b',
filterOptions: {
disableRelation: {
not_equals: true,
},
},
},
{
name: 'files',
type: 'upload',
relationTo: 'files',
filterOptions: {
type: { equals: 'Type 2' },
},
},
{
name: 'demoHiddenField',
type: 'text',
hidden: true,
},
],
timestamps: true,
};
export default RelationshipA;

View File

@@ -1,65 +0,0 @@
import { CollectionConfig } from '../../src/collections/config/types';
const RelationshipB: CollectionConfig = {
slug: 'relationship-b',
access: {
read: () => true,
},
admin: {
useAsTitle: 'title',
},
labels: {
singular: 'Relationship B',
plural: 'Relationship B',
},
fields: [
{
name: 'title',
type: 'text',
},
{
name: 'disableRelation', // used on RelationshipA.filterRelationship field
type: 'checkbox',
required: true,
admin: {
position: 'sidebar',
},
},
{
name: 'post',
label: 'Post',
type: 'relationship',
relationTo: 'relationship-a',
localized: false,
hasMany: true,
},
{
name: 'postManyRelationships',
label: 'Post Many Relationships',
type: 'relationship',
relationTo: ['relationship-a', 'media'],
localized: true,
hasMany: false,
},
{
name: 'localizedPosts',
label: 'Localized Posts',
type: 'relationship',
hasMany: true,
relationTo: ['localized-posts', 'previewable-post'],
},
{
name: 'nonLocalizedRelationToMany',
type: 'relationship',
relationTo: ['localized-posts', 'relationship-a'],
},
{
name: 'strictAccess',
type: 'relationship',
relationTo: 'strict-access',
},
],
timestamps: true,
};
export default RelationshipB;

View File

@@ -1,112 +0,0 @@
import Button from '../client/components/richText/elements/Button';
import PurpleBackground from '../client/components/richText/leaves/PurpleBackground';
import { CollectionConfig } from '../../src/collections/config/types';
const RichText: CollectionConfig = {
slug: 'rich-text',
labels: {
singular: 'Rich Text',
plural: 'Rich Texts',
},
access: {
read: ({ req }) => {
if (req.user) return true;
return {
_status: {
equals: 'published',
},
};
},
},
versions: {
drafts: {
autosave: false,
},
},
admin: {
preview: () => 'https://payloadcms.com',
useAsTitle: 'title',
},
fields: [
{
name: 'title',
type: 'text',
},
{
name: 'defaultRichText',
type: 'richText',
label: 'Default Rich Text',
required: true,
admin: {
upload: {
collections: {
media: {
fields: [
{
type: 'richText',
name: 'caption',
label: 'Caption',
},
{
type: 'row',
fields: [
{
type: 'relationship',
relationTo: 'admins',
name: 'linkToAdmin',
label: 'Link to Admin',
},
{
type: 'select',
name: 'imageAlignment',
label: 'Image Alignment',
options: [
{
label: 'Left',
value: 'left',
},
{
label: 'Center',
value: 'center',
},
{
label: 'Right',
value: 'right',
},
],
},
],
},
{
type: 'checkbox',
name: 'wrapText',
label: 'Wrap Text',
},
],
},
},
},
},
},
{
name: 'customRichText',
type: 'richText',
label: 'Customized Rich Text',
required: true,
admin: {
elements: [
'h2',
'h3',
Button,
],
leaves: [
'bold',
PurpleBackground,
],
},
},
],
};
export default RichText;

View File

@@ -1,99 +0,0 @@
import { CollectionConfig } from '../../src/collections/config/types';
const Select: CollectionConfig = {
slug: 'select',
labels: {
singular: 'Select',
plural: 'Selects',
},
fields: [
{
name: 'select',
type: 'select',
options: [{
value: 'one',
label: 'One',
}, {
value: 'two',
label: 'Two',
}, {
value: 'three',
label: 'Three',
}],
label: 'Select From',
required: true,
},
{
name: 'selectHasMany',
type: 'select',
options: [{
value: 'one',
label: 'One',
}, {
value: 'two',
label: 'Two',
}, {
value: 'three',
label: 'Three',
}],
label: 'Select HasMany',
required: true,
hasMany: true,
},
{
name: 'selectJustStrings',
type: 'select',
options: ['blue', 'green', 'yellow'],
label: 'Select Just Strings',
required: true,
hasMany: true,
},
{
name: 'selectWithEmptyString',
type: 'select',
defaultValue: '',
options: [{
value: '',
label: 'None',
}, {
value: 'option',
label: 'Option',
}],
required: true,
},
{
name: 'radio',
type: 'radio',
options: [{
value: 'one',
label: 'One',
}, {
value: 'two',
label: 'Two',
}, {
value: 'three',
label: 'Three',
}],
label: 'Choose From',
required: true,
},
{
name: 'radioWithEmptyString',
type: 'radio',
defaultValue: '',
options: [{
value: '',
label: 'None',
}, {
value: 'one',
label: 'One',
}, {
value: 'two',
label: 'Two',
}],
required: true,
},
],
};
export default Select;

View File

@@ -1,76 +0,0 @@
import { CollectionConfig } from '../../src/collections/config/types';
import checkRole from '../access/checkRole';
const StrictAccess: CollectionConfig = {
slug: 'strict-access',
labels: {
singular: 'Strict Access',
plural: 'Strict Access',
},
admin: {
useAsTitle: 'address',
},
access: {
create: () => true,
read: ({ req: { user } }) => {
if (checkRole(['admin'], user)) {
return true;
}
if (user) {
return {
owner: {
equals: user.id,
},
};
}
return false;
},
update: ({ req: { user } }) => {
if (checkRole(['admin'], user)) {
return true;
}
if (user) {
return {
owner: {
equals: user.id,
},
};
}
return false;
},
delete: ({ req: { user } }) => checkRole(['admin'], user),
},
fields: [
{
name: 'address',
type: 'text',
label: 'Address',
required: true,
},
{
name: 'city',
type: 'text',
label: 'City',
required: true,
},
{
name: 'state',
type: 'text',
label: 'State',
required: true,
},
{
name: 'zip',
type: 'number',
label: 'ZIP Code',
required: true,
},
],
timestamps: true,
};
export default StrictAccess;

View File

@@ -1,25 +0,0 @@
import { CollectionConfig } from '../../src/collections/config/types';
const Uniques: CollectionConfig = {
slug: 'uniques',
labels: {
singular: 'Unique',
plural: 'Uniques',
},
fields: [
{
name: 'title',
type: 'text',
label: 'Title',
required: true,
unique: true,
},
{
name: 'description',
type: 'textarea',
label: 'Description',
},
],
};
export default Uniques;

View File

@@ -1,35 +0,0 @@
import { CollectionConfig } from '../../src/collections/config/types';
const UnstoredMedia: CollectionConfig = {
slug: 'unstored-media',
labels: {
singular: 'Unstored Media',
plural: 'Unstored Media',
},
access: {
read: () => true,
},
upload: {
staticURL: '/unstored-media',
disableLocalStorage: true,
imageSizes: [
{
name: 'tablet',
width: 640,
height: 480,
crop: 'left top',
},
],
},
fields: [
{
name: 'alt',
label: 'Alt Text',
type: 'text',
required: true,
localized: true,
},
],
};
export default UnstoredMedia;

View File

@@ -1,141 +0,0 @@
import { CollectionConfig } from '../../src/collections/config/types';
const Validations: CollectionConfig = {
slug: 'validations',
labels: {
singular: 'Validation',
plural: 'Validations',
},
access: {
read: () => true,
},
fields: [
{
name: 'validationOptions',
type: 'text',
label: 'Text with siblingData Validation',
required: true,
validate: (value: string, { data, siblingData, id, operation, user }) => {
if (typeof value === 'undefined') {
return 'Validation is missing value';
}
if (data?.text !== 'test') {
return 'The next field should be test';
}
if (siblingData?.text !== 'test') {
return 'The next field should be test';
}
if (!user) {
return 'ValidationOptions is missing "user"';
}
if (typeof operation === 'undefined') {
return 'ValidationOptions is missing "operation"';
}
if (operation === 'update' && typeof id === 'undefined') {
return 'ValidationOptions is missing "id"';
}
return true;
},
},
{
name: 'text',
type: 'text',
label: 'Text',
required: true,
validate: (value) => {
const result = value === 'test';
if (!result) {
return 'The only accepted value of this field is "test".';
}
return true;
},
},
{
type: 'row',
fields: [
{
name: 'lessThan10',
label: 'Less than 10',
type: 'number',
required: true,
validate: (value) => {
const result = parseInt(value, 10) < 10;
if (!result) {
return 'The value of this field needs to be less than 10.';
}
return true;
},
}, {
name: 'greaterThan10LessThan50',
label: 'Greater than 10, Less than 50',
type: 'number',
required: true,
min: 10,
max: 50,
},
],
},
{
type: 'array',
label: 'Should have at least 3 rows',
name: 'atLeast3Rows',
required: true,
validate: (value) => {
const result = value >= 3;
if (!result) {
return 'This array needs to have at least 3 rows.';
}
return true;
},
fields: [
{
type: 'number',
name: 'greaterThan30',
label: 'Number should be greater than 30',
required: true,
validate: (value) => {
const result = value > 30;
if (!result) {
return 'This value of this field needs to be greater than 30.';
}
return true;
},
},
],
},
{
type: 'array',
label: 'Default array validation',
name: 'array',
required: true,
fields: [
{
type: 'number',
name: 'lessThan20',
label: 'Number should be less than 20',
required: true,
validate: (value) => {
const result = value < 20;
if (!result) {
return 'This value of this field needs to be less than 20.';
}
return true;
},
},
],
},
],
};
export default Validations;

View File

@@ -1,15 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="application-name" content="My Payload Application" />
</head>
<body>
<div id="app"></div>
<div id="portal"></div>
</body>
</html>

View File

@@ -1,9 +0,0 @@
import React from 'react';
const CollectionDescription: React.FC = () => (
<div>
Collection description
</div>
);
export default CollectionDescription;

View File

@@ -1,11 +0,0 @@
import React from 'react';
const CustomDescriptionComponent: React.FC = ({ value }) => (
<div>
Character count:
{' '}
{ value?.length || 0 }
</div>
);
export default CustomDescriptionComponent;

View File

@@ -1,5 +0,0 @@
import React from 'react';
const CustomAccountView: React.FC = () => <div>fake account view</div>;
export default CustomAccountView;

View File

@@ -1,5 +0,0 @@
import React from 'react';
const CustomDashboardView: React.FC = () => <div>fake dashboard view</div>;
export default CustomDashboardView;

View File

@@ -1,43 +0,0 @@
import checkRole from '../access/checkRole';
import Quote from '../blocks/Quote';
import CallToAction from '../blocks/CallToAction';
import { GlobalConfig } from '../../src/globals/config/types';
const BlocksGlobal: GlobalConfig = {
slug: 'blocks-global',
label: 'Blocks Global',
versions: {
max: 20,
drafts: {
autosave: true,
},
},
access: {
update: ({ req: { user } }) => checkRole(['admin'], user),
read: ({ draft, req: { user } }) => {
// To read a draft of this global, you need to be authenticated
if (draft) {
return Boolean(user);
}
return true;
},
},
fields: [
{
name: 'title',
type: 'text',
required: true,
localized: true,
},
{
name: 'blocks',
label: 'Blocks',
type: 'blocks',
blocks: [Quote, CallToAction],
localized: true,
},
],
};
export default BlocksGlobal;

View File

@@ -1,37 +0,0 @@
import { GlobalConfig } from '../../src/globals/config/types';
import checkRole from '../access/checkRole';
const GlobalWithAccess: GlobalConfig = {
slug: 'global-with-access',
label: 'Global with Strict Access',
access: {
update: ({ req: { user } }) => checkRole(['admin'], user),
read: ({ req: { user } }) => checkRole(['admin'], user),
},
fields: [
{
name: 'title',
label: 'Site Title',
type: 'text',
maxLength: 100,
required: true,
},
{
name: 'relationship',
label: 'Test Relationship',
type: 'relationship',
relationTo: 'localized-posts',
hasMany: true,
required: true,
},
{
name: 'singleRelationship',
label: 'Test Single Relationship',
type: 'relationship',
relationTo: 'localized-posts',
required: true,
},
],
};
export default GlobalWithAccess;

View File

@@ -1,48 +0,0 @@
import { Response } from 'express';
import { GlobalConfig } from '../../src/globals/config/types';
import checkRole from '../access/checkRole';
import { NavigationArray as TNavigationArray } from '../payload-types';
import { PayloadRequest } from '../../src/express/types';
const NavigationArray: GlobalConfig = {
slug: 'navigation-array',
access: {
update: ({ req: { user } }) => checkRole(['admin', 'user'], user),
read: () => true,
},
admin: {
description: 'A description for the editor',
},
endpoints: [
{
path: '/count',
method: 'get',
handler: async (req: PayloadRequest, res: Response): Promise<void> => {
const { array } = await req.payload.findGlobal<TNavigationArray>({
slug: 'navigation-array',
});
res.json({ count: array.length });
},
},
],
fields: [
{
name: 'array',
label: 'Array',
type: 'array',
localized: true,
fields: [{
name: 'text',
label: 'Text',
type: 'text',
}, {
name: 'textarea',
label: 'Textarea',
type: 'textarea',
}],
},
],
};
export default NavigationArray;

View File

@@ -1,14 +0,0 @@
const babelConfig = require('../babel.config');
require('@babel/register')({
...babelConfig,
extensions: ['.ts', '.tsx', '.js', '.jsx'],
env: {
development: {
sourceMaps: 'inline',
retainLines: true,
},
},
});
require('./server.ts');

View File

@@ -1,760 +0,0 @@
/* tslint:disable */
/**
* This file was automatically generated by Payload CMS.
* DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,
* and re-run `payload generate:types` to regenerate this file.
*/
export interface Config {}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "blocks-global".
*/
export interface BlocksGlobal {
id: string;
_status?: 'draft' | 'published';
title: string;
blocks?: (
| {
quote: string;
color: string;
id?: string;
blockName?: string;
blockType: 'quote';
}
| {
label: string;
url: string;
id?: string;
blockName?: string;
blockType: 'cta';
}
)[];
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "navigation-array".
*/
export interface NavigationArray {
id: string;
array?: {
text?: string;
textarea?: string;
id?: string;
}[];
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "global-with-access".
*/
export interface GlobalWithStrictAccess {
id: string;
title: string;
relationship: (string | LocalizedPost)[];
singleRelationship: string | LocalizedPost;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "localized-posts".
*/
export interface LocalizedPost {
id: string;
title: string;
summary?: string;
description: string;
richText?: {
[k: string]: unknown;
}[];
priority?: number;
localizedGroup?: {
text?: string;
demoHiddenField?: string;
};
nonLocalizedGroup?: {
text?: string;
};
nonLocalizedArray?: {
localizedEmbeddedText?: string;
id?: string;
}[];
richTextBlocks?: {
content?: {
[k: string]: unknown;
}[];
id?: string;
blockName?: string;
blockType: 'richTextBlock';
}[];
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "admins".
*/
export interface Admin {
id: string;
email?: string;
resetPasswordToken?: string;
resetPasswordExpiration?: string;
enableAPIKey?: boolean;
apiKey?: string;
apiKeyIndex?: string;
loginAttempts?: number;
lockUntil?: string;
roles: ('admin' | 'editor' | 'moderator' | 'user' | 'viewer')[];
publicUser?: (string | PublicUser)[];
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "public-users".
*/
export interface PublicUser {
id: string;
email?: string;
resetPasswordToken?: string;
resetPasswordExpiration?: string;
_verified?: boolean;
_verificationToken?: string;
loginAttempts?: number;
lockUntil?: string;
adminOnly?: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "all-fields".
*/
export interface AllFields {
id: string;
text: string;
descriptionText?: string;
descriptionFunction?: string;
image?: string | Media;
select: 'option-1' | 'option-2' | 'option-3' | 'option-4';
selectMany: ('option-1' | 'option-2' | 'option-3' | 'option-4')[];
dayOnlyDateFieldExample: string;
timeOnlyDateFieldExample?: string;
point?: [number, number];
radioGroupExample: 'option-1' | 'option-2' | 'option-3';
email?: string;
number?: number;
group?: {
nestedText1?: string;
nestedText2?: string;
};
array?: {
arrayText1: string;
arrayText2: string;
arrayText3?: string;
checkbox?: boolean;
id?: string;
}[];
readOnlyArray?: {
text?: string;
id?: string;
}[];
blocks: (
| {
testEmail: string;
id?: string;
blockName?: string;
blockType: 'email';
}
| {
testNumber: number;
id?: string;
blockName?: string;
blockType: 'number';
}
| {
quote: string;
color: string;
id?: string;
blockName?: string;
blockType: 'quote';
}
| {
label: string;
url: string;
id?: string;
blockName?: string;
blockType: 'cta';
}
)[];
relationship?: string | Conditions;
relationshipHasMany?: (string | LocalizedPost)[];
relationshipMultipleCollections?:
| {
value: string | LocalizedPost;
relationTo: 'localized-posts';
}
| {
value: string | Conditions;
relationTo: 'conditions';
};
textarea?: string;
richText: {
[k: string]: unknown;
}[];
slug: string;
checkbox?: boolean;
dateFieldExample?: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "media".
*/
export interface Media {
id: string;
url?: string;
filename?: string;
mimeType?: string;
filesize?: number;
width?: number;
height?: number;
sizes?: {
maintainedAspectRatio?: {
url?: string;
width?: number;
height?: number;
mimeType?: string;
filesize?: number;
filename?: string;
};
tablet?: {
url?: string;
width?: number;
height?: number;
mimeType?: string;
filesize?: number;
filename?: string;
};
mobile?: {
url?: string;
width?: number;
height?: number;
mimeType?: string;
filesize?: number;
filename?: string;
};
icon?: {
url?: string;
width?: number;
height?: number;
mimeType?: string;
filesize?: number;
filename?: string;
};
};
alt: string;
foundUploadSizes?: boolean;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "conditions".
*/
export interface Conditions {
id: string;
title: string;
enableTest?: boolean;
number?: number;
simpleCondition: string;
orCondition: string;
nestedConditions?: string;
blocks: (
| {
testEmail: string;
id?: string;
blockName?: string;
blockType: 'email';
}
| {
testNumber: number;
id?: string;
blockName?: string;
blockType: 'number';
}
| {
quote: string;
color: string;
id?: string;
blockName?: string;
blockType: 'quote';
}
| {
label: string;
url: string;
id?: string;
blockName?: string;
blockType: 'cta';
}
)[];
customComponent: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "auto-label".
*/
export interface AutoLabel {
id: string;
autoLabelField?: string;
noLabel?: string;
labelOverride?: string;
testRelationship?: string | AllFields;
specialBlock?: {
testNumber?: number;
id?: string;
blockName?: string;
blockType: 'number';
}[];
noLabelBlock?: {
testNumber?: number;
id?: string;
blockName?: string;
blockType: 'number';
}[];
items?: {
itemName?: string;
id?: string;
}[];
noLabelArray?: {
textField?: string;
id?: string;
}[];
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "autosave-posts".
*/
export interface AutosavePost {
id: string;
_status?: 'draft' | 'published';
title: string;
description: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "blocks".
*/
export interface Blocks {
id: string;
_status?: 'draft' | 'published';
layout: (
| {
testEmail: string;
id?: string;
blockName?: string;
blockType: 'email';
}
| {
testNumber: number;
id?: string;
blockName?: string;
blockType: 'number';
}
| {
quote: string;
color: string;
id?: string;
blockName?: string;
blockType: 'quote';
}
| {
label: string;
url: string;
id?: string;
blockName?: string;
blockType: 'cta';
}
)[];
nonLocalizedLayout: (
| {
testEmail: string;
id?: string;
blockName?: string;
blockType: 'email';
}
| {
testNumber: number;
id?: string;
blockName?: string;
blockType: 'number';
}
| {
quote: string;
color: string;
id?: string;
blockName?: string;
blockType: 'quote';
}
| {
label: string;
url: string;
id?: string;
blockName?: string;
blockType: 'cta';
}
)[];
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "code".
*/
export interface Code {
id: string;
code: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "custom-id".
*/
export interface CustomID {
id: number;
name: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "default-values".
*/
export interface DefaultValueTest {
id: string;
text?: string;
image?: string | Media;
select?: 'option-1' | 'option-2' | 'option-3' | 'option-4';
selectMany?: ('option-1' | 'option-2' | 'option-3' | 'option-4')[];
radioGroupExample?: 'option-1' | 'option-2' | 'option-3';
email?: string;
number?: number;
group?: {
nestedText1?: string;
nestedText2?: string;
nestedText3?: string;
};
array?: {
arrayText1?: string;
arrayText2?: string;
arrayText3?: string;
checkbox?: boolean;
id?: string;
}[];
blocks?: (
| {
testEmail: string;
id?: string;
blockName?: string;
blockType: 'email';
}
| {
testNumber: number;
id?: string;
blockName?: string;
blockType: 'number';
}
| {
quote: string;
color: string;
id?: string;
blockName?: string;
blockType: 'quote';
}
| {
label: string;
url: string;
id?: string;
blockName?: string;
blockType: 'cta';
}
)[];
relationship?: string | Conditions;
relationshipHasMany?: (string | LocalizedPost)[];
relationshipMultipleCollections?:
| {
value: string | LocalizedPost;
relationTo: 'localized-posts';
}
| {
value: string | Conditions;
relationTo: 'conditions';
};
textarea?: string;
slug?: string;
checkbox?: boolean;
richText?: {
[k: string]: unknown;
}[];
asyncArray?: {
child?: string;
id?: string;
}[];
asyncText?: string;
function?: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "endpoints".
*/
export interface Endpoint {
id: string;
title?: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "files".
*/
export interface File {
id: string;
url?: string;
filename?: string;
mimeType?: string;
filesize?: number;
type: 'Type 1' | 'Type 2' | 'Type 3';
owner: string | Admin;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "geolocation".
*/
export interface Geolocation {
id: string;
location?: [number, number];
localizedPoint?: [number, number];
group?: {
point?: [number, number];
};
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "hidden-fields".
*/
export interface HiddenFields {
id: string;
title: string;
hiddenAdmin: string;
hiddenAPI: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "hooks".
*/
export interface Hook {
id: string;
title: string;
description: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "localized-arrays".
*/
export interface LocalizedArray {
id: string;
array: {
allowPublicReadability?: boolean;
arrayText1: string;
arrayText2: string;
arrayText3?: string;
id?: string;
}[];
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "local-operations".
*/
export interface LocalOperation {
id: string;
title: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "images".
*/
export interface Image {
id: string;
upload?: string | Media;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "nested-arrays".
*/
export interface NestedArray {
id: string;
array: {
parentIdentifier: string;
nestedArray: {
childIdentifier: string;
deeplyNestedArray: {
grandchildIdentifier?: string;
id?: string;
}[];
id?: string;
}[];
id?: string;
}[];
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "previewable-post".
*/
export interface PreviewablePost {
id: string;
title: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "relationship-a".
*/
export interface RelationshipA {
id: string;
post?: string | RelationshipB;
LocalizedPost?: (string | LocalizedPost)[];
postLocalizedMultiple?: (
| {
value: string | LocalizedPost;
relationTo: 'localized-posts';
}
| {
value: string | AllFields;
relationTo: 'all-fields';
}
| {
value: number | CustomID;
relationTo: 'custom-id';
}
)[];
postManyRelationships?: {
value: string | RelationshipB;
relationTo: 'relationship-b';
};
postMaxDepth?: string | RelationshipB;
customID?: (number | CustomID)[];
filterRelationship?: string | RelationshipB;
files?: string | File;
demoHiddenField?: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "relationship-b".
*/
export interface RelationshipB {
id: string;
title?: string;
disableRelation: boolean;
post?: (string | RelationshipA)[];
postManyRelationships?:
| {
value: string | RelationshipA;
relationTo: 'relationship-a';
}
| {
value: string | Media;
relationTo: 'media';
};
localizedPosts?: (
| {
value: string | LocalizedPost;
relationTo: 'localized-posts';
}
| {
value: string | PreviewablePost;
relationTo: 'previewable-post';
}
)[];
nonLocalizedRelationToMany?:
| {
value: string | LocalizedPost;
relationTo: 'localized-posts';
}
| {
value: string | RelationshipA;
relationTo: 'relationship-a';
};
strictAccess?: string | StrictAccess;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "strict-access".
*/
export interface StrictAccess {
id: string;
address: string;
city: string;
state: string;
zip: number;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "rich-text".
*/
export interface RichText {
id: string;
_status?: 'draft' | 'published';
title?: string;
defaultRichText: {
[k: string]: unknown;
}[];
customRichText: {
[k: string]: unknown;
}[];
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "select".
*/
export interface Select {
id: string;
select: 'one' | 'two' | 'three';
selectHasMany: ('one' | 'two' | 'three')[];
selectJustStrings: ('blue' | 'green' | 'yellow')[];
selectWithEmptyString: '' | 'option';
radio: 'one' | 'two' | 'three';
radioWithEmptyString: '' | 'one' | 'two';
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "validations".
*/
export interface Validation {
id: string;
validationOptions: string;
text: string;
lessThan10: number;
greaterThan10LessThan50: number;
atLeast3Rows: {
greaterThan30: number;
id?: string;
}[];
array: {
lessThan20: number;
id?: string;
}[];
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "uniques".
*/
export interface Unique {
id: string;
title: string;
description?: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "unstored-media".
*/
export interface UnstoredMedia {
id: string;
url?: string;
filename?: string;
mimeType?: string;
filesize?: number;
width?: number;
height?: number;
sizes?: {
tablet?: {
url?: string;
width?: number;
height?: number;
mimeType?: string;
filesize?: number;
filename?: string;
};
};
alt: string;
}

View File

@@ -1,181 +0,0 @@
import path from 'path';
import { buildConfig } from '../src/config/build';
import Admin from './collections/Admin';
import AllFields from './collections/AllFields';
import AutoLabel from './collections/AutoLabel';
import Autosave from './collections/Autosave';
import Code from './collections/Code';
import Conditions from './collections/Conditions';
// import CustomComponents from './collections/CustomComponents';
import Endpoints from './collections/Endpoints';
import File from './collections/File';
import Blocks from './collections/Blocks';
import CustomID from './collections/CustomID';
import DefaultValues from './collections/DefaultValues';
import HiddenFields from './collections/HiddenFields';
import Hooks from './collections/Hooks';
import Localized from './collections/Localized';
import LocalizedArray from './collections/LocalizedArray';
import LocalOperations from './collections/LocalOperations';
import Media from './collections/Media';
import Images from './collections/Images';
import NestedArrays from './collections/NestedArrays';
import Preview from './collections/Preview';
import PublicUsers from './collections/PublicUsers';
import RelationshipA from './collections/RelationshipA';
import RelationshipB from './collections/RelationshipB';
import RichText from './collections/RichText';
import Select from './collections/Select';
import StrictAccess from './collections/StrictAccess';
import Validations from './collections/Validations';
import Uniques from './collections/Uniques';
import Geolocation from './collections/Geolocation';
import BlocksGlobal from './globals/BlocksGlobal';
import NavigationArray from './globals/NavigationArray';
import GlobalWithStrictAccess from './globals/GlobalWithStrictAccess';
import UnstoredMedia from './collections/UnstoredMedia';
import CustomRouteWithMinimalTemplate from './client/components/views/CustomMinimal';
import CustomRouteWithDefaultTemplate from './client/components/views/CustomDefault';
import AfterDashboard from './client/components/AfterDashboard';
import AfterNavLinks from './client/components/AfterNavLinks';
import BeforeLogin from './client/components/BeforeLogin';
// import CustomProvider from './client/components/CustomProvider';
export default buildConfig({
cookiePrefix: 'payload',
serverURL: 'http://localhost:3000',
typescript: {
outputFile: path.resolve(__dirname, './payload-types.ts'),
},
admin: {
user: 'admins',
indexHTML: path.resolve(__dirname, './client/index.html'),
// meta: {
// titleSuffix: '- Payload Demo',
// // ogImage: '/static/find-image-here.jpg',
// // favicon: '/img/whatever.png',
// },
// disable: true,
scss: path.resolve(__dirname, './client/scss/overrides.scss'),
components: {
// providers: [CustomProvider, CustomProvider],
routes: [
{
path: '/custom-minimal-route',
Component: CustomRouteWithMinimalTemplate,
},
{
path: '/custom-default-route',
Component: CustomRouteWithDefaultTemplate,
},
],
afterDashboard: [
AfterDashboard,
],
beforeLogin: [
BeforeLogin,
],
afterNavLinks: [
AfterNavLinks,
],
// Nav: () => (
// <div>Hello</div>
// ),
views: {
// Dashboard: CustomDashboardView,
// Account: CustomAccountView,
},
},
webpack: (config) => config,
},
collections: [
Admin,
AllFields,
AutoLabel,
Autosave,
Blocks,
Code,
Conditions,
// CustomComponents,
CustomID,
DefaultValues,
Endpoints,
File,
Geolocation,
HiddenFields,
Hooks,
Localized,
LocalizedArray,
LocalOperations,
Media,
Images,
NestedArrays,
Preview,
PublicUsers,
RelationshipA,
RelationshipB,
RichText,
Select,
StrictAccess,
Validations,
Uniques,
UnstoredMedia,
],
globals: [
BlocksGlobal,
NavigationArray,
GlobalWithStrictAccess,
],
// cors: [
// 'http://localhost',
// 'http://localhost:3000',
// 'http://localhost:8080',
// 'http://localhost:8081',
// ],
// csrf: [
// 'http://localhost:3000',
// 'https://other-app-here.com',
// ],
routes: {
api: '/api',
admin: '/admin',
graphQL: '/graphql',
graphQLPlayground: '/graphql-playground',
},
defaultDepth: 2,
graphQL: {
maxComplexity: 1000,
disablePlaygroundInProduction: false,
disable: false,
},
// rateLimit: {
// window: 15 * 60 * 100,
// max: 100,
// trustProxy: true,
// skip: (req) => req.ip === '127.0.0.1',
// },
maxDepth: 10,
localization: {
locales: [
'en',
'es',
],
defaultLocale: 'en',
fallback: true,
},
// indexSortableFields: true,
hooks: {
afterError: (err) => {
if (process.env.DISABLE_LOGGING !== 'true') {
console.error('global error config handler', err);
}
},
},
upload: {
limits: {
fileSize: 10000000, // 10MB
},
},
});

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