Skip to content

[WC-2984] Events: Parameter type support #1771

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

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
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
4 changes: 4 additions & 0 deletions packages/pluggableWidgets/events-web/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

## [Unreleased]

### Added

- Added support for using expressions to configure both delay and repeat interval values during component load. Expression support was also extended to delay values when attributes change dynamically.

## [1.0.1] - 2024-04-25

### Fixed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,27 @@ export function getProperties(
defaultProperties: Properties /* , target: Platform*/
): Properties {
if (!values.componentLoadRepeat) {
hidePropertiesIn(defaultProperties, values, ["componentLoadRepeatInterval"]);
hidePropertiesIn(defaultProperties, values, [
"componentLoadRepeatInterval",
"componentLoadRepeatIntervalExpression",
"componentLoadRepeatIntervalParameterType"
]);
} else {
if (values.componentLoadRepeatIntervalParameterType === "expression") {
hidePropertiesIn(defaultProperties, values, ["componentLoadRepeatInterval"]);
} else {
hidePropertiesIn(defaultProperties, values, ["componentLoadRepeatIntervalExpression"]);
}
}
if (values.componentLoadDelayParameterType === "expression") {
hidePropertiesIn(defaultProperties, values, ["componentLoadDelay"]);
} else {
hidePropertiesIn(defaultProperties, values, ["componentLoadDelayExpression"]);
}
if (values.onEventChangeDelayParameterType === "expression") {
hidePropertiesIn(defaultProperties, values, ["onEventChangeDelay"]);
} else {
hidePropertiesIn(defaultProperties, values, ["onEventChangeDelayExpression"]);
}
return defaultProperties;
}
Expand Down
36 changes: 29 additions & 7 deletions packages/pluggableWidgets/events-web/src/Events.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,49 @@ import { EditableValue } from "mendix";
import { ReactElement, createElement, useRef } from "react";
import { EventsContainerProps } from "../typings/EventsProps";
import { useActionTimer } from "./hooks/timer";
import { useParameterValue } from "./hooks/parameterValue";
import "./ui/Events.scss";

