diff --git a/packages/form-js-editor/src/features/properties-panel/PropertiesPanel.js b/packages/form-js-editor/src/features/properties-panel/PropertiesPanel.js
index 507fc00d2..a905aacfa 100644
--- a/packages/form-js-editor/src/features/properties-panel/PropertiesPanel.js
+++ b/packages/form-js-editor/src/features/properties-panel/PropertiesPanel.js
@@ -6,16 +6,18 @@ import { reduce, isArray } from 'min-dash';
import { FormPropertiesPanelContext } from './context';
-import { PropertiesPanelHeaderProvider } from './PropertiesPanelHeaderProvider';
+import { getPropertiesPanelHeaderProvider } from './PropertiesPanelHeaderProvider';
import { PropertiesPanelPlaceholderProvider } from './PropertiesPanelPlaceholderProvider';
+const EMPTY = {};
+
export function PropertiesPanel(props) {
const { eventBus, getProviders, injector } = props;
const formEditor = injector.get('formEditor');
const modeling = injector.get('modeling');
const selectionModule = injector.get('selection');
- const propertiesPanelConfig = injector.get('config.propertiesPanel') || {};
+ const propertiesPanelConfig = injector.get('config.propertiesPanel') || EMPTY;
const { feelPopupContainer } = propertiesPanelConfig;
@@ -83,6 +85,17 @@ export function PropertiesPanel(props) {
);
}, [providers, selectedFormField, editField]);
+ const formFields = getService('formFields');
+
+ const PropertiesPanelHeaderProvider = useMemo(
+ () =>
+ getPropertiesPanelHeaderProvider({
+ getDocumentationRef: propertiesPanelConfig.getDocumentationRef,
+ formFields,
+ }),
+ [formFields, propertiesPanelConfig],
+ );
+
return (
{
- const { type } = field;
-
- if (headerlessTypes.includes(type)) {
- return '';
- }
-
- if (type === 'text') {
- return textToLabel(field.text);
- }
-
- if (type === 'image') {
- return field.alt;
- }
-
- if (type === 'default') {
- return field.id;
- }
-
- return field.label;
- },
-
- getElementIcon: (field) => {
- const { type } = field;
-
- // @Note: We know that we are inside the properties panel context,
- // so we can savely use the hook here.
- // eslint-disable-next-line react-hooks/rules-of-hooks
- const fieldDefinition = useService('formFields').get(type).config;
-
- const Icon = fieldDefinition.icon || iconsByType(type);
-
- if (Icon) {
- return () => ;
- } else if (fieldDefinition.iconUrl) {
- return getPaletteIcon({ iconUrl: fieldDefinition.iconUrl, label: fieldDefinition.label });
- }
- },
-
- getTypeLabel: (field) => {
- const { type } = field;
-
- if (type === 'default') {
- return 'Form';
- }
-
- // @Note: We know that we are inside the properties panel context,
- // so we can savely use the hook here.
- // eslint-disable-next-line react-hooks/rules-of-hooks
- const fieldDefinition = useService('formFields').get(type).config;
-
- return fieldDefinition.label || type;
- },
-};
+export function getPropertiesPanelHeaderProvider(options = {}) {
+ const { getDocumentationRef, formFields } = options;
+
+ return {
+ getElementLabel: (field) => {
+ const { type } = field;
+ if (headerlessTypes.includes(type)) {
+ return '';
+ }
+ if (type === 'text') {
+ return textToLabel(field.text);
+ }
+ if (type === 'image') {
+ return field.alt;
+ }
+ if (type === 'default') {
+ return field.id;
+ }
+ return field.label;
+ },
+
+ getElementIcon: (field) => {
+ const { type } = field;
+ const fieldDefinition = formFields.get(type).config;
+ const Icon = fieldDefinition.icon || iconsByType(type);
+ if (Icon) {
+ return () => ;
+ } else if (fieldDefinition.iconUrl) {
+ return getPaletteIcon({ iconUrl: fieldDefinition.iconUrl, label: fieldDefinition.label });
+ }
+ },
+
+ getTypeLabel: (field) => {
+ const { type } = field;
+ if (type === 'default') {
+ return 'Form';
+ }
+ const fieldDefinition = formFields.get(type).config;
+ return fieldDefinition.label || type;
+ },
+
+ getDocumentationRef,
+ };
+}
diff --git a/packages/form-js-editor/test/spec/features/properties-panel/PropertiesPanelHeaderProvider.spec.js b/packages/form-js-editor/test/spec/features/properties-panel/PropertiesPanelHeaderProvider.spec.js
index 453ecbcfb..4a519f30e 100644
--- a/packages/form-js-editor/test/spec/features/properties-panel/PropertiesPanelHeaderProvider.spec.js
+++ b/packages/form-js-editor/test/spec/features/properties-panel/PropertiesPanelHeaderProvider.spec.js
@@ -2,7 +2,7 @@ import { cleanup, render } from '@testing-library/preact/pure';
import { FormFields } from '@bpmn-io/form-js-viewer';
-import { PropertiesPanelHeaderProvider } from '../../../../src/features/properties-panel/PropertiesPanelHeaderProvider';
+import { getPropertiesPanelHeaderProvider } from '../../../../src/features/properties-panel/PropertiesPanelHeaderProvider';
import { MockPropertiesPanelContext, TestPropertiesPanel } from './helper';
@@ -50,6 +50,22 @@ describe('PropertiesPanelHeaderProvider', function () {
expect(label.innerText).to.eql(field.label);
});
+ it('should render documentation link', function () {
+ // given
+ const field = { type: 'textfield' };
+
+ const getDocumentationRef = () => 'https://example.com/';
+
+ // when
+ const { container } = renderHeader({ field, getDocumentationRef });
+
+ // then
+ const documentationLink = container.querySelector('.bio-properties-panel-header-link');
+
+ expect(documentationLink).to.exist;
+ expect(documentationLink.href).to.eql(getDocumentationRef());
+ });
+
describe('extension support', function () {
it('should render type label from config', function () {
// given
@@ -130,7 +146,7 @@ describe('PropertiesPanelHeaderProvider', function () {
// helpers /////////
-function renderHeader({ services, ...restOptions }) {
+function renderHeader({ services = {}, getDocumentationRef, ...restOptions }) {
const defaultField = { type: 'textfield' };
const options = {
@@ -140,7 +156,13 @@ function renderHeader({ services, ...restOptions }) {
return render(
-
+
,
);
}
diff --git a/packages/form-js-playground/test/spec/Playground.spec.js b/packages/form-js-playground/test/spec/Playground.spec.js
index 62003c2cd..45a426811 100644
--- a/packages/form-js-playground/test/spec/Playground.spec.js
+++ b/packages/form-js-playground/test/spec/Playground.spec.js
@@ -144,6 +144,47 @@ describe('playground', function () {
expect(playground).to.exist;
});
+ it('should render with documentation links', async function () {
+ // given
+ const data = {};
+
+ const getDocumentationRef = (field) => {
+ if (field.type === 'default') {
+ return 'https://example.com/default';
+ }
+
+ return 'https://example.com/other';
+ };
+
+ // when
+ await act(() => {
+ playground = new Playground({
+ container,
+ schema,
+ data,
+ propertiesPanel: {
+ getDocumentationRef,
+ },
+ });
+ });
+
+ // then
+ expect(playground).to.exist;
+
+ const documentationLink = domQuery('.bio-properties-panel-header-link', container);
+ expect(documentationLink).to.exist;
+ expect(documentationLink.href).to.eql('https://example.com/default');
+
+ // when
+ const formField = domQuery('.fjs-form-field', container);
+ await act(() => formField.click());
+
+ // then
+ const documentationLinkSelected = domQuery('.bio-properties-panel-header-link', container);
+ expect(documentationLinkSelected).to.exist;
+ expect(documentationLinkSelected.href).to.eql('https://example.com/other');
+ });
+
it('should append sample data', async function () {
// given
const attrs = {