Skip to content

Commit

Permalink
feat: enable passing of documentation link to form properties panel
Browse files Browse the repository at this point in the history
  • Loading branch information
Skaiir committed Jun 6, 2024
1 parent e54bc9d commit 281cf4d
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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 (
<div
class="fjs-properties-panel"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,65 +1,50 @@
import { textToLabel } from './Util';

import { iconsByType } from '../../render/components/icons';

import { getPaletteIcon } from '../palette/components/Palette';

import { useService } from './hooks';

const headerlessTypes = ['spacer', 'separator', 'expression', 'html'];

export const PropertiesPanelHeaderProvider = {
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;

// @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 () => <Icon width="36" height="36" viewBox="0 0 54 54" />;
} 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 () => <Icon width="36" height="36" viewBox="0 0 54 54" />;
} 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,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -130,7 +146,7 @@ describe('PropertiesPanelHeaderProvider', function () {

// helpers /////////

function renderHeader({ services, ...restOptions }) {
function renderHeader({ services = {}, getDocumentationRef, ...restOptions }) {
const defaultField = { type: 'textfield' };

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

return render(
<MockPropertiesPanelContext options={options} services={services}>
<TestPropertiesPanel field={options.field} headerProvider={PropertiesPanelHeaderProvider} />
<TestPropertiesPanel
field={options.field}
headerProvider={getPropertiesPanelHeaderProvider({
formFields: services.formFields || new FormFields(),
getDocumentationRef,
})}
/>
</MockPropertiesPanelContext>,
);
}
41 changes: 41 additions & 0 deletions packages/form-js-playground/test/spec/Playground.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down

0 comments on commit 281cf4d

Please sign in to comment.