export default function Events(props: EventsContainerProps): ReactElement {
const {
class: className,
onComponentLoad,
componentLoadDelayExpression,
componentLoadDelay,
componentLoadRepeat,
componentLoadRepeatInterval,
onEventChangeAttribute,
onEventChange,
onEventChangeDelay
// optionsSourceType
componentLoadRepeatInterval,
componentLoadRepeatIntervalParameterType,
componentLoadRepeatIntervalExpression,
componentLoadDelayParameterType,
onEventChangeDelay,
onEventChangeDelayParameterType,
onEventChangeDelayExpression
} = props;
const prevOnChangeAttributeValue = useRef<EditableValue<any> | undefined>();

const delayValue = useParameterValue({
parameterType: componentLoadDelayParameterType,
parameterValue: componentLoadDelay,
parameterExpression: componentLoadDelayExpression
});
const intervalValue = useParameterValue({
parameterType: componentLoadRepeatIntervalParameterType,
parameterValue: componentLoadRepeatInterval,
parameterExpression: componentLoadRepeatIntervalExpression
});
const onEventChangeDelayValue = useParameterValue({
parameterType: onEventChangeDelayParameterType,
parameterValue: onEventChangeDelay,
parameterExpression: onEventChangeDelayExpression
});

useActionTimer({
canExecute: onComponentLoad?.canExecute,
execute: onComponentLoad?.execute,
delay: componentLoadDelay,
interval: componentLoadRepeatInterval,
delay: delayValue,
interval: intervalValue,
repeat: componentLoadRepeat,
attribute: undefined
});
Expand All @@ -33,7 +56,6 @@ export default function Events(props: EventsContainerProps): ReactElement {
return;
}
if (prevOnChangeAttributeValue?.current?.value === undefined) {
// ignore initial load
prevOnChangeAttributeValue.current = onEventChangeAttribute;
} else {
if (onEventChangeAttribute?.value !== prevOnChangeAttributeValue.current?.value) {
Expand All @@ -42,7 +64,7 @@ export default function Events(props: EventsContainerProps): ReactElement {
}
}
},
delay: onEventChangeDelay,
delay: onEventChangeDelayValue,
interval: 0,
repeat: false,
attribute: onEventChangeAttribute
Expand Down
51 changes: 50 additions & 1 deletion packages/pluggableWidgets/events-web/src/Events.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,51 @@
<caption>Action</caption>
<description />
</property>

<property key="componentLoadDelayParameterType" type="enumeration" defaultValue="number" required="true">
<caption>Parameter type</caption>
<description />
<enumerationValues>
<enumerationValue key="number">Value</enumerationValue>
<enumerationValue key="expression">Expression</enumerationValue>
</enumerationValues>
</property>

<property key="componentLoadDelay" type="integer" required="true" defaultValue="0">
<caption>Delay</caption>
<description>Timer delay to first action execution. Value is in milliseconds. If set to 0, action will be triggered immediately.</description>
</property>

<property key="componentLoadDelayExpression" type="expression">
<caption>Delay</caption>
<description>Timer delay to first action execution. Value is in milliseconds. If set to 0, action will be triggered immediately.</description>
<returnType type="Integer" />
</property>

<property key="componentLoadRepeat" type="boolean" required="true" defaultValue="false">
<caption>Repeat</caption>
<description />
</property>

<property key="componentLoadRepeatIntervalParameterType" type="enumeration" defaultValue="number" required="true">
<caption>Parameter type</caption>
<description />
<enumerationValues>
<enumerationValue key="number">Value</enumerationValue>
<enumerationValue key="expression">Expression</enumerationValue>
</enumerationValues>
</property>

<property key="componentLoadRepeatInterval" type="integer" required="true" defaultValue="30000">
<caption>Delay</caption>
<caption>Interval</caption>
<description>Interval between repeat action execution. Value is in milliseconds.</description>
</property>

<property key="componentLoadRepeatIntervalExpression" type="expression" required="false">
<caption>Interval</caption>
<description>Interval between repeat action execution. Value is in milliseconds.</description>
<returnType type="Integer" />
</property>
</propertyGroup>
<propertyGroup caption="On change">
<property key="onEventChangeAttribute" type="attribute" required="false">
Expand All @@ -45,10 +78,26 @@
<caption>Action</caption>
<description />
</property>

<property key="onEventChangeDelayParameterType" type="enumeration" defaultValue="number" required="true">
<caption>Parameter type</caption>
<description />
<enumerationValues>
<enumerationValue key="number">Value</enumerationValue>
<enumerationValue key="expression">Expression</enumerationValue>
</enumerationValues>
</property>

<property key="onEventChangeDelay" type="integer" required="true" defaultValue="0">
<caption>Delay</caption>
<description>Timer delay to first action execution. Value is in milliseconds. If set to 0, action will be triggered immediately.</description>
</property>

<property key="onEventChangeDelayExpression" type="expression">
<caption>Delay</caption>
<description>Timer delay to first action execution. Value is in milliseconds. If set to 0, action will be triggered immediately.</description>
<returnType type="Integer" />
</property>
</propertyGroup>
<!-- END OPTIONS SOURCE -->
</propertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { actionValue } from "@mendix/widget-plugin-test-utils";
import { actionValue, dynamicValue } from "@mendix/widget-plugin-test-utils";
import "@testing-library/jest-dom";
import { render } from "@testing-library/react";
import { createElement } from "react";
Expand All @@ -12,10 +12,18 @@ describe("App events (load)", () => {
name: "app events",
class: "app-events",
onComponentLoad: actionValue(),
componentLoadDelayParameterType: "number",
componentLoadDelay: 0,
onEventChangeDelay: 0,
componentLoadDelayExpression: dynamicValue(),
componentLoadRepeat: false,
componentLoadRepeatInterval: 0
componentLoadRepeatIntervalParameterType: "number",
componentLoadRepeatInterval: 0,
componentLoadRepeatIntervalExpression: dynamicValue(),
onEventChangeAttribute: undefined,
onEventChange: undefined,
onEventChangeDelayParameterType: "number",
onEventChangeDelay: 0,
onEventChangeDelayExpression: dynamicValue()
};
});
it("render app events", async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useMemo } from "react";

