diff --git a/packages/pluggableWidgets/combobox-web/e2e/OnChange.spec.js b/packages/pluggableWidgets/combobox-web/e2e/OnChange.spec.js new file mode 100644 index 0000000000..d40c3cf782 --- /dev/null +++ b/packages/pluggableWidgets/combobox-web/e2e/OnChange.spec.js @@ -0,0 +1,132 @@ +import { expect, test } from "@mendix/run-e2e/fixtures"; +import { waitForMendixApp, waitFrames } from "@mendix/run-e2e/mendix-helpers"; +import Combobox from "./utils/Combobox.pageObject"; +import { parseLogEntries } from "./utils/logEntryParser"; + +test.describe("combobox-web onChange", () => { + test.describe("boolean", () => { + test.beforeEach(async ({ page }) => { + await page.goto("/p/events/onchange/boolean"); + await waitForMendixApp(page); + }); + + test("should trigger onChange event", async ({ page }) => { + const combobox = new Combobox(getCombobox(page)); + + await combobox.selectOption("Yes"); + + const entries = await getLogs(page); + expect(entries[entries.length - 1].booleanAttr).toBe(true); + }); + }); + + test.describe("enum", () => { + test.beforeEach(async ({ page }) => { + await page.goto("/p/events/onchange/enum"); + await waitForMendixApp(page); + }); + + test("should trigger onChange event", async ({ page }) => { + const combobox = new Combobox(getCombobox(page)); + + await combobox.selectOption("Green"); + + const entries = await getLogs(page); + expect(entries[entries.length - 1].enumColorAttr).toBe("Green"); + }); + }); + + test.describe("single assoc", () => { + test.beforeEach(async ({ page }) => { + await page.goto("/p/events/onchange/singleassoc"); + await waitForMendixApp(page); + }); + + test("should trigger onChange event", async ({ page }) => { + const combobox = new Combobox(getCombobox(page)); + + await combobox.selectOption("Single Option nr.1"); + + const entries = await getLogs(page); + expect(entries[entries.length - 1].singleAssocTitle).toBe("Single Option nr.1"); + }); + }); + + test.describe("milti assoc", () => { + test.beforeEach(async ({ page }) => { + await page.goto("/p/events/onchange/multiassoc"); + await waitForMendixApp(page); + }); + + test("should trigger onChange event", async ({ page }) => { + const combobox = new Combobox(getCombobox(page)); + + await combobox.selectOption("Multi Option nr.1"); + await combobox.selectOption("Multi Option nr.2"); + + const entries = await getLogs(page); + console.log(entries); + expect(entries[entries.length - 1].multiAssocTitles).toEqual(["Multi Option nr.1", "Multi Option nr.2"]); + }); + }); + + test.describe("database options over string", () => { + test.beforeEach(async ({ page }) => { + await page.goto("/p/events/onchange/databaseoverstring"); + await waitForMendixApp(page); + }); + + test("should trigger onChange event", async ({ page }) => { + const combobox = new Combobox(getCombobox(page)); + + await combobox.selectOption("Single Option nr.2"); + + const entries = await getLogs(page); + expect(entries[entries.length - 1].stringAsOptionAttr).toBe("Single Option nr.2"); + }); + }); + + test.describe("static options over string", () => { + test.beforeEach(async ({ page }) => { + await page.goto("/p/events/onchange/staticoverstring"); + await waitForMendixApp(page); + }); + + test("should trigger onChange event", async ({ page }) => { + const combobox = new Combobox(getCombobox(page)); + + await combobox.selectOption("Option 3"); + + const entries = await getLogs(page); + expect(entries[entries.length - 1].stringAsOptionAttr).toBe("option3"); + }); + }); + + test.describe("read association to pass to onChange", () => { + test.beforeEach(async ({ page }) => { + await page.goto("/p/events/onchange/passassoc"); + await waitForMendixApp(page); + }); + + test("should trigger onChange event with updated value", async ({ page }) => { + const combobox = new Combobox(getCombobox(page)); + + await combobox.selectOption("Single Option nr.2"); + + const entries = await getLogs(page); + console.log(entries); + expect(entries[entries.length - 1].passedAssociationTitle).toBe("Single Option nr.2"); + }); + }); +}); + +function getCombobox(page) { + return page.locator(".mx-name-comboBox3"); +} + +async function getLogs(page) { + await waitFrames(page, 10); + const text = await page.locator(".mx-name-text2").innerText(); + + return parseLogEntries(text); +} diff --git a/packages/pluggableWidgets/combobox-web/e2e/utils/Combobox.pageObject.js b/packages/pluggableWidgets/combobox-web/e2e/utils/Combobox.pageObject.js new file mode 100644 index 0000000000..ce80de7b2c --- /dev/null +++ b/packages/pluggableWidgets/combobox-web/e2e/utils/Combobox.pageObject.js @@ -0,0 +1,56 @@ +export default class Combobox { + constructor(locator) { + this.locator = locator; + } + + async open() { + await this.locator.locator(".widget-combobox").click(); + } + + async close() { + await this.locator.page().keyboard.press("Escape"); + } + + getFilterInput() { + return this.locator.locator("input"); + } + + getMenu() { + return this.locator.locator(".widget-combobox-menu").first(); + } + + getOptions() { + return this.locator.locator("[role=listbox] [role=option]"); + } + + getOptionByText(text) { + return this.locator.locator(`[role=listbox] [role=option]:has-text("${text}")`); + } + + getSelectedText() { + return this.locator.locator(".widget-combobox-placeholder-text"); + } + + async filter(text) { + await this.getFilterInput().fill(text); + } + + isOpen() { + return this.getMenu().isVisible(); + } + + async selectOption(text) { + if (!(await this.isOpen())) { + await this.open(); + } + await this.getOptionByText(text).click({ delay: 10 }); + } + + async clear() { + await this.locator.locator(".widget-combobox-clear-button").first().click(); + } + + async removeSelectedOption(index = 0) { + await this.locator.locator(".widget-combobox-icon-container").nth(index).click(); + } +} diff --git a/packages/pluggableWidgets/combobox-web/e2e/utils/logEntryParser.js b/packages/pluggableWidgets/combobox-web/e2e/utils/logEntryParser.js new file mode 100644 index 0000000000..711befdb35 --- /dev/null +++ b/packages/pluggableWidgets/combobox-web/e2e/utils/logEntryParser.js @@ -0,0 +1,119 @@ +/** + * Helper for parsing onChange log output produced by the test project's + * change-tracking microflow, e.g.: + * + * [#false#Green####] + * [#false#Blue####] + * [#false#Red####] + * + * Which is generated in Mendix by concatenating: + * + * '[' + + * Title + '#' + + * toString(BooleanAttr) + '#' + + * toString(EnumColorAttr) + '#' + + * StringAsOptionAttr + '#' + + * OnChangeEntity_OnChangeSingleRelation/TitleSingle + '#' + + * Variable + '#' + + * OnChangeSingleRelation/TitleSingle + + * ']' + * + * Note: `Variable` here is + * `$OnChangeEntity/MyFirstModule.OnChangeEntity_OnChangeMultiRelation/MyFirstModule.OnChangeMultiRelation`, + * i.e. a multi-relation (list of objects). When Mendix renders a list + * association reference as part of a string concatenation, it produces a + * "!"-prefixed, "!"-separated list of the associated objects' name + * attributes, e.g.: + * + * [#false####!Multi Option nr.1#] + * [#false####!Multi Option nr.1!Multi Option nr.2#] + * [#false####!Multi Option nr.1!Multi Option nr.2!Multi Option nr.3#] + * + * This field is therefore parsed into an array of strings (e.g. + * `["Multi Option nr.1", "Multi Option nr.2"]`) rather than a single + * scalar value. + */ + +/** Ordered field names matching the microflow concatenation above. */ +const LOG_ENTRY_FIELDS = [ + "title", + "booleanAttr", + "enumColorAttr", + "stringAsOptionAttr", + "singleAssocTitle", + "multiAssocTitles", + "passedAssociationTitle" +]; + +/** Field names whose raw value is a comma-separated list (multi-relation). */ +const LIST_FIELDS = new Set(["multiAssocTitles"]); + +/** + * Converts a raw field string into a more meaningful JS value. + * Empty strings become `null`, and "true"/"false" become booleans. + * @param {string} value + * @returns {string | boolean | null} + */ +function coerceValue(value) { + if (value === "") { + return null; + } + if (value === "true" || value === "false") { + return value === "true"; + } + return value; +} + +/** + * Converts a raw "!"-prefixed, "!"-separated field string into an array of + * trimmed, non-empty strings. An empty input yields an empty array. + * + * e.g. "!Multi Option nr.1!Multi Option nr.2" -> ["Multi Option nr.1", "Multi Option nr.2"] + * @param {string} value + * @returns {string[]} + */ +function coerceListValue(value) { + if (value === "") { + return []; + } + return value + .split("!") + .map(item => item.trim()) + .filter(Boolean); +} + +/** + * Parses a single log entry line, e.g. "[#false#Green####]", into a + * structured object. + * @param {string} line + * @returns {Record} + */ +export function parseLogEntry(line) { + const trimmed = line.trim(); + const match = trimmed.match(/^\[(.*)]$/); + if (!match) { + throw new Error(`Invalid log entry format: "${line}"`); + } + + const fields = match[1].split("#"); + + return LOG_ENTRY_FIELDS.reduce((entry, fieldName, index) => { + const rawValue = fields[index] ?? ""; + entry[fieldName] = LIST_FIELDS.has(fieldName) ? coerceListValue(rawValue) : coerceValue(rawValue); + return entry; + }, {}); +} + +/** + * Parses multi-line log output into an array of structured entries. + * Blank lines are ignored. + * @param {string} text + * @returns {Array>} + */ +export function parseLogEntries(text) { + return text + .split("\n") + .map(line => line.trim()) + .filter(Boolean) + .map(parseLogEntry); +} diff --git a/packages/pluggableWidgets/combobox-web/openspec/changes/attach-onchange-to-attributes/.openspec.yaml b/packages/pluggableWidgets/combobox-web/openspec/changes/attach-onchange-to-attributes/.openspec.yaml new file mode 100644 index 0000000000..d7bc0110d8 --- /dev/null +++ b/packages/pluggableWidgets/combobox-web/openspec/changes/attach-onchange-to-attributes/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-08-10 diff --git a/packages/pluggableWidgets/combobox-web/openspec/changes/attach-onchange-to-attributes/design.md b/packages/pluggableWidgets/combobox-web/openspec/changes/attach-onchange-to-attributes/design.md new file mode 100644 index 0000000000..42d29d39a6 --- /dev/null +++ b/packages/pluggableWidgets/combobox-web/openspec/changes/attach-onchange-to-attributes/design.md @@ -0,0 +1,47 @@ +## Context + +The combobox widget supports multiple data sources (enum/boolean, static, association, database). Each has its own selector class. All of them currently fire `onChangeEvent` by calling `executeAction(this.onChangeEvent)` manually inside `setValue()`. This is boilerplate that every selector must remember to include and maintain. + +The Mendix Pluggable Widgets API supports declaring `onChange=""` on `type="attribute"` and `type="association"` properties in XML. When declared, the platform fires the bound action automatically whenever the widget calls `.setValue()` on that attribute — no code needed. + +## Goals / Non-Goals + +**Goals:** + +- Remove all manual `executeAction(this.onChangeEvent)` calls from selectors. +- Wire `onChange="onChangeEvent"` in XML for all five relevant attribute properties. +- Keep `onChangeDatabaseEvent` (Selection API path) exactly as-is. + +**Non-Goals:** + +- Changing when the action fires for any source type (this is a pure internal refactor — behaviour is identical). +- Modifying `DatabaseMultiSelectionSelector` or the `onChangeDatabaseEvent` mechanism. +- Adding new user-facing capabilities or changing the action's semantics. + +## Decisions + +### Decision: Wire onChange in XML rather than a shared base class + +Adding `onChange="onChangeEvent"` in XML is the platform-idiomatic approach. It removes the coupling between selector code and the action, and ensures the action fires even if a selector forgets to call it. The alternative — creating a shared base class method — still requires every selector to call the base, which is the same maintenance burden as today. + +### Decision: Remove the \_valuesIsEqual guard on database single select + +`DatabaseSingleSelectionSelector.setAttributeValue()` currently guards `executeAction` with `_valuesIsEqual`. This guard is redundant with the platform's own behaviour: the platform only fires `onChange` when the attribute value actually changes. The guard can be removed without any change in observable behaviour. + +### Decision: Keep onChangeEvent extraction in extractDatabaseProps only if needed elsewhere + +After removing `onChangeEvent` from `DatabaseSingleSelectionSelector`, the field is no longer needed in `extractDatabaseProps`'s return type. Remove it from there and from all other `utils.ts` extraction helpers where it is no longer consumed. + +## Risks / Trade-offs + +- **[Risk] Generated typings change** → Removing `onChangeEvent` from `updateProps` arguments means the generated `ComboboxProps.ts` (via `typings/`) may change. Verify that the generated props still include `onChangeEvent?: ActionValue` from the XML action definition, independent of the attribute `onChange` binding. + +## Migration Plan + +No migration required. This is a pure internal refactor: + +- XML `onChange` binding is additive. +- Removing `executeAction` calls from selectors is invisible to consumers. +- No public API, prop names, or user-facing behaviour changes. + +Rollback: revert the XML change and restore `executeAction` calls in the affected selector files. diff --git a/packages/pluggableWidgets/combobox-web/openspec/changes/attach-onchange-to-attributes/proposal.md b/packages/pluggableWidgets/combobox-web/openspec/changes/attach-onchange-to-attributes/proposal.md new file mode 100644 index 0000000000..6926650508 --- /dev/null +++ b/packages/pluggableWidgets/combobox-web/openspec/changes/attach-onchange-to-attributes/proposal.md @@ -0,0 +1,35 @@ +## Why + +The `onChangeEvent` action is currently fired manually via `executeAction()` in every selector's `setValue()` method. This is error-prone and inconsistent — each selector duplicates the call, and the database single selector has an extra guard that makes its behavior subtly different from the rest. The Mendix platform already supports automatic action firing via `onChange` on attribute properties in XML, which is the idiomatic way to hook into value changes. + +## What Changes + +- Add `onChange="onChangeEvent"` to each attribute property in `Combobox.xml`: `attributeEnumeration`, `attributeBoolean`, `staticAttribute`, `attributeAssociation`, and `databaseAttributeString`. +- Remove manual `executeAction(this.onChangeEvent)` calls from: + - `BaseAssociationSelector.setValue()` + - `EnumBoolSingleSelector.setValue()` + - `StaticSingleSelector.setValue()` + - `DatabaseSingleSelectionSelector.setAttributeValue()` +- Remove the `onChangeEvent` field and its extraction from selector classes and `extractDatabaseProps` / `extractAssociationProps` / other `utils.ts` helpers. +- No behavioral difference: the platform also only fires `onChange` when the attribute value actually changes, so the existing `_valuesIsEqual` guard in `DatabaseSingleSelectionSelector` becomes redundant and is removed. + +## Capabilities + +### New Capabilities + +- `attribute-driven-onchange`: The `onChangeEvent` action is wired to attribute properties in XML so the platform fires it automatically on every attribute value change, eliminating manual `executeAction` calls in selectors. + +### Modified Capabilities + + + +## Impact + +- `src/Combobox.xml`: 5 attribute properties gain `onChange="onChangeEvent"`. +- `src/helpers/Association/BaseAssociationSelector.ts`: remove `onChangeEvent` field and `executeAction` call. +- `src/helpers/EnumBool/EnumBoolSingleSelector.tsx`: remove `onChangeEvent` field and `executeAction` call. +- `src/helpers/Static/StaticSingleSelector.ts`: remove `onChangeEvent` field and `executeAction` call. +- `src/helpers/Database/DatabaseSingleSelectionSelector.ts`: remove `onChangeEvent` field and `executeAction` call (and the `_valuesIsEqual` guard around it). +- `src/helpers/Database/utils.ts`: remove `onChangeEvent` from `ExtractionReturnValue` and `extractDatabaseProps`. +- `src/helpers/Association/utils.ts`, `src/helpers/Static/utils.ts`, `src/helpers/EnumBool/utils.ts`: remove `onChangeEvent` extraction where present. +- No public API changes. No new dependencies. `DatabaseMultiSelectionSelector` is unaffected. diff --git a/packages/pluggableWidgets/combobox-web/openspec/changes/attach-onchange-to-attributes/specs/attribute-driven-onchange/spec.md b/packages/pluggableWidgets/combobox-web/openspec/changes/attach-onchange-to-attributes/specs/attribute-driven-onchange/spec.md new file mode 100644 index 0000000000..89181dba6e --- /dev/null +++ b/packages/pluggableWidgets/combobox-web/openspec/changes/attach-onchange-to-attributes/specs/attribute-driven-onchange/spec.md @@ -0,0 +1,57 @@ +## ADDED Requirements + +### Requirement: Platform fires onChangeEvent automatically via XML attribute binding + +The `onChangeEvent` action SHALL be declared in the XML with `onChange="onChangeEvent"` on each relevant attribute property (`attributeEnumeration`, `attributeBoolean`, `staticAttribute`, `attributeAssociation`, `databaseAttributeString`). The Mendix platform SHALL fire the action automatically whenever the attribute value is set, without any manual `executeAction()` call in selector code. + +#### Scenario: Action fires on enum attribute change + +- **WHEN** the user selects a different enum option in the combobox +- **THEN** the platform fires `onChangeEvent` automatically after `attributeEnumeration.setValue()` is called + +#### Scenario: Action fires on boolean attribute change + +- **WHEN** the user selects a different boolean option in the combobox +- **THEN** the platform fires `onChangeEvent` automatically after `attributeBoolean.setValue()` is called + +#### Scenario: Action fires on static attribute change + +- **WHEN** the user selects a different option from a static datasource combobox +- **THEN** the platform fires `onChangeEvent` automatically after `staticAttribute.setValue()` is called + +#### Scenario: Action fires on association attribute change + +- **WHEN** the user selects a different item in an association-backed combobox +- **THEN** the platform fires `onChangeEvent` automatically after `attributeAssociation.setValue()` is called +- **THEN** the platform does NOT fire `onChangeEvent` if the same item is selected again (value unchanged) + +#### Scenario: Action fires on database single select attribute change + +- **WHEN** the user selects a different item in a database-backed single-select combobox that has `databaseAttributeString` configured +- **THEN** the platform fires `onChangeEvent` automatically after `databaseAttributeString.setValue()` is called +- **THEN** the platform does NOT fire `onChangeEvent` if the same item is selected again (value unchanged) + +#### Scenario: Action does not fire when database attribute is not configured + +- **WHEN** the database-source combobox is used without `databaseAttributeString` configured +- **THEN** `onChangeEvent` SHALL NOT fire (no attribute to bind to) + +### Requirement: No manual executeAction calls in selectors + +Selector classes SHALL NOT call `executeAction(this.onChangeEvent)` directly. The `onChangeEvent` field SHALL be removed from all selector classes and their `updateProps` methods. + +#### Scenario: Selector setValue does not call executeAction + +- **WHEN** `selector.setValue()` is called for any source type (enum, boolean, static, association, database single) +- **THEN** the selector writes the attribute value without calling `executeAction` +- **THEN** the platform fires the action via the XML `onChange` binding + +### Requirement: Database multi-select is unaffected + +`DatabaseMultiSelectionSelector` SHALL continue to use `onChangeDatabaseEvent` via the Mendix Selection API. No XML `onChange` binding is added for the database multi-select path. + +#### Scenario: Database multi-select action unchanged + +- **WHEN** the user selects or deselects items in a database-backed multi-select combobox +- **THEN** `onChangeDatabaseEvent` fires via the platform's Selection API +- **THEN** `onChangeEvent` is not involved in this path diff --git a/packages/pluggableWidgets/combobox-web/openspec/changes/attach-onchange-to-attributes/tasks.md b/packages/pluggableWidgets/combobox-web/openspec/changes/attach-onchange-to-attributes/tasks.md new file mode 100644 index 0000000000..7b36d881c9 --- /dev/null +++ b/packages/pluggableWidgets/combobox-web/openspec/changes/attach-onchange-to-attributes/tasks.md @@ -0,0 +1,32 @@ +## 1. XML: Add onChange binding to attribute properties + +- [ ] 1.1 Add `onChange="onChangeEvent"` to `attributeEnumeration` in `Combobox.xml` +- [ ] 1.2 Add `onChange="onChangeEvent"` to `attributeBoolean` in `Combobox.xml` +- [ ] 1.3 Add `onChange="onChangeEvent"` to `staticAttribute` in `Combobox.xml` +- [ ] 1.4 Add `onChange="onChangeEvent"` to `attributeAssociation` in `Combobox.xml` +- [ ] 1.5 Add `onChange="onChangeEvent"` to `databaseAttributeString` in `Combobox.xml` + +## 2. Remove manual executeAction calls from selectors + +- [ ] 2.1 Remove `onChangeEvent` field and `executeAction(this.onChangeEvent)` call from `BaseAssociationSelector.setValue()` +- [ ] 2.2 Remove `onChangeEvent` field and `executeAction(this.onChangeEvent)` call from `EnumBoolSingleSelector.setValue()` +- [ ] 2.3 Remove `onChangeEvent` field and `executeAction(this.onChangeEvent)` call from `StaticSingleSelector.setValue()` +- [ ] 2.4 Remove `onChangeEvent` field, `_valuesIsEqual` guard, and `executeAction(this.onChangeEvent)` call from `DatabaseSingleSelectionSelector.setAttributeValue()` + +## 3. Clean up onChangeEvent extraction in utility helpers + +- [ ] 3.1 Remove `onChangeEvent` from `ExtractionReturnValue` type and `extractDatabaseProps` return in `Database/utils.ts` +- [ ] 3.2 Remove `onChangeEvent` extraction from `Association/utils.ts` if present +- [ ] 3.3 Remove `onChangeEvent` extraction from `Static/utils.ts` if present +- [ ] 3.4 Remove `onChangeEvent` extraction from `EnumBool/utils.ts` if present +- [ ] 3.5 Verify no remaining references to `this.onChangeEvent` in any selector file + +## 4. Verify generated typings are correct + +- [ ] 4.1 Run `pnpm turbo build` and confirm `typings/ComboboxProps.ts` still includes `onChangeEvent?: ActionValue` from the XML action definition +- [ ] 4.2 Confirm no TypeScript errors from removed fields + +## 5. Run tests + +- [ ] 5.1 Run `pnpm run test` in `combobox-web` and confirm all existing tests pass +- [ ] 5.2 Update any unit tests that assert `executeAction` is called directly in selectors to instead assert the attribute `setValue` was called diff --git a/packages/pluggableWidgets/combobox-web/src/Combobox.xml b/packages/pluggableWidgets/combobox-web/src/Combobox.xml index 32e6d4261a..9f19b10211 100644 --- a/packages/pluggableWidgets/combobox-web/src/Combobox.xml +++ b/packages/pluggableWidgets/combobox-web/src/Combobox.xml @@ -30,14 +30,14 @@ - + Attribute - + Attribute @@ -104,7 +104,7 @@ - + Target @@ -128,7 +128,7 @@ - + Entity @@ -144,7 +144,7 @@ - + Attribute @@ -314,7 +314,6 @@ - On change diff --git a/packages/pluggableWidgets/combobox-web/src/helpers/Association/AssociationMultiSelector.ts b/packages/pluggableWidgets/combobox-web/src/helpers/Association/AssociationMultiSelector.ts index 7bbff280cc..ed4c9ad155 100644 --- a/packages/pluggableWidgets/combobox-web/src/helpers/Association/AssociationMultiSelector.ts +++ b/packages/pluggableWidgets/combobox-web/src/helpers/Association/AssociationMultiSelector.ts @@ -39,7 +39,6 @@ export class AssociationMultiSelector setValue(value: string[] | null): void { const newValue = value?.map(v => this.options._optionToValue(v)!); this._attr?.setValue(newValue); - super.setValue(value); } getOptions(): string[] { diff --git a/packages/pluggableWidgets/combobox-web/src/helpers/Association/AssociationSingleSelector.ts b/packages/pluggableWidgets/combobox-web/src/helpers/Association/AssociationSingleSelector.ts index 5ad879285c..8f0455dbcd 100644 --- a/packages/pluggableWidgets/combobox-web/src/helpers/Association/AssociationSingleSelector.ts +++ b/packages/pluggableWidgets/combobox-web/src/helpers/Association/AssociationSingleSelector.ts @@ -14,6 +14,5 @@ export class AssociationSingleSelector } setValue(value: string | null): void { this._attr?.setValue(this.options._optionToValue(value)); - super.setValue(value); } } diff --git a/packages/pluggableWidgets/combobox-web/src/helpers/Association/BaseAssociationSelector.ts b/packages/pluggableWidgets/combobox-web/src/helpers/Association/BaseAssociationSelector.ts index a4e65f8c16..30b4bad3ea 100644 --- a/packages/pluggableWidgets/combobox-web/src/helpers/Association/BaseAssociationSelector.ts +++ b/packages/pluggableWidgets/combobox-web/src/helpers/Association/BaseAssociationSelector.ts @@ -1,5 +1,4 @@ -import { ActionValue, ListAttributeValue, ObjectItem, ReferenceSetValue, ReferenceValue } from "mendix"; -import { executeAction } from "@mendix/widget-plugin-platform/framework/execute-action"; +import { ListAttributeValue, ObjectItem, ReferenceSetValue, ReferenceValue } from "mendix"; import { ComboboxContainerProps, LoadingTypeEnum, @@ -23,7 +22,6 @@ export class BaseAssociationSelector = new Map(); private lazyLoader: LazyLoadProvider = new LazyLoadProvider(); @@ -41,7 +39,6 @@ export class BaseAssociationSelector | undefined, boolean, FilterTypeEnum, - ActionValue | undefined, ListWidgetValue | undefined, OptionsSourceAssociationCustomContentTypeEnum, boolean, @@ -35,7 +33,6 @@ type ExtractionReturnValue = [ export function extractAssociationProps(props: ComboboxContainerProps): ExtractionReturnValue { const attr = props.attributeAssociation; const filterType = props.filterType; - const onChangeEvent = props.onChangeEvent; const filterInputDebounceInterval = props.filterInputDebounceInterval; if (!attr) { @@ -77,7 +74,6 @@ export function extractAssociationProps(props: ComboboxContainerProps): Extracti emptyOption, clearable, filterType, - onChangeEvent, customContent, customContentType, lazyLoading, diff --git a/packages/pluggableWidgets/combobox-web/src/helpers/Database/DatabaseSingleSelectionSelector.ts b/packages/pluggableWidgets/combobox-web/src/helpers/Database/DatabaseSingleSelectionSelector.ts index 610d022087..6510505fa6 100644 --- a/packages/pluggableWidgets/combobox-web/src/helpers/Database/DatabaseSingleSelectionSelector.ts +++ b/packages/pluggableWidgets/combobox-web/src/helpers/Database/DatabaseSingleSelectionSelector.ts @@ -1,4 +1,4 @@ -import { ActionValue, EditableValue, ListAttributeValue, ObjectItem, SelectionSingleValue } from "mendix"; +import { EditableValue, ListAttributeValue, ObjectItem, SelectionSingleValue } from "mendix"; import { ComboboxContainerProps, LoadingTypeEnum, @@ -11,7 +11,6 @@ import { DatabaseCaptionsProvider } from "./DatabaseCaptionsProvider"; import { DatabaseOptionsProvider } from "./DatabaseOptionsProvider"; import { DatabaseValuesProvider } from "./DatabaseValuesProvider"; import { extractDatabaseProps, getReadonly } from "./utils"; -import { executeAction } from "@mendix/widget-plugin-platform/framework/execute-action"; export class DatabaseSingleSelectionSelector< T extends string | Big, @@ -32,7 +31,6 @@ export class DatabaseSingleSelectionSelector< validation?: string = undefined; values: DatabaseValuesProvider; - private onChangeEvent?: ActionValue; protected _attr: R | undefined; private selection?: SelectionSingleValue; @@ -55,8 +53,7 @@ export class DatabaseSingleSelectionSelector< filterType, lazyLoading, loadingType, - valueSourceAttribute, - onChangeEvent + valueSourceAttribute } = extractDatabaseProps(props); if (ds.status === "loading" && (!lazyLoading || ds.limit !== Infinity)) { @@ -64,7 +61,6 @@ export class DatabaseSingleSelectionSelector< } this._attr = targetAttribute as R; - this.onChangeEvent = onChangeEvent; this.readOnly = getReadonly(targetAttribute, props.customEditability, props.customEditabilityExpression); this.lazyLoader.updateProps(ds); this.lazyLoader.setLimit( @@ -154,11 +150,7 @@ export class DatabaseSingleSelectionSelector< setAttributeValue(value: T): void { if (this._attr) { - const oldValue = this._attr.value; this._attr.setValue(value); - if (!_valuesIsEqual(oldValue, value)) { - executeAction(this.onChangeEvent); - } } } diff --git a/packages/pluggableWidgets/combobox-web/src/helpers/Database/utils.ts b/packages/pluggableWidgets/combobox-web/src/helpers/Database/utils.ts index b2a8ecfe6e..11a600d225 100644 --- a/packages/pluggableWidgets/combobox-web/src/helpers/Database/utils.ts +++ b/packages/pluggableWidgets/combobox-web/src/helpers/Database/utils.ts @@ -1,5 +1,4 @@ import { - ActionValue, DynamicValue, EditableValue, ListAttributeValue, @@ -30,7 +29,6 @@ type ExtractionReturnValue = { loadingType: LoadingTypeEnum; valueSourceAttribute: ListAttributeValue | undefined; filterInputDebounceInterval: number; - onChangeEvent: ActionValue | undefined; }; export function extractDatabaseProps(props: ComboboxContainerProps): ExtractionReturnValue { @@ -74,8 +72,6 @@ export function extractDatabaseProps(props: ComboboxContainerProps): ExtractionR } } - const onChangeEvent = props.onChangeEvent; - return { targetAttribute, captionProvider: captionType === "attribute" ? captionAttribute : captionExpression, @@ -89,8 +85,7 @@ export function extractDatabaseProps(props: ComboboxContainerProps): ExtractionR lazyLoading, loadingType, valueSourceAttribute, - filterInputDebounceInterval, - onChangeEvent + filterInputDebounceInterval }; } diff --git a/packages/pluggableWidgets/combobox-web/src/helpers/EnumBool/EnumBoolSingleSelector.tsx b/packages/pluggableWidgets/combobox-web/src/helpers/EnumBool/EnumBoolSingleSelector.tsx index 8caf2e00f1..d0db872e39 100644 --- a/packages/pluggableWidgets/combobox-web/src/helpers/EnumBool/EnumBoolSingleSelector.tsx +++ b/packages/pluggableWidgets/combobox-web/src/helpers/EnumBool/EnumBoolSingleSelector.tsx @@ -1,5 +1,4 @@ -import { executeAction } from "@mendix/widget-plugin-platform/framework/execute-action"; -import { ActionValue, EditableValue } from "mendix"; +import { EditableValue } from "mendix"; import { ComboboxContainerProps, OptionsSourceAssociationCustomContentTypeEnum } from "../../../typings/ComboboxProps"; import { SingleSelector, Status } from "../types"; import { EnumAndBooleanSimpleCaptionsProvider } from "./EnumAndBooleanSimpleCaptionsProvider"; @@ -12,7 +11,6 @@ export class EnumBooleanSingleSelector implements SingleSelector { validation?: string = undefined; private isBoolean = false; private _attr: EditableValue | undefined; - private onChangeEvent?: ActionValue; currentId: string | null = null; caption: EnumAndBooleanSimpleCaptionsProvider; @@ -48,7 +46,6 @@ export class EnumBooleanSingleSelector implements SingleSelector { return; } - this.onChangeEvent = props.onChangeEvent; this.status = attr.status; this.isBoolean = typeof attr.universe?.[0] === "boolean"; this.clearable = this.isBoolean ? false : clearable; @@ -59,6 +56,5 @@ export class EnumBooleanSingleSelector implements SingleSelector { setValue(value: string | null): void { this._attr?.setValue(this.options._optionToValue(value)); - executeAction(this.onChangeEvent); } } diff --git a/packages/pluggableWidgets/combobox-web/src/helpers/Static/StaticSingleSelector.ts b/packages/pluggableWidgets/combobox-web/src/helpers/Static/StaticSingleSelector.ts index 6a99e6bff9..481de479c8 100644 --- a/packages/pluggableWidgets/combobox-web/src/helpers/Static/StaticSingleSelector.ts +++ b/packages/pluggableWidgets/combobox-web/src/helpers/Static/StaticSingleSelector.ts @@ -1,4 +1,4 @@ -import { ActionValue, EditableValue } from "mendix"; +import { EditableValue } from "mendix"; import { ComboboxContainerProps, OptionsSourceStaticDataSourceType, @@ -8,7 +8,6 @@ import { SingleSelector, Status } from "../types"; import { StaticOptionsProvider } from "./StaticOptionsProvider"; import { StaticCaptionsProvider } from "./StaticCaptionsProvider"; import { extractStaticProps } from "./utils"; -import { executeAction } from "@mendix/widget-plugin-platform/framework/execute-action"; import { _valuesIsEqual } from "../utils"; export class StaticSingleSelector implements SingleSelector { @@ -24,7 +23,6 @@ export class StaticSingleSelector implements SingleSelector { customContentType: StaticDataSourceCustomContentTypeEnum = "no"; validation?: string = undefined; protected _attr: EditableValue | undefined; - private onChangeEvent?: ActionValue; private _objectsMap: Map = new Map(); constructor() { @@ -33,8 +31,7 @@ export class StaticSingleSelector implements SingleSelector { } updateProps(props: ComboboxContainerProps): void { - const [attr, ds, emptyOption, clearable, filterType, onChangeEvent, customContentType] = - extractStaticProps(props); + const [attr, ds, emptyOption, clearable, filterType, customContentType] = extractStaticProps(props); this._attr = attr; this.caption.updateProps({ emptyOptionText: emptyOption, @@ -70,7 +67,6 @@ export class StaticSingleSelector implements SingleSelector { this.clearable = clearable; this.status = attr.status; this.readOnly = attr.readOnly; - this.onChangeEvent = onChangeEvent; this.customContentType = customContentType; this.validation = attr.validation; this.attributeType = @@ -85,6 +81,5 @@ export class StaticSingleSelector implements SingleSelector { const value = this._objectsMap.get(key || ""); this._attr?.setValue(value?.staticDataSourceValue.value); this.currentId = key; - executeAction(this.onChangeEvent); } } diff --git a/packages/pluggableWidgets/combobox-web/src/helpers/Static/utils.ts b/packages/pluggableWidgets/combobox-web/src/helpers/Static/utils.ts index cb5c7f0730..688e7c4ac9 100644 --- a/packages/pluggableWidgets/combobox-web/src/helpers/Static/utils.ts +++ b/packages/pluggableWidgets/combobox-web/src/helpers/Static/utils.ts @@ -1,4 +1,4 @@ -import { ActionValue, DynamicValue, EditableValue } from "mendix"; +import { DynamicValue, EditableValue } from "mendix"; import { ComboboxContainerProps, FilterTypeEnum, @@ -12,14 +12,12 @@ type ExtractionReturnValue = [ DynamicValue | undefined, boolean, FilterTypeEnum, - ActionValue | undefined, StaticDataSourceCustomContentTypeEnum ]; export function extractStaticProps(props: ComboboxContainerProps): ExtractionReturnValue { const attr = props.staticAttribute; const filterType = props.filterType; - const onChangeEvent = props.onChangeEvent; if (!attr) { throw new Error("'optionsSourceType' type is 'Database' but 'databaseAttributeString' is not defined."); @@ -33,5 +31,5 @@ export function extractStaticProps(props: ComboboxContainerProps): ExtractionRet const clearable = typeof props.staticAttribute.value === "boolean" ? false : props.clearable; const customContentType = props.staticDataSourceCustomContentType; - return [attr, ds, emptyOption, clearable, filterType, onChangeEvent, customContentType]; + return [attr, ds, emptyOption, clearable, filterType, customContentType]; } diff --git a/packages/pluggableWidgets/combobox-web/typings/ComboboxProps.d.ts b/packages/pluggableWidgets/combobox-web/typings/ComboboxProps.d.ts index 0705b92135..c17090dba5 100644 --- a/packages/pluggableWidgets/combobox-web/typings/ComboboxProps.d.ts +++ b/packages/pluggableWidgets/combobox-web/typings/ComboboxProps.d.ts @@ -86,7 +86,6 @@ export interface ComboboxContainerProps { customEditability: CustomEditabilityEnum; customEditabilityExpression: DynamicValue; readOnlyStyle: ReadOnlyStyleEnum; - onChangeEvent?: ActionValue; onEnterEvent?: ActionValue; onLeaveEvent?: ActionValue; onChangeFilterInputEvent?: ActionValue<{ filterInput: Option }>;