diff --git a/packages/form-js-editor/assets/form-js-editor-base.css b/packages/form-js-editor/assets/form-js-editor-base.css
index 6962e9cb2..2c62beaeb 100644
--- a/packages/form-js-editor/assets/form-js-editor-base.css
+++ b/packages/form-js-editor/assets/form-js-editor-base.css
@@ -61,6 +61,7 @@
--cds-border-strong,
var(--cds-border-strong-01, var(--color-grey-225-10-80))
);
+ --color-palette-field-focus: var(--cds-border-interactive, var(--color-blue-219-100-53));
--color-palette-field-hover-background: var(--cds-background-hover, var(--color-grey-225-10-90));
--cursor-palette-field: grab;
--palette-width: 250px;
@@ -625,6 +626,8 @@
flex-direction: column;
justify-content: center;
font-size: 11px;
+ align-items: center;
+ border: none;
user-select: none;
color: var( --color-palette-field);
background: var(--color-palette-field-background);
@@ -634,6 +637,11 @@
width: 68px;
}
+.fjs-palette-container .fjs-palette-field:focus {
+ outline: none;
+ border: solid 1px var(--color-palette-field-focus);
+}
+
.fjs-palette-field .fjs-palette-field-icon {
margin: 0 auto;
}
diff --git a/packages/form-js-editor/src/features/palette/components/Palette.js b/packages/form-js-editor/src/features/palette/components/Palette.js
index a10dd6afb..e13102a84 100644
--- a/packages/form-js-editor/src/features/palette/components/Palette.js
+++ b/packages/form-js-editor/src/features/palette/components/Palette.js
@@ -11,10 +11,11 @@ import {
import {
CloseIcon,
- iconsByType,
SearchIcon
} from '../../../render/components/icons';
+import PaletteEntry from './PaletteEntry';
+
import { formFields } from '@bpmn-io/form-js-viewer';
export const PALETTE_ENTRIES = formFields.filter(({ config: fieldConfig }) => fieldConfig.type !== 'default').map(({ config: fieldConfig }) => {
@@ -120,20 +121,11 @@ export default function Palette(props) {
{ label }
{
- entries.map(({ label, type }) => {
- const Icon = iconsByType(type);
-
+ entries.map(entry => {
return (
-
- {
- Icon ? : null
- }
- { label }
-
+
);
})
}
@@ -174,14 +166,4 @@ function groupEntries(entries) {
});
return groups.filter(g => g.entries.length);
-}
-
-function getIndefiniteArticle(type) {
- if ([
- 'image'
- ].includes(type)) {
- return 'an';
- }
-
- return 'a';
}
\ No newline at end of file
diff --git a/packages/form-js-editor/src/features/palette/components/PaletteEntry.js b/packages/form-js-editor/src/features/palette/components/PaletteEntry.js
new file mode 100644
index 000000000..1a284e97a
--- /dev/null
+++ b/packages/form-js-editor/src/features/palette/components/PaletteEntry.js
@@ -0,0 +1,56 @@
+import {
+ iconsByType
+} from '../../../render/components/icons';
+
+import { useService } from '../../../render/hooks';
+
+export default function PaletteEntry(props) {
+ const {
+ type,
+ label
+ } = props;
+
+ const modeling = useService('modeling');
+ const formEditor = useService('formEditor');
+
+ const Icon = iconsByType(type);
+
+ const onKeyDown = (event) => {
+ if (event.code === 'Enter') {
+
+ const { fieldType: type } = event.target.dataset;
+
+ const { schema } = formEditor._getState();
+
+ // add new form field to last position
+ modeling.addFormField({ type }, schema, schema.components.length);
+ }
+ };
+
+ return (
+
+ );
+}
+
+
+// helpers ///////////
+
+function getIndefiniteArticle(type) {
+ if ([
+ 'image'
+ ].includes(type)) {
+ return 'an';
+ }
+
+ return 'a';
+}
\ No newline at end of file
diff --git a/packages/form-js-editor/test/spec/features/palette/Palette.spec.js b/packages/form-js-editor/test/spec/features/palette/Palette.spec.js
index ee35c3390..e3ceb630c 100644
--- a/packages/form-js-editor/test/spec/features/palette/Palette.spec.js
+++ b/packages/form-js-editor/test/spec/features/palette/Palette.spec.js
@@ -173,6 +173,65 @@ describe('palette', function() {
});
+ describe('keyboard support', function() {
+
+
+ it('should add entry on ENTER', async function() {
+
+ // given
+ const spy = sinon.spy();
+
+ const schema = {
+ components: []
+ };
+
+ const result = createPalette({
+ container,
+ modeling: { addFormField: spy },
+ formEditor: { _getState: () => ({ schema }) }
+ });
+
+ const entry = result.container.querySelector('[data-field-type="textfield"]');
+
+ // when
+ fireEvent.focus(entry);
+ fireEvent.keyDown(entry, { key: 'Enter', code: 'Enter' });
+
+ // then
+ expect(spy).to.have.been.calledOnceWith({ type: 'textfield' }, schema, 0);
+ });
+
+
+ it('should add entry to last position', async function() {
+
+ // given
+ const spy = sinon.spy();
+
+ const schema = {
+ components: [ {
+ type: 'textfield',
+ id: 'foo'
+ } ]
+ };
+
+ const result = createPalette({
+ container,
+ modeling: { addFormField: spy },
+ formEditor: { _getState: () => ({ schema }) }
+ });
+
+ const entry = result.container.querySelector('[data-field-type="textfield"]');
+
+ // when
+ fireEvent.focus(entry);
+ fireEvent.keyDown(entry, { key: 'Enter', code: 'Enter' });
+
+ // then
+ expect(spy).to.have.been.calledOnceWith({ type: 'textfield' }, schema, 1);
+ });
+ });
+
+
describe('a11y', function() {
it('should have no violations', async function() {
@@ -183,17 +242,7 @@ describe('palette', function() {
const result = createPalette({ container });
// then
- // @Note(pinussilvestrus): the palette entries are currently
- // not keyboard accessible, as we need to invest in an overall
- // editor keyboard experience
- // cf. https://github.com/bpmn-io/form-js/issues/536
- await expectNoViolations(result.container, {
- rules: {
- 'scrollable-region-focusable': {
- enabled: false
- }
- }
- });
+ await expectNoViolations(result.container);
});
@@ -210,15 +259,8 @@ describe('palette', function() {
fireEvent.input(search, { target: { value: 'text' } });
// then
- await expectNoViolations(result.container, {
- rules: {
- 'scrollable-region-focusable': {
- enabled: false
- }
- }
- });
+ await expectNoViolations(result.container);
});
-
});
});
@@ -230,7 +272,7 @@ function createPalette(options = {}) {
const { container } = options;
return render(
- WithFormEditorContext(
),
+ WithFormEditorContext(
, options),
{
container
}