Skip to content

[WTF-2132] Generate types for actions with action variables #115

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions packages/pluggable-widgets-tools/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

## [Unreleased]

### Added

- We added support for action variables, introduced in Mendix 10.21.

### Changed

- We updated the Mendix package to 10.21.64362.

## [10.18.2] - 2025-03-21

### Fixed
Expand Down
18 changes: 9 additions & 9 deletions packages/pluggable-widgets-tools/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions packages/pluggable-widgets-tools/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@mendix/pluggable-widgets-tools",
"version": "10.18.2",
"version": "10.21.0",
"description": "Mendix Pluggable Widgets Tools",
"engines": {
"node": ">=20"
Expand Down Expand Up @@ -81,7 +81,7 @@
"jest-junit": "^13.0.0",
"jest-react-hooks-shallow": "^1.5.1",
"make-dir": "^3.1.0",
"mendix": "^10.18.54340",
"mendix": "^10.21.64362",
"metro-react-native-babel-preset": "^0.74.1",
"mime": "^3.0.0",
"node-fetch": "^2.6.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export interface Property {
properties?: Properties[];
enumerationValues?: Enumeration[];
selectionTypes?: SelectionTypes[];
actionVariables?: ActionVariableTypes[];
}

export interface AttributeType {
Expand Down Expand Up @@ -123,3 +124,14 @@ export interface SelectionType {
name: string;
};
}

export interface ActionVariableTypes {
actionVariable: ActionVariableType[];
}

