@cooperco/cooper-component-library 0.1.68 → 0.1.71

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,76 @@
1
+ module.exports = {
2
+ // @ts-check
3
+ /** @type { import('contentful-migration').MigrationFunction } */
4
+ up: function (migration) {
5
+ const contentModule = migration.editContentType('contentModule')
6
+
7
+ // Add image field (appears below body copy)
8
+ contentModule
9
+ .createField('image')
10
+ .name('Image')
11
+ .type('Link')
12
+ .linkType('Entry')
13
+ .required(false)
14
+ .validations([{ linkContentType: ['image'] }])
15
+ contentModule.changeFieldControl('image', 'builtin', 'entryLinkEditor', {
16
+ helpText: 'Image that appears below the body copy text',
17
+ })
18
+ contentModule.moveField('image').afterField('bodyCopy')
19
+
20
+ // Update bodyCopy field to enable tables and embedded testimonials
21
+ contentModule.editField('bodyCopy').validations([
22
+ {
23
+ enabledMarks: ['bold', 'italic', 'underline'],
24
+ message: 'Only bold, italic, and underline marks are allowed',
25
+ },
26
+ {
27
+ enabledNodeTypes: [
28
+ 'ordered-list',
29
+ 'unordered-list',
30
+ 'hr',
31
+ 'hyperlink',
32
+ 'embedded-entry-inline',
33
+ 'table',
34
+ ],
35
+ },
36
+ {
37
+ nodes: {
38
+ 'embedded-entry-inline': [
39
+ {
40
+ linkContentType: ['testimonialModule'],
41
+ message: 'You can embed Testimonial entries.',
42
+ },
43
+ ],
44
+ },
45
+ },
46
+ ])
47
+ },
48
+
49
+ // @ts-check
50
+ /** @type { import('contentful-migration').MigrationFunction } */
51
+ down: async function (migration) {
52
+ const contentModule = migration.editContentType('contentModule')
53
+
54
+ // Remove image field
55
+ contentModule.deleteField('image')
56
+
57
+ // Revert bodyCopy field to original validations (without tables and testimonials)
58
+ contentModule.editField('bodyCopy').validations([
59
+ {
60
+ enabledMarks: ['bold', 'italic', 'underline'],
61
+ message: 'Only bold, italic, and underline marks are allowed',
62
+ },
63
+ {
64
+ enabledNodeTypes: [
65
+ 'ordered-list',
66
+ 'unordered-list',
67
+ 'hr',
68
+ 'hyperlink',
69
+ ],
70
+ },
71
+ {
72
+ nodes: {},
73
+ },
74
+ ])
75
+ },
76
+ }
@@ -1,42 +1,112 @@
1
- # Migrations
1
+ # GraphQL Queries
2
2
 
3
- ## Prerequisites
3
+ This directory contains GraphQL queries for fetching content from Contentful CMS.
4
4
 
5
- - Run `pnpm install`
6
- - Obtain a Contentful CMA token
7
- - Confirm and double check what space and environment you should be running these in
8
- - Run `pnpm exec contentful-cli-migrations --environment-id <your-env-name> --space-id <space-id> --management-token <CMA-TOKEN> --initialise` on environment if not already and update `.env`
5
+ ## Directory Structure
9
6
 
7
+ ```
8
+ cms/contentful/queries/
9
+ ├── index.ts # Barrel export file (manual updates required)
10
+ ├── fragments.ts # Shared GraphQL fragments
11
+ ├── *.query.ts # Individual component queries
12
+ └── README.md # This file
13
+ ```
10
14
 
11
- ## Helpful Docs
15
+ ## How Queries Are Exported
12
16
 
