From 326b313419ddbb45015c09f0083c4b9fc931e638 Mon Sep 17 00:00:00 2001 From: Yordan Stoyanov Date: Thu, 23 Jul 2026 15:47:07 +0200 Subject: [PATCH] fix(image-web): migrate image properties to allowUpload, fix deprecation warning --- .../.openspec.yaml | 2 + .../proposal.md | 39 ++++++++++ .../tests.md | 17 +++++ .../pluggableWidgets/image-web/CHANGELOG.md | 2 + .../pluggableWidgets/image-web/package.json | 2 +- .../pluggableWidgets/image-web/src/Image.xml | 4 +- .../image-web/src/__tests__/Image.spec.tsx | 76 +++++++++++++++++++ .../image-web/typings/ImageProps.d.ts | 6 +- 8 files changed, 142 insertions(+), 6 deletions(-) create mode 100644 openspec/changes/fix-image-allow-upload-deprecation/.openspec.yaml create mode 100644 openspec/changes/fix-image-allow-upload-deprecation/proposal.md create mode 100644 openspec/changes/fix-image-allow-upload-deprecation/tests.md create mode 100644 packages/pluggableWidgets/image-web/src/__tests__/Image.spec.tsx diff --git a/openspec/changes/fix-image-allow-upload-deprecation/.openspec.yaml b/openspec/changes/fix-image-allow-upload-deprecation/.openspec.yaml new file mode 100644 index 0000000000..e7ed6cbc07 --- /dev/null +++ b/openspec/changes/fix-image-allow-upload-deprecation/.openspec.yaml @@ -0,0 +1,2 @@ +schema: tdd-refactor +created: 2026-07-23 diff --git a/openspec/changes/fix-image-allow-upload-deprecation/proposal.md b/openspec/changes/fix-image-allow-upload-deprecation/proposal.md new file mode 100644 index 0000000000..a6fe237fa9 --- /dev/null +++ b/openspec/changes/fix-image-allow-upload-deprecation/proposal.md @@ -0,0 +1,39 @@ +## Why + +The Image widget's `imageObject` and `defaultImageDynamic` properties are declared as `type="image"` without `allowUpload`. Mendix has deprecated this form of the image/file property type — `DynamicValue` will be removed in Mendix 12 — and apps using the widget on Studio Pro 11.8+ already see a deprecation warning at design time. The fix is to set `allowUpload="true"` on both properties, per Mendix's own migration guidance. + +## What Changes + +- `allowUpload="true"` added to `imageObject` and `defaultImageDynamic` in `Image.xml`. +- Generated typings (`ImageProps.d.ts`) change `imageObject`/`defaultImageDynamic` from `DynamicValue` to `EditableImageValue`. +- `Image.tsx` reads `.status` and `.value.uri` off both props today — `EditableImageValue` exposes the same shape (extends the same status/value base as `DynamicValue`), so no logic change is required. Widget stays display-only; no upload UI is added. +- `package.json` `marketplace.minimumMXVersion` bumped to `11.12.0` (LTS, confirmed with ticket reporter — 11.11 has native `allowUpload` support but 11.12 is the intended floor). +- CHANGELOG entry added under `[Unreleased]`. Widget version stays unbumped for now — version bumps happen at release time per repo convention; this change requires a major bump (2.0.0) once released, since `minimumMXVersion` is raised. + +Verified in Studio Pro (11.12, fresh test app) that `allowUpload="true"` does not remove the "Static" image-source option — both Static and Dynamic configuration remain available in the property editor, so existing apps configuring `imageObject`/`defaultImageDynamic` as a static asset are not broken by this change. + +## Capabilities + +### New Capabilities + + + +### Modified Capabilities + + + +## Root cause + +Widget XML predates the `allowUpload` attribute (introduced Mendix 11.8, native widget support 11.11). Not fixed proactively when the attribute was introduced; ticket WC-3471 flags the resulting deprecation warning. + +## Impact + +- **Files**: + - `packages/pluggableWidgets/image-web/src/Image.xml` + - `packages/pluggableWidgets/image-web/typings/ImageProps.d.ts` (regenerated) + - `packages/pluggableWidgets/image-web/package.json` (version, minimumMXVersion) + - `packages/pluggableWidgets/image-web/CHANGELOG.md` +- **Behavior**: none — widget remains display-only, no upload UI added, no runtime logic change +- **Studio Pro UX**: none — Static/Dynamic image-source configuration both remain available (manually verified) +- **Breaking change**: minimum Mendix version raised from 9.24.0 to 11.12.0 — apps on older Mendix versions cannot upgrade to this widget version once released +- **Affected widget**: `@mendix/image-web` — requires a major version bump (2.0.0) at release time; not bumped in this change diff --git a/openspec/changes/fix-image-allow-upload-deprecation/tests.md b/openspec/changes/fix-image-allow-upload-deprecation/tests.md new file mode 100644 index 0000000000..a94aa7749a --- /dev/null +++ b/openspec/changes/fix-image-allow-upload-deprecation/tests.md @@ -0,0 +1,17 @@ +- [x] **Container renders the bound image when imageObject is Available** + - **Type:** unit + - **Given:** `ImageContainerProps` with `datasource: "image"`, `imageObject` as an `EditableImageValue`-shaped object with `status: ValueStatus.Available` and `value.uri: "https://example.com/a.png"` + - **When:** `Image` (src/Image.tsx container) is rendered + - **Then:** the rendered `` `src` equals `"https://example.com/a.png"` + +- [x] **Container falls back to defaultImageDynamic when imageObject is Unavailable** + - **Type:** unit + - **Given:** `imageObject` with `status: ValueStatus.Unavailable`, `defaultImageDynamic` as `EditableImageValue`-shaped with `status: ValueStatus.Available` and `value.uri: "https://example.com/default.png"` + - **When:** `Image` is rendered + - **Then:** the rendered `` `src` equals `"https://example.com/default.png"` + +- [x] **Container renders nothing bound when both imageObject and defaultImageDynamic are unavailable** + - **Type:** unit + - **Given:** `imageObject` with `status: ValueStatus.Unavailable`, `defaultImageDynamic` undefined + - **When:** `Image` is rendered + - **Then:** the rendered image element has no meaningful `src` (placeholder/empty state), matching current behavior before this change diff --git a/packages/pluggableWidgets/image-web/CHANGELOG.md b/packages/pluggableWidgets/image-web/CHANGELOG.md index 874df3e573..fc26f44f37 100644 --- a/packages/pluggableWidgets/image-web/CHANGELOG.md +++ b/packages/pluggableWidgets/image-web/CHANGELOG.md @@ -10,6 +10,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - We fixed an issue where previews in Design and Structure modes did not respect the configured minimum and maximum image height. +- We fixed a deprecation warning by migrating the image source properties to the `allowUpload` property type. This raises the minimum supported Mendix version to 11.12.0. + ## [1.5.1] - 2025-10-29 ### Fixed diff --git a/packages/pluggableWidgets/image-web/package.json b/packages/pluggableWidgets/image-web/package.json index 9c644692a6..635b0379ef 100644 --- a/packages/pluggableWidgets/image-web/package.json +++ b/packages/pluggableWidgets/image-web/package.json @@ -17,7 +17,7 @@ }, "packagePath": "com.mendix.widget.web", "marketplace": { - "minimumMXVersion": "9.24.0", + "minimumMXVersion": "11.12.0", "appNumber": 118579, "appName": "Image", "reactReady": true diff --git a/packages/pluggableWidgets/image-web/src/Image.xml b/packages/pluggableWidgets/image-web/src/Image.xml index 5daaad62fe..351e505915 100644 --- a/packages/pluggableWidgets/image-web/src/Image.xml +++ b/packages/pluggableWidgets/image-web/src/Image.xml @@ -17,11 +17,11 @@ Icon - + Image source - + Default image This is the image that is displayed if no image is uploaded. diff --git a/packages/pluggableWidgets/image-web/src/__tests__/Image.spec.tsx b/packages/pluggableWidgets/image-web/src/__tests__/Image.spec.tsx new file mode 100644 index 0000000000..32f6fdd73a --- /dev/null +++ b/packages/pluggableWidgets/image-web/src/__tests__/Image.spec.tsx @@ -0,0 +1,76 @@ +import { render } from "@testing-library/react"; +import { ValueStatus } from "mendix"; +import { ImageContainerProps } from "../../typings/ImageProps"; +import { Image } from "../Image"; + +type ImageProp = NonNullable; + +function makeImageValue(overrides: Partial = {}): ImageProp { + return { + status: ValueStatus.Available, + value: { uri: "https://example.com/a.png", name: "a.png" }, + readOnly: false, + validation: undefined, + setValidator: jest.fn(), + setValue: jest.fn(), + setThumbnailSize: jest.fn(), + ...overrides + } as ImageProp; +} + +function makeProps(overrides: Partial = {}): ImageContainerProps { + return { + name: "image1", + class: "", + style: undefined, + tabIndex: 0, + datasource: "image", + isBackgroundImage: false, + onClickType: "action", + widthUnit: "auto", + width: 100, + heightUnit: "auto", + height: 100, + minHeightUnit: "none", + minHeight: 0, + maxHeightUnit: "none", + maxHeight: 0, + iconSize: 14, + displayAs: "fullImage", + responsive: true, + ...overrides + } as ImageContainerProps; +} + +describe("Image container", () => { + it("renders the bound image when imageObject is Available", () => { + const { getByRole } = render(); + const image = getByRole("img") as HTMLImageElement; + expect(image.src).toBe("https://example.com/a.png"); + }); + + it("falls back to defaultImageDynamic when imageObject is Unavailable", () => { + const { getByRole } = render( + + ); + const image = getByRole("img") as HTMLImageElement; + expect(image.src).toBe("https://example.com/default.png"); + }); + + it("renders no image src when both imageObject and defaultImageDynamic are unavailable", () => { + const { container } = render( + + ); + const image = container.querySelector("img"); + expect(image?.getAttribute("src")).toBeFalsy(); + }); +}); diff --git a/packages/pluggableWidgets/image-web/typings/ImageProps.d.ts b/packages/pluggableWidgets/image-web/typings/ImageProps.d.ts index 0d3a7d156a..59472fd39e 100644 --- a/packages/pluggableWidgets/image-web/typings/ImageProps.d.ts +++ b/packages/pluggableWidgets/image-web/typings/ImageProps.d.ts @@ -3,7 +3,7 @@ * WARNING: All changes made to this file will be overwritten * @author Mendix Widgets Framework Team */ -import { ActionValue, DynamicValue, WebIcon, WebImage } from "mendix"; +import { ActionValue, DynamicValue, EditableImageValue, WebIcon, WebImage } from "mendix"; import { ComponentType, CSSProperties, ReactNode } from "react"; export type DatasourceEnum = "image" | "imageUrl" | "icon"; @@ -26,8 +26,8 @@ export interface ImageContainerProps { style?: CSSProperties; tabIndex?: number; datasource: DatasourceEnum; - imageObject?: DynamicValue; - defaultImageDynamic?: DynamicValue; + imageObject?: EditableImageValue; + defaultImageDynamic?: EditableImageValue; imageUrl?: DynamicValue; imageIcon?: DynamicValue; isBackgroundImage: boolean;