@sitecore-feaas/clientside 0.2.0 → 0.2.2

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 (120) hide show
  1. package/README.md +56 -36
  2. package/build/components/FEAASComponent.js +71 -11
  3. package/build/components/FEAASComponent.js.map +1 -1
  4. package/build/components/FEAASElement.js +411 -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/FEAAS.js +5 -0
  15. package/build/ui/FEAAS.js.map +1 -0
  16. package/build/ui/FEAASContext.js +20 -8
  17. package/build/ui/FEAASContext.js.map +1 -1
  18. package/build/ui/FEAASEditor.js +2 -2
  19. package/build/ui/FEAASEditor.js.map +1 -1
  20. package/build/ui/FEAASExternal.js +128 -24
  21. package/build/ui/FEAASExternal.js.map +1 -1
  22. package/build/ui/FEAASPicker.js +7 -2
  23. package/build/ui/FEAASPicker.js.map +1 -1
  24. package/build/ui/index.js +3 -4
  25. package/build/ui/index.js.map +1 -1
  26. package/build/ui/react.js.map +1 -1
  27. package/build/utils/fetch.js +26 -0
  28. package/build/utils/fetch.js.map +1 -0
  29. package/dist/browser/headless.cjs +6 -6
  30. package/dist/browser/headless.cjs.map +4 -4
  31. package/dist/browser/headless.esm.js +6 -6
  32. package/dist/browser/headless.esm.js.map +1 -1
  33. package/dist/browser/index.cjs +7 -7
  34. package/dist/browser/index.cjs.map +4 -4
  35. package/dist/browser/index.esm.js +7 -7
  36. package/dist/browser/index.esm.js.map +1 -1
  37. package/dist/browser/react.cjs +6 -6
  38. package/dist/browser/react.cjs.map +4 -4
  39. package/dist/browser/react.esm.js +6 -6
  40. package/dist/browser/react.esm.js.map +1 -1
  41. package/dist/node/headless.cjs +4 -4
  42. package/dist/node/headless.cjs.map +4 -4
  43. package/dist/node/headless.esm.js +4 -4
  44. package/dist/node/headless.esm.js.map +4 -4
  45. package/dist/node/index.cjs +4 -4
  46. package/dist/node/index.cjs.map +4 -4
  47. package/dist/node/index.esm.js +4 -4
  48. package/dist/node/index.esm.js.map +4 -4
  49. package/dist/node/react.cjs +4 -4
  50. package/dist/node/react.cjs.map +4 -4
  51. package/dist/node/react.esm.js +4 -4
  52. package/dist/node/react.esm.js.map +4 -4
  53. package/headless.cjs +4 -4
  54. package/headless.cjs.map +4 -4
  55. package/headless.js +4 -4
  56. package/headless.js.map +4 -4
  57. package/package.json +11 -3
  58. package/react.cjs +6 -6
  59. package/react.cjs.map +4 -4
  60. package/react.js +6 -6
  61. package/react.js.map +4 -4
  62. package/scripts/build.js +1 -1
  63. package/scripts/test-setup.ts +1 -0
  64. package/src/components/FEAASComponent.tsx +96 -12
  65. package/src/components/FEAASElement.tsx +479 -41
  66. package/src/components/FEAASLoader.tsx +2 -2
  67. package/src/components/FEAASReactElement.tsx +61 -0
  68. package/src/components/FEAASStylesheet.tsx +3 -3
  69. package/src/headless.ts +5 -4
  70. package/src/ui/FEAAS.tsx +4 -0
  71. package/src/ui/FEAASContext.tsx +25 -7
  72. package/src/ui/FEAASEditor.tsx +7 -7
  73. package/src/ui/FEAASExternal.tsx +134 -24
  74. package/src/ui/FEAASPicker.tsx +9 -5
  75. package/src/ui/index.ts +4 -4
  76. package/src/ui/react.tsx +5 -0
  77. package/src/ui/tsconfig.json +1 -1
  78. package/src/utils/fetch.ts +44 -0
  79. package/test/FEAASBase.test.tsx +2 -2
  80. package/test/FEAASComponent.test.tsx +4 -1
  81. package/test/FEAASComponent2.test.tsx +215 -0
  82. package/test/FEAASContext.test.tsx +206 -29
  83. package/test/FEAASExternal.test.tsx +1 -8
  84. package/test/FEAASPicker.test.tsx +4 -2
  85. package/test/fetch-data.ts +55 -0
  86. package/types/components/FEAASComponent.d.ts +33 -3
  87. package/types/components/FEAASComponent.d.ts.map +1 -1
  88. package/types/components/FEAASElement.d.ts +299 -22
  89. package/types/components/FEAASElement.d.ts.map +1 -1
  90. package/types/components/FEAASLoader.d.ts +2 -2
  91. package/types/components/FEAASLoader.d.ts.map +1 -1
  92. package/types/components/FEAASReactElement.d.ts +30 -0
  93. package/types/components/FEAASReactElement.d.ts.map +1 -0
  94. package/types/components/FEAASStylesheet.d.ts +3 -3
  95. package/types/components/FEAASStylesheet.d.ts.map +1 -1
  96. package/types/headless.d.ts +4 -3
  97. package/types/headless.d.ts.map +1 -1
  98. package/types/tsbuildinfo +1 -1
  99. package/types/ui/FEAAS.d.ts +5 -0
  100. package/types/ui/FEAAS.d.ts.map +1 -0
  101. package/types/ui/FEAASContext.d.ts +16 -7
  102. package/types/ui/FEAASContext.d.ts.map +1 -1
  103. package/types/ui/FEAASEditor.d.ts +6 -6
  104. package/types/ui/FEAASEditor.d.ts.map +1 -1
  105. package/types/ui/FEAASExternal.d.ts +59 -10
  106. package/types/ui/FEAASExternal.d.ts.map +1 -1
  107. package/types/ui/FEAASPicker.d.ts +11 -9
  108. package/types/ui/FEAASPicker.d.ts.map +1 -1
  109. package/types/ui/index.d.ts +1 -4
  110. package/types/ui/index.d.ts.map +1 -1
  111. package/types/ui/react.d.ts.map +1 -1
  112. package/types/ui/tsbuildinfo +1 -0
  113. package/types/utils/fetch.d.ts +9 -0
  114. package/types/utils/fetch.d.ts.map +1 -0
  115. package/build/components/FEAASBase.js +0 -416
  116. package/build/components/FEAASBase.js.map +0 -1
  117. package/src/components/FEAASBase.tsx +0 -485
  118. package/types/components/FEAASBase.d.ts +0 -299
  119. package/types/components/FEAASBase.d.ts.map +0 -1
  120. package/types/tsbuildinfo_ui +0 -1