13
- - [Contentful Migration](https://github.com/contentful/contentful-migration)
14
- - [Contentful Migration Reference Docs](https://github.com/contentful/contentful-migration/blob/main/README.md#reference-documentation)
15
- - [Contentful Data Model](https://www.contentful.com/developers/docs/concepts/data-model/)
17
+ The query export system in this project is **semi-automatic**:
16
18
 
17
- ### Instructions
19
+ ### The Process
18
20
 
19
- - Create your migration file in the scripts folder with the following format: `0002-description.cjs`
20
- - Write your migrations
21
- - This should be with an up and a down function
22
- - The up function updates the model
23
- - The down function reversts your changes
24
- - Run your migration on a test environment with the following command: `pnpm exec contentful-cli-migrations --environment-id <your-env-name> --space-id <space-id> --management-token <CMA-TOKEN>`
25
- - Optional Flags:
26
- - `--rollback`: Rolls back one migration.
21
+ 1. **Individual Query Files** - Queries are defined in individual files with the pattern `*.query.ts`
22
+ - Example: `accordion.query.ts`, `carousel.query.ts`
27
23
 
28
- ### Gotchas
24
+ 2. **Manual Barrel Export** - Queries are re-exported through `index.ts`
25
+ - Uses wildcard exports: `export * from './accordion.query.js'`
26
+ - **Note:** Exports use `.js` extensions even though source files are `.ts` (ESM compatibility)
29
27
 
30
- - Currently the rollback may change the version counter even if the migration doesnt run.
31
- - for this just update the counter to the correct counter in contentful and rerun.
32
- - The migration may run and create a content type even if the whole migration doesnt run correctly.
33
- - To fix you will need to delete the content type and re-run
28
+ 3. **Package-Level Export** - The barrel export is exposed via `package.json`:
29
+ ```json
30
+ "./cms/contentful/graphql": {
31
+ "import": "./dist/cms/contentful/queries/index.js",
32
+ "types": "./dist/cms/contentful/queries/index.d.ts"
33
+ }
34
+ ```
34
35
 
35
- ### TODO
36
+ ## Creating a New Query
36
37
 
37
- - Fix rollback versioning
38
- - This flag should cause a rollback to that specific version, in this case 0001 `--version 0001`
39
- - It should be used with `--rollback`
40
- - It should update dthe versionTracker entry on successful rollback
41
- - Run migration error handling
42
- - runMigration is currently not working as expected and bumping the counter when the migration was aborted
38
+ ### Steps Required
39
+
40
+ 1. **Create Query File** - Add your query file: `yourComponent.query.ts`
41
+
42
+ 2. **Define Query** - Follow the established pattern:
43
+ ```typescript
44
+ import { gql } from 'graphql-tag'
45
+ import type { DocumentNode } from 'graphql'
46
+ import { someFragment } from './fragments.js' // If needed
47
+
48
+ export const getYourComponent: DocumentNode = gql`
49
+ ${someFragment} // Include fragments if needed
50
+ query yourComponentQuery($id: String!, $preview: Boolean = false) {
51
+ yourComponent(id: $id, preview: $preview) {
52
+ __typename
53
+ # Add your fields here
54
+ }
55
+ }
56
+ `
57
+ ```
58
+
59
+ 3. **Update Barrel Export** - **MANUALLY ADD** export to `index.ts`:
60
+ ```typescript
61
+ export * from './yourComponent.query.js'
62
+ ```
63
+ - Maintain alphabetical order for consistency
64
+ - Use `.js` extension (not `.ts`)
65
+
66
+ ### Query Naming Conventions
67
+
68
+ - **File Name:** `componentName.query.ts` (camelCase)
69
+ - **Export Name:** `getComponentName` (camelCase with `get` prefix)
70
+ - **Query Name:** `componentNameQuery` (camelCase with `Query` suffix)
71
+
72
+ ### Example
73
+
74
+ ```typescript
75
+ // accordion.query.ts
76
+ import { gql } from 'graphql-tag'
77
+ import type { DocumentNode } from 'graphql'
78
+ import { accordionItemFragment } from './fragments.js'
79
+
80
+ export const getAccordion: DocumentNode = gql`
81
+ ${accordionItemFragment}
82
+ query accordionQuery($id: String!, $preview: Boolean = false) {
83
+ accordion(id: $id, preview: $preview) {
84
+ __typename
85
+ headline
86
+ accordionType
87
+ loadMoreButtonTitle
88
+ startOpen
89
+ accordionItemCollection {
90
+ items {
91
+ ...accordionItemFragment
92
+ }
93
+ }
94
+ }
95
+ }
96
+ `
97
+ ```
98
+
99
+ ## Using Queries in Consumer Projects
100
+
101
+ Queries are consumed via the package export:
102
+
103
+ ```typescript
104
+ import { getAccordion, getCarousel } from '@cooperco/cooper-component-library/cms/contentful/graphql'
105
+ ```
106
+
107
+ ## Important Notes
108
+
109
+ - **Manual Export Required:** The system is NOT fully automatic - you MUST manually update `index.ts` when adding new queries
110
+ - **ESM Extensions:** Always use `.js` extensions in exports (even for `.ts` source files)
111
+ - **Preview Mode:** All queries support a `preview` parameter for Contentful preview API
112
+ - **Fragments:** Shared fragments are defined in `fragments.ts` and can be reused across queries
@@ -1,16 +1,20 @@
1
1
  import { gql } from 'graphql-tag'
2
2
  import type { DocumentNode } from 'graphql'
3
- import { imageFragment, ctaFragment } from './fragments.js'
3
+ import { imageFragment, ctaFragment, videoFragment } from './fragments.js'
4
4
 
5
5
  export const getContentModule: DocumentNode = gql`
6
6
  ${imageFragment}
7
7
  ${ctaFragment}
8
+ ${videoFragment}
8
9
  query contentModuleQuery($id: String!, $preview: Boolean = false) {
9
10
  contentModule(id: $id, preview: $preview) {
10
11
  alignment
11
12
  logo {
12
13
  ...imageFragment
13
14
  }
15
+ image {
16
+ ...imageFragment
17
+ }
14
18
  subHeadline
15
19
  headline {
16
20
  json
@@ -35,6 +39,20 @@ export const getContentModule: DocumentNode = gql`
35
39
  sys {
36
40
  id
37
41
  }
42
+ ... on TestimonialModule {
43
+ headline
44
+ quote
45
+ author
46
+ details
47
+ media {
48
+ ... on Image {
49
+ ...imageFragment
50
+ }
51
+ ... on Video {
52
+ ...videoFragment
53
+ }
54
+ }
55
+ }
38
56
  }
39
57
  }
40
58
  }
