@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.
- package/README.md +56 -36
- package/build/components/FEAASComponent.js +71 -11
- package/build/components/FEAASComponent.js.map +1 -1
- package/build/components/FEAASElement.js +411 -37
- package/build/components/FEAASElement.js.map +1 -1
- package/build/components/FEAASLoader.js +2 -2
- package/build/components/FEAASLoader.js.map +1 -1
- package/build/components/FEAASReactElement.js +47 -0
- package/build/components/FEAASReactElement.js.map +1 -0
- package/build/components/FEAASStylesheet.js +2 -2
- package/build/components/FEAASStylesheet.js.map +1 -1
- package/build/headless.js +4 -3
- package/build/headless.js.map +1 -1
- package/build/ui/FEAAS.js +5 -0
- package/build/ui/FEAAS.js.map +1 -0
- package/build/ui/FEAASContext.js +20 -8
- package/build/ui/FEAASContext.js.map +1 -1
- package/build/ui/FEAASEditor.js +2 -2
- package/build/ui/FEAASEditor.js.map +1 -1
- package/build/ui/FEAASExternal.js +128 -24
- package/build/ui/FEAASExternal.js.map +1 -1
- package/build/ui/FEAASPicker.js +7 -2
- package/build/ui/FEAASPicker.js.map +1 -1
- package/build/ui/index.js +3 -4
- package/build/ui/index.js.map +1 -1
- package/build/ui/react.js.map +1 -1
- package/build/utils/fetch.js +26 -0
- package/build/utils/fetch.js.map +1 -0
- package/dist/browser/headless.cjs +6 -6
- package/dist/browser/headless.cjs.map +4 -4
- package/dist/browser/headless.esm.js +6 -6
- package/dist/browser/headless.esm.js.map +1 -1
- package/dist/browser/index.cjs +7 -7
- package/dist/browser/index.cjs.map +4 -4
- package/dist/browser/index.esm.js +7 -7
- package/dist/browser/index.esm.js.map +1 -1
- package/dist/browser/react.cjs +6 -6
- package/dist/browser/react.cjs.map +4 -4
- package/dist/browser/react.esm.js +6 -6
- package/dist/browser/react.esm.js.map +1 -1
- package/dist/node/headless.cjs +4 -4
- package/dist/node/headless.cjs.map +4 -4
- package/dist/node/headless.esm.js +4 -4
- package/dist/node/headless.esm.js.map +4 -4
- package/dist/node/index.cjs +4 -4
- package/dist/node/index.cjs.map +4 -4
- package/dist/node/index.esm.js +4 -4
- package/dist/node/index.esm.js.map +4 -4
- package/dist/node/react.cjs +4 -4
- package/dist/node/react.cjs.map +4 -4
- package/dist/node/react.esm.js +4 -4
- package/dist/node/react.esm.js.map +4 -4
- package/headless.cjs +4 -4
- package/headless.cjs.map +4 -4
- package/headless.js +4 -4
- package/headless.js.map +4 -4
- package/package.json +11 -3
- package/react.cjs +6 -6
- package/react.cjs.map +4 -4
- package/react.js +6 -6
- package/react.js.map +4 -4
- package/scripts/build.js +1 -1
- package/scripts/test-setup.ts +1 -0
- package/src/components/FEAASComponent.tsx +96 -12
- package/src/components/FEAASElement.tsx +479 -41
- package/src/components/FEAASLoader.tsx +2 -2
- package/src/components/FEAASReactElement.tsx +61 -0
- package/src/components/FEAASStylesheet.tsx +3 -3
- package/src/headless.ts +5 -4
- package/src/ui/FEAAS.tsx +4 -0
- package/src/ui/FEAASContext.tsx +25 -7
- package/src/ui/FEAASEditor.tsx +7 -7
- package/src/ui/FEAASExternal.tsx +134 -24
- package/src/ui/FEAASPicker.tsx +9 -5
- package/src/ui/index.ts +4 -4
- package/src/ui/react.tsx +5 -0
- package/src/ui/tsconfig.json +1 -1
- package/src/utils/fetch.ts +44 -0
- package/test/FEAASBase.test.tsx +2 -2
- package/test/FEAASComponent.test.tsx +4 -1
- package/test/FEAASComponent2.test.tsx +215 -0
- package/test/FEAASContext.test.tsx +206 -29
- package/test/FEAASExternal.test.tsx +1 -8
- package/test/FEAASPicker.test.tsx +4 -2
- package/test/fetch-data.ts +55 -0
- package/types/components/FEAASComponent.d.ts +33 -3
- package/types/components/FEAASComponent.d.ts.map +1 -1
- package/types/components/FEAASElement.d.ts +299 -22
- package/types/components/FEAASElement.d.ts.map +1 -1
- package/types/components/FEAASLoader.d.ts +2 -2
- package/types/components/FEAASLoader.d.ts.map +1 -1
- package/types/components/FEAASReactElement.d.ts +30 -0
- package/types/components/FEAASReactElement.d.ts.map +1 -0
- package/types/components/FEAASStylesheet.d.ts +3 -3
- package/types/components/FEAASStylesheet.d.ts.map +1 -1
- package/types/headless.d.ts +4 -3
- package/types/headless.d.ts.map +1 -1
- package/types/tsbuildinfo +1 -1
- package/types/ui/FEAAS.d.ts +5 -0
- package/types/ui/FEAAS.d.ts.map +1 -0
- package/types/ui/FEAASContext.d.ts +16 -7
- package/types/ui/FEAASContext.d.ts.map +1 -1
- package/types/ui/FEAASEditor.d.ts +6 -6
- package/types/ui/FEAASEditor.d.ts.map +1 -1
- package/types/ui/FEAASExternal.d.ts +59 -10
- package/types/ui/FEAASExternal.d.ts.map +1 -1
- package/types/ui/FEAASPicker.d.ts +11 -9
- package/types/ui/FEAASPicker.d.ts.map +1 -1
- package/types/ui/index.d.ts +1 -4
- package/types/ui/index.d.ts.map +1 -1
- package/types/ui/react.d.ts.map +1 -1
- package/types/ui/tsbuildinfo +1 -0
- package/types/utils/fetch.d.ts +9 -0
- package/types/utils/fetch.d.ts.map +1 -0
- package/build/components/FEAASBase.js +0 -416
- package/build/components/FEAASBase.js.map +0 -1
- package/src/components/FEAASBase.tsx +0 -485
- package/types/components/FEAASBase.d.ts +0 -299
- package/types/components/FEAASBase.d.ts.map +0 -1
- package/types/tsbuildinfo_ui +0 -1
package/src/ui/FEAASContext.tsx
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
11
|
-
|
|
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
|
|
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 |
|
|
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()
|
|
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')
|
package/src/ui/FEAASEditor.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type * as FEAASEditorImports from '@sitecore-feaas/frontend/editor'
|
|
2
|
-
import {
|
|
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:
|
|
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
|
|
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.
|
|
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.
|
|
427
|
-
version: FEAASEditorImports.
|
|
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.
|
|
461
|
+
const [component, setComponent] = useState<FEAASEditorImports.Model.ComponentModel>()
|
|
462
462
|
const { setConfirm } = useContext(ConfirmationContext)
|
|
463
463
|
const usedId = instanceId || versionId
|
|
464
464
|
|
package/src/ui/FEAASExternal.tsx
CHANGED
|
@@ -1,26 +1,69 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import ReactDOM from 'react-dom'
|
|
3
|
-
import {
|
|
4
|
-
import { ReactJSX, ReactModules } from '../components/
|
|
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
|
-
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
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:
|
|
85
|
+
export function FEAASExternalProps(element: FEAASElement) {
|
|
43
86
|
const attributes = element.getAttributeNames()
|
|
44
87
|
const id = element.getAttribute('data-external-id')
|
|
45
|
-
const 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 = {
|
|
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
|
|
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
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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:
|
|
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
|
|
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.
|
|
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 }
|
package/src/ui/FEAASPicker.tsx
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type * as FEAASPickerImports from '@sitecore-feaas/frontend/picker'
|
|
2
|
-
import {
|
|
2
|
+
import { FEAASElement } from '../components/FEAASElement.js'
|
|
3
3
|
|
|
4
|
-
export function FEAASPickerProps(element:
|
|
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
|
|
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 '
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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}
|
package/src/ui/tsconfig.json
CHANGED
|
@@ -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
|
+
}
|
package/test/FEAASBase.test.tsx
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { describe, it, assert, beforeAll, expect } from 'vitest'
|
|
2
|
-
import {
|
|
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
|
|
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.
|
|
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 () => {
|