@@ -1,14 +1,17 @@
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
+ import type * as FEAAS from './index.js'
5
6
 
6
7
  export function FEAASContextProps(element: HTMLElement) {
7
8
  return {
8
9
  library: element?.getAttribute('library') as string,
9
10
  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',
11
+ // override environment
12
+ environment: element?.getAttribute('environment') ?? undefined,
13
+ frontend: element?.getAttribute('frontend') ?? 'https://components.sitecorecloud.io',
14
+ backend: element?.getAttribute('backend') ?? 'https://components.sitecorecloud.io/api',
12
15
  cdn: element?.getAttribute('cdn') ?? 'https://feaas.blob.core.windows.net',
13
16
  auth: null as Partial<AuthModel>,
14
17
  isolate: element?.getAttribute('isolate') == null || element?.getAttribute('isolate') == 'true'
@@ -20,7 +23,7 @@ export type FEAASContextProps = ReturnType<typeof FEAASContextProps>
20
23
  * Context provides shared parameters and react providers for its nested elements. It loads lazily once any of its
21
24
  * nested components load. Just placing it on the page without any consumers does not impose any overhead.
22
25
  */
23
- export class FEAASContext extends FEAASBase<FEAASContextProps, typeof FEAASContextImports> {
26
+ export class FEAASContext extends FEAASElement<FEAASContextProps, typeof FEAASContextImports> {
24
27
  static observedAttributes = ['library', 'component', 'version', 'revision', 'hostname', 'template']
25
28
  getProps() {
26
29
  return FEAASContextProps(this)
@@ -35,7 +38,6 @@ export class FEAASContext extends FEAASBase<FEAASContextProps, typeof FEAASConte
35
38
  }
36
39
 
37
40
  isReadyToLoad() {
38
- console.log('Is ready?', this.props, super.isReadyToLoad() && this.props.library != null)
39
41
  return super.isReadyToLoad() && this.props.library != null
40
42
  }
41
43
 
@@ -44,7 +46,7 @@ export class FEAASContext extends FEAASBase<FEAASContextProps, typeof FEAASConte
44
46
  return theme
45
47
  }
46
48
 
47
- getRoot(): HTMLElement | FEAASBase<any, any> | ShadowRoot {
49
+ getRoot(): HTMLElement | FEAASElement<any, any> | ShadowRoot {
48
50
  if (this.getAttribute('isolate') === 'false') return this
49
51
  return this.shadowRoot || this.attachShadow({ mode: 'open' })
50
52
  }
@@ -60,14 +62,24 @@ export class FEAASContext extends FEAASBase<FEAASContextProps, typeof FEAASConte
60
62
  }
61
63
 
62
64
  /** Pull in children FEAAS react elements into the shadow DOM so they receieve the styles */
63
- connectedCallback(): void {
65
+ connectedCallback() {
66
+ this.setAttribute('feaas-role', 'ui')
67
+ //if (this.props.isolate)
64
68
  Array.from(this.children).forEach((child) => {
65
69
  this.getRoot().appendChild(child)
66
70
  })
71
+ document.addEventListener('feaasMount', this)
72
+ document.addEventListener('feaasUnmount', this)
67
73
  super.connectedCallback()
68
74
  }
75
+ disconnectedCallback() {
76
+ document.removeEventListener('feaasMount', this)
77
+ document.removeEventListener('feaasUnmount', this)
78
+ super.disconnectedCallback()
79
+ }
69
80
 
70
81
  sdk: FEAASContextImports.SDK
82
+ clientside: typeof FEAAS
71
83
  onStatusChange: FEAASContextImports.SDKStatusCallback = (status, sdk) => {
72
84
  if (status == 'ready') {
73
85
  this.whenSDKReady.resolve(sdk)
@@ -92,6 +104,7 @@ export class FEAASContext extends FEAASBase<FEAASContextProps, typeof FEAASConte
92
104
  }
93
105
  return (
94
106
  <AppContext
107
+ environment={this.props.environment as any}
95
108
  backend={this.props.backend}
96
109
  libraryId={this.props.library}
97
110
  container={this.getRoot()}
@@ -102,6 +115,7 @@ export class FEAASContext extends FEAASBase<FEAASContextProps, typeof FEAASConte
102
115
  portalProps
103
116
  }}
104
117
  chakraProps={{
118
+ resetScope: ':where([feaas-role="ui"])',
105
119
  cssVarsRoot: ':host',
106
120
  resetCSS: true,
107
121
  toastOptions: {
@@ -121,6 +135,10 @@ declare global {
121
135
  'feaas-context': OptionalExcept<FEAASContextProps, 'library'>
122
136
  }
123
137
  }
138
+ interface DocumentEventMap {
139
+ feaasReady: CustomEvent<FEAASContextImports.SDK>
140
+ }
124
141
  }
125
142
 
143
+ export { FEAASContext as Context }
126
144
  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,69 @@
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, JSONSchema, UISchema } 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
+ /** Converts the keys of an object to kebab case. */
25
+ const objectKeysToKebabCase = (object: { [key: string]: any } = {}) =>
26
+ Object.keys(object).reduce((acc: { [key: string]: any }, key) => {
27
+ acc[kebabCase(key)] = object[key]
28
+ return acc
29
+ }, {})
30
+
31
+ const transformSchema = (schema: JSONSchema) => {
9
32
  if (!schema) return
33
+ if (!schema.properties) return schema
34
+ let properties, required
10
35
 
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
- }, {})
36
+ /* kebab-case properties */
37
+ properties = objectKeysToKebabCase(schema.properties)
38
+ required = (schema.required || []).map((item: string) => kebabCase(item))
17
39
 
18
- kebabRequiredProperties = (schema.required || []).map((item: string) => kebabCase(item))
19
- }
20
- return { ...schema, properties: kebabSchemaProperties, required: kebabRequiredProperties }
40
+ /* generate titles if not exist */
41
+ properties = addTitles(properties)
42
+
43
+ /* remove title from schema - it will be passed separately to use custom styling in the form */
44
+ const title: string = undefined
45
+ return { ...schema, properties, required, title }
21
46
  }
22
47
 
23
- export const parseProps = (propName: string, propValue: string, properties: any) => {
48
+ const transformUiSchema = (uiSchema: UISchema, properties: { [key: string]: any }) => {
49
+ if (!uiSchema || !properties) return
50
+ let transformed: UISchema
51
+
52
+ /* kebab-case keys */
53
+ transformed = objectKeysToKebabCase(uiSchema)
54
+
55
+ /* use updown input widget as default for numbers */
56
+ const numberProperties = Object.keys(properties).filter((key) => /(integer|number)/.test(properties[key].type))
57
+ numberProperties.forEach((propertyName) => {
58
+ if (!transformed[propertyName]) transformed[propertyName] = { 'ui:widget': 'updown' }
59
+ else if (!transformed[propertyName]['ui:widget']) transformed[propertyName]['ui:widget'] = 'updown'
60
+ })
61
+
62
+ return transformed
63
+ }
64
+ /** Parses a property value based on its property name and type defined in the JSON properties object. */
65
+ export const parseProps = (propName: string, propValue: string, properties: JSONSchema['properties']) => {
66
+ //@ts-ignore
24
67
  switch (properties?.[propName]?.type) {
25
68
  case 'string':
26
69
  return propValue
@@ -39,10 +82,10 @@ export const parseProps = (propName: string, propValue: string, properties: any)
39
82
  }
40
83
  }
41
84
 
42
- export function FEAASExternalProps(element: FEAASBase) {
85
+ export function FEAASExternalProps(element: FEAASElement) {
43
86
  const attributes = element.getAttributeNames()
44
87
  const id = element.getAttribute('data-external-id')
45
- const schema: any = FEAASExternal.registered[id]?.schema
88
+ const schema: JSONSchema = FEAASExternal.registered[id]?.schema
46
89
  const props = attributes.reduce((prev, curr) => {
47
90
  if (!curr.startsWith('data-attribute') && !['class', 'id', 'contenteditable'].includes(curr)) {
48
91
  const value = element.getAttribute(curr)
@@ -54,10 +97,12 @@ export function FEAASExternalProps(element: FEAASBase) {
54
97
  return props
55
98
  }
56
99
 
57
- export type RegisteredComponents = { [id: string]: { component: React.ElementType; schema: any } }
100
+ export type RegisteredComponents = {
101
+ [id: string]: ExternalComponent
102
+ }
58
103
  export type FEAASExternalProps = ReturnType<typeof FEAASExternalProps> & { dataExternalId: string }
59
104
 
60
- export default class FEAASExternal extends FEAASBase<FEAASExternalProps, ReactModules> {
105
+ export default class FEAASExternal extends FEAASElement<FEAASExternalProps, ReactModules> {
61
106
  static registered: RegisteredComponents = {}
62
107
  static registrationCallback: NodeJS.Timeout
63
108
  load() {
@@ -75,10 +120,70 @@ export default class FEAASExternal extends FEAASBase<FEAASExternalProps, ReactMo
75
120
  return FEAASExternalProps(this)
76
121
  }
77
122
 
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 }
123
+ /**
124
+ * Register React component to be renderable as Sitecore component (in Components and Pages). Properties are defined
125
+ * as JSON schema, from which a configuration form will be produced using RJSF library. The library allows passing a
126
+ * UI schema that allows tight customization of form widgets, styles and its behavior.
127
+ *
128
+ * @example
129
+ * FEAAS.External.registerComponent(MyComponent, {
130
+ * // Name of a component as it will be presented to the user
131
+ * name: 'My Component',
132
+ * // Title of a component as it will be presented in the inputs form
133
+ * title: 'My Component',
134
+ * // Optional: Thumbnail image for component to be displayed in UI
135
+ * thumbnail: 'http://examaple.com/component.jpg',
136
+ * // Optional: Group components by name
137
+ * group: 'Assorted components',
138
+ * // Definitions of three configurable properties that MyComponent accepts
139
+ * properties: {
140
+ * // Color is a string, but only Red/Green/Blue are allowed
141
+ * color: { type: 'string', enum: ['red', 'green', 'blue'] },
142
+ * // Text is a property labelled as "Text summary"
143
+ * text: { type: 'string', title: 'Text summary' },
144
+ * // Count is a number with 1 as default value
145
+ * count: { type: 'number', default: 1 }
146
+ * },
147
+ * // Optional UI schema customization
148
+ * ui: {
149
+ * // Present color choices as radio buttons
150
+ * color: { 'ui:widget': 'radio' },
151
+ * // Text is a textarea with 5 rows
152
+ * text: { 'ui:widget': 'textarea', 'ui:options': { rows: 5 } },
153
+ * // Count is a number field with spinner button
154
+ * count: { 'ui:widget': 'updown' }
155
+ * },
156
+ * // Color and text area required properties
157
+ * required: ['color', 'text']
158
+ * })
159
+ *
160
+ * @param {React.ComponentType} component React component that will be rendered.
161
+ * @param {string} options.name Component name as presented to the user.
162
+ * @param {string} options.title Component title to be used in the inputs form. Defaults to component name.
163
+ * @param {JSONSchema['properties']} options.properties Object of properties.
164
+ * @param {JSONSchema['type']} options.properties[].type JSON Schema type of a property.
165
+ * @param {string} [options.properties[].title] Optional title displayed to the user.
166
+ * @param {string} [options.group] Optional Image to be displayed in the UI.
167
+ * @param {string} [options.thumbnail] Optional title displayed to the user.
168
+ * @param {UISchema} [options.ui] Optional component groupping.
169
+ * @see https://rjsf-team.github.io/react-jsonschema-form/docs/api-reference/uiSchema
170
+ * @see https://json-schema.org
171
+ */
172
+ static registerComponent(component: React.ComponentType, options: ExternalComponentOptions) {
173
+ if (!options.name)
174
+ throw new Error('Could not register external component. Please make sure you provide a name in the options')
175
+ const id = options.name
176
+ const { thumbnail, group, ui: uiSchema, ...schemaOptions } = options
177
+ const schema = { ...schemaOptions, type: 'object' } as JSONSchema
178
+ FEAASExternal.registered[id] = {
179
+ component,
180
+ name: id,
181
+ schema,
182
+ uiSchema,
183
+ thumbnail,
184
+ group,
185
+ title: schema?.title || id
186
+ }
82
187
  FEAASExternal.setRegistrationCallback()
83
188
  }
84
189
 
@@ -90,12 +195,16 @@ export default class FEAASExternal extends FEAASBase<FEAASExternalProps, ReactMo
90
195
  const components = Object.keys(FEAASExternal.registered).map((id) => ({
91
196
  ...FEAASExternal.registered[id],
92
197
  name: id,
93
- schema: convertSchemaPropertiesToKebabCase(FEAASExternal.registered[id].schema)
198
+ schema: transformSchema(FEAASExternal.registered[id].schema),
199
+ uiSchema: transformUiSchema(
200
+ FEAASExternal.registered[id].uiSchema,
201
+ FEAASExternal.registered[id].schema?.properties
202
+ )
94
203
  //component: FEAASExternal.registered[name].component.toString()
95
204
  }))
96
205
 
97
206
  // send components from iframe to parent window
98
- window?.parent?.postMessage(
207
+ window.parent?.postMessage(
99
208
  JSON.stringify({
100
209
  action: 'register-components',
101
210
  data: components
@@ -108,7 +217,7 @@ export default class FEAASExternal extends FEAASBase<FEAASExternalProps, ReactMo
108
217
 
109
218
  /*static loadComponents() {
110
219
  const components = JSON.parse(sessionStorage.getItem('feaas-external-components'))
111
- components.forEach((c: { name: string; schema: any; component: React.FC }) => {
220
+ components.forEach((c: { name: string; schema: any; component: React.ComponentType }) => {
112
221
  const name = c.name
113
222
  const schema = c.schema
114
223
  const component = eval('(' + c.component + ')')
@@ -139,4 +248,5 @@ declare global {
139
248
  FEAASExternal.register('feaas-external')
140
249
  FEAASExternal.setRegistrationCallback()
141
250
 
251
+ export const registerComponent = FEAASExternal.registerComponent
142
252
  export { FEAASExternal as External }
@@ -1,11 +1,11 @@
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',
8
- mode: element?.getAttribute('mode') as 'library' | 'element' | 'themes',
8
+ mode: element?.getAttribute('mode') as 'library' | 'element' | 'themes' | 'datasources',
9
9
  placeholder: element?.getAttribute('placeholder') || 'No theme',
10
10
  frontend: element?.getContextAttribute('frontend') ?? 'https://components.sitecorecloud.io',
11
11
  src: element?.getAttribute('src') ?? '/picker.js',
@@ -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 = {
@@ -32,7 +32,10 @@ export class FEAASPicker extends FEAASBase<FEAASPickerProps, typeof FEAASPickerI
32
32
  load() {
33
33
  return this.import(this.formatURL(this.props.src, this.props.frontend))
34
34
  }
35
-
35
+ connectedCallback() {
36
+ this.setAttribute('feaas-role', 'ui')
37
+ super.connectedCallback()
38
+ }
36
39
  render(
37
40
  { placeholder, context, mode, children, variant }: React.PropsWithChildren<FEAASPickerProps>,
38
41
  { Picker, React, useLibrary }: typeof FEAASPickerImports
@@ -66,4 +69,5 @@ declare global {
66
69
  }
67
70
  }
68
71
 
72
+ export { FEAASPicker as Picker }
69
73
  FEAASPicker.register('feaas-picker')
package/src/ui/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export * from '../headless.js'
2
- export * from './FEAASEditor.js'
3
- export * from './FEAASPicker.js'
4
- export * from './FEAASContext.js'
1
+ export * from './FEAAS.js'
2
+ import * as FEAAS from './FEAAS.js'
3
+
4
+ FEAAS.Context.prototype.clientside = FEAAS
package/src/ui/react.tsx CHANGED
@@ -1,7 +1,10 @@
1
+ import { DatasourceModel, SDK } from '@sitecore-feaas/sdk'
2
+
1
3
  export * from './index.js'
2
4
  import { FEAASComponentProps, parseComponentSource } from './index.js'
3
5
  import React from 'react'
4
6
  import { renderHTMLContent } from '../dom/html.js'
7
+
5
8
  export { renderHTMLContent }
6
9
  export { External } from './FEAASExternal.js'
7
10
 
@@ -9,12 +12,14 @@ export function FEAASEmbed({ data, ...props }: FEAASComponentProps<any>) {
9
12
  if ('template' in props && props.template) {
10
13
  var html = renderHTMLContent(props.template, data || {})
11
14
  }
15
+
12
16
  if ('src' in props) {
13
17
  props = {
14
18
  ...parseComponentSource(props.src),
15
19
  ...props
16
20
  }
17
21
  }
22
+
18
23
  return (
19
24
  <feaas-component
20
25
  {...props}
@@ -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
  },
@@ -0,0 +1,44 @@
1
+ type Data = object | any[]
2
+
3
+ type DataSettings = { jsonpath: string; url: string }
4
+
5
+ export const isDataSettings = (data: any): data is DataSettings =>
6
+ typeof data === 'object' && 'jsonpath' in data && 'url' in data
7
+
8
+ const performFetch = async (
9
+ settings: Parameters<typeof fetch>[1] & {
10
+ url: Parameters<typeof fetch>[0]
11
+ }
12
+ ): Promise<Data> => {
13
+ try {
14
+ const response = await fetch(settings.url, settings)
15
+
16
+ if (!response) return {}
17
+
18
+ return await response.json()
19
+ } catch (e) {
20
+ return {}
21
+ }
22
+ }
23
+
24
+ export async function fetchData(data: DataSettings | Record<string, DataSettings | Data> | Data): Promise<Data> {
25
+ if (!data) return data
26
+
27
+ console.log(data)
28
+
29
+ if (isDataSettings(data)) return performFetch(data)
30
+
31
+ const keys = Object.keys(data)
32
+
33
+ const allRequests = await Promise.all(
34
+ keys.map((key: keyof typeof data) => (isDataSettings(data[key]) ? performFetch(data[key]) : null)).filter(Boolean)
35
+ )
36
+
37
+ return keys.reduce(
38
+ (obj, key: keyof typeof data) => ({
39
+ ...obj,
40
+ [key]: isDataSettings(data[key]) ? allRequests.shift() : data[key]
41
+ }),
42
+ {}
43
+ )
44
+ }
@@ -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() {}
@@ -6,6 +6,7 @@ import { clearFetchCache } from '../src/fetch.js'
6
6
  const earliest = new Date(2000, 1, 1, 0, 0, 0)
7
7
  const earlier = new Date(2000, 1, 2, 0, 0, 0)
8
8
  const now = new Date(2000, 1, 2, 12, 0, 0)
9
+
9
10
  describe('Components', () => {
10
11
  beforeAll(() => {
11
12
  // vi.useFakeTimers()
@@ -15,7 +16,9 @@ describe('Components', () => {
15
16
  // vi.useRealTimers()
16
17
  })
17
18
  beforeEach(() => {
18
- document.write()
19
+ document.open()
20
+ document.write('<html><head></head><body></body></html>')
21
+ document.close()
19
22
  clearFetchCache()
20
23
  })
21
24
  it('should load component using', async () => {