@@ -0,0 +1,76 @@
1
+ module.exports = {
2
+ // @ts-check
3
+ /** @type { import('contentful-migration').MigrationFunction } */
4
+ up: function (migration) {
5
+ const contentModule = migration.editContentType('contentModule')
6
+
7
+ // Add image field (appears below body copy)
8
+ contentModule
9
+ .createField('image')
10
+ .name('Image')
11
+ .type('Link')
12
+ .linkType('Entry')
13
+ .required(false)
14
+ .validations([{ linkContentType: ['image'] }])
15
+ contentModule.changeFieldControl('image', 'builtin', 'entryLinkEditor', {
16
+ helpText: 'Image that appears below the body copy text',
17
+ })
18
+ contentModule.moveField('image').afterField('bodyCopy')
19
+
20
+ // Update bodyCopy field to enable tables and embedded testimonials
21
+ contentModule.editField('bodyCopy').validations([
22
+ {
23
+ enabledMarks: ['bold', 'italic', 'underline'],
24
+ message: 'Only bold, italic, and underline marks are allowed',
25
+ },
26
+ {
27
+ enabledNodeTypes: [
28
+ 'ordered-list',
29
+ 'unordered-list',
30
+ 'hr',
31
+ 'hyperlink',
32
+ 'embedded-entry-inline',
33
+ 'table',
34
+ ],
35
+ },
36
+ {
37
+ nodes: {
38
+ 'embedded-entry-inline': [
39
+ {
40
+ linkContentType: ['testimonialModule'],
41
+ message: 'You can embed Testimonial entries.',
42
+ },
43
+ ],
44
+ },
45
+ },
46
+ ])
47
+ },
48
+
49
+ // @ts-check
50
+ /** @type { import('contentful-migration').MigrationFunction } */
51
+ down: async function (migration) {
52
+ const contentModule = migration.editContentType('contentModule')
53
+
54
+ // Remove image field
55
+ contentModule.deleteField('image')
56
+
57
+ // Revert bodyCopy field to original validations (without tables and testimonials)
58
+ contentModule.editField('bodyCopy').validations([
59
+ {
60
+ enabledMarks: ['bold', 'italic', 'underline'],
61
+ message: 'Only bold, italic, and underline marks are allowed',
62
+ },
63
+ {
64
+ enabledNodeTypes: [
65
+ 'ordered-list',
66
+ 'unordered-list',
67
+ 'hr',
68
+ 'hyperlink',
69
+ ],
70
+ },
71
+ {
72
+ nodes: {},
73
+ },
74
+ ])
75
+ },
76
+ }
@@ -1,14 +1,18 @@
1
1
  import { gql } from 'graphql-tag';
2
- import { imageFragment, ctaFragment } from './fragments.js';
2
+ import { imageFragment, ctaFragment, videoFragment } from './fragments.js';
3
3
  export const getContentModule = gql `
4
4
  ${imageFragment}
5
5
  ${ctaFragment}
6
+ ${videoFragment}
6
7
  query contentModuleQuery($id: String!, $preview: Boolean = false) {
7
8
  contentModule(id: $id, preview: $preview) {
8
9
  alignment
9
10
  logo {
10
11
  ...imageFragment
11
12
  }
13
+ image {
14
+ ...imageFragment
15
+ }
12
16
  subHeadline
13
17
  headline {
14
18
  json
@@ -33,6 +37,20 @@ export const getContentModule = gql `
33
37
  sys {
34
38
  id
35
39
  }
