@sitecore-feaas/clientside 0.1.56 → 0.2.1

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.
Files changed (98) hide show
  1. package/README.md +559 -56
  2. package/build/components/FEAASComponent.js +2 -2
  3. package/build/components/FEAASComponent.js.map +1 -1
  4. package/build/components/FEAASElement.js +408 -37
  5. package/build/components/FEAASElement.js.map +1 -1
  6. package/build/components/FEAASLoader.js +2 -2
  7. package/build/components/FEAASLoader.js.map +1 -1
  8. package/build/components/FEAASReactElement.js +47 -0
  9. package/build/components/FEAASReactElement.js.map +1 -0
  10. package/build/components/FEAASStylesheet.js +2 -2
  11. package/build/components/FEAASStylesheet.js.map +1 -1
  12. package/build/headless.js +4 -3
  13. package/build/headless.js.map +1 -1
  14. package/build/ui/FEAASContext.js +14 -5
  15. package/build/ui/FEAASContext.js.map +1 -1
  16. package/build/ui/FEAASEditor.js +2 -2
  17. package/build/ui/FEAASEditor.js.map +1 -1
  18. package/build/ui/FEAASExternal.js +66 -23
  19. package/build/ui/FEAASExternal.js.map +1 -1
  20. package/build/ui/FEAASPicker.js +3 -2
  21. package/build/ui/FEAASPicker.js.map +1 -1
  22. package/dist/browser/headless.cjs +5 -5
  23. package/dist/browser/headless.cjs.map +4 -4
  24. package/dist/browser/headless.esm.js +5 -5
  25. package/dist/browser/headless.esm.js.map +1 -1
  26. package/dist/browser/index.cjs +7 -7
  27. package/dist/browser/index.cjs.map +4 -4
  28. package/dist/browser/index.esm.js +7 -7
  29. package/dist/browser/index.esm.js.map +1 -1
  30. package/dist/browser/react.cjs +6 -6
  31. package/dist/browser/react.cjs.map +4 -4
  32. package/dist/browser/react.esm.js +6 -6
  33. package/dist/browser/react.esm.js.map +1 -1
  34. package/dist/node/headless.cjs +3 -3
  35. package/dist/node/headless.cjs.map +4 -4
  36. package/dist/node/headless.esm.js +2 -2
  37. package/dist/node/headless.esm.js.map +4 -4
  38. package/dist/node/index.cjs +4 -4
  39. package/dist/node/index.cjs.map +4 -4
  40. package/dist/node/index.esm.js +3 -3
  41. package/dist/node/index.esm.js.map +4 -4
  42. package/dist/node/react.cjs +4 -4
  43. package/dist/node/react.cjs.map +4 -4
  44. package/dist/node/react.esm.js +4 -4
  45. package/dist/node/react.esm.js.map +4 -4
  46. package/headless.cjs +3 -3
  47. package/headless.cjs.map +4 -4
  48. package/headless.js +3 -3
  49. package/headless.js.map +4 -4
  50. package/package.json +10 -2
  51. package/react.cjs +6 -6
  52. package/react.cjs.map +4 -4
  53. package/react.js +6 -6
  54. package/react.js.map +4 -4
  55. package/scripts/test-setup.ts +1 -0
  56. package/src/components/FEAASComponent.tsx +4 -4
  57. package/src/components/FEAASElement.tsx +467 -42
  58. package/src/components/FEAASLoader.tsx +2 -2
  59. package/src/components/FEAASReactElement.tsx +61 -0
  60. package/src/components/FEAASStylesheet.tsx +3 -3
  61. package/src/headless.ts +5 -4
  62. package/src/ui/FEAASContext.tsx +16 -7
  63. package/src/ui/FEAASEditor.tsx +7 -7
  64. package/src/ui/FEAASExternal.tsx +77 -23
  65. package/src/ui/FEAASPicker.tsx +4 -3
  66. package/src/ui/tsconfig.json +1 -1
  67. package/test/FEAASBase.test.tsx +2 -2
  68. package/test/FEAASComponent.test.tsx +3 -1
  69. package/test/FEAASContext.test.tsx +206 -29
  70. package/test/FEAASPicker.test.tsx +4 -2
  71. package/types/components/FEAASComponent.d.ts +3 -3
  72. package/types/components/FEAASComponent.d.ts.map +1 -1
  73. package/types/components/FEAASElement.d.ts +292 -23
  74. package/types/components/FEAASElement.d.ts.map +1 -1
  75. package/types/components/FEAASLoader.d.ts +2 -2
  76. package/types/components/FEAASLoader.d.ts.map +1 -1
  77. package/types/components/FEAASReactElement.d.ts +30 -0
  78. package/types/components/FEAASReactElement.d.ts.map +1 -0
  79. package/types/components/FEAASStylesheet.d.ts +3 -3
  80. package/types/components/FEAASStylesheet.d.ts.map +1 -1
  81. package/types/headless.d.ts +4 -3
  82. package/types/headless.d.ts.map +1 -1
  83. package/types/tsbuildinfo +1 -1
  84. package/types/ui/FEAASContext.d.ts +8 -7
  85. package/types/ui/FEAASContext.d.ts.map +1 -1
  86. package/types/ui/FEAASEditor.d.ts +6 -6
  87. package/types/ui/FEAASEditor.d.ts.map +1 -1
  88. package/types/ui/FEAASExternal.d.ts +8 -10
  89. package/types/ui/FEAASExternal.d.ts.map +1 -1
  90. package/types/ui/FEAASPicker.d.ts +7 -6
  91. package/types/ui/FEAASPicker.d.ts.map +1 -1
  92. package/types/ui/tsbuildinfo +1 -0
  93. package/build/components/FEAASBase.js +0 -416
  94. package/build/components/FEAASBase.js.map +0 -1
  95. package/src/components/FEAASBase.tsx +0 -485
  96. package/types/components/FEAASBase.d.ts +0 -299
  97. package/types/components/FEAASBase.d.ts.map +0 -1
  98. package/types/tsbuildinfo_ui +0 -1