interface parameterValueProps {
parameterType: "number" | "expression";
parameterValue: number | undefined;
parameterExpression: { status: string; value: { toNumber: () => number } | undefined } | undefined;
}

export function useParameterValue({
parameterType,
parameterValue,
parameterExpression
}: parameterValueProps): number | undefined {
return useMemo(() => {
if (parameterType === "number") {
return parameterValue;
}

return parameterExpression?.status === "available" && parameterExpression.value !== undefined
? parameterExpression.value.toNumber()
: undefined;
}, [parameterType, parameterValue, parameterExpression]);
}
8 changes: 6 additions & 2 deletions packages/pluggableWidgets/events-web/src/hooks/timer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { useEffect, useState } from "react";
interface ActionTimerProps {
canExecute?: boolean;
execute?: () => void;
delay: number;
interval: number;
delay: number | undefined;
interval: number | undefined;
repeat: boolean;
attribute?: EditableValue;
}
Expand All @@ -14,6 +14,10 @@ export function useActionTimer(props: ActionTimerProps): void {
const { canExecute, execute, delay, interval, repeat, attribute } = props;
const [toggleTimer, setToggleTimer] = useState(-1);
useEffect(() => {
// If the delay is set to undefined, we should not start a timer.
if (delay === undefined || delay < 0 || (interval === undefined && repeat)) {
return;
}
let counter: NodeJS.Timeout;
if (canExecute) {
if (repeat) {
Expand Down
20 changes: 19 additions & 1 deletion packages/pluggableWidgets/events-web/typings/EventsProps.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,33 @@
* @author Mendix Widgets Framework Team
*/
import { CSSProperties } from "react";
import { ActionValue, EditableValue } from "mendix";
import { ActionValue, DynamicValue, EditableValue } from "mendix";
import { Big } from "big.js";

export type ComponentLoadDelayParameterTypeEnum = "number" | "expression";

export type ComponentLoadRepeatIntervalParameterTypeEnum = "number" | "expression";

export type OnEventChangeDelayParameterTypeEnum = "number" | "expression";

export interface EventsContainerProps {
name: string;
class: string;
style?: CSSProperties;
tabIndex?: number;
onComponentLoad?: ActionValue;
componentLoadDelayParameterType: ComponentLoadDelayParameterTypeEnum;
componentLoadDelay: number;
componentLoadDelayExpression: DynamicValue<Big>;
componentLoadRepeat: boolean;
componentLoadRepeatIntervalParameterType: ComponentLoadRepeatIntervalParameterTypeEnum;
componentLoadRepeatInterval: number;
componentLoadRepeatIntervalExpression?: DynamicValue<Big>;
onEventChangeAttribute?: EditableValue<Big | any | boolean | Date | string>;
onEventChange?: ActionValue;
onEventChangeDelayParameterType: OnEventChangeDelayParameterTypeEnum;
onEventChangeDelay: number;
onEventChangeDelayExpression: DynamicValue<Big>;
}

export interface EventsPreviewProps {
Expand All @@ -33,10 +45,16 @@ export interface EventsPreviewProps {
renderMode: "design" | "xray" | "structure";
translate: (text: string) => string;
onComponentLoad: {} | null;
componentLoadDelayParameterType: ComponentLoadDelayParameterTypeEnum;
componentLoadDelay: number | null;
componentLoadDelayExpression: string;
componentLoadRepeat: boolean;
componentLoadRepeatIntervalParameterType: ComponentLoadRepeatIntervalParameterTypeEnum;
componentLoadRepeatInterval: number | null;
componentLoadRepeatIntervalExpression: string;
onEventChangeAttribute: string;
onEventChange: {} | null;
onEventChangeDelayParameterType: OnEventChangeDelayParameterTypeEnum;
onEventChangeDelay: number | null;
onEventChangeDelayExpression: string;
}
Loading