Skip to content

Commit 18a164a

Browse files
committed
feat: enable passing of documentation link to form properties panel
Related to camunda/team-hto#630
1 parent 7549b6d commit 18a164a

File tree

4 files changed

+125
-64
lines changed

4 files changed

+125
-64
lines changed

packages/form-js-editor/src/features/properties-panel/PropertiesPanel.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,18 @@ import { reduce, isArray } from 'min-dash';
66

77
import { FormPropertiesPanelContext } from './context';
88

9-
import { PropertiesPanelHeaderProvider } from './PropertiesPanelHeaderProvider';
9+
import { getPropertiesPanelHeaderProvider } from './PropertiesPanelHeaderProvider';
1010
import { PropertiesPanelPlaceholderProvider } from './PropertiesPanelPlaceholderProvider';
1111

12+
const EMPTY = {};
13+
1214
export function PropertiesPanel(props) {
1315
const { eventBus, getProviders, injector } = props;
1416

1517
const formEditor = injector.get('formEditor');
1618
const modeling = injector.get('modeling');
1719
const selectionModule = injector.get('selection');
18-
const propertiesPanelConfig = injector.get('config.propertiesPanel') || {};
20+
const propertiesPanelConfig = injector.get('config.propertiesPanel') || EMPTY;
1921

2022
const { feelPopupContainer } = propertiesPanelConfig;
2123

@@ -83,6 +85,17 @@ export function PropertiesPanel(props) {
8385
);
8486
}, [providers, selectedFormField, editField]);
8587