40
+ ... on TestimonialModule {
41
+ headline
42
+ quote
43
+ author
44
+ details
45
+ media {
46
+ ... on Image {
47
+ ...imageFragment
48
+ }
49
+ ... on Video {
50
+ ...videoFragment
51
+ }
52
+ }
53
+ }
36
54
  }
37
55
  }
38
56
  }
@@ -1,16 +1,20 @@
1
1
  import { gql } from 'graphql-tag'
2
2
  import type { DocumentNode } from 'graphql'
3
- import { imageFragment, ctaFragment } from './fragments.js'
3
+ import { imageFragment, ctaFragment, videoFragment } from './fragments.js'
4
4
 
5
5
  export const getContentModule: DocumentNode = gql`
6
6
  ${imageFragment}
7
7
  ${ctaFragment}
8
+ ${videoFragment}
8
9
  query contentModuleQuery($id: String!, $preview: Boolean = false) {
9
10
  contentModule(id: $id, preview: $preview) {
10
11
  alignment
11
12
  logo {
12
13
  ...imageFragment
13
14
  }
15
+ image {
16
+ ...imageFragment
17
+ }
14
18
  subHeadline
15
19
  headline {
16
20
  json
@@ -35,6 +39,20 @@ export const getContentModule: DocumentNode = gql`
35
39
  sys {
36
40
  id
37
41
  }
42
+ ... on TestimonialModule {
43
+ headline
44
+ quote
45
+ author
46
+ details
47
+ media {
48
+ ... on Image {
49
+ ...imageFragment
50
+ }
51
+ ... on Video {
52
+ ...videoFragment
53
+ }
54
+ }
55
+ }
38
56
  }
39
57
  }
40
58
  }
@@ -18,6 +18,7 @@ export const getTestimonialModule = gql `
18
18
  ...videoFragment
19
19
  }
20
20
  }
21
+ mediaPosition
21
22
  }
22
23
  }
23
24
  `;
@@ -20,6 +20,7 @@ export const getTestimonialModule: DocumentNode = gql`
20
20
  ...videoFragment
21
21
  }
22
22
  }
23
+ mediaPosition
23
24
  }
24
25
  }
25
26
  `
@@ -0,0 +1,76 @@
1
+ module.exports = {
2
+ // @ts-check
3
+ /** @type { import('contentful-migration').MigrationFunction } */
4
+ up: function (migration) {
5
+ const contentModule = migration.editContentType('contentModule')
6
+
7
+ // Add image field (appears below body copy)
8
+ contentModule
9
+ .createField('image')
10
+ .name('Image')
11
+ .type('Link')
12
+ .linkType('Entry')
13
+ .required(false)
14
+ .validations([{ linkContentType: ['image'] }])
15
+ contentModule.changeFieldControl('image', 'builtin', 'entryLinkEditor', {
16
+ helpText: 'Image that appears below the body copy text',
17
+ })
18
+ contentModule.moveField('image').afterField('bodyCopy')
19
+
20
+ // Update bodyCopy field to enable tables and embedded testimonials
21
+ contentModule.editField('bodyCopy').validations([
22
+ {
23
+ enabledMarks: ['bold', 'italic', 'underline'],
24
+ message: 'Only bold, italic, and underline marks are allowed',
25
+ },
26
+ {
27
+ enabledNodeTypes: [
28
+ 'ordered-list',
29
+ 'unordered-list',
30
+ 'hr',
31
+ 'hyperlink',
32
+ 'embedded-entry-inline',
33
+ 'table',
34
+ ],
35
+ },
36
+ {
37
+ nodes: {
38
+ 'embedded-entry-inline': [
39
+ {
40
+ linkContentType: ['testimonialModule'],
41
+ message: 'You can embed Testimonial entries.',
42
+ },
43
+ ],
44
+ },
45
+ },
46
+ ])
47
+ },
48
+
49
+ // @ts-check
50
+ /** @type { import('contentful-migration').MigrationFunction } */
51
+ down: async function (migration) {
52
+ const contentModule = migration.editContentType('contentModule')
53
+
54
+ // Remove image field
55
+ contentModule.deleteField('image')
56
+
57
+ // Revert bodyCopy field to original validations (without tables and testimonials)
58
+ contentModule.editField('bodyCopy').validations([
59
+ {
60
+ enabledMarks: ['bold', 'italic', 'underline'],
61
+ message: 'Only bold, italic, and underline marks are allowed',
62
+ },
63
+ {
64
+ enabledNodeTypes: [
65
+ 'ordered-list',
66
+ 'unordered-list',
67
+ 'hr',
68
+ 'hyperlink',
69
+ ],
70
+ },
71
+ {
72
+ nodes: {},
73
+ },
74
+ ])
75
+ },
76
+ }
@@ -1,16 +1,20 @@
1
1
  import { gql } from 'graphql-tag'