export interface ActionVariableType {
$: {
key: string;
type: string;
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ import { attributeMetaDataNativeInput, attributeMetaDataWebInput } from "./input
import { attributeMetaDataNativeOutput, attributeMetaDataWebOutput } from "./outputs/metadata-attribute";
import { associationMetaDataNativeInput, associationMetaDataWebInput } from "./inputs/metadata-association";
import { associationMetaDataNativeOutput, associationMetaDataWebOutput } from "./outputs/metadata-association";
import {listActionWithVariablesInput, listActionWithVariablesInputNative} from "./inputs/list-action-with-variables";
import {listActionWithVariablesOutput, listActionWithVariablesOutputNative} from "./outputs/list-action-with-variables";

describe("Generating tests", () => {
it("Generates a parsed typing from XML for native", () => {
Expand Down Expand Up @@ -75,6 +77,16 @@ describe("Generating tests", () => {
expect(newContent).toBe(listActionWebOutput);
});

it("Generates a parsed typing from XML for native using list of actions with variables", () => {
const newContent = generateNativeTypesFor(listActionWithVariablesInputNative);
expect(newContent).toBe(listActionWithVariablesOutputNative);
});

it("Generates a parsed typing from XML for web using list of actions with variables", () => {
const newContent = generateFullTypesFor(listActionWithVariablesInput);
expect(newContent).toBe(listActionWithVariablesOutput);
});

it("Generates a parsed typing from XML for native using list of images", () => {
const newContent = generateNativeTypesFor(listImageInputNative);
expect(newContent).toBe(listImageNativeOutput);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
export const listActionWithVariablesInput = `<?xml version="1.0" encoding="utf-8"?>
<widget id="mendix.mywidget.MyWidget" needsEntityContext="true" offlineCapable="true" pluginWidget="true"
xmlns="http://www.mendix.com/widget/1.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.mendix.com/widget/1.0/ ../xsd/widget.xsd">
<properties>
<propertyGroup caption="Events">
<property key="actions" type="object" isList="true">
<caption>Actions</caption>
<description />
<properties>
<propertyGroup caption="Action">
<property key="description" type="attribute">
<caption>Action</caption>
<description />
<attributeTypes>
<attributeType name="String"/>
</attributeTypes>
</property>
<property key="action" type="action">
<caption>Action</caption>
<description />
<actionVariables>
<actionVariable key="boolean_v" type="Boolean" caption="Boolean" />
<actionVariable key="integer_v" type="Integer" caption="Integer" />
<actionVariable key="datetime_v" type="DateTime" caption="DateTime" />
<actionVariable key="string_v" type="String" caption="String" />
<actionVariable key="decimal_v" type="Decimal" caption="Decimal" />
</actionVariables>
</property>
</propertyGroup>
</properties>
</property>
</propertyGroup>
<propertyGroup caption="System Properties">
<systemProperty key="Label"></systemProperty>
<systemProperty key="TabIndex"></systemProperty>
</propertyGroup>
</properties>
</widget>`;

export const listActionWithVariablesInputNative = `<?xml version="1.0" encoding="utf-8"?>
<widget id="mendix.mywidget.MyWidget" needsEntityContext="true" offlineCapable="true" pluginWidget="true" supportedPlatform="Native"
xmlns="http://www.mendix.com/widget/1.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.mendix.com/widget/1.0/ ../xsd/widget.xsd">
<properties>
<propertyGroup caption="Events">
<property key="actions" type="object" isList="true">
<caption>Actions</caption>
<description />
<properties>
<propertyGroup caption="Action">
<property key="description" type="attribute">
<caption>Action</caption>
<description />
<attributeTypes>
<attributeType name="String"/>
</attributeTypes>
</property>
<property key="action" type="action">
<caption>Action</caption>
<description />
<actionVariables>
<actionVariable key="boolean_v" type="Boolean" caption="Boolean" />
<actionVariable key="integer_v" type="Integer" caption="Integer" />
<actionVariable key="datetime_v" type="DateTime" caption="DateTime" />
<actionVariable key="string_v" type="String" caption="String" />
<actionVariable key="decimal_v" type="Decimal" caption="Decimal" />
</actionVariables>
</property>
</propertyGroup>
</properties>
</property>
</propertyGroup>
<propertyGroup caption="System Properties">
<systemProperty key="Label"></systemProperty>
<systemProperty key="TabIndex"></systemProperty>
</propertyGroup>
</properties>
</widget>`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
export const listActionWithVariablesOutput = `/**
* This file was generated from MyWidget.xml
* WARNING: All changes made to this file will be overwritten
* @author Mendix Widgets Framework Team
*/
import { ActionValue, EditableValue, Option } from "mendix";
import { Big } from "big.js";

export interface ActionsType {
description: EditableValue<string>;
action?: ActionValue<{ boolean_v: Option<boolean>; integer_v: Option<Big>; datetime_v: Option<Date>; string_v: Option<string>; decimal_v: Option<Big> }>;
}

export interface ActionsPreviewType {
description: string;
action: {} | null;
}

export interface MyWidgetContainerProps {
name: string;
tabIndex?: number;
id: string;
actions: ActionsType[];
}

export interface MyWidgetPreviewProps {
readOnly: boolean;
renderMode: "design" | "xray" | "structure";
translate: (text: string) => string;
actions: ActionsPreviewType[];
}
`;
export const listActionWithVariablesOutputNative = `export interface ActionsType {
description: EditableValue<string>;
action?: ActionValue<{ boolean_v: Option<boolean>; integer_v: Option<Big>; datetime_v: Option<Date>; string_v: Option<string>; decimal_v: Option<Big> }>;
}

export interface MyWidgetProps<Style> {
name: string;
style: Style[];
actions: ActionsType[];
}`;
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const mxExports = [
"ListValue",
"NativeIcon",
"NativeImage",
"Option",
"ListActionValue",
"ListAttributeValue",
"ListAttributeListValue",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Property, ReturnType, SystemProperty } from "./WidgetXml";
import { ActionVariableTypes, Property, ReturnType, SystemProperty } from "./WidgetXml";
import { capitalizeFirstLetter, commasAnd, extractProperties } from "./helpers";

export function generateClientTypes(
Expand Down Expand Up @@ -100,6 +100,14 @@ export function hasOptionalDataSource(prop: Property, resolveProp: (key: string)
return prop.$.dataSource && resolveProp(prop.$.dataSource)?.$.required === "false";
}

function toActionVariablesOutputType(actionVariables?: ActionVariableTypes[]) {
const types = actionVariables?.flatMap(av => av.actionVariable)
.map(avt => `${avt.$.key}: ${toOption(toAttributeClientType(avt.$.type))}`)
.join("; ");

return types ? `<{ ${types} }>` : "";
}

function toClientPropType(
prop: Property,
isNative: boolean,
Expand All @@ -112,7 +120,8 @@ function toClientPropType(
case "string":
return "string";
case "action":
return prop.$.dataSource ? "ListActionValue" : "ActionValue";
const variableTypes = toActionVariablesOutputType(prop.actionVariables);
return (prop.$.dataSource ? "ListActionValue" : "ActionValue") + variableTypes;
case "textTemplate":
return prop.$.dataSource ? "ListExpressionValue<string>" : "DynamicValue<string>";
case "integer":
Expand Down Expand Up @@ -328,6 +337,10 @@ function toSelectionClientType(xmlType: string) {
}
}

function toOption(type: string) {
return `Option<${type}>`;
}

export function toUniqueUnionType(types: string[]) {
return types.length ? Array.from(new Set(types)).join(" | ") : "any";
}
Loading