@@ -1,14 +1,14 @@
1
1
  import type * as FEAASContextImports from '@sitecore-feaas/frontend/context'
2
2
  import type { AuthModel } from '@sitecore-feaas/sdk'
3
- import { FEAASBase, OptionalExcept } from '../components/FEAASBase.js'
3
+ import { FEAASElement, OptionalExcept } from '../components/FEAASElement.js'
4
4
  import { ControlledPromise } from '../utils/promise.js'
5
5
 
6
6
  export function FEAASContextProps(element: HTMLElement) {
7
7
  return {
8
8
  library: element?.getAttribute('library') as string,
9
9
  src: element?.getAttribute('src') ?? '/context.js',
10
- frontend: element?.getAttribute('frontend') ?? 'https://components.sitecoreclud.io',
11
- backend: element?.getAttribute('backend') ?? 'https://components.sitecoreclud.io/api',
10
+ frontend: element?.getAttribute('frontend') ?? 'https://components.sitecorecloud.io',
11
+ backend: element?.getAttribute('backend') ?? 'https://components.sitecorecloud.io/api',
12
12
  cdn: element?.getAttribute('cdn') ?? 'https://feaas.blob.core.windows.net',
13
13
  auth: null as Partial<AuthModel>,
14
14
  isolate: element?.getAttribute('isolate') == null || element?.getAttribute('isolate') == 'true'
@@ -20,7 +20,7 @@ export type FEAASContextProps = ReturnType<typeof FEAASContextProps>
20
20
  * Context provides shared parameters and react providers for its nested elements. It loads lazily once any of its
21
21
  * nested components load. Just placing it on the page without any consumers does not impose any overhead.
22
22
  */
23
- export class FEAASContext extends FEAASBase<FEAASContextProps, typeof FEAASContextImports> {
23
+ export class FEAASContext extends FEAASElement<FEAASContextProps, typeof FEAASContextImports> {
24
24
  static observedAttributes = ['library', 'component', 'version', 'revision', 'hostname', 'template']
25
25
  getProps() {
26
26
  return FEAASContextProps(this)
@@ -35,7 +35,6 @@ export class FEAASContext extends FEAASBase<FEAASContextProps, typeof FEAASConte
35
35
  }
36
36
 
37
37
  isReadyToLoad() {
38
- console.log('Is ready?', this.props, super.isReadyToLoad() && this.props.library != null)
39
38
  return super.isReadyToLoad() && this.props.library != null
40
39
  }
41
40
 
@@ -44,7 +43,7 @@ export class FEAASContext extends FEAASBase<FEAASContextProps, typeof FEAASConte
44
43
  return theme
45
44
  }
46
45
 
47
- getRoot(): HTMLElement | FEAASBase<any, any> | ShadowRoot {
46
+ getRoot(): HTMLElement | FEAASElement<any, any> | ShadowRoot {
48
47
  if (this.getAttribute('isolate') === 'false') return this
49
48
  return this.shadowRoot || this.attachShadow({ mode: 'open' })
50
49
  }
@@ -60,12 +59,20 @@ export class FEAASContext extends FEAASBase<FEAASContextProps, typeof FEAASConte
60
59
  }
61
60
 
62
61
  /** Pull in children FEAAS react elements into the shadow DOM so they receieve the styles */
63
- connectedCallback(): void {
62
+ connectedCallback() {
63
+ //if (this.props.isolate)
64
64
  Array.from(this.children).forEach((child) => {
65
65
  this.getRoot().appendChild(child)
66
66
  })
67
+ document.addEventListener('feaasMount', this)
68
+ document.addEventListener('feaasUnmount', this)
67
69
  super.connectedCallback()
68
70
  }
71
+ disconnectedCallback() {
72
+ document.removeEventListener('feaasMount', this)
73
+ document.removeEventListener('feaasUnmount', this)
74
+ super.disconnectedCallback()
75
+ }
69
76
 
70
77
  sdk: FEAASContextImports.SDK
71
78
  onStatusChange: FEAASContextImports.SDKStatusCallback = (status, sdk) => {
@@ -102,6 +109,7 @@ export class FEAASContext extends FEAASBase<FEAASContextProps, typeof FEAASConte
102
109
  portalProps
103
110
  }}
104
111
  chakraProps={{
112
+ resetScope: '.feaas-reset',
105
113
  cssVarsRoot: ':host',
106
114
  resetCSS: true,
107
115
  toastOptions: {
@@ -123,4 +131,5 @@ declare global {
123
131
  }
124
132
  }
125
133
 
134
+ export { FEAASContext as Context }
126
135
  FEAASContext.register('feaas-context')
@@ -1,5 +1,5 @@
1
1
  import type * as FEAASEditorImports from '@sitecore-feaas/frontend/editor'
2
- import { FEAASBase, OptionalExcept } from '../components/FEAASBase.js'
2
+ import { FEAASElement, OptionalExcept } from '../components/FEAASElement.js'
3
3
  import { FEAASLoader } from '../components/FEAASLoader.js'
4
4
 
5
5
  export function getFramePosition(element: HTMLElement) {
@@ -38,7 +38,7 @@ export function getFramePosition(element: HTMLElement) {
38
38
  { left: 0, top: 0 }
39
39
  )
40
40
  }
41
- export function FEAASEditorProps(element: FEAASBase) {
41
+ export function FEAASEditorProps(element: FEAASElement) {
42
42
  return {
43
43
  context: HTMLElement,
44
44
  frontend: element?.getContextAttribute('frontend'),
@@ -63,7 +63,7 @@ type Rect = {
63
63
  height: number
64
64
  }
65
65
 
66
- export class FEAASEditor extends FEAASBase<FEAASEditorProps, typeof FEAASEditorImports> {
66
+ export class FEAASEditor extends FEAASElement<FEAASEditorProps, typeof FEAASEditorImports> {
67
67
  framePoint: Pick<Rect, 'top' | 'left'> = { top: 0, left: 0 }
68
68
  wrapperRect: Rect = { top: 0, left: 0, width: 0, height: 0 }
69
69
  bodyRect: Rect = { top: 0, left: 0, width: 0, height: 0 }
@@ -329,7 +329,7 @@ export class FEAASEditor extends FEAASBase<FEAASEditorProps, typeof FEAASEditorI
329
329
  this.setLoadingState()
330
330
  }
331
331
 
332
- shouldFork(version: FEAASEditorImports.SDK.VersionModel) {
332
+ shouldFork(version: FEAASEditorImports.Model.VersionModel) {
333
333
  return version.forkOriginId == null && this.props.instance != null
334
334
  }
335
335
 
@@ -423,8 +423,8 @@ export class FEAASEditor extends FEAASBase<FEAASEditorProps, typeof FEAASEditorI
423
423
  this.setAttribute('state', 'ready')
424
424
  }
425
425
 
426
- component: FEAASEditorImports.SDK.ComponentModel
427
- version: FEAASEditorImports.SDK.VersionModel
426
+ component: FEAASEditorImports.Model.ComponentModel
427
+ version: FEAASEditorImports.Model.VersionModel
428
428
  closing: ReturnType<typeof setTimeout>
429
429
 
430
430
  render(
@@ -458,7 +458,7 @@ export class FEAASEditor extends FEAASBase<FEAASEditorProps, typeof FEAASEditorI
458
458
  const { React } = this.payload
459
459
  const sdk = useSDK()
460
460
  const library = useLibrary()
461
- const [component, setComponent] = useState<FEAASEditorImports.SDK.ComponentModel>()
461
+ const [component, setComponent] = useState<FEAASEditorImports.Model.ComponentModel>()
462
462
  const { setConfirm } = useContext(ConfirmationContext)
463
463
  const usedId = instanceId || versionId
464
464
 
@@ -1,26 +1,72 @@
1
1
  import React from 'react'
2
2
  import ReactDOM from 'react-dom'
3
- import { FEAASBase } from '../components/FEAASBase.js'
4
- import { ReactJSX, ReactModules } from '../components/FEAASElement.js'
3
+ import { FEAASElement } from '../components/FEAASElement.js'
4
+ import { ReactJSX, ReactModules } from '../components/FEAASReactElement.js'
5
5
  import { kebabCase, reverseKebabCase } from '../utils/string.js'
6
6
  import { Component, FEAASComponentProps } from './react.js'
7
+ import type { ExternalComponent, ExternalComponentOptions } from '@sitecore-feaas/sdk'
7
8
 
8
- const convertSchemaPropertiesToKebabCase = (schema: any) => {
9
+ /** Checks if title exists in a property and if not, it generates it from (kebab-cased) property key */
10
+ const addTitles = (properties: { [key: string]: any }) =>
11
+ Object.keys(properties).reduce((acc: { [key: string]: any }, key) => {
12
+ if (properties[key].title) {
13
+ acc[key] = properties[key]
14
+ } else {
15
+ const title = key
16
+ .split('-')
17
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
18
+ .join(' ')
19
+ acc[key] = { ...properties[key], title }
20
+ }
21
+ return acc
22
+ }, {})
23
+
24
+ const objectKeysToKebabCase = (object: { [key: string]: any } = {}) =>
25
+ Object.keys(object).reduce((acc: { [key: string]: any }, key) => {
26
+ acc[kebabCase(key)] = object[key]
27
+ return acc
28
+ }, {})
29
+
30
+ const transformSchema = (schema: ExternalComponent['schema']) => {
9
31
  if (!schema) return
32
+ if (!schema.properties) return schema
33
+ let properties, required
10
34
 
11
- let kebabSchemaProperties, kebabRequiredProperties
12
- if (schema.properties) {
13
- kebabSchemaProperties = Object.keys(schema.properties).reduce((acc: { [key: string]: string }, key) => {
14
- acc[kebabCase(key)] = schema.properties[key]
15
- return acc
16
- }, {})
35
+ /* kebab-case properties */
36
+ properties = objectKeysToKebabCase(schema.properties)
37
+ required = (schema.required || []).map((item: string) => kebabCase(item))
17
38
 
18
- kebabRequiredProperties = (schema.required || []).map((item: string) => kebabCase(item))
19
- }
20
- return { ...schema, properties: kebabSchemaProperties, required: kebabRequiredProperties }
39
+ /* generate titles if not exist */
40
+ properties = addTitles(properties)
41
+
42
+ /* remove title from schema - it will be passed separately to use custom styling in the form */
43
+ const title: string = undefined
44
+ return { ...schema, properties, required, title }
21
45
  }
22
46
 
23
- export const parseProps = (propName: string, propValue: string, properties: any) => {
47
+ const transformUiSchema = (uiSchema: ExternalComponent['uiSchema'], properties: { [key: string]: any }) => {
48
+ if (!uiSchema || !properties) return
49
+ let transformed: ExternalComponent['uiSchema']
50
+
51
+ /* kebab-case keys */
52
+ transformed = objectKeysToKebabCase(uiSchema)
53
+
54
+ /* use updown input widget as default for numbers */
55
+ const numberProperties = Object.keys(properties).filter((key) => /(integer|number)/.test(properties[key].type))
56
+ numberProperties.forEach((propertyName) => {
57
+ if (!transformed[propertyName]) transformed[propertyName] = { 'ui:widget': 'updown' }
58
+ else if (!transformed[propertyName]['ui:widget']) transformed[propertyName]['ui:widget'] = 'updown'
59
+ })
60
+
61
+ return transformed
62
+ }
63
+
64
+ export const parseProps = (
65
+ propName: string,
66
+ propValue: string,
67
+ properties: ExternalComponent['schema']['properties']
68
+ ) => {
69
+ //@ts-ignore
24
70
  switch (properties?.[propName]?.type) {
25
71
  case 'string':
26
72
  return propValue
@@ -39,10 +85,10 @@ export const parseProps = (propName: string, propValue: string, properties: any)
39
85
  }
40
86
  }
41
87
 
42
- export function FEAASExternalProps(element: FEAASBase) {
88
+ export function FEAASExternalProps(element: FEAASElement) {
43
89
  const attributes = element.getAttributeNames()
44
90
  const id = element.getAttribute('data-external-id')
45
- const schema: any = FEAASExternal.registered[id]?.schema
91
+ const schema: ExternalComponent['schema'] = FEAASExternal.registered[id]?.schema
46
92
  const props = attributes.reduce((prev, curr) => {
47
93
  if (!curr.startsWith('data-attribute') && !['class', 'id', 'contenteditable'].includes(curr)) {
48
94
  const value = element.getAttribute(curr)
@@ -54,10 +100,12 @@ export function FEAASExternalProps(element: FEAASBase) {
54
100
  return props
55
101
  }
56
102
 
57
- export type RegisteredComponents = { [id: string]: { component: React.ElementType; schema: any } }
103
+ export type RegisteredComponents = {
104
+ [id: string]: ExternalComponent
105
+ }
58
106
  export type FEAASExternalProps = ReturnType<typeof FEAASExternalProps> & { dataExternalId: string }
59
107
 
60
- export default class FEAASExternal extends FEAASBase<FEAASExternalProps, ReactModules> {
108
+ export default class FEAASExternal extends FEAASElement<FEAASExternalProps, ReactModules> {
61
109
  static registered: RegisteredComponents = {}
62
110
  static registrationCallback: NodeJS.Timeout
63
111
  load() {
@@ -75,10 +123,12 @@ export default class FEAASExternal extends FEAASBase<FEAASExternalProps, ReactMo
75
123
  return FEAASExternalProps(this)
76
124
  }
77
125
 
78
- static registerComponent(component: React.FC, schema: any = {}) {
79
- const id = schema.name || component.name
80
- const enrichedSchema = { ...schema, title: id, type: 'object' }
81
- FEAASExternal.registered[id] = { component, schema: enrichedSchema }
126
+ static registerComponent(component: React.FC, options: ExternalComponentOptions = {}) {
127
+ const id = options.name || component.name
128
+ const { thumbnail, group, ui: uiSchema, ...schemaOptions } = options
129
+ const schema = { ...schemaOptions, type: 'object' } as ExternalComponent['schema']
130
+ FEAASExternal.registered[id] = { component, schema, uiSchema, thumbnail, group, title: schema?.title || id }
131
+ FEAASExternal.setRegistrationCallback()
82
132
  }
83
133
 
84
134
  static setRegistrationCallback() {
@@ -89,12 +139,16 @@ export default class FEAASExternal extends FEAASBase<FEAASExternalProps, ReactMo
89
139
  const components = Object.keys(FEAASExternal.registered).map((id) => ({
90
140
  ...FEAASExternal.registered[id],
91
141
  name: id,
92
- schema: convertSchemaPropertiesToKebabCase(FEAASExternal.registered[id].schema)
142
+ schema: transformSchema(FEAASExternal.registered[id].schema),
143
+ uiSchema: transformUiSchema(
144
+ FEAASExternal.registered[id].uiSchema,
145
+ FEAASExternal.registered[id].schema?.properties
146
+ )
93
147
  //component: FEAASExternal.registered[name].component.toString()
94
148
  }))
95
149
 
96
150
  // send components from iframe to parent window
97
- window?.parent?.postMessage(
151
+ window.parent?.postMessage(
98
152
  JSON.stringify({
99
153
  action: 'register-components',
100
154
  data: components
@@ -1,7 +1,7 @@
1
1
  import type * as FEAASPickerImports from '@sitecore-feaas/frontend/picker'
2
- import { FEAASBase } from '../components/FEAASBase.js'
2
+ import { FEAASElement } from '../components/FEAASElement.js'
3
3
 
4
- export function FEAASPickerProps(element: FEAASBase<any, any>) {
4
+ export function FEAASPickerProps(element: FEAASElement<any, any>) {
5
5
  return {
6
6
  part: element?.getAttribute('part') || 'picker',
7
7
  slot: element?.getAttribute('part') || 'picker',
@@ -18,7 +18,7 @@ export function FEAASPickerProps(element: FEAASBase<any, any>) {
18
18
  }
19
19
  export type FEAASPickerProps = ReturnType<typeof FEAASPickerProps>
20
20
 
21
- export class FEAASPicker extends FEAASBase<FEAASPickerProps, typeof FEAASPickerImports> {
21
+ export class FEAASPicker extends FEAASElement<FEAASPickerProps, typeof FEAASPickerImports> {
22
22
  static observedAttributes = ['library', 'mode', 'frontend', 'src', 'hidden', 'variant']
23
23
 
24
24
  defaultProps = {
@@ -66,4 +66,5 @@ declare global {
66
66
  }
67
67
  }
68
68
 
69
+ export { FEAASPicker as Picker }
69
70
  FEAASPicker.register('feaas-picker')
@@ -3,7 +3,7 @@
3
3
  "compilerOptions": {
4
4
  "declaration": true,
5
5
  "rootDir": ".",
6
- "tsBuildInfoFile": "../../types/tsbuildinfo_ui",
6
+ "tsBuildInfoFile": "../../types/ui/tsbuildinfo",
7
7
  "declarationDir": "../../types/ui",
8
8
  "outDir": "../../build/ui"
9
9
  },
@@ -1,10 +1,10 @@
1
1
  import { describe, it, assert, beforeAll, expect } from 'vitest'
2
- import { FEAASBase, ReactPayload } from '../src/headless.js'
2
+ import { FEAASElement, ReactPayload } from '../src/headless.js'
3
3
  import { JSDOM } from 'jsdom'
4
4
 
5
5
  describe('React', function () {
6
6
  describe('Error boundary', () => {
7
- class FEAASErrorTest extends FEAASBase<{ throwAt: string }> {
7
+ class FEAASErrorTest extends FEAASElement<{ throwAt: string }> {
8
8
  static observedAttributes = ['test']
9
9
 
10
10
  log() {}
@@ -15,7 +15,9 @@ describe('Components', () => {
15
15
  // vi.useRealTimers()
16
16
  })
17
17
  beforeEach(() => {
18
- document.write()
18
+ document.open()
19
+ document.write('<html><head></head><body></body></html>')
20
+ document.close()
19
21
  clearFetchCache()
20
22
  })
21
23
  it('should load component using', async () => {
@@ -1,13 +1,16 @@
1
1
  import { beforeEach, describe, it, assert, beforeAll, expect } from 'vitest'
2
- import { FEAASBase, ReactPayload } from '../src/headless.js'
2
+ import { FEAASElement, ReactPayload } from '../src/headless.js'
3
3
  import { FEAASContextProps, FEAASContext } from '../src/ui/FEAASContext.js'
4
4
  import { JSDOM } from 'jsdom'
5
5
  import { vi } from 'vitest'
6
+ import { ReactNode, ReactElement, JSXElementConstructor } from 'react'
6
7
 
7
8
  describe('React', function () {
8
9
  describe('FEAASContext', () => {
9
10
  beforeEach(() => {
10
- document.write()
11
+ document.open()
12
+ document.write('<html><head></head><body></body></html>')
13
+ document.close()
11
14
  })
12
15
  it('should initiate loading with isolation', async () => {
13
16
  // @ts-ignore
@@ -37,40 +40,214 @@ describe('React', function () {
37
40
  expect(spyLoad).toHaveBeenCalledTimes(1)
38
41
 
39
42
  expect(component.outerHTML).toBe(
40
- '<feaas-context src="test.js" library="abc" isolate="false" frontend="https://components.sitecoreclud.io" backend="https://components.sitecoreclud.io/api" cdn="https://feaas.blob.core.windows.net"><div><p>Context abc</p></div></feaas-context>'
43
+ '<feaas-context src="test.js" library="abc" isolate="false" frontend="https://components.sitecorecloud.io" backend="https://components.sitecorecloud.io/api" cdn="https://feaas.blob.core.windows.net"><div><p>Context abc</p></div></feaas-context>'
41
44
  )
42
45
  })
43
- })
44
- it('should initiate loading without isolation', async () => {
45
- // @ts-ignore
46
- const spyLoad = vi.spyOn(FEAASContext.prototype, 'import')
47
- // @ts-ignore
48
- spyLoad.mockImplementation(async (url: string) => {
49
- if (url.match('test.js')) {
50
- return Promise.all([import('react'), import('react-dom'), import('react-dom/client')]).then(
51
- ([React, ReactDOM, ReactDOMClient]) => {
52
- return {
53
- React,
54
- ReactDOM,
55
- ReactDOMClient,
56
- Chakra: { Tooltip: {} },
57
- AppContext: ({ libraryId }: any) => <p>Context {libraryId}</p>
46
+ it('should initiate loading without isolation', async () => {
47
+ // @ts-ignore
48
+ const spyLoad = vi.spyOn(FEAASContext.prototype, 'import')
49
+ // @ts-ignore
50
+ spyLoad.mockImplementation(async (url: string) => {
51
+ if (url.match('test.js')) {
52
+ return Promise.all([import('react'), import('react-dom'), import('react-dom/client')]).then(
53
+ ([React, ReactDOM, ReactDOMClient]) => {
54
+ return {
55
+ React,
56
+ ReactDOM,
57
+ ReactDOMClient,
58
+ Chakra: { Tooltip: {} },
59
+ AppContext: ({ libraryId }: any) => <p>Context {libraryId}</p>
60
+ }
58
61
  }
59
- }
60
- )
62
+ )
63
+ }
64
+ })
65
+ // Fixme change to production component
66
+ document.body.innerHTML = '<feaas-context src="test.js" library="abc" isolate="false"></feaas-context>'
67
+
68
+ const component = document.body.firstElementChild as FEAASContext
69
+
70
+ await expect(component.whenRendered).resolves.toEqual(component)
71
+ expect(spyLoad).toHaveBeenCalledTimes(1)
72
+
73
+ expect(component.outerHTML).toBe(
74
+ '<feaas-context src="test.js" library="abc" isolate="false" frontend="https://components.sitecorecloud.io" backend="https://components.sitecorecloud.io/api" cdn="https://feaas.blob.core.windows.net"><div><p>Context abc</p></div></feaas-context>'
75
+ )
76
+ })
77
+ it('should initiate loading with isolation', async () => {
78
+ // @ts-ignore
79
+ const spyLoad = vi.spyOn(FEAASContext.prototype, 'import')
80
+ // @ts-ignore
81
+ spyLoad.mockImplementation(async (url: string) => {
82
+ if (url.match('test.js')) {
83
+ return Promise.all([import('react'), import('react-dom'), import('react-dom/client')]).then(
84
+ ([React, ReactDOM, ReactDOMClient]) => {
85
+ return {
86
+ React,
87
+ ReactDOM,
88
+ ReactDOMClient,
89
+ Chakra: { Tooltip: {} },
90
+ AppContext: ({ libraryId }: any) => <p>Context {libraryId}</p>
91
+ }
92
+ }
93
+ )
94
+ }
95
+ })
96
+ // Fixme change to production component
97
+ document.body.innerHTML = '<feaas-context src="test.js" library="abc"></feaas-context>'
98
+
99
+ const component = document.body.firstElementChild as FEAASContext
100
+
101
+ await expect(component.whenRendered).resolves.toEqual(component)
102
+ expect(spyLoad).toHaveBeenCalledTimes(1)
103
+
104
+ expect(component.outerHTML).toBe(
105
+ '<feaas-context src="test.js" library="abc" frontend="https://components.sitecorecloud.io" backend="https://components.sitecorecloud.io/api" cdn="https://feaas.blob.core.windows.net"></feaas-context>'
106
+ )
107
+ expect(component.shadowRoot.innerHTML).toBe('<div><p>Context abc</p></div>')
108
+ })
109
+ it('should nest components and render them in order', async () => {
110
+ const renders = [] as FEAASElement[]
111
+ class FEAASElementTestRenderOrder extends FEAASElement {
112
+ update() {
113
+ renders.push(this)
114
+ }
61
115
  }
116
+ FEAASElementTestRenderOrder.register('feaas-element-test-order')
117
+
118
+ const top = document.createElement('feaas-element-test-order') as FEAASElement
119
+ top.innerHTML = `
120
+ <feaas-element-test-order>
121
+ <feaas-element-test-order></feaas-element-test-order>
122
+ <feaas-element-test-order></feaas-element-test-order>
123
+ </feaas-element-test-order>
124
+ <feaas-element-test-order></feaas-element-test-order>
125
+ <feaas-element-test-order>
126
+ <feaas-element-test-order></feaas-element-test-order>
127
+ </feaas-element-test-order>
128
+ `
129
+ document.body.appendChild(top)
130
+ await expect(top.whenRendered).resolves.toEqual(top)
131
+ expect(top.descendants).toEqual([
132
+ top.children[0],
133
+ top.children[0].children[0],
134
+
135
+ top.children[0].children[1],
136
+
137
+ top.children[1],
138
+
139
+ top.children[2],
140
+
141
+ top.children[2].children[0]
142
+ ])
143
+ expect(renders).toEqual([
144
+ top.children[0].children[0],
145
+ top.children[0].children[1],
146
+ top.children[0],
147
+ top.children[1],
148
+ top.children[2].children[0],
149
+ top.children[2],
150
+ top
151
+ ])
62
152
  })
63
- // Fixme change to production component
64
- document.body.innerHTML = '<feaas-context src="test.js" library="abc"></feaas-context>'
65
153
 
66
- const component = document.body.firstElementChild as FEAASContext
154
+ it('should nest components into context', async () => {
155
+ const renders = [] as FEAASElement[]
156
+ class FEAASElementTestRenderOrder extends FEAASElement {
157
+ render(props: any, payload: any): ReactNode | ReactElement<any, string | JSXElementConstructor<any>> {
158
+ return null
159
+ }
160
+ load() {
161
+ return this.import('context.js')
162
+ }
163
+ }
164
+ ;[FEAASElementTestRenderOrder, FEAASContext].forEach((klass) => {
165
+ // @ts-ignore
166
+ const spyLoad = vi.spyOn(klass.prototype, 'import')
167
+ // @ts-ignore
168
+ spyLoad.mockImplementation(async (url: string) => {
169
+ if (url.match('context.js')) {
170
+ return Promise.all([import('react'), import('react-dom'), import('react-dom/client')]).then(
171
+ ([React, ReactDOM, ReactDOMClient]) => {
172
+ return {
173
+ React,
174
+ ReactDOM,
175
+ ReactDOMClient,
176
+ Chakra: { Tooltip: {} },
177
+ AppContext: ({ libraryId }: any) => <p>Context {libraryId}</p>
178
+ }
179
+ }
180
+ )
181
+ }
182
+ })
183
+ const spyRender = vi.spyOn(klass.prototype, 'render')
184
+ spyRender.mockImplementation(function (this: FEAASContext, { children }, { React }) {
185
+ renders.push(this)
186
+ return <>{children}</>
187
+ })
188
+ })
189
+
190
+ FEAASElementTestRenderOrder.register('feaas-element-test-nesting')
191
+
192
+ const context = document.createElement('feaas-context') as FEAASElement
193
+ context.setAttribute('library', 'library1')
194
+ const top = document.createElement('feaas-element-test-nesting') as FEAASElement
195
+ top.innerHTML = `
196
+ <feaas-element-test-nesting>
197
+ <feaas-element-test-nesting></feaas-element-test-nesting>
198
+ <feaas-element-test-nesting></feaas-element-test-nesting>
199
+ </feaas-element-test-nesting>
200
+ <feaas-element-test-nesting></feaas-element-test-nesting>
201
+ <feaas-element-test-nesting>
202
+ <feaas-element-test-nesting></feaas-element-test-nesting>
203
+ </feaas-element-test-nesting>
204
+ `
205
+ document.body.appendChild(context)
206
+ context.render = function () {
207
+ renders.push(this)
208
+ return FEAASContext.prototype.render.apply(this, arguments)
209
+ }
210
+ document.body.appendChild(top)
211
+ await expect(top.whenRendered).resolves.toEqual(top)
212
+ await expect(context.whenRendered).resolves.toEqual(context)
213
+ expect(top.refs.top).toEqual(context)
214
+ expect(top.refs.topReact).toEqual(context)
215
+ expect(top.refs.parentReact).toEqual(context)
216
+ expect(context.descendants).toEqual([
217
+ top,
218
+ top.children[0],
219
+ top.children[0].children[0],
67
220
 
68
- await expect(component.whenRendered).resolves.toEqual(component)
69
- expect(spyLoad).toHaveBeenCalledTimes(1)
221
+ top.children[0].children[1],
70
222
 
71
- expect(component.outerHTML).toBe(
72
- '<feaas-context src="test.js" library="abc" frontend="https://components.sitecoreclud.io" backend="https://components.sitecoreclud.io/api" cdn="https://feaas.blob.core.windows.net"></feaas-context>'
73
- )
74
- expect(component.shadowRoot.innerHTML).toBe('<div><p>Context abc</p></div>')
223
+ top.children[1],
224
+
225
+ top.children[2],
226
+
227
+ top.children[2].children[0]
228
+ ])
229
+ // FIXME: Currently re-renders happen on each element addition
230
+ //expect(renders).toEqual([
231
+ // context,
232
+ // top.children[0].children[0],
233
+ // top.children[0].children[1],
234
+ // top.children[0],
235
+ // top.children[1],
236
+ // top.children[2].children[0],
237
+ // top.children[2],
238
+ // top
239
+ //])
240
+
241
+ const els = [
242
+ context,
243
+ top.children[0].children[0],
244
+ top.children[0].children[1],
245
+ top.children[0],
246
+ top.children[1],
247
+ top.children[2].children[0],
248
+ top.children[2],
249
+ top
250
+ ].forEach((el) => expect(renders).toContain(el))
251
+ })
75
252
  })
76
253
  })
@@ -1,5 +1,5 @@
1
1
  import { beforeEach, describe, it, assert, beforeAll, expect } from 'vitest'
2
- import { FEAASBase, ReactPayload } from '../src/headless.js'
2
+ import { FEAASElement, ReactPayload } from '../src/headless.js'
3
3
  import { FEAASPickerProps, FEAASPicker } from '../src/ui/FEAASPicker.js'
4
4
  import { JSDOM } from 'jsdom'
5
5
  import { vi } from 'vitest'
@@ -7,7 +7,9 @@ import { vi } from 'vitest'
7
7
  describe('React', function () {
8
8
  describe('FEAASPicker', () => {
9
9
  beforeEach(() => {
10
- document.write()
10
+ document.open()
11
+ document.write('<html><head></head><body></body></html>')
12
+ document.close()
11
13
  })
12
14
  it('should initiate loading', async () => {
13
15
  // @ts-ignore
@@ -1,5 +1,5 @@
1
- import { FEAASBase } from './FEAASBase.js';
2
- export declare function FEAASComponentsProps(element: FEAASBase): {
1
+ import { FEAASElement } from './FEAASElement.js';
2
+ export declare function FEAASComponentsProps(element: FEAASElement): {
3
3
  data: any;
4
4
  cdn: string;
5
5
  library: string;
@@ -15,7 +15,7 @@ export declare function FEAASComponentsProps(element: FEAASBase): {
15
15
  withStylesheet: boolean;
16
16
  };
17
17
  export type FEAASComponentsProps = ReturnType<typeof FEAASComponentsProps>;
18
- export declare class FEAASComponent extends FEAASBase<FEAASComponentsProps, string> {
18
+ export declare class FEAASComponent extends FEAASElement<FEAASComponentsProps, string> {
19
19
  static observedAttributes: string[];
20
20
  defaultProps: Partial<{
21
21
  data: any;
@@ -1 +1 @@
1
- {"version":3,"file":"FEAASComponent.d.ts","sourceRoot":"","sources":["../../src/components/FEAASComponent.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE1C,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,SAAS;;;;;;;;;;;;;;EA6BtD;AACD,MAAM,MAAM,oBAAoB,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAA;AAC1E,qBAAa,cAAe,SAAQ,SAAS,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACzE,MAAM,CAAC,kBAAkB,WAYxB;IAED,YAAY;;;;;;;;;;;;;;OAOsB;IAElC,YAAY,EAAE,OAAO,CAAA;IACrB,OAAO,CAAC,IAAI,EAAE,GAAG;IAIjB,QAAQ;;;;;;;;;;;;;;;IAIR,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAUhF,SAAS,IAAI,GAAG;;IAahB,IAAI;IASJ,WAAW;IAaX,aAAa;IAMb,MAAM,SAAU,MAAM,UAGrB;IAEK,IAAI;IAsBV,MAAM;CAYP;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,GAAG,CAAC;QACZ,UAAU,iBAAiB;YACzB,iBAAiB,EAAE;gBACjB,IAAI,CAAC,EAAE,GAAG,CAAA;gBACV,QAAQ,CAAC,EAAE,MAAM,CAAA;gBACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;gBACjB,QAAQ,CAAC,EAAE,OAAO,CAAA;gBAClB,QAAQ,CAAC,EAAE,GAAG,CAAA;gBACd,uBAAuB,CAAC,EAAE;oBAAE,MAAM,EAAE,MAAM,CAAA;iBAAE,CAAA;gBAC5C,eAAe,CAAC,EAAE,MAAM,CAAA;gBACxB,iBAAiB,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,EAAE,CAAA;aAC1C,GAAG,CACA;gBACE,kCAAkC;gBAClC,QAAQ,CAAC,EAAE,MAAM,CAAA;gBACjB,GAAG,CAAC,EAAE,MAAM,CAAA;gBACZ,OAAO,EAAE,MAAM,CAAA;gBACf,SAAS,EAAE,MAAM,CAAA;gBACjB,OAAO,CAAC,EAAE,MAAM,CAAA;gBAChB,QAAQ,CAAC,EAAE,oBAAoB,CAAC,UAAU,CAAC,CAAA;aAC5C,GACD;gBAAE,GAAG,EAAE,MAAM,CAAA;aAAE,GACf;gBAAE,QAAQ,EAAE,MAAM,CAAA;aAAE,CACvB,CAAA;SACF;KACF;CACF;AAGD,OAAO,EAAE,cAAc,IAAI,SAAS,EAAE,oBAAoB,IAAI,cAAc,EAAE,CAAA"}
1
+ {"version":3,"file":"FEAASComponent.d.ts","sourceRoot":"","sources":["../../src/components/FEAASComponent.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAEhD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,YAAY;;;;;;;;;;;;;;EA6BzD;AACD,MAAM,MAAM,oBAAoB,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAA;AAC1E,qBAAa,cAAe,SAAQ,YAAY,CAAC,oBAAoB,EAAE,MAAM,CAAC;IAC5E,MAAM,CAAC,kBAAkB,WAYxB;IAED,YAAY;;;;;;;;;;;;;;OAOsB;IAElC,YAAY,EAAE,OAAO,CAAA;IACrB,OAAO,CAAC,IAAI,EAAE,GAAG;IAIjB,QAAQ;;;;;;;;;;;;;;;IAIR,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAUhF,SAAS,IAAI,GAAG;;IAahB,IAAI;IASJ,WAAW;IAaX,aAAa;IAMb,MAAM,SAAU,MAAM,UAGrB;IAEK,IAAI;IAsBV,MAAM;CAYP;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,GAAG,CAAC;QACZ,UAAU,iBAAiB;YACzB,iBAAiB,EAAE;gBACjB,IAAI,CAAC,EAAE,GAAG,CAAA;gBACV,QAAQ,CAAC,EAAE,MAAM,CAAA;gBACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;gBACjB,QAAQ,CAAC,EAAE,OAAO,CAAA;gBAClB,QAAQ,CAAC,EAAE,GAAG,CAAA;gBACd,uBAAuB,CAAC,EAAE;oBAAE,MAAM,EAAE,MAAM,CAAA;iBAAE,CAAA;gBAC5C,eAAe,CAAC,EAAE,MAAM,CAAA;gBACxB,iBAAiB,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,EAAE,CAAA;aAC1C,GAAG,CACA;gBACE,kCAAkC;gBAClC,QAAQ,CAAC,EAAE,MAAM,CAAA;gBACjB,GAAG,CAAC,EAAE,MAAM,CAAA;gBACZ,OAAO,EAAE,MAAM,CAAA;gBACf,SAAS,EAAE,MAAM,CAAA;gBACjB,OAAO,CAAC,EAAE,MAAM,CAAA;gBAChB,QAAQ,CAAC,EAAE,oBAAoB,CAAC,UAAU,CAAC,CAAA;aAC5C,GACD;gBAAE,GAAG,EAAE,MAAM,CAAA;aAAE,GACf;gBAAE,QAAQ,EAAE,MAAM,CAAA;aAAE,CACvB,CAAA;SACF;KACF;CACF;AAGD,OAAO,EAAE,cAAc,IAAI,SAAS,EAAE,oBAAoB,IAAI,cAAc,EAAE,CAAA"}