@graphcommerce/hygraph-dynamic-rows 6.2.0-canary.8

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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ # @graphcommerce/hygraph-dynamic-rows
2
+
3
+ ## 6.2.0-canary.8
4
+
5
+ ### Minor Changes
6
+
7
+ - [#1912](https://github.com/graphcommerce-org/graphcommerce/pull/1912) [`a43d389e9`](https://github.com/graphcommerce-org/graphcommerce/commit/a43d389e956fe69b73238b12c98c781b7044e4bb) - Added dynamic rows feature and better performance for Hygraph ([@JoshuaS98](https://github.com/JoshuaS98))
@@ -0,0 +1,11 @@
1
+ query AllDynamicRows {
2
+ dynamicRows(first: 1000) {
3
+ id
4
+ conditions {
5
+ __typename
6
+ ...ConditionNumber
7
+ ...ConditionText
8
+ ...ConditionAnd
9
+ }
10
+ }
11
+ }
@@ -0,0 +1,29 @@
1
+ fragment ConditionNumber on ConditionNumber {
2
+ __typename
3
+ property
4
+ operator
5
+ numberValue: value
6
+ }
7
+
8
+ fragment ConditionText on ConditionText {
9
+ __typename
10
+ property
11
+ stringValue: value
12
+ }
13
+
14
+ fragment ConditionOr on ConditionOr {
15
+ __typename
16
+ conditions {
17
+ ...ConditionNumber
18
+ ...ConditionText
19
+ }
20
+ }
21
+
22
+ fragment ConditionAnd on ConditionAnd {
23
+ __typename
24
+ conditions {
25
+ ...ConditionNumber
26
+ ...ConditionText
27
+ ...ConditionOr
28
+ }
29
+ }
@@ -0,0 +1,19 @@
1
+ fragment DynamicRow on DynamicRow @injectable {
2
+ placement
3
+ target {
4
+ ... on Node {
5
+ id
6
+ }
7
+ }
8
+
9
+ row {
10
+ __typename
11
+ ... on Node {
12
+ id
13
+ }
14
+
15
+ ...RowColumnOne
16
+ ...RowQuote
17
+ ...RowLinks
18
+ }
19
+ }
@@ -0,0 +1,5 @@
1
+ query DynamicRows($rowIds: [ID!]!) {
2
+ dynamicRows(where: { id_in: $rowIds }) {
3
+ ...DynamicRow
4
+ }
5
+ }
@@ -0,0 +1,4 @@
1
+ export * from './AllDynamicRows.gql'
2
+ export * from './Conditions.gql'
3
+ export * from './DynamicRow.gql'
4
+ export * from './DynamicRows.gql'
@@ -0,0 +1,145 @@
1
+ /**
2
+ * - Boven de product description zetten? in rowrenderer zetten
3
+ * - Hoe gaan we dit optioneel maken?
4
+ * - Hoe gaan we dit upgradebaar maken? management sdk
5
+ */
6
+
7
+ import { HygraphPagesQuery } from '@graphcommerce/graphcms-ui'
8
+ import { ApolloClient, NormalizedCacheObject } from '@graphcommerce/graphql'
9
+ import {
10
+ AllDynamicRowsDocument,
11
+ ConditionTextFragment,
12
+ ConditionNumberFragment,
13
+ ConditionOrFragment,
14
+ ConditionAndFragment,
15
+ DynamicRowsDocument,
16
+ } from '../graphql'
17
+
18
+ /**
19
+ * This generally works the same way as lodash get, however, when encountering an array it will
20
+ * return all values.
21
+ */
22
+ function getByPath(
23
+ value: unknown,
24
+ query: string | Array<string | number>,
25
+ ): (undefined | string | number | bigint)[] {
26
+ const splitQuery = Array.isArray(query)
27
+ ? query
28
+ : query
29
+ .replace(/(\[(\d)\])/g, '.$2')
30
+ .replace(/^\./, '')
31
+ .split('.')
32
+
33
+ if (!splitQuery.length || splitQuery[0] === undefined)
34
+ return [value as undefined | string | number | bigint]
35
+
36
+ const key = splitQuery[0]
37
+
38
+ if (Array.isArray(value)) {
39
+ return value.map((item) => getByPath(item, splitQuery)).flat(1000)
40
+ }
41
+
42
+ if (
43
+ typeof value !== 'object' ||
44
+ value === null ||
45
+ !(key in value) ||
46
+ (value as Record<string | number, unknown>)[key] === undefined
47
+ ) {
48
+ return [undefined]
49
+ }
50
+
51
+ const res = getByPath((value as Record<string | number, unknown>)[key], splitQuery.slice(1))
52
+ return Array.isArray(res) ? res : [res]
53
+ }
54
+
55
+ /** A recursive match function that is able to match a condition against the requested conditions. */
56
+ function matchCondition(
57
+ condition:
58
+ | ConditionTextFragment
59
+ | ConditionNumberFragment
60
+ | ConditionOrFragment
61
+ | ConditionAndFragment,
62
+ obj: object,
63
+ ) {
64
+ if (condition.__typename === 'ConditionOr')
65
+ return condition.conditions.some((c) => matchCondition(c, obj))
66
+
67
+ if (condition.__typename === 'ConditionAnd')
68
+ return condition.conditions.every((c) => matchCondition(c, obj))
69
+
70
+ if (condition.__typename === 'ConditionNumber') {
71
+ return getByPath(obj, condition.property).some((v) => {
72
+ const value = Number(v)
73
+ if (!value || Number.isNaN(value)) return false
74
+
75
+ if (condition.operator === 'EQUAL') return value === condition.numberValue
76
+ if (condition.operator === 'LTE') return value <= condition.numberValue
77
+ if (condition.operator === 'GTE') return value >= condition.numberValue
78
+ return false
79
+ })
80
+ }
81
+ if (condition.__typename === 'ConditionText') {
82
+ return getByPath(obj, condition.property).some((value) => value === condition.stringValue)
83
+ }
84
+
85
+ return false
86
+ }
87
+
88
+ /**
89
+ * Fetch the page content for the given urls.
90
+ *
91
+ * - Uses an early bailout to check to reduce hygraph calls.
92
+ * - Implements an alias sytem to merge the content of multiple pages.
93
+ */
94
+ export async function hygraphDynamicRows(
95
+ client: ApolloClient<NormalizedCacheObject>,
96
+ pageQuery: Promise<{ data: HygraphPagesQuery }>,
97
+ url: string,
98
+ cached: boolean,
99
+ additionalProperties?: Promise<object> | object,
100
+ ): Promise<{ data: HygraphPagesQuery }> {
101
+ const alwaysCache = process.env.NODE_ENV !== 'development' ? 'cache-first' : undefined
102
+ const fetchPolicy = cached ? alwaysCache : undefined
103
+
104
+ const allRoutes = await client.query({ query: AllDynamicRowsDocument, fetchPolicy: alwaysCache })
105
+
106
+ // Get the required rowIds from the conditions
107
+ const properties = { ...(await additionalProperties), url }
108
+
109
+ const rowIds = allRoutes.data.dynamicRows
110
+ .filter((availableDynamicRow) =>
111
+ availableDynamicRow.conditions.some((condition) => matchCondition(condition, properties)),
112
+ )
113
+ .map((row) => row.id)
114
+
115
+ const dynamicRows =
116
+ rowIds.length !== 0
117
+ ? client.query({ query: DynamicRowsDocument, variables: { rowIds }, fetchPolicy })
118
+ : undefined
119
+
120
+ const [pageResult, dynamicResult] = await Promise.all([pageQuery, dynamicRows])
121
+
122
+ // Create a copy of the content array.
123
+ const content = [...(pageResult.data.pages[0]?.content ?? [])]
124
+
125
+ dynamicResult?.data.dynamicRows.forEach((dynamicRow) => {
126
+ const { placement, target, row } = dynamicRow
127
+ if (!row) return
128
+
129
+ if (!target) {
130
+ if (placement === 'BEFORE') content.unshift(row)
131
+ else content.push(row)
132
+ return
133
+ }
134
+
135
+ const targetIdx = content.findIndex((c) => c.id === target.id)
136
+ if (placement === 'BEFORE') content.splice(targetIdx, 0, row)
137
+ if (placement === 'AFTER') content.splice(targetIdx + 1, 0, row)
138
+ if (placement === 'REPLACE') content.splice(targetIdx, 1, row)
139
+ })
140
+
141
+ if (!content.length) return pageResult
142
+
143
+ // Return the merged page result.
144
+ return { data: { ...pageResult.data, pages: [{ ...pageResult.data.pages[0], content }] } }
145
+ }
package/next-env.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ /// <reference types="next" />
2
+ /// <reference types="next/types/global" />
3
+ /// <reference types="next/image-types/global" />
4
+ /// <reference types="@graphcommerce/next-ui/types" />
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@graphcommerce/hygraph-dynamic-rows",
3
+ "homepage": "https://www.graphcommerce.org/",
4
+ "repository": "github:graphcommerce-org/graphcommerce",
5
+ "version": "6.2.0-canary.8",
6
+ "sideEffects": false,
7
+ "prettier": "@graphcommerce/prettier-config-pwa",
8
+ "eslintConfig": {
9
+ "extends": "@graphcommerce/eslint-config-pwa",
10
+ "parserOptions": {
11
+ "project": "./tsconfig.json"
12
+ }
13
+ },
14
+ "devDependencies": {
15
+ "@graphcommerce/eslint-config-pwa": "6.2.0-canary.8",
16
+ "@graphcommerce/prettier-config-pwa": "6.2.0-canary.8",
17
+ "@graphcommerce/typescript-config-pwa": "6.2.0-canary.8"
18
+ },
19
+ "dependencies": {
20
+ "@graphcommerce/graphql": "6.2.0-canary.8",
21
+ "@graphcommerce/image": "6.2.0-canary.8",
22
+ "@graphcommerce/next-ui": "6.2.0-canary.8",
23
+ "@graphcommerce/graphcms-ui": "6.2.0-canary.8"
24
+ },
25
+ "peerDependencies": {
26
+ "@mui/material": "^5.10.16",
27
+ "next": "^13.2.0",
28
+ "react": "^18.2.0",
29
+ "react-dom": "^18.2.0"
30
+ }
31
+ }
@@ -0,0 +1,19 @@
1
+ import type { hygraphPageContent } from '@graphcommerce/graphcms-ui'
2
+ import type { MethodPlugin } from '@graphcommerce/next-config'
3
+ import { hygraphDynamicRows } from '../lib/hygraphDynamicRows'
4
+
5
+ export const func = 'hygraphPageContent'
6
+ export const exported = '@graphcommerce/graphcms-ui'
7
+
8
+ const hygraphDynamicRowsPageContent: MethodPlugin<typeof hygraphPageContent> = (
9
+ prev,
10
+ client,
11
+ url,
12
+ additionalProperties,
13
+ cached = false,
14
+ ) => {
15
+ const pageQuery = prev(client, url, additionalProperties, cached)
16
+ return hygraphDynamicRows(client, pageQuery, url, cached, additionalProperties)
17
+ }
18
+
19
+ export const plugin = hygraphDynamicRowsPageContent
package/tsconfig.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "exclude": ["**/node_modules", "**/.*/"],
3
+ "include": ["**/*.ts", "**/*.tsx"],
4
+ "extends": "@graphcommerce/typescript-config-pwa/nextjs.json"
5
+ }