From 2861964d30eb2467588ec694dd404433618ea00f Mon Sep 17 00:00:00 2001 From: Claudio Guglielmo Date: Tue, 31 Mar 2026 16:58:10 +0200 Subject: [PATCH] FieldStatus: menus are enabled even though field is disabled If a field is disabled, the menus should be disabled as well, unless they have inheritAccessibility set to false. This broke with commit 811f99656ecf58e445596554b3f1451eef046a40. Before that commit, the field status was accessible even if it was disabled -> it ignored the enabled property completely, which was confusing. This is why inheritAccessibility was set to false on the field status and the css rules and click handlers were adjusted to respect the enabled property. Unfortunately, the child menus are now always enabled because their parent (field status if a context menu is open) is always enabled. To fix it, the field status now passes the enabled state of its parent to its children instead of its own state when the enabled state of its children is computed. Note: a regular menu has a slight different semantic: it is disabled, if every child menu is disabled and enabled, if a child menu is enabled. This would be a good behavior for field status as well, but the field status is not a menu and that behavior is implemented only for menus (see Menu.ts#recomputeEnabled) 455532 --- .../src/form/fields/FieldStatus.ts | 8 + .../test/form/fields/FieldStatusSpec.ts | 166 +++++++++++++++++- 2 files changed, 173 insertions(+), 1 deletion(-) diff --git a/eclipse-scout-core/src/form/fields/FieldStatus.ts b/eclipse-scout-core/src/form/fields/FieldStatus.ts index d0085a25cf2..db868c82d62 100644 --- a/eclipse-scout-core/src/form/fields/FieldStatus.ts +++ b/eclipse-scout-core/src/form/fields/FieldStatus.ts @@ -93,6 +93,12 @@ export class FieldStatus extends Widget implements FieldStatusModel { this.$container.setTabbable(hasMenus && this.enabledComputed && !Device.get().supportsOnlyTouch()); } + protected override _updateEnabledComputed(enabledComputed: boolean, enabledComputedForChildren?: boolean) { + // The enabled state of the field status is irrelevant for its child menus -> always pass the state of the field + // This is because the field status should be enabled even if the parent field is disabled (inheritAccessibility is false), but the actual menus should be disabled + super._updateEnabledComputed(enabledComputed, this.parent.enabledComputed); + } + update(status: StatusOrModel, menus: Menu | Menu[], autoRemove: boolean, showStatus?: boolean) { this.updating = true; this.setStatus(status); @@ -290,6 +296,7 @@ export class FieldStatus extends Widget implements FieldStatusModel { this.$container.addClass('selected'); aria.expanded(this.$container, true); aria.linkElementWithControls(this.$container, this.tooltip.$container); + this.recomputeEnabled(); // triggers _updateEnabledComputed this.tooltip.one('destroy', () => { this.tooltip = null; if (this.$container) { @@ -336,6 +343,7 @@ export class FieldStatus extends Widget implements FieldStatusModel { this.$container.addClass('selected'); aria.expanded(this.$container, true); aria.linkElementWithControls(this.$container, this.contextMenu.$container); + this.recomputeEnabled(); // triggers _updateEnabledComputed this.contextMenu.one('destroy', () => { this.contextMenu = null; if (this.$container) { diff --git a/eclipse-scout-core/test/form/fields/FieldStatusSpec.ts b/eclipse-scout-core/test/form/fields/FieldStatusSpec.ts index c655f159d67..e0bd0075bc2 100644 --- a/eclipse-scout-core/test/form/fields/FieldStatusSpec.ts +++ b/eclipse-scout-core/test/form/fields/FieldStatusSpec.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2025 BSI Business Systems Integration AG + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -110,6 +110,170 @@ describe('FieldStatus', () => { }); }); + describe('enabled', () => { + it('is enabled but children disabled even if parent is disabled', () => { + let formField = scout.create(StringField, { + parent: session.desktop, + enabled: false, + menus: [{ + objectType: Menu, + childActions: [{objectType: Menu}] + }] + }); + formField.render(); + expect(formField.enabledComputed).toBe(false); + expect(formField.fieldStatus.enabledComputed).toBe(true); + expect(formField.menus[0].enabledComputed).toBe(false); + expect(formField.menus[0].childActions[0].enabledComputed).toBe(false); + + // open menu + formField.fieldStatus.doAction(); + expect(formField.fieldStatus.contextMenu.menuItems[0].enabledComputed).toBe(false); + expect(formField.fieldStatus.contextMenu.menuItems[0].childActions[0].enabledComputed).toBe(false); + + // close menu + formField.fieldStatus.contextMenu.animateRemoval = false; + formField.fieldStatus.doAction(); + expect(formField.fieldStatus.contextMenu).toBe(null); + + formField.setEnabled(true); + expect(formField.enabledComputed).toBe(true); + expect(formField.fieldStatus.enabledComputed).toBe(true); + expect(formField.menus[0].enabledComputed).toBe(true); + expect(formField.menus[0].childActions[0].enabledComputed).toBe(true); + + // open menu + formField.fieldStatus.doAction(); + expect(formField.fieldStatus.contextMenu.menuItems[0].enabledComputed).toBe(true); + expect(formField.fieldStatus.contextMenu.menuItems[0].childActions[0].enabledComputed).toBe(true); + + // close menu + formField.fieldStatus.contextMenu.animateRemoval = false; + formField.fieldStatus.doAction(); + expect(formField.fieldStatus.contextMenu).toBe(null); + + formField.setEnabled(false); + expect(formField.enabledComputed).toBe(false); + expect(formField.fieldStatus.enabledComputed).toBe(true); + expect(formField.menus[0].enabledComputed).toBe(false); + expect(formField.menus[0].childActions[0].enabledComputed).toBe(false); + + // open menu + formField.fieldStatus.doAction(); + expect(formField.fieldStatus.contextMenu.menuItems[0].enabledComputed).toBe(false); + expect(formField.fieldStatus.contextMenu.menuItems[0].childActions[0].enabledComputed).toBe(false); + + formField.insertMenu({objectType: Menu}); + expect(formField.menus[1].enabledComputed).toBe(false); + expect(formField.enabledComputed).toBe(false); + expect(formField.fieldStatus.enabledComputed).toBe(true); + + // close menu + formField.fieldStatus.contextMenu.animateRemoval = false; + formField.fieldStatus.doAction(); + expect(formField.fieldStatus.contextMenu).toBe(null); + + // open menu + formField.fieldStatus.doAction(); + expect(formField.fieldStatus.contextMenu.menuItems[1].enabledComputed).toBe(false); + + // close menu + formField.fieldStatus.contextMenu.animateRemoval = false; + formField.fieldStatus.doAction(); + expect(formField.fieldStatus.contextMenu).toBe(null); + + formField.insertMenu({objectType: Menu, inheritAccessibility: false}); + expect(formField.menus[2].enabledComputed).toBe(true); + expect(formField.enabledComputed).toBe(false); + expect(formField.fieldStatus.enabledComputed).toBe(true); + + // open menu + formField.fieldStatus.doAction(); + expect(formField.fieldStatus.contextMenu.menuItems[1].enabledComputed).toBe(false); + expect(formField.fieldStatus.contextMenu.menuItems[2].enabledComputed).toBe(true); + }); + + it('is enabled but children disabled even if parent is disabled and menus are shown in tooltip', () => { + let formField = scout.create(StringField, { + parent: session.desktop, + enabled: false, + tooltipText: 'hi', + menus: [{ + objectType: Menu, + childActions: [{objectType: Menu}] + }] + }); + formField.render(); + expect(formField.enabledComputed).toBe(false); + expect(formField.fieldStatus.enabledComputed).toBe(true); + expect(formField.menus[0].enabledComputed).toBe(false); + expect(formField.menus[0].childActions[0].enabledComputed).toBe(false); + + // open tooltip + formField.fieldStatus.doAction(); + expect(formField.fieldStatus.tooltip.menus[0].enabledComputed).toBe(false); + expect(formField.fieldStatus.tooltip.menus[0].childActions[0].enabledComputed).toBe(false); + + // close tooltip + formField.fieldStatus.doAction(); + expect(formField.fieldStatus.tooltip).toBe(null); + + formField.setEnabled(true); + expect(formField.enabledComputed).toBe(true); + expect(formField.fieldStatus.enabledComputed).toBe(true); + expect(formField.menus[0].enabledComputed).toBe(true); + expect(formField.menus[0].childActions[0].enabledComputed).toBe(true); + + // open tooltip + formField.fieldStatus.doAction(); + expect(formField.fieldStatus.tooltip.menus[0].enabledComputed).toBe(true); + expect(formField.fieldStatus.tooltip.menus[0].childActions[0].enabledComputed).toBe(true); + + // close tooltip + formField.fieldStatus.doAction(); + expect(formField.fieldStatus.tooltip).toBe(null); + + formField.setEnabled(false); + expect(formField.enabledComputed).toBe(false); + expect(formField.fieldStatus.enabledComputed).toBe(true); + expect(formField.menus[0].enabledComputed).toBe(false); + expect(formField.menus[0].childActions[0].enabledComputed).toBe(false); + + // open tooltip + formField.fieldStatus.doAction(); + expect(formField.fieldStatus.tooltip.menus[0].enabledComputed).toBe(false); + expect(formField.fieldStatus.tooltip.menus[0].childActions[0].enabledComputed).toBe(false); + + // close tooltip + formField.fieldStatus.doAction(); + expect(formField.fieldStatus.tooltip).toBe(null); + + formField.insertMenu({objectType: Menu}); + expect(formField.menus[1].enabledComputed).toBe(false); + expect(formField.enabledComputed).toBe(false); + expect(formField.fieldStatus.enabledComputed).toBe(true); + + // open tooltip + formField.fieldStatus.doAction(); + expect(formField.fieldStatus.tooltip.menus[1].enabledComputed).toBe(false); + + // close tooltip + formField.fieldStatus.tooltip.animateRemoval = false; + formField.fieldStatus.doAction(); + expect(formField.fieldStatus.tooltip).toBe(null); + + formField.insertMenu({objectType: Menu, inheritAccessibility: false}); + expect(formField.menus[2].enabledComputed).toBe(true); + expect(formField.enabledComputed).toBe(false); + expect(formField.fieldStatus.enabledComputed).toBe(true); + + // open tooltip + formField.fieldStatus.doAction(); + expect(formField.fieldStatus.tooltip.menus[1].enabledComputed).toBe(false); + expect(formField.fieldStatus.tooltip.menus[2].enabledComputed).toBe(true); + }); + }); + describe('parent changes visibility', () => { let formField: StringField;