2
2
  import type { DocumentNode } from 'graphql'
3
- import { imageFragment, ctaFragment } from './fragments.js'
3
+ import { imageFragment, ctaFragment, videoFragment } from './fragments.js'
4
4
 
5
5
  export const getContentModule: DocumentNode = gql`
6
6
  ${imageFragment}
7
7
  ${ctaFragment}
8
+ ${videoFragment}
8
9
  query contentModuleQuery($id: String!, $preview: Boolean = false) {
9
10
  contentModule(id: $id, preview: $preview) {
10
11
  alignment
11
12
  logo {
12
13
  ...imageFragment
13
14
  }
15
+ image {
16
+ ...imageFragment
17
+ }
14
18
  subHeadline
15
19
  headline {
16
20
  json
@@ -35,6 +39,20 @@ export const getContentModule: DocumentNode = gql`
35
39
  sys {
36
40
  id
37
41
  }
42
+ ... on TestimonialModule {
43
+ headline
44
+ quote
45
+ author
46
+ details
47
+ media {
48
+ ... on Image {
49
+ ...imageFragment
50
+ }
51
+ ... on Video {
52
+ ...videoFragment
53
+ }
54
+ }
55
+ }
38
56
  }
39
57
  }
40
58
  }
@@ -20,6 +20,7 @@ export const getTestimonialModule: DocumentNode = gql`
20
20
  ...videoFragment
21
21
  }
22
22
  }
23
+ mediaPosition
23
24
  }
24
25
  }
25
26
  `
@@ -0,0 +1,76 @@
1
+ module.exports = {
2
+ // @ts-check
3
+ /** @type { import('contentful-migration').MigrationFunction } */
4
+ up: function (migration) {
5
+ const contentModule = migration.editContentType('contentModule')
6
+
7
+ // Add image field (appears below body copy)
8
+ contentModule
9
+ .createField('image')
10
+ .name('Image')
11
+ .type('Link')
12
+ .linkType('Entry')
13
+ .required(false)
14
+ .validations([{ linkContentType: ['image'] }])
15
+ contentModule.changeFieldControl('image', 'builtin', 'entryLinkEditor', {
16
+ helpText: 'Image that appears below the body copy text',
17
+ })
18
+ contentModule.moveField('image').afterField('bodyCopy')
19
+
20
+ // Update bodyCopy field to enable tables and embedded testimonials
21
+ contentModule.editField('bodyCopy').validations([
22
+ {
23
+ enabledMarks: ['bold', 'italic', 'underline'],
24
+ message: 'Only bold, italic, and underline marks are allowed',
25
+ },
26
+ {
27
+ enabledNodeTypes: [
28
+ 'ordered-list',
29
+ 'unordered-list',
30
+ 'hr',
31
+ 'hyperlink',
32
+ 'embedded-entry-inline',
33
+ 'table',
34
+ ],
35
+ },
36
+ {
37
+ nodes: {
38
+ 'embedded-entry-inline': [
39
+ {
40
+ linkContentType: ['testimonialModule'],
41
+ message: 'You can embed Testimonial entries.',
42
+ },
43
+ ],
44
+ },
45
+ },
46
+ ])
47
+ },
48
+
49
+ // @ts-check
50
+ /** @type { import('contentful-migration').MigrationFunction } */
51
+ down: async function (migration) {
52
+ const contentModule = migration.editContentType('contentModule')
53
+
54
+ // Remove image field
55
+ contentModule.deleteField('image')
56
+
57
+ // Revert bodyCopy field to original validations (without tables and testimonials)
58
+ contentModule.editField('bodyCopy').validations([
59
+ {
60
+ enabledMarks: ['bold', 'italic', 'underline'],
61
+ message: 'Only bold, italic, and underline marks are allowed',
62
+ },
63
+ {
64
+ enabledNodeTypes: [
65
+ 'ordered-list',
66
+ 'unordered-list',
67
+ 'hr',
68
+ 'hyperlink',
69
+ ],
70
+ },
71
+ {
72
+ nodes: {},
73
+ },
74
+ ])
75
+ },
76
+ }
@@ -20,6 +20,7 @@ export const getTestimonialModule: DocumentNode = gql`
20
20
  ...videoFragment
21
21
  }
22
22
  }
23
+ mediaPosition
23
24
  }
24
25
  }
25
26
  `