diff --git a/packages/pluggableWidgets/barcode-generator-web/src/__tests__/BarcodeGenerator.spec.tsx b/packages/pluggableWidgets/barcode-generator-web/src/__tests__/BarcodeGenerator.spec.tsx index 01b4ef0c56..97fda1610d 100644 --- a/packages/pluggableWidgets/barcode-generator-web/src/__tests__/BarcodeGenerator.spec.tsx +++ b/packages/pluggableWidgets/barcode-generator-web/src/__tests__/BarcodeGenerator.spec.tsx @@ -60,8 +60,8 @@ const createBarcodeProps = ( lastChar: "", enableMod43: false, allowDownload: false, - downloadButtonCaption: dynamic("Download"), - downloadButtonAriaLabel: dynamic("Download barcode"), + downloadButtonCaption: dynamic.available("Download"), + downloadButtonAriaLabel: dynamic.available("Download barcode"), displayValue: false, showAsCard: false, codeWidth: 2, @@ -69,10 +69,10 @@ const createBarcodeProps = ( codeMargin: 4, qrSize: 128, qrMargin: 2, - qrTitle: dynamic(""), + qrTitle: dynamic.available(""), qrLevel: "L", qrOverlay: false, - qrOverlaySrc: dynamic(createMockWebImage()), + qrOverlaySrc: dynamic.available(createMockWebImage()), qrOverlayCenter: true, qrOverlayX: 0, qrOverlayY: 0, @@ -81,10 +81,10 @@ const createBarcodeProps = ( qrOverlayOpacity: new Big(1), qrOverlayExcavate: true, addonFormat: "None", - addonValue: dynamic(), + addonValue: dynamic.unavailable(), addonSpacing: 20, buttonPosition: "bottom", - codeValue: dynamic("test-barcode-value"), + codeValue: dynamic.available("test-barcode-value"), logLevel: "Info", showTitle: false, ...overrides @@ -99,7 +99,7 @@ describe("BarcodeGenerator", () => { describe("core rendering", () => { it("renders QR code when codeValue is available", () => { const props = createBarcodeProps({ - codeValue: dynamic("Hello World") + codeValue: dynamic.available("Hello World") }); render(); @@ -111,7 +111,7 @@ describe("BarcodeGenerator", () => { it("shows fallback message when codeValue is loading", () => { const props = createBarcodeProps({ - codeValue: dynamic("", true) + codeValue: dynamic.loading("") }); render(); @@ -122,7 +122,7 @@ describe("BarcodeGenerator", () => { it("shows fallback message when codeValue is unavailable", () => { const props = createBarcodeProps({ - codeValue: dynamic() + codeValue: dynamic.unavailable() }); render(); @@ -135,7 +135,7 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ class: "custom-class", tabIndex: 2, - codeValue: dynamic("test") + codeValue: dynamic.available("test") }); const { container } = render(); @@ -148,7 +148,7 @@ describe("BarcodeGenerator", () => { it("applies card styling when showAsCard is true", () => { const props = createBarcodeProps({ showAsCard: true, - codeValue: dynamic("test") + codeValue: dynamic.available("test") }); const { container } = render(); @@ -163,7 +163,7 @@ describe("BarcodeGenerator", () => { it("renders CODE128 barcode correctly", () => { const props = createBarcodeProps({ codeFormat: "CODE128" as CodeFormatEnum, - codeValue: dynamic("123456789") + codeValue: dynamic.available("123456789") }); render(); @@ -179,7 +179,7 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ codeFormat: "Custom" as CodeFormatEnum, customCodeFormat: "CODE39" as CustomCodeFormatEnum, - codeValue: dynamic("ABC-123") + codeValue: dynamic.available("ABC-123") }); render(); @@ -195,7 +195,7 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ codeFormat: "Custom" as CodeFormatEnum, customCodeFormat: "CODE93" as CustomCodeFormatEnum, - codeValue: dynamic("CODE93VALUE") + codeValue: dynamic.available("CODE93VALUE") }); render(); @@ -211,7 +211,7 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ codeFormat: "Custom" as CodeFormatEnum, customCodeFormat: "EAN13" as CustomCodeFormatEnum, - codeValue: dynamic("1234567890128") + codeValue: dynamic.available("1234567890128") }); render(); @@ -227,7 +227,7 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ codeFormat: "Custom" as CodeFormatEnum, customCodeFormat: "EAN8" as CustomCodeFormatEnum, - codeValue: dynamic("12345678") + codeValue: dynamic.available("12345678") }); render(); @@ -243,7 +243,7 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ codeFormat: "Custom" as CodeFormatEnum, customCodeFormat: "UPC" as CustomCodeFormatEnum, - codeValue: dynamic("123456789012") + codeValue: dynamic.available("123456789012") }); render(); @@ -259,7 +259,7 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ codeFormat: "Custom" as CodeFormatEnum, customCodeFormat: "ITF14" as CustomCodeFormatEnum, - codeValue: dynamic("12345678901234") + codeValue: dynamic.available("12345678901234") }); render(); @@ -275,7 +275,7 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ codeFormat: "Custom" as CodeFormatEnum, customCodeFormat: "MSI" as CustomCodeFormatEnum, - codeValue: dynamic("123456") + codeValue: dynamic.available("123456") }); render(); @@ -291,7 +291,7 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ codeFormat: "Custom" as CodeFormatEnum, customCodeFormat: "pharmacode" as CustomCodeFormatEnum, - codeValue: dynamic("1234567") + codeValue: dynamic.available("1234567") }); render(); @@ -307,7 +307,7 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ codeFormat: "Custom" as CodeFormatEnum, customCodeFormat: "codabar" as CustomCodeFormatEnum, - codeValue: dynamic("123-456") + codeValue: dynamic.available("123-456") }); render(); @@ -325,7 +325,7 @@ describe("BarcodeGenerator", () => { it("renders QR code with custom size", () => { const props = createBarcodeProps({ qrSize: 256, - codeValue: dynamic("Custom Size QR") + codeValue: dynamic.available("Custom Size QR") }); render(); @@ -336,7 +336,7 @@ describe("BarcodeGenerator", () => { it("renders QR code with custom margin", () => { const props = createBarcodeProps({ qrMargin: 5, - codeValue: dynamic("test") + codeValue: dynamic.available("test") }); render(); @@ -350,7 +350,7 @@ describe("BarcodeGenerator", () => { levels.forEach(level => { const props = createBarcodeProps({ qrLevel: level, - codeValue: dynamic("test") + codeValue: dynamic.available("test") }); const { unmount } = render(); @@ -362,9 +362,9 @@ describe("BarcodeGenerator", () => { it("renders QR code with title", () => { const props = createBarcodeProps({ - qrTitle: dynamic("QR Code Title"), + qrTitle: dynamic.available("QR Code Title"), showTitle: true, - codeValue: dynamic("test") + codeValue: dynamic.available("test") }); render(); @@ -378,9 +378,9 @@ describe("BarcodeGenerator", () => { it("renders QR code with image overlay when qrOverlay is true", () => { const props = createBarcodeProps({ qrOverlay: true, - qrOverlaySrc: dynamic(createMockWebImage()), + qrOverlaySrc: dynamic.available(createMockWebImage()), qrOverlayCenter: true, - codeValue: dynamic("test") + codeValue: dynamic.available("test") }); render(); @@ -391,11 +391,11 @@ describe("BarcodeGenerator", () => { it("renders QR code with centered image overlay", () => { const props = createBarcodeProps({ qrOverlay: true, - qrOverlaySrc: dynamic(createMockWebImage()), + qrOverlaySrc: dynamic.available(createMockWebImage()), qrOverlayCenter: true, qrOverlayX: 10, qrOverlayY: 20, - codeValue: dynamic("test") + codeValue: dynamic.available("test") }); render(); @@ -406,11 +406,11 @@ describe("BarcodeGenerator", () => { it("renders QR code with positioned image overlay", () => { const props = createBarcodeProps({ qrOverlay: true, - qrOverlaySrc: dynamic(createMockWebImage()), + qrOverlaySrc: dynamic.available(createMockWebImage()), qrOverlayCenter: false, qrOverlayX: 10, qrOverlayY: 20, - codeValue: dynamic("test") + codeValue: dynamic.available("test") }); render(); @@ -421,10 +421,10 @@ describe("BarcodeGenerator", () => { it("renders QR code with image overlay custom dimensions", () => { const props = createBarcodeProps({ qrOverlay: true, - qrOverlaySrc: dynamic(createMockWebImage()), + qrOverlaySrc: dynamic.available(createMockWebImage()), qrOverlayWidth: 50, qrOverlayHeight: 50, - codeValue: dynamic("test") + codeValue: dynamic.available("test") }); render(); @@ -435,9 +435,9 @@ describe("BarcodeGenerator", () => { it("renders QR code with image overlay opacity", () => { const props = createBarcodeProps({ qrOverlay: true, - qrOverlaySrc: dynamic(createMockWebImage()), + qrOverlaySrc: dynamic.available(createMockWebImage()), qrOverlayOpacity: Big(0.75), - codeValue: dynamic("test") + codeValue: dynamic.available("test") }); render(); @@ -448,9 +448,9 @@ describe("BarcodeGenerator", () => { it("renders QR code with image excavation enabled", () => { const props = createBarcodeProps({ qrOverlay: true, - qrOverlaySrc: dynamic(createMockWebImage()), + qrOverlaySrc: dynamic.available(createMockWebImage()), qrOverlayExcavate: true, - codeValue: dynamic("test") + codeValue: dynamic.available("test") }); render(); @@ -461,8 +461,8 @@ describe("BarcodeGenerator", () => { it("does not render image overlay when qrOverlaySrc is unavailable", () => { const props = createBarcodeProps({ qrOverlay: true, - qrOverlaySrc: dynamic(), - codeValue: dynamic("test") + qrOverlaySrc: dynamic.unavailable(), + codeValue: dynamic.available("test") }); render(); @@ -477,7 +477,7 @@ describe("BarcodeGenerator", () => { it("does not render download button when allowDownload is false", () => { const props = createBarcodeProps({ allowDownload: false, - codeValue: dynamic("test") + codeValue: dynamic.available("test") }); render(); @@ -488,8 +488,8 @@ describe("BarcodeGenerator", () => { it("renders download button with custom caption", () => { const props = createBarcodeProps({ allowDownload: true, - downloadButtonCaption: dynamic("Export Code"), - codeValue: dynamic("test") + downloadButtonCaption: dynamic.available("Export Code"), + codeValue: dynamic.available("test") }); render(); @@ -500,9 +500,9 @@ describe("BarcodeGenerator", () => { it("renders download button with correct aria-label for QR code", () => { const props = createBarcodeProps({ allowDownload: true, - downloadButtonAriaLabel: dynamic("Download QR code"), + downloadButtonAriaLabel: dynamic.available("Download QR code"), codeFormat: "QRCode" as CodeFormatEnum, - codeValue: dynamic("test") + codeValue: dynamic.available("test") }); render(); @@ -514,8 +514,8 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ allowDownload: true, buttonPosition: "top" as const, - downloadButtonCaption: dynamic("Download"), - codeValue: dynamic("test") + downloadButtonCaption: dynamic.available("Download"), + codeValue: dynamic.available("test") }); const { container } = render(); @@ -533,8 +533,8 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ allowDownload: true, buttonPosition: "bottom" as const, - downloadButtonCaption: dynamic("Download"), - codeValue: dynamic("test") + downloadButtonCaption: dynamic.available("Download"), + codeValue: dynamic.available("test") }); render(); @@ -548,9 +548,9 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ allowDownload: true, - downloadButtonCaption: dynamic("Download"), + downloadButtonCaption: dynamic.available("Download"), codeFormat: "QRCode" as CodeFormatEnum, - codeValue: dynamic("test") + codeValue: dynamic.available("test") }); render(); @@ -564,8 +564,8 @@ describe("BarcodeGenerator", () => { it("renders download button with icon and caption", () => { const props = createBarcodeProps({ allowDownload: true, - downloadButtonCaption: dynamic("Save"), - codeValue: dynamic("test") + downloadButtonCaption: dynamic.available("Save"), + codeValue: dynamic.available("test") }); render(); @@ -581,7 +581,7 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ codeFormat: "CODE128" as CodeFormatEnum, displayValue: true, - codeValue: dynamic("DISPLAY123") + codeValue: dynamic.available("DISPLAY123") }); render(); @@ -597,7 +597,7 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ codeFormat: "CODE128" as CodeFormatEnum, displayValue: false, - codeValue: dynamic("NODISPLAY") + codeValue: dynamic.available("NODISPLAY") }); render(); @@ -613,7 +613,7 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ codeFormat: "CODE128" as CodeFormatEnum, codeWidth: 3, - codeValue: dynamic("WIDTH_TEST") + codeValue: dynamic.available("WIDTH_TEST") }); render(); @@ -629,7 +629,7 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ codeFormat: "CODE128" as CodeFormatEnum, codeHeight: 300, - codeValue: dynamic("HEIGHT_TEST") + codeValue: dynamic.available("HEIGHT_TEST") }); render(); @@ -645,7 +645,7 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ codeFormat: "CODE128" as CodeFormatEnum, codeMargin: 8, - codeValue: dynamic("MARGIN_TEST") + codeValue: dynamic.available("MARGIN_TEST") }); render(); @@ -664,7 +664,7 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ codeFormat: "CODE128" as CodeFormatEnum, enableEan128: true, - codeValue: dynamic("EAN128TEST") + codeValue: dynamic.available("EAN128TEST") }); render(); @@ -680,7 +680,7 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ codeFormat: "CODE128" as CodeFormatEnum, enableFlat: true, - codeValue: dynamic("FLATTEST") + codeValue: dynamic.available("FLATTEST") }); render(); @@ -696,7 +696,7 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ codeFormat: "CODE128" as CodeFormatEnum, enableMod43: true, - codeValue: dynamic("MOD43TEST") + codeValue: dynamic.available("MOD43TEST") }); render(); @@ -712,7 +712,7 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ codeFormat: "CODE128" as CodeFormatEnum, lastChar: "X", - codeValue: dynamic("LASTCHARTEST") + codeValue: dynamic.available("LASTCHARTEST") }); render(); @@ -740,10 +740,10 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ codeFormat: "Custom" as CodeFormatEnum, customCodeFormat: "EAN13" as CustomCodeFormatEnum, - addonValue: dynamic("12345"), + addonValue: dynamic.available("12345"), addonFormat: "EAN5", addonSpacing: 25, - codeValue: dynamic("1234567890128") + codeValue: dynamic.available("1234567890128") }); render(); @@ -768,9 +768,9 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ codeFormat: "Custom" as CodeFormatEnum, customCodeFormat: "EAN13" as CustomCodeFormatEnum, - addonValue: dynamic("12"), + addonValue: dynamic.available("12"), addonFormat: "EAN2", - codeValue: dynamic("1234567890128") + codeValue: dynamic.available("1234567890128") }); render(); @@ -784,9 +784,9 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ codeFormat: "Custom" as CodeFormatEnum, customCodeFormat: "EAN13" as CustomCodeFormatEnum, - addonValue: dynamic("12345"), + addonValue: dynamic.available("12345"), addonFormat: "None", - codeValue: dynamic("1234567890128") + codeValue: dynamic.available("1234567890128") }); render(); @@ -807,10 +807,10 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ codeFormat: "Custom" as CodeFormatEnum, customCodeFormat: "EAN13" as CustomCodeFormatEnum, - addonValue: dynamic("12345"), + addonValue: dynamic.available("12345"), addonFormat: "EAN5", addonSpacing: 40, - codeValue: dynamic("1234567890128") + codeValue: dynamic.available("1234567890128") }); render(); @@ -828,7 +828,7 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ codeFormat: "CODE128" as CodeFormatEnum, - codeValue: dynamic("INVALID") + codeValue: dynamic.available("INVALID") }); render(); @@ -844,7 +844,7 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ codeFormat: "CODE128" as CodeFormatEnum, - codeValue: dynamic("TEST") + codeValue: dynamic.available("TEST") }); render(); @@ -861,7 +861,7 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ codeFormat: "CODE128" as CodeFormatEnum, - codeValue: dynamic("BAD") + codeValue: dynamic.available("BAD") }); const { unmount } = render(); @@ -876,7 +876,7 @@ describe("BarcodeGenerator", () => { const goodProps = createBarcodeProps({ codeFormat: "CODE128" as CodeFormatEnum, - codeValue: dynamic("GOOD") + codeValue: dynamic.available("GOOD") }); render(); @@ -889,9 +889,9 @@ describe("BarcodeGenerator", () => { describe("accessibility", () => { it("renders QR code title as semantic element when provided", () => { const props = createBarcodeProps({ - qrTitle: dynamic("Invoice QR Code"), + qrTitle: dynamic.available("Invoice QR Code"), showTitle: true, - codeValue: dynamic("test") + codeValue: dynamic.available("test") }); render(); @@ -903,9 +903,9 @@ describe("BarcodeGenerator", () => { it("does not render title when showTitle is false", () => { const props = createBarcodeProps({ - qrTitle: dynamic("title"), + qrTitle: dynamic.available("title"), showTitle: false, - codeValue: dynamic("test") + codeValue: dynamic.available("test") }); render(); @@ -916,9 +916,9 @@ describe("BarcodeGenerator", () => { it("download button has proper semantics", () => { const props = createBarcodeProps({ allowDownload: true, - downloadButtonCaption: dynamic("Download Barcode"), - downloadButtonAriaLabel: dynamic("Download current barcode as PNG"), - codeValue: dynamic("test") + downloadButtonCaption: dynamic.available("Download Barcode"), + downloadButtonAriaLabel: dynamic.available("Download current barcode as PNG"), + codeValue: dynamic.available("test") }); render(); @@ -933,8 +933,8 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ allowDownload: true, - downloadButtonCaption: dynamic("Download"), - codeValue: dynamic("test") + downloadButtonCaption: dynamic.available("Download"), + codeValue: dynamic.available("test") }); render(); @@ -954,7 +954,7 @@ describe("BarcodeGenerator", () => { const props = createBarcodeProps({ codeFormat: "CODE128" as CodeFormatEnum, - codeValue: dynamic("TEST") + codeValue: dynamic.available("TEST") }); render(); @@ -965,7 +965,7 @@ describe("BarcodeGenerator", () => { it("barcode widget container is focusable when tabIndex is set", () => { const props = createBarcodeProps({ tabIndex: 0, - codeValue: dynamic("test") + codeValue: dynamic.available("test") }); const { container } = render(); @@ -980,12 +980,12 @@ describe("BarcodeGenerator", () => { it("renders QR code with download, title, and image overlay", () => { const props = createBarcodeProps({ allowDownload: true, - qrTitle: dynamic("Secure QR"), + qrTitle: dynamic.available("Secure QR"), showTitle: true, qrOverlay: true, - qrOverlaySrc: dynamic(createMockWebImage()), - downloadButtonCaption: dynamic("Save QR"), - codeValue: dynamic("secure-data") + qrOverlaySrc: dynamic.available(createMockWebImage()), + downloadButtonCaption: dynamic.available("Save QR"), + codeValue: dynamic.available("secure-data") }); render(); @@ -1009,12 +1009,12 @@ describe("BarcodeGenerator", () => { enableFlat: true, enableMod43: true, allowDownload: true, - downloadButtonCaption: dynamic("Export"), + downloadButtonCaption: dynamic.available("Export"), codeWidth: 3, codeHeight: 250, codeMargin: 5, lastChar: "Z", - codeValue: dynamic("FULL_TEST") + codeValue: dynamic.available("FULL_TEST") }); render(); diff --git a/packages/pluggableWidgets/calendar-web/src/__tests__/Calendar.spec.tsx b/packages/pluggableWidgets/calendar-web/src/__tests__/Calendar.spec.tsx index 6174878704..e31691c5cf 100644 --- a/packages/pluggableWidgets/calendar-web/src/__tests__/Calendar.spec.tsx +++ b/packages/pluggableWidgets/calendar-web/src/__tests__/Calendar.spec.tsx @@ -72,8 +72,8 @@ const customViewProps: CalendarContainerProps = { view: "custom", defaultViewStandard: "month", defaultViewCustom: "work_week", - editable: dynamic(true), - showEventDate: dynamic(true), + editable: dynamic.available(true), + showEventDate: dynamic.available(true), widthUnit: "percentage", width: 100, heightUnit: "pixels", diff --git a/packages/pluggableWidgets/checkbox-radio-selection-web/src/CheckboxRadioSelection.editorPreview.tsx b/packages/pluggableWidgets/checkbox-radio-selection-web/src/CheckboxRadioSelection.editorPreview.tsx index 27cea5406b..a85e59e263 100644 --- a/packages/pluggableWidgets/checkbox-radio-selection-web/src/CheckboxRadioSelection.editorPreview.tsx +++ b/packages/pluggableWidgets/checkbox-radio-selection-web/src/CheckboxRadioSelection.editorPreview.tsx @@ -1,18 +1,24 @@ -import { generateUUID } from "@mendix/widget-plugin-platform/framework/generate-uuid"; +import type { DynamicValue } from "mendix"; import { ReactElement, useMemo } from "react"; +import { generateUUID } from "@mendix/widget-plugin-platform/framework/generate-uuid"; import { CheckboxRadioSelectionPreviewProps } from "../typings/CheckboxRadioSelectionProps"; +import { CheckboxSelection } from "./components/CheckboxSelection/CheckboxSelection"; import { RadioSelection } from "./components/RadioSelection/RadioSelection"; -import { dynamic } from "@mendix/widget-plugin-test-utils"; -import { MultiSelector, SelectionBaseProps, SingleSelector } from "./helpers/types"; -import { StaticPreviewSelector } from "./helpers/Static/Preview/StaticPreviewSelector"; +import { AssociationPreviewSelector } from "./helpers/Association/Preview/AssociationPreviewSelector"; import { DatabaseMultiPreviewSelector, DatabasePreviewSelector } from "./helpers/Database/Preview/DatabasePreviewSelector"; -import { AssociationPreviewSelector } from "./helpers/Association/Preview/AssociationPreviewSelector"; +import { StaticPreviewSelector } from "./helpers/Static/Preview/StaticPreviewSelector"; +import { MultiSelector, SelectionBaseProps, SingleSelector } from "./helpers/types"; import "./ui/CheckboxRadioSelection.scss"; import "./ui/CheckboxRadioSelectionPreview.scss"; -import { CheckboxSelection } from "./components/CheckboxSelection/CheckboxSelection"; + +const available = (value: T): DynamicValue => + ({ + status: "available", + value + }) as DynamicValue; export const preview = (props: CheckboxRadioSelectionPreviewProps): ReactElement => { const id = generateUUID().toString(); @@ -21,9 +27,9 @@ export const preview = (props: CheckboxRadioSelectionPreviewProps): ReactElement inputId: id, labelId: `${id}-label`, readOnlyStyle: props.readOnlyStyle, - ariaRequired: dynamic(false), - ariaLabel: dynamic(""), - groupName: dynamic(`${id}-group`), + ariaRequired: available(false), + ariaLabel: available(""), + groupName: available(`${id}-group`), noOptionsText: "No options available" }; diff --git a/packages/pluggableWidgets/combobox-web/AGENTS.md b/packages/pluggableWidgets/combobox-web/AGENTS.md index 492e5e7b10..6166c7c4f8 100644 --- a/packages/pluggableWidgets/combobox-web/AGENTS.md +++ b/packages/pluggableWidgets/combobox-web/AGENTS.md @@ -297,7 +297,7 @@ Uses `@mendix/widget-plugin-test-utils` builders: - `ReferenceValueBuilder` — mocks ReferenceValue (single association) - `ReferenceSetValueBuilder` — mocks ReferenceSetValue (multi association) - `ListValueBuilder` — mocks ListValue datasource with `.withItems()`, `.withHasMore()` -- `dynamic(value?)` — wraps in DynamicValue +- `dynamic.[available|loading|unavailable](value?)` — wraps in DynamicValue - `obj(id?)` — creates mock ObjectItem - `list(items)` — creates mock ListValue - `setupIntersectionObserverStub()` — stubs browser IntersectionObserver diff --git a/packages/pluggableWidgets/combobox-web/src/Combobox.editorPreview.tsx b/packages/pluggableWidgets/combobox-web/src/Combobox.editorPreview.tsx index 43f931961f..289b9c1edc 100644 --- a/packages/pluggableWidgets/combobox-web/src/Combobox.editorPreview.tsx +++ b/packages/pluggableWidgets/combobox-web/src/Combobox.editorPreview.tsx @@ -1,13 +1,19 @@ -import { generateUUID } from "@mendix/widget-plugin-platform/framework/generate-uuid"; +import type { DynamicValue } from "mendix"; import { ReactElement, useMemo } from "react"; +import { generateUUID } from "@mendix/widget-plugin-platform/framework/generate-uuid"; import { ComboboxPreviewProps } from "../typings/ComboboxProps"; import { SingleSelection } from "./components/SingleSelection/SingleSelection"; -import { dynamic } from "@mendix/widget-plugin-test-utils"; -import { SelectionBaseProps, SingleSelector } from "./helpers/types"; import "./ui/Combobox.scss"; import { AssociationPreviewSelector } from "./helpers/Association/Preview/AssociationPreviewSelector"; -import { StaticPreviewSelector } from "./helpers/Static/Preview/StaticPreviewSelector"; import { DatabasePreviewSelector } from "./helpers/Database/Preview/DatabasePreviewSelector"; +import { StaticPreviewSelector } from "./helpers/Static/Preview/StaticPreviewSelector"; +import { SelectionBaseProps, SingleSelector } from "./helpers/types"; + +const available = (value: T): DynamicValue => + ({ + status: "available", + value + }) as DynamicValue; export const preview = (props: ComboboxPreviewProps): ReactElement => { const id = generateUUID().toString(); @@ -16,7 +22,7 @@ export const preview = (props: ComboboxPreviewProps): ReactElement => { inputId: id, labelId: `${id}-label`, readOnlyStyle: props.readOnlyStyle, - ariaRequired: dynamic(false), + ariaRequired: available(false), a11yConfig: { ariaLabels: { clearSelection: props.clearButtonAriaLabel, diff --git a/packages/pluggableWidgets/combobox-web/src/__tests__/MultiSelection.spec.tsx b/packages/pluggableWidgets/combobox-web/src/__tests__/MultiSelection.spec.tsx index d9c439bf98..a2d67ee5cf 100644 --- a/packages/pluggableWidgets/combobox-web/src/__tests__/MultiSelection.spec.tsx +++ b/packages/pluggableWidgets/combobox-web/src/__tests__/MultiSelection.spec.tsx @@ -40,23 +40,23 @@ describe("Combo box (Association)", () => { optionsSourceAssociationCaptionExpression: listExpression(() => "$currentObject/CountryName"), optionsSourceAssociationCustomContentType: "no", optionsSourceAssociationCustomContent: undefined, - emptyOptionText: dynamic("Select an option 111"), - ariaRequired: dynamic(true), + emptyOptionText: dynamic.available("Select an option 111"), + ariaRequired: dynamic.available(true), clearable: true, filterType: "contains", selectedItemsStyle: "text", readOnlyStyle: "bordered", lazyLoading: false, loadingType: "spinner", - noOptionsText: dynamic("no options found"), - clearButtonAriaLabel: dynamic("Clear selection"), - removeValueAriaLabel: dynamic("Remove value"), + noOptionsText: dynamic.available("no options found"), + clearButtonAriaLabel: dynamic.available("Clear selection"), + removeValueAriaLabel: dynamic.available("Remove value"), selectAllButton: true, // Causes +1 option to be added to the menu - selectAllButtonCaption: dynamic("Select All"), + selectAllButtonCaption: dynamic.available("Select All"), selectionMethod: "checkbox", - a11ySelectedValue: dynamic("Selected value:"), - a11yOptionsAvailable: dynamic("Options available:"), - a11yInstructions: dynamic("a11yInstructions"), + a11ySelectedValue: dynamic.available("Selected value:"), + a11yOptionsAvailable: dynamic.available("Options available:"), + a11yInstructions: dynamic.available("a11yInstructions"), showFooter: false, databaseAttributeString: new EditableValueBuilder().build(), optionsSourceDatabaseCaptionType: "attribute", @@ -65,23 +65,23 @@ describe("Combo box (Association)", () => { staticAttribute: new EditableValueBuilder().build(), optionsSourceStaticDataSource: [ { - staticDataSourceValue: dynamic("value1"), + staticDataSourceValue: dynamic.available("value1"), staticDataSourceCustomContent: undefined, - staticDataSourceCaption: dynamic("caption1") + staticDataSourceCaption: dynamic.available("caption1") }, { - staticDataSourceValue: dynamic("value2"), + staticDataSourceValue: dynamic.available("value2"), staticDataSourceCustomContent: undefined, - staticDataSourceCaption: dynamic("caption2") + staticDataSourceCaption: dynamic.available("caption2") } ], selectedItemsSorting: "none", customEditability: "default", - customEditabilityExpression: dynamic(false), + customEditabilityExpression: dynamic.available(false), filterInputDebounceInterval: 200 }; if (defaultProps.optionsSourceAssociationCaptionType === "expression") { - defaultProps.optionsSourceAssociationCaptionExpression!.get = i => dynamic(`${i.id}`); + defaultProps.optionsSourceAssociationCaptionExpression!.get = i => dynamic.available(`${i.id}`); } }); diff --git a/packages/pluggableWidgets/combobox-web/src/__tests__/SingleSelection.spec.tsx b/packages/pluggableWidgets/combobox-web/src/__tests__/SingleSelection.spec.tsx index 519961d022..f587113377 100644 --- a/packages/pluggableWidgets/combobox-web/src/__tests__/SingleSelection.spec.tsx +++ b/packages/pluggableWidgets/combobox-web/src/__tests__/SingleSelection.spec.tsx @@ -50,22 +50,22 @@ describe("Combo box (Association)", () => { optionsSourceAssociationCaptionExpression: listExpression(() => "$currentObject/CountryName"), optionsSourceAssociationCustomContentType: "no", optionsSourceAssociationCustomContent: undefined, - emptyOptionText: dynamic("Select an option 111"), - ariaRequired: dynamic(true), + emptyOptionText: dynamic.available("Select an option 111"), + ariaRequired: dynamic.available(true), clearable: true, filterType: "contains", selectedItemsStyle: "text", readOnlyStyle: "bordered", lazyLoading: false, loadingType: "spinner", - clearButtonAriaLabel: dynamic("Clear selection"), - removeValueAriaLabel: dynamic("Remove value"), - selectAllButtonCaption: dynamic("Select All"), + clearButtonAriaLabel: dynamic.available("Clear selection"), + removeValueAriaLabel: dynamic.available("Remove value"), + selectAllButtonCaption: dynamic.available("Select All"), selectAllButton: false, selectionMethod: "checkbox", - a11ySelectedValue: dynamic("Selected value:"), - a11yOptionsAvailable: dynamic("Options available:"), - a11yInstructions: dynamic("a11yInstructions"), + a11ySelectedValue: dynamic.available("Selected value:"), + a11yOptionsAvailable: dynamic.available("Options available:"), + a11yInstructions: dynamic.available("a11yInstructions"), showFooter: false, databaseAttributeString: new EditableValueBuilder().build(), optionsSourceDatabaseCaptionType: "attribute", @@ -74,23 +74,23 @@ describe("Combo box (Association)", () => { staticAttribute: new EditableValueBuilder().build(), optionsSourceStaticDataSource: [ { - staticDataSourceValue: dynamic("value1"), + staticDataSourceValue: dynamic.available("value1"), staticDataSourceCustomContent: undefined, - staticDataSourceCaption: dynamic("caption1") + staticDataSourceCaption: dynamic.available("caption1") }, { - staticDataSourceValue: dynamic("value2"), + staticDataSourceValue: dynamic.available("value2"), staticDataSourceCustomContent: undefined, - staticDataSourceCaption: dynamic("caption2") + staticDataSourceCaption: dynamic.available("caption2") } ], selectedItemsSorting: "none", customEditability: "default", - customEditabilityExpression: dynamic(false), + customEditabilityExpression: dynamic.available(false), filterInputDebounceInterval: 200 }; if (defaultProps.optionsSourceAssociationCaptionType === "expression") { - defaultProps.optionsSourceAssociationCaptionExpression!.get = i => dynamic(`${i.id}`); + defaultProps.optionsSourceAssociationCaptionExpression!.get = i => dynamic.available(`${i.id}`); } }); it("renders combobox widget", () => { diff --git a/packages/pluggableWidgets/combobox-web/src/__tests__/StaticSelection.spec.tsx b/packages/pluggableWidgets/combobox-web/src/__tests__/StaticSelection.spec.tsx index b9f28f6d85..cf9c8d6103 100644 --- a/packages/pluggableWidgets/combobox-web/src/__tests__/StaticSelection.spec.tsx +++ b/packages/pluggableWidgets/combobox-web/src/__tests__/StaticSelection.spec.tsx @@ -50,22 +50,22 @@ describe("Combo box (Static values)", () => { optionsSourceAssociationCaptionExpression: listExpression(() => "$currentObject/CountryName"), optionsSourceAssociationCustomContentType: "no", optionsSourceAssociationCustomContent: undefined, - emptyOptionText: dynamic("Select an option 111"), - ariaRequired: dynamic(true), + emptyOptionText: dynamic.available("Select an option 111"), + ariaRequired: dynamic.available(true), clearable: true, filterType: "contains", selectedItemsStyle: "text", readOnlyStyle: "bordered", lazyLoading: false, loadingType: "spinner", - clearButtonAriaLabel: dynamic("Clear selection"), - removeValueAriaLabel: dynamic("Remove value"), - selectAllButtonCaption: dynamic("Select All"), + clearButtonAriaLabel: dynamic.available("Clear selection"), + removeValueAriaLabel: dynamic.available("Remove value"), + selectAllButtonCaption: dynamic.available("Select All"), selectAllButton: false, selectionMethod: "checkbox", - a11ySelectedValue: dynamic("Selected value:"), - a11yOptionsAvailable: dynamic("Options available:"), - a11yInstructions: dynamic("a11yInstructions"), + a11ySelectedValue: dynamic.available("Selected value:"), + a11yOptionsAvailable: dynamic.available("Options available:"), + a11yInstructions: dynamic.available("a11yInstructions"), showFooter: false, databaseAttributeString: new EditableValueBuilder().build(), optionsSourceDatabaseCaptionType: "attribute", @@ -74,23 +74,23 @@ describe("Combo box (Static values)", () => { staticAttribute: new EditableValueBuilder().withValue("value1").build(), optionsSourceStaticDataSource: [ { - staticDataSourceValue: dynamic("value1"), + staticDataSourceValue: dynamic.available("value1"), staticDataSourceCustomContent: undefined, - staticDataSourceCaption: dynamic("caption1") + staticDataSourceCaption: dynamic.available("caption1") }, { - staticDataSourceValue: dynamic("value2"), + staticDataSourceValue: dynamic.available("value2"), staticDataSourceCustomContent: undefined, - staticDataSourceCaption: dynamic("caption2") + staticDataSourceCaption: dynamic.available("caption2") } ], selectedItemsSorting: "none", customEditability: "default", - customEditabilityExpression: dynamic(false), + customEditabilityExpression: dynamic.available(false), filterInputDebounceInterval: 200 }; if (defaultProps.optionsSourceAssociationCaptionType === "expression") { - defaultProps.optionsSourceAssociationCaptionExpression!.get = i => dynamic(`${i.id}`); + defaultProps.optionsSourceAssociationCaptionExpression!.get = i => dynamic.available(`${i.id}`); } }); it("renders combobox widget", () => { diff --git a/packages/pluggableWidgets/combobox-web/src/helpers/Database/__tests__/DatabaseSingleSelectionSelector.spec.ts b/packages/pluggableWidgets/combobox-web/src/helpers/Database/__tests__/DatabaseSingleSelectionSelector.spec.ts index 876c77aea4..41783d99f2 100644 --- a/packages/pluggableWidgets/combobox-web/src/helpers/Database/__tests__/DatabaseSingleSelectionSelector.spec.ts +++ b/packages/pluggableWidgets/combobox-web/src/helpers/Database/__tests__/DatabaseSingleSelectionSelector.spec.ts @@ -40,7 +40,7 @@ function buildProps({ items, values, targetValue, selection }: PropsOverrides): optionsSourceDatabaseCaptionAttribute: captionAttr, optionsSourceDatabaseValueAttribute: valueAttr, databaseAttributeString: targetAttr as any, - emptyOptionText: dynamic("Select..."), + emptyOptionText: dynamic.available("Select..."), optionsSourceDatabaseCustomContentType: "no", optionsSourceAssociationCustomContentType: "no", staticDataSourceCustomContentType: "no", @@ -50,14 +50,14 @@ function buildProps({ items, values, targetValue, selection }: PropsOverrides): lazyLoading: false, loadingType: "spinner", customEditability: "default", - customEditabilityExpression: dynamic(false), + customEditabilityExpression: dynamic.available(false), filterInputDebounceInterval: 200, selectedItemsStyle: "text", readOnlyStyle: "bordered", selectionMethod: "checkbox", selectAllButton: false, - selectAllButtonCaption: dynamic("Select All"), - ariaRequired: dynamic(true), + selectAllButtonCaption: dynamic.available("Select All"), + ariaRequired: dynamic.available(true), showFooter: false, selectedItemsSorting: "none", attributeEnumeration: new EditableValueBuilder().build(), diff --git a/packages/pluggableWidgets/datagrid-date-filter-web/src/components/__tests__/DatagridDateFilter.spec.tsx b/packages/pluggableWidgets/datagrid-date-filter-web/src/components/__tests__/DatagridDateFilter.spec.tsx index 0b309fff11..c858ba8c2f 100644 --- a/packages/pluggableWidgets/datagrid-date-filter-web/src/components/__tests__/DatagridDateFilter.spec.tsx +++ b/packages/pluggableWidgets/datagrid-date-filter-web/src/components/__tests__/DatagridDateFilter.spec.tsx @@ -3,7 +3,7 @@ import { DateInputFilterStore } from "@mendix/widget-plugin-filtering/stores/inp import { ObservableFilterHost } from "@mendix/widget-plugin-filtering/typings/ObservableFilterHost"; import { actionValue, - dynamicValue, + dynamic, EditableValueBuilder, ListAttributeValueBuilder } from "@mendix/widget-plugin-test-utils"; @@ -97,7 +97,7 @@ describe("Date Filter", () => { {...commonProps} onChange={action} valueAttribute={attribute} - placeholder={dynamicValue("Placeholder")} + placeholder={dynamic.available("Placeholder")} /> ); @@ -111,7 +111,7 @@ describe("Date Filter", () => { it("initialize with defaultValue", async () => { // 946684800000 = 01.01.2000 const date = new Date(946684800000); - render((date)} />); + render(); expect(screen.getByRole("textbox")).toHaveValue("01/01/2000"); }); @@ -121,7 +121,7 @@ describe("Date Filter", () => { const { rerender } = render(); expect(screen.getByRole("textbox")).toHaveValue(""); - rerender((date)} />); + rerender(); expect(screen.getByRole("textbox")).toHaveValue(""); }); @@ -129,7 +129,7 @@ describe("Date Filter", () => { // 946684800000 = 01.01.2000 const date = new Date(946684800000); const { rerender } = render( - (date)} /> + ); expect(screen.getByRole("textbox")).toHaveValue("01/01/2000"); @@ -271,7 +271,7 @@ describe("Date Filter", () => { it("has correct short week days for en-US", async () => { window.mx = createMXObjectMock("en_US", "en-US"); - render(); + render(); const input = screen.getByRole("textbox"); await act(async () => { @@ -283,7 +283,7 @@ describe("Date Filter", () => { it("has correct short week days for en-US and starts week at Monday", async () => { window.mx = createMXObjectMock("en_US", "en-US", 1); - render(); + render(); const input = screen.getByRole("textbox"); await act(async () => { @@ -295,7 +295,7 @@ describe("Date Filter", () => { it("has correct short week days for pt-Br", async () => { window.mx = createMXObjectMock("pt_BR", "pt-BR"); - render(); + render(); const input = screen.getByRole("textbox"); await act(async () => { @@ -307,7 +307,7 @@ describe("Date Filter", () => { it("has correct short week days for fi-FI and starts on monday", async () => { window.mx = createMXObjectMock("fi_FI", "fi-FI", 1); - render(); + render(); const input = screen.getByRole("textbox"); await act(async () => { @@ -319,7 +319,7 @@ describe("Date Filter", () => { it("has correct short week days for fi-FI", async () => { window.mx = createMXObjectMock("fi_FI", "fi-FI"); - render(); + render(); const input = screen.getByRole("textbox"); await act(async () => { diff --git a/packages/pluggableWidgets/datagrid-dropdown-filter-web/src/__tests__/DatagridDropdownFilter.spec.tsx b/packages/pluggableWidgets/datagrid-dropdown-filter-web/src/__tests__/DatagridDropdownFilter.spec.tsx index a9574add32..02a7b4f0b9 100644 --- a/packages/pluggableWidgets/datagrid-dropdown-filter-web/src/__tests__/DatagridDropdownFilter.spec.tsx +++ b/packages/pluggableWidgets/datagrid-dropdown-filter-web/src/__tests__/DatagridDropdownFilter.spec.tsx @@ -1,7 +1,7 @@ import { EnumFilterStore } from "@mendix/widget-plugin-dropdown-filter/stores/EnumFilterStore"; import { FilterAPI } from "@mendix/widget-plugin-filtering/context"; import { ObservableFilterHost } from "@mendix/widget-plugin-filtering/typings/ObservableFilterHost"; -import { dynamicValue, listAttribute } from "@mendix/widget-plugin-test-utils"; +import { dynamic, listAttribute } from "@mendix/widget-plugin-test-utils"; import "@testing-library/jest-dom"; import { render, waitFor } from "@testing-library/react"; import { AssociationMetaData, AttributeMetaData } from "mendix"; @@ -23,9 +23,9 @@ const commonProps: DatagridDropdownFilterContainerProps = { filterable: false, multiSelect: false, clearable: false, - emptySelectionCaption: dynamicValue("Select"), - emptyOptionCaption: dynamicValue("None"), - ariaLabel: dynamicValue("AriaLabel"), + emptySelectionCaption: dynamic.available("Select"), + emptyOptionCaption: dynamic.available("None"), + ariaLabel: dynamic.available("AriaLabel"), selectedItemsStyle: "text", selectionMethod: "checkbox", refCaptionSource: "attr" diff --git a/packages/pluggableWidgets/datagrid-dropdown-filter-web/src/__tests__/__snapshots__/DatagridDropdownFilter.spec.tsx.snap b/packages/pluggableWidgets/datagrid-dropdown-filter-web/src/__tests__/__snapshots__/DatagridDropdownFilter.spec.tsx.snap index 30a3fc29b9..5edf28105f 100644 --- a/packages/pluggableWidgets/datagrid-dropdown-filter-web/src/__tests__/__snapshots__/DatagridDropdownFilter.spec.tsx.snap +++ b/packages/pluggableWidgets/datagrid-dropdown-filter-web/src/__tests__/__snapshots__/DatagridDropdownFilter.spec.tsx.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`Dropdown Filter with single instance with single attribute renders correctly 1`] = ` diff --git a/packages/pluggableWidgets/datagrid-number-filter-web/src/components/__tests__/DatagridNumberFilter.spec.tsx b/packages/pluggableWidgets/datagrid-number-filter-web/src/components/__tests__/DatagridNumberFilter.spec.tsx index ac6a05b69e..d147b9e01b 100644 --- a/packages/pluggableWidgets/datagrid-number-filter-web/src/components/__tests__/DatagridNumberFilter.spec.tsx +++ b/packages/pluggableWidgets/datagrid-number-filter-web/src/components/__tests__/DatagridNumberFilter.spec.tsx @@ -12,7 +12,6 @@ import { ObservableFilterHost } from "@mendix/widget-plugin-filtering/typings/Ob import { actionValue, dynamic, - dynamicValue, EditableValueBuilder, ListAttributeValueBuilder } from "@mendix/widget-plugin-test-utils"; @@ -126,20 +125,20 @@ describe("Number Filter", () => { describe("with defaultValue", () => { it("initializes with defaultValue", () => { - render((new Big(100))} />); + render(); expect(screen.getByRole("textbox")).toHaveValue("100"); }); it("do not sync value and defaultValue when defaultValue changes from undefined to number", () => { const { rerender } = render(); expect(screen.getByRole("textbox")).toHaveValue(""); - rerender((new Big(100))} />); + rerender(); expect(screen.getByRole("textbox")).toHaveValue(""); }); it("do not sync value and defaultValue when defaultValue changes from number to undefined", async () => { const { rerender } = render( - (new Big(100))} /> + ); expect(screen.getByRole("textbox")).toHaveValue("100"); rerender(); @@ -150,7 +149,7 @@ describe("Number Filter", () => { it("clears value when external reset all event is triggered", async () => { const attribute = new EditableValueBuilder().build(); - const value = dynamic(Big(123)); + const value = dynamic.available(Big(123)); const { getByRole } = render( ); diff --git a/packages/pluggableWidgets/datagrid-text-filter-web/src/components/__tests__/DatagridTextFilter.spec.tsx b/packages/pluggableWidgets/datagrid-text-filter-web/src/components/__tests__/DatagridTextFilter.spec.tsx index a35183e18d..5e73dcfe4a 100644 --- a/packages/pluggableWidgets/datagrid-text-filter-web/src/components/__tests__/DatagridTextFilter.spec.tsx +++ b/packages/pluggableWidgets/datagrid-text-filter-web/src/components/__tests__/DatagridTextFilter.spec.tsx @@ -11,7 +11,7 @@ import { StringInputFilterStore } from "@mendix/widget-plugin-filtering/stores/i import { ObservableFilterHost } from "@mendix/widget-plugin-filtering/typings/ObservableFilterHost"; import { actionValue, - dynamicValue, + dynamic, EditableValueBuilder, ListAttributeValueBuilder } from "@mendix/widget-plugin-test-utils"; @@ -79,22 +79,22 @@ describe("Text Filter", () => { const { rerender } = render(); expect(screen.getByRole("textbox")).toHaveValue(""); - rerender(("xyz")} />); + rerender(); expect(screen.getByRole("textbox")).toHaveValue(""); }); it("don't sync value when defaultValue changes from string to string", async () => { const { rerender } = render( - ("abc")} /> + ); expect(screen.getByRole("textbox")).toHaveValue("abc"); - rerender(("xyz")} />); + rerender(); expect(screen.getByRole("textbox")).toHaveValue("abc"); }); it("don't sync value when defaultValue changes from string to undefined", async () => { const { rerender } = render( - ("abc")} /> + ); expect(screen.getByRole("textbox")).toHaveValue("abc"); rerender(); @@ -103,7 +103,7 @@ describe("Text Filter", () => { it("clears value when external reset all event is triggered with defaultValue", async () => { const attribute = new EditableValueBuilder().build(); - const value = dynamicValue("a string"); + const value = dynamic.available("a string"); const { getByRole } = render( ); @@ -133,7 +133,7 @@ describe("Text Filter", () => { }); it("sets value when external set value event is triggered with defaultValue", async () => { const attribute = new EditableValueBuilder().build(); - const value = dynamicValue("a string"); + const value = dynamic.available("a string"); const { getByRole } = render( ); diff --git a/packages/pluggableWidgets/datagrid-web/src/components/__tests__/Header.spec.tsx b/packages/pluggableWidgets/datagrid-web/src/components/__tests__/Header.spec.tsx index 8700700918..6a89633dc6 100644 --- a/packages/pluggableWidgets/datagrid-web/src/components/__tests__/Header.spec.tsx +++ b/packages/pluggableWidgets/datagrid-web/src/components/__tests__/Header.spec.tsx @@ -146,7 +146,7 @@ describe("Header", () => { it("renders the structure correctly when value is empty", () => { const columnsType = column("Column 1", col => { - col.header = dynamic(" "); + col.header = dynamic.available(" "); }); const props = mockContainerProps({ columns: [columnsType] diff --git a/packages/pluggableWidgets/datagrid-web/src/features/data-export/__tests__/cell-readers.spec.ts b/packages/pluggableWidgets/datagrid-web/src/features/data-export/__tests__/cell-readers.spec.ts index 73f8c2ef1f..cc85e44750 100644 --- a/packages/pluggableWidgets/datagrid-web/src/features/data-export/__tests__/cell-readers.spec.ts +++ b/packages/pluggableWidgets/datagrid-web/src/features/data-export/__tests__/cell-readers.spec.ts @@ -40,7 +40,7 @@ describe("cell-readers", () => { c.showContentAs = "attribute"; c.attribute = listAttribute(() => new Big("1234.56")); c.exportType = "number"; - c.exportNumberFormat = dynamic("#,##0.00"); + c.exportNumberFormat = dynamic.available("#,##0.00"); }); const cell = readSingleCell(col); expect(cell.t).toBe("n"); @@ -74,7 +74,7 @@ describe("cell-readers", () => { c.showContentAs = "attribute"; c.attribute = listAttribute(() => testDate); c.exportType = "date"; - c.exportDateFormat = dynamic("yyyy-mm-dd"); + c.exportDateFormat = dynamic.available("yyyy-mm-dd"); }); const cell = readSingleCell(col); expect(cell.t).toBe("d"); @@ -164,7 +164,7 @@ describe("cell-readers", () => { c.showContentAs = "customContent"; c.exportValue = listExpression(() => "1234.56"); c.exportType = "number"; - c.exportNumberFormat = dynamic("#,##0.00"); + c.exportNumberFormat = dynamic.available("#,##0.00"); }); const cell = readSingleCell(col); expect(cell.t).toBe("n"); @@ -221,7 +221,7 @@ describe("cell-readers", () => { c.showContentAs = "customContent"; c.exportValue = listExpression(() => "2024-06-15T00:00:00.000Z"); c.exportType = "date"; - c.exportDateFormat = dynamic("yyyy-mm-dd"); + c.exportDateFormat = dynamic.available("yyyy-mm-dd"); }); const cell = readSingleCell(col); expect(cell.t).toBe("d"); @@ -246,7 +246,7 @@ describe("cell-readers", () => { c.showContentAs = "customContent"; c.exportValue = listExpression(() => "not-a-date"); c.exportType = "date"; - c.exportDateFormat = dynamic("yyyy-mm-dd"); + c.exportDateFormat = dynamic.available("yyyy-mm-dd"); }); const cell = readSingleCell(col); expect(cell.t).toBe("s"); @@ -258,7 +258,7 @@ describe("cell-readers", () => { c.showContentAs = "customContent"; c.exportValue = listExpression(() => ""); c.exportType = "date"; - c.exportDateFormat = dynamic("yyyy-mm-dd"); + c.exportDateFormat = dynamic.available("yyyy-mm-dd"); }); const cell = readSingleCell(col); expect(cell.t).toBe("s"); @@ -351,7 +351,7 @@ describe("cell-readers", () => { c.showContentAs = "attribute"; c.attribute = listAttribute(() => new Big("9999999999999999999")); c.exportType = "number"; - c.exportNumberFormat = dynamic("0"); + c.exportNumberFormat = dynamic.available("0"); }); const cell = readSingleCell(col); expect(cell.t).toBe("s"); @@ -376,7 +376,7 @@ describe("cell-readers", () => { c.showContentAs = "attribute"; c.attribute = listAttribute(() => testDate); c.exportType = "date"; - c.exportDateFormat = dynamic("dd-mmm-yyyy"); + c.exportDateFormat = dynamic.available("dd-mmm-yyyy"); }); const cell = readSingleCell(col); expect(cell.t).toBe("d"); @@ -390,7 +390,7 @@ describe("cell-readers", () => { c.showContentAs = "attribute"; c.attribute = listAttribute(() => testDate); c.exportType = "date"; - c.exportDateFormat = dynamic("yyyy-mm-dd hh:mm:ss"); + c.exportDateFormat = dynamic.available("yyyy-mm-dd hh:mm:ss"); }); const cell = readSingleCell(col); expect(cell.t).toBe("d"); @@ -402,7 +402,7 @@ describe("cell-readers", () => { c.showContentAs = "customContent"; c.exportValue = listExpression(() => "2024-06-15T10:30:00Z"); c.exportType = "date"; - c.exportDateFormat = dynamic("dd-mmm-yyyy"); + c.exportDateFormat = dynamic.available("dd-mmm-yyyy"); }); const cell = readSingleCell(col); expect(cell.t).toBe("d"); @@ -414,7 +414,7 @@ describe("cell-readers", () => { c.showContentAs = "customContent"; c.exportValue = listExpression(() => "2024-06-15T10:30:00Z"); c.exportType = "date"; - c.exportDateFormat = dynamic("yyyy-mm-dd hh:mm:ss"); + c.exportDateFormat = dynamic.available("yyyy-mm-dd hh:mm:ss"); }); const cell = readSingleCell(col); expect(cell.t).toBe("d"); diff --git a/packages/pluggableWidgets/datagrid-web/src/utils/test-utils.tsx b/packages/pluggableWidgets/datagrid-web/src/utils/test-utils.tsx index 006aa771f8..d18c6e9ac5 100644 --- a/packages/pluggableWidgets/datagrid-web/src/utils/test-utils.tsx +++ b/packages/pluggableWidgets/datagrid-web/src/utils/test-utils.tsx @@ -10,7 +10,7 @@ export const column = (header = "Test", patch?: (col: ColumnsType) => void): Col attribute: listAttribute(() => "Attr value"), dynamicText: listExpression(() => "Dynamic text"), draggable: false, - header: dynamic(header), + header: dynamic.available(header), hidable: "no" as const, resizable: false, showContentAs: "attribute", @@ -18,7 +18,7 @@ export const column = (header = "Test", patch?: (col: ColumnsType) => void): Col sortable: false, width: "autoFill" as const, wrapText: false, - visible: dynamic(true), + visible: dynamic.available(true), minWidth: "auto", minWidthLimit: 100, allowEventPropagation: true, @@ -82,9 +82,9 @@ export function mockContainerProps(overrides?: Partial): configurationStorageType: "attribute", configurationAttribute: undefined, storeFiltersInPersonalization: true, - selectAllText: dynamic("Select all items"), - selectAllTemplate: dynamic("Select all %d items"), - allSelectedText: dynamic("All items selected"), + selectAllText: dynamic.available("Select all items"), + selectAllTemplate: dynamic.available("Select all %d items"), + allSelectedText: dynamic.available("All items selected"), useCustomPagination: false, customPagination: undefined, ...overrides diff --git a/packages/pluggableWidgets/dropdown-sort-web/src/components/__test__/DropdownSort.spec.tsx b/packages/pluggableWidgets/dropdown-sort-web/src/components/__test__/DropdownSort.spec.tsx index dad836d8ac..731728dd26 100644 --- a/packages/pluggableWidgets/dropdown-sort-web/src/components/__test__/DropdownSort.spec.tsx +++ b/packages/pluggableWidgets/dropdown-sort-web/src/components/__test__/DropdownSort.spec.tsx @@ -1,8 +1,8 @@ -import { getGlobalSortContext, SortAPI } from "@mendix/widget-plugin-sorting/react/context"; -import { SortStoreHost } from "@mendix/widget-plugin-sorting/stores/SortStoreHost"; -import { attrId, dynamicValue, ListAttributeValueBuilder } from "@mendix/widget-plugin-test-utils"; import { fireEvent, render } from "@testing-library/react"; import { ReactElement } from "react"; +import { getGlobalSortContext, SortAPI } from "@mendix/widget-plugin-sorting/react/context"; +import { SortStoreHost } from "@mendix/widget-plugin-sorting/stores/SortStoreHost"; +import { attrId, dynamic, ListAttributeValueBuilder } from "@mendix/widget-plugin-test-utils"; import { AttributesType, DropdownSortContainerProps } from "../../../typings/DropdownSortProps"; import { DropdownSort } from "../../DropdownSort"; @@ -21,11 +21,11 @@ const createAPI = (observer: SortStoreHost): SortAPI => ({ const mockAttributes = (): AttributesType[] => [ { attribute: new ListAttributeValueBuilder().withId(attrId("1")).withType("String").withSortable(true).build(), - caption: dynamicValue("Option 1") + caption: dynamic.available("Option 1") }, { attribute: new ListAttributeValueBuilder().withId(attrId("2")).withType("Decimal").withSortable(true).build(), - caption: dynamicValue("Option 2") + caption: dynamic.available("Option 2") } ]; diff --git a/packages/pluggableWidgets/events-web/src/__tests__/AppEvents.spec.tsx b/packages/pluggableWidgets/events-web/src/__tests__/AppEvents.spec.tsx index 02e155aa25..ab2960281e 100644 --- a/packages/pluggableWidgets/events-web/src/__tests__/AppEvents.spec.tsx +++ b/packages/pluggableWidgets/events-web/src/__tests__/AppEvents.spec.tsx @@ -1,4 +1,4 @@ -import { actionValue, dynamicValue } from "@mendix/widget-plugin-test-utils"; +import { actionValue, dynamic } from "@mendix/widget-plugin-test-utils"; import "@testing-library/jest-dom"; import { render } from "@testing-library/react"; import { EventsContainerProps } from "../../typings/EventsProps"; @@ -13,16 +13,16 @@ describe("App events (load)", () => { onComponentLoad: actionValue(), componentLoadDelayParameterType: "number", componentLoadDelay: 0, - componentLoadDelayExpression: dynamicValue(), + componentLoadDelayExpression: dynamic.unavailable(), componentLoadRepeat: false, componentLoadRepeatIntervalParameterType: "number", componentLoadRepeatInterval: 0, - componentLoadRepeatIntervalExpression: dynamicValue(), + componentLoadRepeatIntervalExpression: dynamic.unavailable(), onEventChangeAttribute: undefined, onEventChange: undefined, onEventChangeDelayParameterType: "number", onEventChangeDelay: 0, - onEventChangeDelayExpression: dynamicValue() + onEventChangeDelayExpression: dynamic.unavailable() }; }); it("render app events", async () => { diff --git a/packages/pluggableWidgets/file-uploader-web/src/stores/__tests__/FileUploaderStore.spec.ts b/packages/pluggableWidgets/file-uploader-web/src/stores/__tests__/FileUploaderStore.spec.ts index 722ea57bdf..148fbca112 100644 --- a/packages/pluggableWidgets/file-uploader-web/src/stores/__tests__/FileUploaderStore.spec.ts +++ b/packages/pluggableWidgets/file-uploader-web/src/stores/__tests__/FileUploaderStore.spec.ts @@ -32,29 +32,33 @@ function buildProps(overrides: Partial = {}): FileUp createFileAction: actionValue(true, false), createImageAction: actionValue(true, false), allowedFileFormats: [], - maxFilesPerUpload: dynamic(new Big(5)), + maxFilesPerUpload: dynamic.available(new Big(5)), maxFilesPerBatch: unavailableDynamic(), maxFileSize: 25, objectCreationTimeout: 10, - dropzoneIdleMessage: dynamic("Drag and drop files here"), - dropzoneAcceptedMessage: dynamic("All files can be uploaded."), - dropzoneRejectedMessage: dynamic("Some files may not be uploadable."), - uploadInProgressMessage: dynamic("Uploading..."), - uploadQueuedMessage: dynamic("Waiting..."), - uploadSuccessMessage: dynamic("Uploaded successfully."), - uploadFailureGenericMessage: dynamic("An error occurred during uploading."), - uploadFailureInvalidFileFormatMessage: dynamic("File format is not supported, supported formats are ###."), - uploadFailureFileIsTooBigMessage: dynamic("File size exceeds the maximum limit of ### megabytes."), - uploadFailureTooManyFilesMessage: dynamic("Too many files added. Only ### files per upload are allowed."), - uploadLimitReachedMessage: dynamic("Maximum file count of ### reached."), - unavailableCreateActionMessage: dynamic( + dropzoneIdleMessage: dynamic.available("Drag and drop files here"), + dropzoneAcceptedMessage: dynamic.available("All files can be uploaded."), + dropzoneRejectedMessage: dynamic.available("Some files may not be uploadable."), + uploadInProgressMessage: dynamic.available("Uploading..."), + uploadQueuedMessage: dynamic.available("Waiting..."), + uploadSuccessMessage: dynamic.available("Uploaded successfully."), + uploadFailureGenericMessage: dynamic.available("An error occurred during uploading."), + uploadFailureInvalidFileFormatMessage: dynamic.available( + "File format is not supported, supported formats are ###." + ), + uploadFailureFileIsTooBigMessage: dynamic.available("File size exceeds the maximum limit of ### megabytes."), + uploadFailureTooManyFilesMessage: dynamic.available( + "Too many files added. Only ### files per upload are allowed." + ), + uploadLimitReachedMessage: dynamic.available("Maximum file count of ### reached."), + unavailableCreateActionMessage: dynamic.available( "Can't upload files at this time. Please contact your system administrator." ), - downloadButtonTextMessage: dynamic("Download this file"), - removeButtonTextMessage: dynamic("Remove this file"), - retryButtonTextMessage: dynamic("Retry upload"), - removeSuccessMessage: dynamic("Removed successfully."), - removeErrorMessage: dynamic("An error occurred while removing this file."), + downloadButtonTextMessage: dynamic.available("Download this file"), + removeButtonTextMessage: dynamic.available("Remove this file"), + retryButtonTextMessage: dynamic.available("Retry upload"), + removeSuccessMessage: dynamic.available("Removed successfully."), + removeErrorMessage: dynamic.available("An error occurred while removing this file."), enableCustomButtons: false, customButtons: [], onUploadSuccessFile: undefined, @@ -239,7 +243,7 @@ describe("FileStore.newRejectedFile", () => { describe("FileStore.canRetry", () => { test("true when file is rejected and limit is not full", () => { const store = buildStore({ - maxFilesPerUpload: dynamic(new Big(3)), + maxFilesPerUpload: dynamic.available(new Big(3)), maxFilesPerBatch: unavailableDynamic() }); const file = FileStore.newRejectedFile(makeFile("x.txt"), store); @@ -250,7 +254,7 @@ describe("FileStore.canRetry", () => { test("false when file is rejected but limit is full", () => { const store = buildStore({ - maxFilesPerUpload: dynamic(new Big(2)), + maxFilesPerUpload: dynamic.available(new Big(2)), maxFilesPerBatch: unavailableDynamic() }); store.objectCreationHelper.request = jest.fn().mockReturnValue(new Promise(() => {})); @@ -275,7 +279,7 @@ describe("FileStore.canRetry", () => { describe("FileStore.canRetry — reacts to freed slots", () => { test("flips to true when an active file errors and frees a slot", async () => { const store = buildStore({ - maxFilesPerUpload: dynamic(new Big(1)), + maxFilesPerUpload: dynamic.available(new Big(1)), maxFilesPerBatch: unavailableDynamic() }); store.objectCreationHelper.request = jest.fn().mockRejectedValueOnce(new Error("fail")); @@ -296,7 +300,7 @@ describe("FileStore.canRetry — reacts to freed slots", () => { test("flips to true when an active file is removed and frees a slot", () => { const store = buildStore({ - maxFilesPerUpload: dynamic(new Big(1)), + maxFilesPerUpload: dynamic.available(new Big(1)), maxFilesPerBatch: unavailableDynamic() }); store.objectCreationHelper.request = jest.fn().mockReturnValue(new Promise(() => {})); @@ -316,7 +320,7 @@ describe("FileStore.canRetry — reacts to freed slots", () => { test("both rejected files get canRetry=true when one slot frees", async () => { const store = buildStore({ - maxFilesPerUpload: dynamic(new Big(2)), + maxFilesPerUpload: dynamic.available(new Big(2)), maxFilesPerBatch: unavailableDynamic() }); store.objectCreationHelper.request = jest @@ -346,7 +350,7 @@ describe("FileStore.canRetry — reacts to freed slots", () => { describe("FileStore.retry", () => { test("transitions rejected file to uploading via queue reaction", () => { const store = buildStore({ - maxFilesPerUpload: dynamic(new Big(3)), + maxFilesPerUpload: dynamic.available(new Big(3)), maxFilesPerBatch: unavailableDynamic() }); store.objectCreationHelper.request = jest.fn().mockReturnValue(new Promise(() => {})); @@ -373,7 +377,7 @@ describe("FileStore.retry", () => { test("does nothing when total limit is full (limit reached)", () => { const store = buildStore({ - maxFilesPerUpload: dynamic(new Big(1)), + maxFilesPerUpload: dynamic.available(new Big(1)), maxFilesPerBatch: unavailableDynamic() }); store.objectCreationHelper.request = jest.fn().mockReturnValue(new Promise(() => {})); @@ -414,8 +418,8 @@ describe("FileUploaderStore.queuedCount", () => { test("increases when processDrop adds queued files", () => { const store = buildStore({ - maxFilesPerUpload: dynamic(new Big(10)), - maxFilesPerBatch: dynamic(new Big(1)) + maxFilesPerUpload: dynamic.available(new Big(10)), + maxFilesPerBatch: dynamic.available(new Big(1)) }); store.objectCreationHelper.request = jest.fn().mockReturnValue(new Promise(() => {})); @@ -430,13 +434,13 @@ describe("FileUploaderStore.queuedCount", () => { describe("FileUploaderStore — renamed properties", () => { test("maxTotalFiles reads from maxFilesPerUpload XML prop", () => { - const store = buildStore({ maxFilesPerUpload: dynamic(new Big(7)) }); + const store = buildStore({ maxFilesPerUpload: dynamic.available(new Big(7)) }); expect(store.maxTotalFiles).toBe(7); }); test("maxConcurrentUploads reads from maxFilesPerBatch XML prop", () => { - const store = buildStore({ maxFilesPerBatch: dynamic(new Big(3)) }); + const store = buildStore({ maxFilesPerBatch: dynamic.available(new Big(3)) }); expect(store.maxConcurrentUploads).toBe(3); }); @@ -474,7 +478,7 @@ describe("FileUploaderStore — removed legacy methods", () => { describe("FileUploaderStore.processDrop — pure classifier", () => { test("accepted files within capacity enter upload pipeline (queued or uploading)", () => { - const store = buildStore({ maxFilesPerUpload: dynamic(new Big(5)) }); + const store = buildStore({ maxFilesPerUpload: dynamic.available(new Big(5)) }); store.processDrop( [1, 2, 3].map(n => makeFile(`file${n}.txt`)), @@ -486,7 +490,7 @@ describe("FileUploaderStore.processDrop — pure classifier", () => { }); test("files exceeding maxTotalFiles go to 'rejected' status", () => { - const store = buildStore({ maxFilesPerUpload: dynamic(new Big(2)) }); + const store = buildStore({ maxFilesPerUpload: dynamic.available(new Big(2)) }); store.processDrop( [1, 2, 3, 4].map(n => makeFile(`file${n}.txt`)), @@ -510,7 +514,10 @@ describe("FileUploaderStore.processDrop — pure classifier", () => { }); test("no file gets 'batchExceeded' treatment — excess files enter pipeline or rejected only", () => { - const store = buildStore({ maxFilesPerBatch: dynamic(new Big(2)), maxFilesPerUpload: dynamic(new Big(10)) }); + const store = buildStore({ + maxFilesPerBatch: dynamic.available(new Big(2)), + maxFilesPerUpload: dynamic.available(new Big(10)) + }); store.processDrop( [1, 2, 3, 4].map(n => makeFile(`file${n}.txt`)), @@ -522,7 +529,7 @@ describe("FileUploaderStore.processDrop — pure classifier", () => { }); test("no file has errorType set after processDrop", () => { - const store = buildStore({ maxFilesPerUpload: dynamic(new Big(2)) }); + const store = buildStore({ maxFilesPerUpload: dynamic.available(new Big(2)) }); store.processDrop( [1, 2, 3, 4].map(n => makeFile(`file${n}.txt`)), @@ -535,8 +542,8 @@ describe("FileUploaderStore.processDrop — pure classifier", () => { test("drop with maxConcurrentUploads=2: exactly 2 start uploading, rest stay queued", () => { const store = buildStore({ - maxFilesPerUpload: dynamic(new Big(10)), - maxFilesPerBatch: dynamic(new Big(2)) + maxFilesPerUpload: dynamic.available(new Big(10)), + maxFilesPerBatch: dynamic.available(new Big(2)) }); store.objectCreationHelper.request = jest.fn().mockRejectedValue(new Error("no server")); @@ -551,7 +558,7 @@ describe("FileUploaderStore.processDrop — pure classifier", () => { test("drop with no concurrent limit: all files start uploading immediately", () => { const store = buildStore({ - maxFilesPerUpload: dynamic(new Big(10)), + maxFilesPerUpload: dynamic.available(new Big(10)), maxFilesPerBatch: unavailableDynamic() }); store.objectCreationHelper.request = jest.fn().mockRejectedValue(new Error("no server")); @@ -571,8 +578,8 @@ describe("FileUploaderStore.processDrop — pure classifier", () => { describe("FileUploaderStore — Reaction 3: queue auto-drains when queued files arrive", () => { test("files queued by processDrop start uploading without any manual drain call", () => { const store = buildStore({ - maxFilesPerUpload: dynamic(new Big(10)), - maxFilesPerBatch: dynamic(new Big(1)) + maxFilesPerUpload: dynamic.available(new Big(10)), + maxFilesPerBatch: dynamic.available(new Big(1)) }); store.objectCreationHelper.request = jest.fn().mockReturnValue(new Promise(() => {})); @@ -586,8 +593,8 @@ describe("FileUploaderStore — Reaction 3: queue auto-drains when queued files test("manually setting a file to queued triggers upload automatically", () => { const store = buildStore({ - maxFilesPerUpload: dynamic(new Big(10)), - maxFilesPerBatch: dynamic(new Big(2)) + maxFilesPerUpload: dynamic.available(new Big(10)), + maxFilesPerBatch: dynamic.available(new Big(2)) }); store.objectCreationHelper.request = jest.fn().mockReturnValue(new Promise(() => {})); @@ -602,7 +609,7 @@ describe("FileUploaderStore — Reaction 3: queue auto-drains when queued files test("rejected file does NOT auto-promote when a slot frees — manual retry required", () => { const store = buildStore({ - maxFilesPerUpload: dynamic(new Big(2)), + maxFilesPerUpload: dynamic.available(new Big(2)), maxFilesPerBatch: unavailableDynamic() }); store.objectCreationHelper.request = jest.fn().mockReturnValue(new Promise(() => {})); @@ -625,14 +632,14 @@ describe("FileUploaderStore — Reaction 3: queue auto-drains when queued files describe("FileUploaderStore.isFileUploadLimitReached", () => { test("false when no files have been dropped", () => { - const store = buildStore({ maxFilesPerUpload: dynamic(new Big(3)) }); + const store = buildStore({ maxFilesPerUpload: dynamic.available(new Big(3)) }); expect(store.isFileUploadLimitReached).toBe(false); }); test("false when below the configured limit", () => { const store = buildStore({ - maxFilesPerUpload: dynamic(new Big(3)), + maxFilesPerUpload: dynamic.available(new Big(3)), maxFilesPerBatch: unavailableDynamic() }); store.objectCreationHelper.request = jest.fn().mockReturnValue(new Promise(() => {})); @@ -644,7 +651,7 @@ describe("FileUploaderStore.isFileUploadLimitReached", () => { test("true when exactly at the limit", () => { const store = buildStore({ - maxFilesPerUpload: dynamic(new Big(2)), + maxFilesPerUpload: dynamic.available(new Big(2)), maxFilesPerBatch: unavailableDynamic() }); store.objectCreationHelper.request = jest.fn().mockReturnValue(new Promise(() => {})); @@ -656,7 +663,7 @@ describe("FileUploaderStore.isFileUploadLimitReached", () => { test("rejected files (over cap) do not contribute to active count", () => { const store = buildStore({ - maxFilesPerUpload: dynamic(new Big(2)), + maxFilesPerUpload: dynamic.available(new Big(2)), maxFilesPerBatch: unavailableDynamic() }); store.objectCreationHelper.request = jest.fn().mockReturnValue(new Promise(() => {})); @@ -668,7 +675,7 @@ describe("FileUploaderStore.isFileUploadLimitReached", () => { }); test("validationError files do not count toward active count", () => { - const store = buildStore({ maxFilesPerUpload: dynamic(new Big(2)) }); + const store = buildStore({ maxFilesPerUpload: dynamic.available(new Big(2)) }); store.processDrop([], [ { file: makeFile("bad1.exe"), errors: [{ code: "file-invalid-type", message: "" }] }, @@ -682,7 +689,7 @@ describe("FileUploaderStore.isFileUploadLimitReached", () => { test("uploadingError files do not count toward active count", async () => { const store = buildStore({ - maxFilesPerUpload: dynamic(new Big(2)), + maxFilesPerUpload: dynamic.available(new Big(2)), maxFilesPerBatch: unavailableDynamic() }); store.objectCreationHelper.request = jest.fn().mockRejectedValue(new Error("fail")); @@ -698,7 +705,7 @@ describe("FileUploaderStore.isFileUploadLimitReached", () => { test("false when maxTotalFiles is 0 (unlimited), regardless of file count", () => { const store = buildStore({ - maxFilesPerUpload: dynamic(new Big(0)), + maxFilesPerUpload: dynamic.available(new Big(0)), maxFilesPerBatch: unavailableDynamic() }); store.objectCreationHelper.request = jest.fn().mockReturnValue(new Promise(() => {})); @@ -725,7 +732,7 @@ describe("FileUploaderStore.isFileUploadLimitReached", () => { describe("FileUploaderStore.sortedFiles", () => { test("validationError files sort to the end", () => { - const store = buildStore({ maxFilesPerUpload: dynamic(new Big(10)) }); + const store = buildStore({ maxFilesPerUpload: dynamic.available(new Big(10)) }); // First drop an accepted file, then a validation error — error ends up at index 0 store.processDrop([makeFile("good.txt")], []); @@ -741,7 +748,7 @@ describe("FileUploaderStore.sortedFiles", () => { }); test("does not mutate the original files array", () => { - const store = buildStore({ maxFilesPerUpload: dynamic(new Big(10)) }); + const store = buildStore({ maxFilesPerUpload: dynamic.available(new Big(10)) }); store.processDrop([makeFile("good.txt")], []); store.processDrop([], [ @@ -759,7 +766,7 @@ describe("FileUploaderStore.sortedFiles", () => { describe("FileUploaderStore.promoteQueuedFiles", () => { test("calls upload() on queued files up to maxConcurrentUploads", () => { - const store = buildStore({ maxFilesPerBatch: dynamic(new Big(2)) }); + const store = buildStore({ maxFilesPerBatch: dynamic.available(new Big(2)) }); const queued1 = { fileStatus: "queued", upload: jest.fn() } as any; const queued2 = { fileStatus: "queued", upload: jest.fn() } as any; @@ -774,7 +781,7 @@ describe("FileUploaderStore.promoteQueuedFiles", () => { }); test("does not promote beyond available concurrent slots", () => { - const store = buildStore({ maxFilesPerBatch: dynamic(new Big(2)) }); + const store = buildStore({ maxFilesPerBatch: dynamic.available(new Big(2)) }); const uploading = { fileStatus: "uploading" } as any; const queued1 = { fileStatus: "queued", upload: jest.fn() } as any; @@ -790,7 +797,7 @@ describe("FileUploaderStore.promoteQueuedFiles", () => { }); test("does nothing when all concurrent slots are occupied", () => { - const store = buildStore({ maxFilesPerBatch: dynamic(new Big(2)) }); + const store = buildStore({ maxFilesPerBatch: dynamic.available(new Big(2)) }); const uploading1 = { fileStatus: "uploading" } as any; const uploading2 = { fileStatus: "uploading" } as any; @@ -804,7 +811,7 @@ describe("FileUploaderStore.promoteQueuedFiles", () => { }); test("does nothing when no queued files exist", () => { - const store = buildStore({ maxFilesPerBatch: dynamic(new Big(2)) }); + const store = buildStore({ maxFilesPerBatch: dynamic.available(new Big(2)) }); store.files.push({ fileStatus: "existingFile" } as any); @@ -813,8 +820,8 @@ describe("FileUploaderStore.promoteQueuedFiles", () => { test("queued file starts uploading when a concurrent slot frees up", async () => { const store = buildStore({ - maxFilesPerBatch: dynamic(new Big(1)), - maxFilesPerUpload: dynamic(new Big(10)) + maxFilesPerBatch: dynamic.available(new Big(1)), + maxFilesPerUpload: dynamic.available(new Big(10)) }); const neverResolve = new Promise(() => {}); store.objectCreationHelper.request = jest.fn().mockReturnValue(neverResolve); @@ -912,7 +919,7 @@ describe("FileUploaderStore.processDrop — error message mapping", () => { describe("FileUploaderStore.updateProps", () => { test("live increase of maxTotalFiles unblocks dropzone mid-session", () => { const store = buildStore({ - maxFilesPerUpload: dynamic(new Big(2)), + maxFilesPerUpload: dynamic.available(new Big(2)), maxFilesPerBatch: unavailableDynamic() }); store.objectCreationHelper.request = jest.fn().mockReturnValue(new Promise(() => {})); @@ -921,17 +928,17 @@ describe("FileUploaderStore.updateProps", () => { expect(store.isFileUploadLimitReached).toBe(true); store.updateProps( - buildProps({ maxFilesPerUpload: dynamic(new Big(5)), maxFilesPerBatch: unavailableDynamic() }) + buildProps({ maxFilesPerUpload: dynamic.available(new Big(5)), maxFilesPerBatch: unavailableDynamic() }) ); expect(store.isFileUploadLimitReached).toBe(false); }); test("live decrease of maxConcurrentUploads is reflected immediately", () => { - const store = buildStore({ maxFilesPerBatch: dynamic(new Big(4)) }); + const store = buildStore({ maxFilesPerBatch: dynamic.available(new Big(4)) }); expect(store.maxConcurrentUploads).toBe(4); - store.updateProps(buildProps({ maxFilesPerBatch: dynamic(new Big(1)) })); + store.updateProps(buildProps({ maxFilesPerBatch: dynamic.available(new Big(1)) })); expect(store.maxConcurrentUploads).toBe(1); }); @@ -942,8 +949,8 @@ describe("FileUploaderStore.updateProps", () => { describe("FileUploaderStore.dispose", () => { test("queue drain reaction stops firing after dispose", () => { const store = buildStore({ - maxFilesPerUpload: dynamic(new Big(10)), - maxFilesPerBatch: dynamic(new Big(1)) + maxFilesPerUpload: dynamic.available(new Big(10)), + maxFilesPerBatch: dynamic.available(new Big(1)) }); const neverResolve = new Promise(() => {}); store.objectCreationHelper.request = jest.fn().mockReturnValue(neverResolve); @@ -967,8 +974,8 @@ describe("FileUploaderStore.dispose", () => { describe("upload queue — end-to-end", () => { test("upload error frees concurrent slot and next queued file starts uploading", async () => { const store = buildStore({ - maxFilesPerUpload: dynamic(new Big(10)), - maxFilesPerBatch: dynamic(new Big(1)) + maxFilesPerUpload: dynamic.available(new Big(10)), + maxFilesPerBatch: dynamic.available(new Big(1)) }); // First request fails, second hangs so we can assert the stable "uploading" state const neverResolve = new Promise(() => {}); @@ -994,7 +1001,7 @@ describe("upload queue — end-to-end", () => { test("upload errors do NOT auto-promote rejected files — user must retry manually", async () => { const store = buildStore({ - maxFilesPerUpload: dynamic(new Big(2)), + maxFilesPerUpload: dynamic.available(new Big(2)), maxFilesPerBatch: unavailableDynamic() }); store.objectCreationHelper.request = jest diff --git a/packages/pluggableWidgets/file-uploader-web/src/utils/__tests__/parseAllowedFormats.spec.ts b/packages/pluggableWidgets/file-uploader-web/src/utils/__tests__/parseAllowedFormats.spec.ts index d23035117c..b9fded2ee9 100644 --- a/packages/pluggableWidgets/file-uploader-web/src/utils/__tests__/parseAllowedFormats.spec.ts +++ b/packages/pluggableWidgets/file-uploader-web/src/utils/__tests__/parseAllowedFormats.spec.ts @@ -1,4 +1,4 @@ -import { dynamicValue } from "@mendix/widget-plugin-test-utils"; +import { dynamic } from "@mendix/widget-plugin-test-utils"; import { AllowedFileFormatsType } from "../../../typings/FileUploaderProps"; import { parseAllowedFormats } from "../parseAllowedFormats"; @@ -7,28 +7,28 @@ describe("parseAllowedFormats", () => { const input: AllowedFileFormatsType[] = [ { configMode: "advanced", - typeFormatDescription: dynamicValue("test"), + typeFormatDescription: dynamic.available("test"), predefinedType: "pdfFile", mimeType: "image/jpeg", extensions: ".jpg,.jpeg" }, { configMode: "advanced", - typeFormatDescription: dynamicValue("test2"), + typeFormatDescription: dynamic.available("test2"), predefinedType: "pdfFile", mimeType: "application/pdf", extensions: ".pdf" }, { configMode: "advanced", - typeFormatDescription: dynamicValue("test"), + typeFormatDescription: dynamic.available("test"), predefinedType: "pdfFile", mimeType: "text/*", extensions: "" }, { configMode: "advanced", - typeFormatDescription: dynamicValue("test2"), + typeFormatDescription: dynamic.available("test2"), predefinedType: "pdfFile", mimeType: "", extensions: ".html,.txt" @@ -56,14 +56,14 @@ describe("parseAllowedFormats", () => { const input: AllowedFileFormatsType[] = [ { configMode: "advanced", - typeFormatDescription: dynamicValue("test"), + typeFormatDescription: dynamic.available("test"), predefinedType: "pdfFile", mimeType: "image/*", extensions: ".jpg" }, { configMode: "advanced", - typeFormatDescription: dynamicValue("test"), + typeFormatDescription: dynamic.available("test"), predefinedType: "pdfFile", mimeType: "image/*", extensions: ".png" @@ -84,7 +84,7 @@ describe("parseAllowedFormats", () => { const input: AllowedFileFormatsType[] = [ { configMode: "advanced", - typeFormatDescription: dynamicValue("test"), + typeFormatDescription: dynamic.available("test"), predefinedType: "pdfFile", mimeType: "application-pdf", extensions: ".pdf" @@ -99,7 +99,7 @@ describe("parseAllowedFormats", () => { const input: AllowedFileFormatsType[] = [ { configMode: "advanced", - typeFormatDescription: dynamicValue("special-extensions"), + typeFormatDescription: dynamic.available("special-extensions"), predefinedType: "pdfFile", mimeType: "application/x-custom", extensions: ".tar-gz,.js-map,.c++" @@ -117,7 +117,7 @@ describe("parseAllowedFormats", () => { const input: AllowedFileFormatsType[] = [ { configMode: "advanced", - typeFormatDescription: dynamicValue("test"), + typeFormatDescription: dynamic.available("test"), predefinedType: "pdfFile", mimeType: "text/*", extensions: ".txt,pdf" @@ -133,7 +133,7 @@ describe("parseAllowedFormats", () => { const input: AllowedFileFormatsType[] = [ { configMode: "advanced", - typeFormatDescription: dynamicValue("test"), + typeFormatDescription: dynamic.available("test"), predefinedType: "pdfFile", mimeType: "text/*", extensions: ".txt,.cvs,abc" @@ -149,7 +149,7 @@ describe("parseAllowedFormats", () => { const input: AllowedFileFormatsType[] = [ { configMode: "advanced", - typeFormatDescription: dynamicValue("test"), + typeFormatDescription: dynamic.available("test"), predefinedType: "pdfFile", mimeType: "text/*", extensions: ".txt,.config.json" diff --git a/packages/pluggableWidgets/maps-web/src/model/services/__tests__/LocationResolver.integration.spec.ts b/packages/pluggableWidgets/maps-web/src/model/services/__tests__/LocationResolver.integration.spec.ts index 915d4345bd..caaf84f79f 100644 --- a/packages/pluggableWidgets/maps-web/src/model/services/__tests__/LocationResolver.integration.spec.ts +++ b/packages/pluggableWidgets/maps-web/src/model/services/__tests__/LocationResolver.integration.spec.ts @@ -41,13 +41,13 @@ describe("LocationResolverService - Integration Tests", () => { geodecodeApiKey: "test-key", markers: [ { - latitude: dynamic("40.7128"), - longitude: dynamic("-74.0060"), - title: dynamic("NYC") + latitude: dynamic.available("40.7128"), + longitude: dynamic.available("-74.0060"), + title: dynamic.available("NYC") } as MarkersType, { - address: dynamic("Boston, MA"), - title: dynamic("Boston") + address: dynamic.available("Boston, MA"), + title: dynamic.available("Boston") } as MarkersType ] }), @@ -78,7 +78,7 @@ describe("LocationResolverService - Integration Tests", () => { geodecodeApiKey: "test-key", markers: [ { - address: dynamic("New York, NY") + address: dynamic.available("New York, NY") } as MarkersType ] }); @@ -114,9 +114,9 @@ describe("LocationResolverService - Integration Tests", () => { props: mockContainerProps({ geodecodeApiKey: "test-key", markers: [ - { address: dynamic("NYC"), title: dynamic("A") } as MarkersType, - { address: dynamic("NYC"), title: dynamic("B") } as MarkersType, - { address: dynamic("NYC"), title: dynamic("C") } as MarkersType + { address: dynamic.available("NYC"), title: dynamic.available("A") } as MarkersType, + { address: dynamic.available("NYC"), title: dynamic.available("B") } as MarkersType, + { address: dynamic.available("NYC"), title: dynamic.available("C") } as MarkersType ] }), geocodeFunction: mockGeocode @@ -139,7 +139,7 @@ describe("LocationResolverService - Integration Tests", () => { geodecodeApiKey: "test-key", markers: [ { - address: dynamic("Invalid Address") + address: dynamic.available("Invalid Address") } as MarkersType ] }), @@ -170,9 +170,9 @@ describe("LocationResolverService - Integration Tests", () => { props: mockContainerProps({ geodecodeApiKey: "test-key", markers: [ - { address: dynamic("NYC") } as MarkersType, - { address: dynamic("Invalid") } as MarkersType, - { address: dynamic("Boston") } as MarkersType + { address: dynamic.available("NYC") } as MarkersType, + { address: dynamic.available("Invalid") } as MarkersType, + { address: dynamic.available("Boston") } as MarkersType ] }), geocodeFunction: mockGeocode @@ -197,9 +197,9 @@ describe("LocationResolverService - Integration Tests", () => { props: mockContainerProps({ markers: [ { - latitude: dynamic("40"), - longitude: dynamic("-74"), - title: dynamic("Static") + latitude: dynamic.available("40"), + longitude: dynamic.available("-74"), + title: dynamic.available("Static") } as MarkersType ], dynamicMarkers: [ @@ -279,8 +279,8 @@ describe("LocationResolverService - Integration Tests", () => { props: mockContainerProps({ markers: [ { - latitude: dynamic("40"), - longitude: dynamic("-74"), + latitude: dynamic.available("40"), + longitude: dynamic.available("-74"), onClick: { execute: mockAction } diff --git a/packages/pluggableWidgets/maps-web/src/model/services/__tests__/LocationResolver.reactivity.spec.ts b/packages/pluggableWidgets/maps-web/src/model/services/__tests__/LocationResolver.reactivity.spec.ts index 2fb6035239..37f6622508 100644 --- a/packages/pluggableWidgets/maps-web/src/model/services/__tests__/LocationResolver.reactivity.spec.ts +++ b/packages/pluggableWidgets/maps-web/src/model/services/__tests__/LocationResolver.reactivity.spec.ts @@ -30,8 +30,8 @@ describe("LocationResolverService - Reactivity Tests", () => { props: mockContainerProps({ markers: [ { - latitude: dynamic("40.7128"), - longitude: dynamic("-74.0060") + latitude: dynamic.available("40.7128"), + longitude: dynamic.available("-74.0060") } as MarkersType ] }), @@ -45,9 +45,9 @@ describe("LocationResolverService - Reactivity Tests", () => { gateProvider.setProps( mockContainerProps({ markers: [ - { latitude: dynamic("40"), longitude: dynamic("-74") } as MarkersType, - { latitude: dynamic("41"), longitude: dynamic("-75") } as MarkersType, - { latitude: dynamic("42"), longitude: dynamic("-76") } as MarkersType + { latitude: dynamic.available("40"), longitude: dynamic.available("-74") } as MarkersType, + { latitude: dynamic.available("41"), longitude: dynamic.available("-75") } as MarkersType, + { latitude: dynamic.available("42"), longitude: dynamic.available("-76") } as MarkersType ] }) ); @@ -63,8 +63,8 @@ describe("LocationResolverService - Reactivity Tests", () => { props: mockContainerProps({ markers: [ { - latitude: dynamic("40"), - longitude: dynamic("-74") + latitude: dynamic.available("40"), + longitude: dynamic.available("-74") } as MarkersType ] }), @@ -94,8 +94,8 @@ describe("LocationResolverService - Reactivity Tests", () => { props: mockContainerProps({ markers: [ { - latitude: dynamic("40"), - longitude: dynamic("-74") + latitude: dynamic.available("40"), + longitude: dynamic.available("-74") } as MarkersType ] }), @@ -117,8 +117,8 @@ describe("LocationResolverService - Reactivity Tests", () => { gateProvider.setProps( mockContainerProps({ markers: [ - { latitude: dynamic("40"), longitude: dynamic("-74") } as MarkersType, - { latitude: dynamic("41"), longitude: dynamic("-75") } as MarkersType + { latitude: dynamic.available("40"), longitude: dynamic.available("-74") } as MarkersType, + { latitude: dynamic.available("41"), longitude: dynamic.available("-75") } as MarkersType ] }) ); @@ -136,9 +136,9 @@ describe("LocationResolverService - Reactivity Tests", () => { const markers = [ { - latitude: dynamic("40"), - longitude: dynamic("-74"), - title: dynamic("Test") + latitude: dynamic.available("40"), + longitude: dynamic.available("-74"), + title: dynamic.available("Test") } as MarkersType ]; @@ -175,8 +175,8 @@ describe("LocationResolverService - Reactivity Tests", () => { mockContainerProps({ markers: [ { - latitude: dynamic(`${40 + i}`), - longitude: dynamic("-74") + latitude: dynamic.available(`${40 + i}`), + longitude: dynamic.available("-74") } as MarkersType ] }) diff --git a/packages/pluggableWidgets/maps-web/src/model/services/__tests__/LocationResolver.unit.spec.ts b/packages/pluggableWidgets/maps-web/src/model/services/__tests__/LocationResolver.unit.spec.ts index 955e49d9a0..87a891cf1d 100644 --- a/packages/pluggableWidgets/maps-web/src/model/services/__tests__/LocationResolver.unit.spec.ts +++ b/packages/pluggableWidgets/maps-web/src/model/services/__tests__/LocationResolver.unit.spec.ts @@ -41,9 +41,9 @@ describe("LocationResolverService - Unit Tests", () => { props: mockContainerProps({ markers: [ { - latitude: dynamic("40.7128"), - longitude: dynamic("-74.0060"), - title: dynamic("NYC") + latitude: dynamic.available("40.7128"), + longitude: dynamic.available("-74.0060"), + title: dynamic.available("NYC") } as MarkersType ] }), @@ -77,8 +77,8 @@ describe("LocationResolverService - Unit Tests", () => { geodecodeApiKey: "test-api-key", markers: [ { - address: dynamic("New York, NY"), - title: dynamic("NYC") + address: dynamic.available("New York, NY"), + title: dynamic.available("NYC") } as MarkersType ] }), @@ -158,10 +158,10 @@ describe("LocationResolverService - Unit Tests", () => { createTestContainer({ props: mockContainerProps({ geodecodeApiKey: "static-key", - geodecodeApiKeyExp: dynamic("expression-key"), + geodecodeApiKeyExp: dynamic.available("expression-key"), markers: [ { - address: dynamic("New York, NY") + address: dynamic.available("New York, NY") } as MarkersType ] }), @@ -181,7 +181,7 @@ describe("LocationResolverService - Unit Tests", () => { props: mockContainerProps({ markers: [ { - address: dynamic("New York, NY") + address: dynamic.available("New York, NY") } as MarkersType ] }), @@ -206,8 +206,8 @@ describe("LocationResolverService - Unit Tests", () => { props: mockContainerProps({ markers: [ { - latitude: dynamic("40"), - longitude: dynamic("-74") + latitude: dynamic.available("40"), + longitude: dynamic.available("-74") } as MarkersType ] }), diff --git a/packages/pluggableWidgets/maps-web/src/utils/__tests__/data.spec.ts b/packages/pluggableWidgets/maps-web/src/utils/__tests__/data.spec.ts index 060c257156..f7cca7c2e5 100644 --- a/packages/pluggableWidgets/maps-web/src/utils/__tests__/data.spec.ts +++ b/packages/pluggableWidgets/maps-web/src/utils/__tests__/data.spec.ts @@ -8,12 +8,12 @@ describe("data.ts - Marker Conversion Functions", () => { it("should convert marker with all fields present", () => { const mockAction = jest.fn(); const marker: MarkersType = { - address: dynamic("123 Main St"), - latitude: dynamic("40.7128"), - longitude: dynamic("-74.0060"), - title: dynamic("New York"), + address: dynamic.available("123 Main St"), + latitude: dynamic.available("40.7128"), + longitude: dynamic.available("-74.0060"), + title: dynamic.available("New York"), onClick: { canExecute: true, isExecuting: false, execute: mockAction }, - customMarker: dynamic({ uri: "marker.png" } as any), + customMarker: dynamic.available({ uri: "marker.png" } as any), locationType: "latlng", markerStyle: "image" }; @@ -56,8 +56,8 @@ describe("data.ts - Marker Conversion Functions", () => { it("should parse numbers with comma as decimal separator", () => { const marker: MarkersType = { - latitude: dynamic("40,7128"), - longitude: dynamic("-74,0060"), + latitude: dynamic.available("40,7128"), + longitude: dynamic.available("-74,0060"), locationType: "latlng", markerStyle: "default" }; @@ -70,8 +70,8 @@ describe("data.ts - Marker Conversion Functions", () => { it("should parse numbers with period as decimal separator", () => { const marker: MarkersType = { - latitude: dynamic("40.7128"), - longitude: dynamic("-74.0060"), + latitude: dynamic.available("40.7128"), + longitude: dynamic.available("-74.0060"), locationType: "latlng", markerStyle: "default" }; @@ -84,9 +84,9 @@ describe("data.ts - Marker Conversion Functions", () => { it("should handle empty customMarker image", () => { const marker: MarkersType = { - latitude: dynamic("40"), - longitude: dynamic("-74"), - customMarker: dynamic(undefined as any), + latitude: dynamic.available("40"), + longitude: dynamic.available("-74"), + customMarker: dynamic.unavailable(), locationType: "latlng", markerStyle: "image" }; @@ -382,7 +382,7 @@ describe("data.ts - Marker Conversion Functions", () => { locationType: "latlng", latitude: listAttribute(() => "40" as any), longitude: listAttribute(() => "-74" as any), - customMarkerDynamic: dynamic({ uri: "custom-marker.png" } as any), + customMarkerDynamic: dynamic.available({ uri: "custom-marker.png" } as any), markerStyleDynamic: "image" }; diff --git a/packages/pluggableWidgets/pie-doughnut-chart-web/src/__tests__/PieChart.spec.tsx b/packages/pluggableWidgets/pie-doughnut-chart-web/src/__tests__/PieChart.spec.tsx index 19c17129d6..2d3f0a9f36 100644 --- a/packages/pluggableWidgets/pie-doughnut-chart-web/src/__tests__/PieChart.spec.tsx +++ b/packages/pluggableWidgets/pie-doughnut-chart-web/src/__tests__/PieChart.spec.tsx @@ -141,8 +141,8 @@ function setupBasicAttributes(): Partial { const seriesName = listExp(() => "name"); seriesName.get = jest .fn() - .mockReturnValueOnce(dynamic("first series")) - .mockReturnValueOnce(dynamic("second series")); + .mockReturnValueOnce(dynamic.available("first series")) + .mockReturnValueOnce(dynamic.available("second series")); const seriesDataSource = list(2); @@ -153,7 +153,10 @@ function setupBasicAttributes(): Partial { .mockReturnValueOnce(new EditableValueBuilder().withValue(new Big(2)).build()); const seriesColorAttribute = listExp(() => "name"); - seriesColorAttribute.get = jest.fn().mockReturnValueOnce(dynamic("red")).mockReturnValueOnce(dynamic("blue")); + seriesColorAttribute.get = jest + .fn() + .mockReturnValueOnce(dynamic.available("red")) + .mockReturnValueOnce(dynamic.available("blue")); const seriesSortAttribute = new ListAttributeValueBuilder().build(); seriesSortAttribute.get = jest diff --git a/packages/pluggableWidgets/popup-menu-web/package.json b/packages/pluggableWidgets/popup-menu-web/package.json index c6542b0368..6cc85b8a6a 100644 --- a/packages/pluggableWidgets/popup-menu-web/package.json +++ b/packages/pluggableWidgets/popup-menu-web/package.json @@ -54,6 +54,7 @@ "@mendix/run-e2e": "workspace:*", "@mendix/widget-plugin-hooks": "workspace:*", "@mendix/widget-plugin-platform": "workspace:*", + "@mendix/widget-plugin-test-utils": "workspace:*", "classnames": "^2.5.1" } } diff --git a/packages/pluggableWidgets/popup-menu-web/src/__tests__/Menu.spec.tsx b/packages/pluggableWidgets/popup-menu-web/src/__tests__/Menu.spec.tsx index 11dcf579e6..4f9fc315bf 100644 --- a/packages/pluggableWidgets/popup-menu-web/src/__tests__/Menu.spec.tsx +++ b/packages/pluggableWidgets/popup-menu-web/src/__tests__/Menu.spec.tsx @@ -1,10 +1,10 @@ import { fireEvent, render, RenderResult } from "@testing-library/react"; import { ValueStatus } from "mendix"; import { createElement } from "react"; +import { actionValue, dynamic } from "@mendix/widget-plugin-test-utils"; import { MenuWithContext as Menu } from "./MenuWithContext"; import { BasicItemsType, CustomItemsType } from "../../typings/PopupMenuProps"; import { MenuProps } from "../components/Menu"; -import { actionValue, dynamicValue } from "../utils/attrValue"; import "@testing-library/jest-dom"; @@ -20,7 +20,7 @@ describe("Menu", () => { const createPopupMenu = (props: MenuProps): RenderResult => render(); const basicItemProps: BasicItemsType = { itemType: "item", - caption: dynamicValue("Caption"), + caption: dynamic.available("Caption"), styleClass: "defaultStyle" }; const customItemProps: CustomItemsType = { content: createElement("div", null, null) }; @@ -38,7 +38,7 @@ describe("Menu", () => { position: "bottom", basicItems: [ basicItemProps, - { itemType: "divider", caption: dynamicValue("Caption"), styleClass: "defaultStyle" } + { itemType: "divider", caption: dynamic.available("Caption"), styleClass: "defaultStyle" } ], customItems: [customItemProps], onItemClick: jest.fn(), @@ -84,7 +84,7 @@ describe("Menu", () => { ...defaultProps, basicItems: [ basicItem, - { itemType: "divider", caption: dynamicValue("Caption"), styleClass: "defaultStyle" } + { itemType: "divider", caption: dynamic.available("Caption"), styleClass: "defaultStyle" } ] }); const { container } = popupMenu; diff --git a/packages/pluggableWidgets/popup-menu-web/src/__tests__/PopupMenu.spec.tsx b/packages/pluggableWidgets/popup-menu-web/src/__tests__/PopupMenu.spec.tsx index 62807d3b2c..1b3ef09c15 100644 --- a/packages/pluggableWidgets/popup-menu-web/src/__tests__/PopupMenu.spec.tsx +++ b/packages/pluggableWidgets/popup-menu-web/src/__tests__/PopupMenu.spec.tsx @@ -1,9 +1,9 @@ import { render, RenderResult } from "@testing-library/react"; import { ValueStatus } from "mendix"; import { createElement } from "react"; +import { dynamic } from "@mendix/widget-plugin-test-utils"; import { BasicItemsType, CustomItemsType, PopupMenuContainerProps } from "../../typings/PopupMenuProps"; import { PopupMenu } from "../components/PopupMenu"; -import { dynamicValue } from "../utils/attrValue"; import "@testing-library/jest-dom"; @@ -13,7 +13,7 @@ describe("Popup Menu", () => { const createPopupMenu = (props: PopupMenuContainerProps): RenderResult => render(); const basicItemProps: BasicItemsType = { itemType: "item", - caption: dynamicValue("Caption"), + caption: dynamic.available("Caption"), styleClass: "defaultStyle" }; const customItemProps: CustomItemsType = { content: createElement("div", null, null) }; @@ -31,7 +31,7 @@ describe("Popup Menu", () => { clickCloseOn: "onClickAnywhere", basicItems: [ basicItemProps, - { itemType: "divider", caption: dynamicValue("Caption"), styleClass: "defaultStyle" } + { itemType: "divider", caption: dynamic.available("Caption"), styleClass: "defaultStyle" } ], customItems: [customItemProps], clippingStrategy: "absolute" diff --git a/packages/pluggableWidgets/popup-menu-web/src/utils/attrValue.ts b/packages/pluggableWidgets/popup-menu-web/src/utils/attrValue.ts deleted file mode 100644 index 1e999017b1..0000000000 --- a/packages/pluggableWidgets/popup-menu-web/src/utils/attrValue.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ActionValue, DynamicValue, ValueStatus } from "mendix"; - -export function dynamicValue(value?: T, loading?: boolean): DynamicValue { - if (loading) { - return { status: ValueStatus.Loading, value }; - } - return value !== undefined - ? { status: ValueStatus.Available, value } - : { status: ValueStatus.Unavailable, value: undefined }; -} - -export function actionValue(canExecute = true, isExecuting = false): ActionValue { - return { canExecute, isExecuting, execute: jest.fn() }; -} diff --git a/packages/pluggableWidgets/signature-web/src/__tests__/Signature.spec.tsx b/packages/pluggableWidgets/signature-web/src/__tests__/Signature.spec.tsx index 3d653cb316..22154a4ca0 100644 --- a/packages/pluggableWidgets/signature-web/src/__tests__/Signature.spec.tsx +++ b/packages/pluggableWidgets/signature-web/src/__tests__/Signature.spec.tsx @@ -1,8 +1,8 @@ import "@testing-library/jest-dom"; import { render, screen } from "@testing-library/react"; import { ReactElement } from "react"; -import { SignatureContainerProps } from "../../typings/SignatureProps"; import { dynamic } from "@mendix/widget-plugin-test-utils"; +import { SignatureContainerProps } from "../../typings/SignatureProps"; import Signature from "../Signature"; const mockSignatureComponent = jest.fn((_props: unknown) =>
); @@ -38,7 +38,7 @@ describe("Signature", () => { gridCellHeight: 20, gridCellWidth: 20, gridBorderWidth: 1, - ariaRequired: dynamic(false) + ariaRequired: dynamic.available(false) }; beforeEach(() => { diff --git a/packages/pluggableWidgets/time-series-chart-web/src/__test__/TimeSeries.spec.tsx b/packages/pluggableWidgets/time-series-chart-web/src/__test__/TimeSeries.spec.tsx index c8db2d37f8..3be62ac2ae 100644 --- a/packages/pluggableWidgets/time-series-chart-web/src/__test__/TimeSeries.spec.tsx +++ b/packages/pluggableWidgets/time-series-chart-web/src/__test__/TimeSeries.spec.tsx @@ -68,7 +68,7 @@ describe("The TimeSeries widget", () => { }); it("sets the line color on the data series based on the lineColor value", () => { - renderTimeSeries([{ lineColor: dynamic("red") }, { lineColor: undefined }]); + renderTimeSeries([{ lineColor: dynamic.available("red") }, { lineColor: undefined }]); const mockCalls = (ChartWidget as jest.Mock).mock.calls; const lastCallProps = mockCalls[mockCalls.length - 1][0]; @@ -77,7 +77,7 @@ describe("The TimeSeries widget", () => { }); it("sets the marker color on the data series based on the markerColor value", () => { - renderTimeSeries([{ markerColor: undefined }, { markerColor: dynamic("blue") }]); + renderTimeSeries([{ markerColor: undefined }, { markerColor: dynamic.available("blue") }]); const mockCalls = (ChartWidget as jest.Mock).mock.calls; const lastCallProps = mockCalls[mockCalls.length - 1][0]; @@ -100,7 +100,7 @@ describe("The TimeSeries widget", () => { }); it("sets the area fill color on the data series based on fillColor", () => { - renderTimeSeries([{ fillColor: dynamic("red") }]); + renderTimeSeries([{ fillColor: dynamic.available("red") }]); const mockCalls = (ChartWidget as jest.Mock).mock.calls; const lastCallProps = mockCalls[mockCalls.length - 1][0]; diff --git a/packages/pluggableWidgets/tree-node-web/src/components/v2/hooks/__tests__/useIncrementalTreeData.spec.ts b/packages/pluggableWidgets/tree-node-web/src/components/v2/hooks/__tests__/useIncrementalTreeData.spec.ts index 3563443eb4..598aeff794 100644 --- a/packages/pluggableWidgets/tree-node-web/src/components/v2/hooks/__tests__/useIncrementalTreeData.spec.ts +++ b/packages/pluggableWidgets/tree-node-web/src/components/v2/hooks/__tests__/useIncrementalTreeData.spec.ts @@ -12,12 +12,10 @@ function makeConfig(overrides: Partial = {}): TreeConfigRef { return { headerType: "text", headerCaption: { - get: jest.fn((item: ObjectItem) => dynamic(String(item.id))) + get: jest.fn((item: ObjectItem) => dynamic.available(String(item.id))) } as any, headerContent: undefined, - parentAssociation: listReference(b => - b.withGet((_item: ObjectItem) => dynamic(undefined as unknown as ObjectItem)).build() - ), + parentAssociation: listReference(b => b.withGet((_item: ObjectItem) => dynamic.unavailable()).build()), startExpanded: false, ...overrides }; @@ -30,14 +28,14 @@ function makeConfigWithParentMap( return { headerType: "text", headerCaption: { - get: jest.fn((item: ObjectItem) => dynamic(String(item.id))) + get: jest.fn((item: ObjectItem) => dynamic.available(String(item.id))) } as any, headerContent: undefined, parentAssociation: listReference(b => b .withGet((item: ObjectItem) => { const parentId = parentMap[String(item.id)]; - return parentId ? dynamic(makeItem(parentId)) : dynamic(undefined as unknown as ObjectItem); + return parentId ? dynamic.available(makeItem(parentId)) : dynamic.unavailable(); }) .build() ), diff --git a/packages/shared/charts/src/hooks/__tests__/usePlotChartDataSeries.spec.ts b/packages/shared/charts/src/hooks/__tests__/usePlotChartDataSeries.spec.ts index 4ff58aa2bd..f91155ae7a 100644 --- a/packages/shared/charts/src/hooks/__tests__/usePlotChartDataSeries.spec.ts +++ b/packages/shared/charts/src/hooks/__tests__/usePlotChartDataSeries.spec.ts @@ -1,6 +1,6 @@ import { renderHook } from "@testing-library/react"; import { PlotDataSeries, SeriesMapper, usePlotChartDataSeries } from "../usePlotChartDataSeries"; -import { dynamicValue, EditableValueBuilder, list, ListAttributeValueBuilder } from "@mendix/widget-plugin-test-utils"; +import { dynamic, EditableValueBuilder, list, ListAttributeValueBuilder } from "@mendix/widget-plugin-test-utils"; import { ListActionValue, ListAttributeValue, ListExpressionValue, ListValue } from "mendix"; function axisAttr(): ListAttributeValue { @@ -33,7 +33,7 @@ function singleData(): PlotDataSeries { function multiData( params: { n: number; groups: string[]; ds?: ListValue } = { n: 12, groups: ["alpha", "beta"] } ): PlotDataSeries { - const x = { get: () => dynamicValue(Math.random().toString()) } as unknown as ListExpressionValue; + const x = { get: () => dynamic.available(Math.random().toString()) } as unknown as ListExpressionValue; return { dataSet: "dynamic", dynamicDataSource: params.ds ?? list(params.n), diff --git a/packages/shared/widget-plugin-component-kit/src/__tests__/__snapshots__/ThreeStateCheckBox.spec.tsx.snap b/packages/shared/widget-plugin-component-kit/src/__tests__/__snapshots__/ThreeStateCheckBox.spec.tsx.snap index 4f91b88801..dcce2c0892 100644 --- a/packages/shared/widget-plugin-component-kit/src/__tests__/__snapshots__/ThreeStateCheckBox.spec.tsx.snap +++ b/packages/shared/widget-plugin-component-kit/src/__tests__/__snapshots__/ThreeStateCheckBox.spec.tsx.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`ThreeStateCheckBox matches snapshot when value is 'all' 1`] = ` diff --git a/packages/shared/widget-plugin-test-utils/__tests__/filters/__snapshots__/builders.test.ts.snap b/packages/shared/widget-plugin-test-utils/__tests__/filters/__snapshots__/builders.test.ts.snap index b0b9762e95..4e2233e55d 100644 --- a/packages/shared/widget-plugin-test-utils/__tests__/filters/__snapshots__/builders.test.ts.snap +++ b/packages/shared/widget-plugin-test-utils/__tests__/filters/__snapshots__/builders.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`mendix/filters/builders mock nested condition case 1 1`] = ` { diff --git a/packages/shared/widget-plugin-test-utils/__tests__/primitives.test.ts b/packages/shared/widget-plugin-test-utils/__tests__/primitives.test.ts index 43699cb692..cc462dd71d 100644 --- a/packages/shared/widget-plugin-test-utils/__tests__/primitives.test.ts +++ b/packages/shared/widget-plugin-test-utils/__tests__/primitives.test.ts @@ -30,24 +30,37 @@ describe("attrId", () => { }); describe("dynamic", () => { - it("returns dynamic value mock", () => { - expect(dynamic("someString")).toMatchObject({ - status: Status.Available, - value: "someString" + describe("dynamic.available", () => { + it("returns dynamic value mock", () => { + expect(dynamic.available("someString")).toMatchObject({ + status: Status.Available, + value: "someString" + }); }); }); - it("returns 'unavailable' by default", () => { - expect(dynamic()).toMatchObject({ - status: Status.Unavailable, - value: undefined + describe("dynamic.loading", () => { + it("returns loading status with a value", () => { + expect(dynamic.loading("someString")).toMatchObject({ + status: Status.Loading, + value: "someString" + }); + }); + + it("returns loading status without a value", () => { + expect(dynamic.loading()).toMatchObject({ + status: Status.Loading, + value: undefined + }); }); }); - it("pass loading status", () => { - expect(dynamic("someString", true)).toMatchObject({ - status: Status.Loading, - value: "someString" + describe("dynamic.unavailable", () => { + it("returns unavailable status", () => { + expect(dynamic.unavailable()).toMatchObject({ + status: Status.Unavailable, + value: undefined + }); }); }); }); diff --git a/packages/shared/widget-plugin-test-utils/src/builders/ListReferenceSetValueBuilder.ts b/packages/shared/widget-plugin-test-utils/src/builders/ListReferenceSetValueBuilder.ts index 46d93a12ed..9dbbf4691f 100644 --- a/packages/shared/widget-plugin-test-utils/src/builders/ListReferenceSetValueBuilder.ts +++ b/packages/shared/widget-plugin-test-utils/src/builders/ListReferenceSetValueBuilder.ts @@ -11,7 +11,7 @@ export class ListReferenceSetValueBuilder { type: "ReferenceSet", id: `listRefSet_${nanoid()}` as any, filterable: true, - get: jest.fn((_: ObjectItem) => dynamic(objArray(1))) + get: jest.fn((_: ObjectItem) => dynamic.available(objArray(1))) }; } diff --git a/packages/shared/widget-plugin-test-utils/src/builders/ListReferenceValueBuilder.ts b/packages/shared/widget-plugin-test-utils/src/builders/ListReferenceValueBuilder.ts index 72d6feab19..b502d08dc2 100644 --- a/packages/shared/widget-plugin-test-utils/src/builders/ListReferenceValueBuilder.ts +++ b/packages/shared/widget-plugin-test-utils/src/builders/ListReferenceValueBuilder.ts @@ -10,7 +10,7 @@ export class ListReferenceValueBuilder { type: "Reference", id: `listRef_${nanoid()}` as any, filterable: true, - get: jest.fn((_: ObjectItem) => dynamic(obj())) + get: jest.fn((_: ObjectItem) => dynamic.available(obj())) }; } diff --git a/packages/shared/widget-plugin-test-utils/src/functions/listExpression.ts b/packages/shared/widget-plugin-test-utils/src/functions/listExpression.ts index f494ebd0ec..36bc0d8ccd 100644 --- a/packages/shared/widget-plugin-test-utils/src/functions/listExpression.ts +++ b/packages/shared/widget-plugin-test-utils/src/functions/listExpression.ts @@ -8,7 +8,7 @@ import { dynamic } from "../primitives/dynamic"; export function listExpression( get: (item: ObjectItem) => T ): ListExpressionValue { - return { get: (item: ObjectItem) => dynamic(get(item)) }; + return { get: (item: ObjectItem) => dynamic.available(get(item)) }; } /** @deprecated Renamed to `listExpression` */ diff --git a/packages/shared/widget-plugin-test-utils/src/primitives/dynamic.ts b/packages/shared/widget-plugin-test-utils/src/primitives/dynamic.ts index d808cf6297..eb1ad937c0 100644 --- a/packages/shared/widget-plugin-test-utils/src/primitives/dynamic.ts +++ b/packages/shared/widget-plugin-test-utils/src/primitives/dynamic.ts @@ -1,12 +1,20 @@ import { DynamicValue } from "mendix"; import { Status } from "../constants.js"; -export function dynamic(value?: T, loading = false): DynamicValue { - if (value === undefined) { - return { status: Status.Unavailable, value: undefined }; - } - return { status: loading ? Status.Loading : Status.Available, value }; +function available(value: T): DynamicValue { + return { status: Status.Available, value }; } -/** @deprecated renamed to {@link dynamic} */ -export const dynamicValue = dynamic; +function loading(value?: T): DynamicValue { + return { status: Status.Loading, value }; +} + +function unavailable(): DynamicValue { + return { status: Status.Unavailable, value: undefined }; +} + +export const dynamic = { + available, + loading, + unavailable +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f632d8ab75..607db78a3f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1988,6 +1988,9 @@ importers: '@mendix/widget-plugin-platform': specifier: workspace:* version: link:../../shared/widget-plugin-platform + '@mendix/widget-plugin-test-utils': + specifier: workspace:* + version: link:../../shared/widget-plugin-test-utils classnames: specifier: ^2.5.1 version: 2.5.1