88+
const formFields = getService('formFields');
89+
90+
const PropertiesPanelHeaderProvider = useMemo(
91+
() =>
92+
getPropertiesPanelHeaderProvider({
93+
getDocumentationRef: propertiesPanelConfig.getDocumentationRef,
94+
formFields,
95+
}),
96+
[formFields, propertiesPanelConfig],
97+
);
98+
8699
return (
87100
<div
88101
class="fjs-properties-panel"
Lines changed: 44 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,50 @@
11
import { textToLabel } from './Util';
2-
32
import { iconsByType } from '../../render/components/icons';
4-
53
import { getPaletteIcon } from '../palette/components/Palette';
64

7-
import { useService } from './hooks';
8-
95
const headerlessTypes = ['spacer', 'separator', 'expression', 'html'];
106

11-
export const PropertiesPanelHeaderProvider = {
12-
getElementLabel: (field) => {
13-
const { type } = field;
14-
15-
if (headerlessTypes.includes(type)) {
16-
return '';
17-
}
18-
19-
if (type === 'text') {
20-
return textToLabel(field.text);
21-
}
22-
23-
if (type === 'image') {
24-
return field.alt;
25-
}
26-
27-
if (type === 'default') {
28-
return field.id;
29-
}
30-
31-
return field.label;
32-
},
33-
34-
getElementIcon: (field) => {
35-
const { type } = field;
36-
37-
// @Note: We know that we are inside the properties panel context,
38-
// so we can savely use the hook here.
39-
// eslint-disable-next-line react-hooks/rules-of-hooks
40-
const fieldDefinition = useService('formFields').get(type).config;
41-
42-
const Icon = fieldDefinition.icon || iconsByType(type);
43-
44-
if (Icon) {
45-
return () => <Icon width="36" height="36" viewBox="0 0 54 54" />;
46-
} else if (fieldDefinition.iconUrl) {
47-
return getPaletteIcon({ iconUrl: fieldDefinition.iconUrl, label: fieldDefinition.label });
48-
}
49-
},
50-
51-
getTypeLabel: (field) => {
52-
const { type } = field;
53-
54-
if (type === 'default') {
55-
return 'Form';
56-
}
57-
58-
// @Note: We know that we are inside the properties panel context,
59-
// so we can savely use the hook here.
60-
// eslint-disable-next-line react-hooks/rules-of-hooks
61-
const fieldDefinition = useService('formFields').get(type).config;
62-
63-
return fieldDefinition.label || type;
64-
},
65-
};
7+
export function getPropertiesPanelHeaderProvider(options = {}) {
8+
const { getDocumentationRef, formFields } = options;
9+
10+
return {
11+
getElementLabel: (field) => {
12+
const { type } = field;
13+
if (headerlessTypes.includes(type)) {
14+
return '';
15+
}
16+
if (type === 'text') {
17+
return textToLabel(field.text);
18+
}
19+
if (type === 'image') {
20+
return field.alt;
21+
}
22+
if (type === 'default') {
23+
return field.id;
24+
}
25+
return field.label;
26+
},
27+
28+
getElementIcon: (field) => {
29+
const { type } = field;
30+
const fieldDefinition = formFields.get(type).config;
31+
const Icon = fieldDefinition.icon || iconsByType(type);
32+
if (Icon) {
33+
return () => <Icon width="36" height="36" viewBox="0 0 54 54" />;
34+
} else if (fieldDefinition.iconUrl) {
35+
return getPaletteIcon({ iconUrl: fieldDefinition.iconUrl, label: fieldDefinition.label });
36+
}
37+
},
38+
39+
getTypeLabel: (field) => {
40+
const { type } = field;
41+
if (type === 'default') {
42+
return 'Form';
43+
}
44+
const fieldDefinition = formFields.get(type).config;
45+
return fieldDefinition.label || type;
46+
},
47+
48+
getDocumentationRef,
49+
};
50+
}

packages/form-js-editor/test/spec/features/properties-panel/PropertiesPanelHeaderProvider.spec.js

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { cleanup, render } from '@testing-library/preact/pure';
22

33
import { FormFields } from '@bpmn-io/form-js-viewer';
44

5-
import { PropertiesPanelHeaderProvider } from '../../../../src/features/properties-panel/PropertiesPanelHeaderProvider';
5+
import { getPropertiesPanelHeaderProvider } from '../../../../src/features/properties-panel/PropertiesPanelHeaderProvider';
66

77
import { MockPropertiesPanelContext, TestPropertiesPanel } from './helper';
88

@@ -50,6 +50,22 @@ describe('PropertiesPanelHeaderProvider', function () {
5050
expect(label.innerText).to.eql(field.label);
5151
});
5252

53+
it('should render documentation link', function () {
54+
// given
55+
const field = { type: 'textfield' };
56+
57+
const getDocumentationRef = () => 'https://example.com/';
58+
59+
// when
60+
const { container } = renderHeader({ field, getDocumentationRef });
61+
62+
// then
63+
const documentationLink = container.querySelector('.bio-properties-panel-header-link');
64+
65+
expect(documentationLink).to.exist;
66+
expect(documentationLink.href).to.eql(getDocumentationRef());
67+
});
68+
5369
describe('extension support', function () {
5470
it('should render type label from config', function () {
5571
// given
@@ -130,7 +146,7 @@ describe('PropertiesPanelHeaderProvider', function () {
130146

131147
// helpers /////////
132148

133-
function renderHeader({ services, ...restOptions }) {
149+
function renderHeader({ services = {}, getDocumentationRef, ...restOptions }) {
134150
const defaultField = { type: 'textfield' };
135151

136152
const options = {
@@ -140,7 +156,13 @@ function renderHeader({ services, ...restOptions }) {
140156

141157
return render(
142158
<MockPropertiesPanelContext options={options} services={services}>
143-
<TestPropertiesPanel field={options.field} headerProvider={PropertiesPanelHeaderProvider} />
159+
<TestPropertiesPanel
160+
field={options.field}
161+
headerProvider={getPropertiesPanelHeaderProvider({
162+
formFields: services.formFields || new FormFields(),
163+
getDocumentationRef,
164+
})}
165+
/>
144166
</MockPropertiesPanelContext>,
145167
);
146168
}

packages/form-js-playground/test/spec/Playground.spec.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,47 @@ describe('playground', function () {
144144
expect(playground).to.exist;
145145
});
146146

147+
it('should render with documentation links', async function () {
148+
// given
149+
const data = {};
150+
151+
const getDocumentationRef = (field) => {
152+
if (field.type === 'default') {
153+
return 'https://example.com/default';
154+
}
155+
156+
return 'https://example.com/other';
157+
};
158+
159+
// when
160+
await act(() => {
161+
playground = new Playground({
162+
container,
163+
schema,
164+
data,
165+
propertiesPanel: {
166+
getDocumentationRef,
167+
},
168+
});
169+
});
170+
171+
// then
172+
expect(playground).to.exist;
173+
174+
const documentationLink = domQuery('.bio-properties-panel-header-link', container);
175+
expect(documentationLink).to.exist;
176+
expect(documentationLink.href).to.eql('https://example.com/default');
177+
178+
// when
179+
const formField = domQuery('.fjs-form-field', container);
180+
await act(() => formField.click());
181+
182+
// then
183+
const documentationLinkSelected = domQuery('.bio-properties-panel-header-link', container);
184+
expect(documentationLinkSelected).to.exist;
185+
expect(documentationLinkSelected.href).to.eql('https://example.com/other');
186+
});
187+
147188
it('should append sample data', async function () {
148189
// given
149190
const attrs = {

0 commit comments

Comments
 (0)