diff --git a/packages/pluggableWidgets/barcode-generator-web/.prettierrc.js b/packages/pluggableWidgets/barcode-generator-web/.prettierrc.js new file mode 100644 index 0000000000..0892704ab0 --- /dev/null +++ b/packages/pluggableWidgets/barcode-generator-web/.prettierrc.js @@ -0,0 +1 @@ +module.exports = require("@mendix/prettier-config-web-widgets"); diff --git a/packages/pluggableWidgets/barcode-generator-web/CHANGELOG.md b/packages/pluggableWidgets/barcode-generator-web/CHANGELOG.md new file mode 100644 index 0000000000..861d88dfaf --- /dev/null +++ b/packages/pluggableWidgets/barcode-generator-web/CHANGELOG.md @@ -0,0 +1,15 @@ +# Changelog + +All notable changes to this widget will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [1.0.0] - 2025-10-09 + +### Added + +- Initial release of QR Code Generator widget +- Generate QR codes from string input +- Configurable QR code properties diff --git a/packages/pluggableWidgets/barcode-generator-web/README.md b/packages/pluggableWidgets/barcode-generator-web/README.md new file mode 100644 index 0000000000..da6c00ee01 --- /dev/null +++ b/packages/pluggableWidgets/barcode-generator-web/README.md @@ -0,0 +1 @@ + diff --git a/packages/pluggableWidgets/barcode-generator-web/e2e/BarcodeGenerator.spec.js b/packages/pluggableWidgets/barcode-generator-web/e2e/BarcodeGenerator.spec.js new file mode 100644 index 0000000000..573fa34389 --- /dev/null +++ b/packages/pluggableWidgets/barcode-generator-web/e2e/BarcodeGenerator.spec.js @@ -0,0 +1,24 @@ +import { test, expect } from "@playwright/test"; + +test.afterEach("Cleanup session", async ({ page }) => { + // Because the test isolation that will open a new session for every test executed, and that exceeds Mendix's license limit of 5 sessions, so we need to force logout after each test. + await page.evaluate(() => window.mx.session.logout()); +}); + +test.describe("BarcodeGenerator", () => { + test.beforeEach(async ({ page }) => { + await page.goto("/"); + await page.waitForLoadState("networkidle"); + }); + + test("renders barcode generator widget", async ({ page }) => { + // TODO: Replace with actual barcode generator test when implementation is complete + // Example test structure for barcode generator: + // await expect(page.locator(".mx-name-barcodeGenerator").first()).toBeVisible(); + // await page.locator(".mx-name-textInput").fill("Test QR Code"); + // await expect(page.locator(".mx-name-barcodeGenerator canvas")).toBeVisible(); + + // Placeholder test for now + await expect(page.locator("body")).toBeVisible(); + }); +}); diff --git a/packages/pluggableWidgets/barcode-generator-web/eslint.config.mjs b/packages/pluggableWidgets/barcode-generator-web/eslint.config.mjs new file mode 100644 index 0000000000..ed68ae9e78 --- /dev/null +++ b/packages/pluggableWidgets/barcode-generator-web/eslint.config.mjs @@ -0,0 +1,3 @@ +import config from "@mendix/eslint-config-web-widgets/widget-ts.mjs"; + +export default config; diff --git a/packages/pluggableWidgets/barcode-generator-web/package.json b/packages/pluggableWidgets/barcode-generator-web/package.json new file mode 100644 index 0000000000..f11aecb8cb --- /dev/null +++ b/packages/pluggableWidgets/barcode-generator-web/package.json @@ -0,0 +1,61 @@ +{ + "name": "@mendix/barcode-generator-web", + "widgetName": "BarcodeGenerator", + "version": "1.0.0", + "description": "Generate barcodes and QR codes from a string input", + "copyright": "© Mendix Technology BV 2025. All rights reserved.", + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "https://github.com/mendix/web-widgets.git" + }, + "config": {}, + "mxpackage": { + "name": "BarcodeGenerator", + "type": "widget", + "mpkName": "BarcodeGenerator.mpk" + }, + "packagePath": "com.mendix.widget.web", + "marketplace": { + "minimumMXVersion": "9.6.0", + "appNumber": -1, + "appName": "Barcode Generator", + "reactReady": true + }, + "testProject": { + "githubUrl": "https://github.com/mendix/testProjects", + "branchName": "barcode-generator-web" + }, + "scripts": { + "build": "pluggable-widgets-tools build:web", + "create-gh-release": "rui-create-gh-release", + "create-translation": "rui-create-translation", + "dev": "pluggable-widgets-tools start:web", + "e2e": "echo \"Skipping barcode-generator-web e2e tests\"", + "e2edev": "run-e2e dev --with-preps", + "format": "prettier --ignore-path ./node_modules/@mendix/prettier-config-web-widgets/global-prettierignore --write .", + "lint": "eslint src/ package.json", + "publish-marketplace": "rui-publish-marketplace", + "release": "pluggable-widgets-tools release:web", + "start": "pluggable-widgets-tools start:server", + "test": "pluggable-widgets-tools test:unit:web:enzyme-free", + "update-changelog": "rui-update-changelog-widget", + "verify": "rui-verify-package-format" + }, + "dependencies": { + "classnames": "^2.5.1", + "jsbarcode": "^3.12.1", + "qrcode.react": "^4.2.0" + }, + "devDependencies": { + "@mendix/automation-utils": "workspace:*", + "@mendix/eslint-config-web-widgets": "workspace:*", + "@mendix/pluggable-widgets-tools": "*", + "@mendix/prettier-config-web-widgets": "workspace:*", + "@mendix/run-e2e": "workspace:*", + "@mendix/widget-plugin-component-kit": "workspace:*", + "@mendix/widget-plugin-platform": "workspace:*", + "@mendix/widget-plugin-test-utils": "workspace:*", + "cross-env": "^7.0.3" + } +} diff --git a/packages/pluggableWidgets/barcode-generator-web/playwright.config.cjs b/packages/pluggableWidgets/barcode-generator-web/playwright.config.cjs new file mode 100644 index 0000000000..29045fc372 --- /dev/null +++ b/packages/pluggableWidgets/barcode-generator-web/playwright.config.cjs @@ -0,0 +1 @@ +module.exports = require("@mendix/run-e2e/playwright.config.cjs"); diff --git a/packages/pluggableWidgets/barcode-generator-web/src/BarcodeGenerator.editorConfig.ts b/packages/pluggableWidgets/barcode-generator-web/src/BarcodeGenerator.editorConfig.ts new file mode 100644 index 0000000000..077e6c5783 --- /dev/null +++ b/packages/pluggableWidgets/barcode-generator-web/src/BarcodeGenerator.editorConfig.ts @@ -0,0 +1,54 @@ +import { hidePropertiesIn, Properties } from "@mendix/pluggable-widgets-tools"; +import { BarcodeGeneratorPreviewProps } from "../typings/BarcodeGeneratorProps"; + +export type Problem = { + property?: string; // key of the property, at which the problem exists + severity?: "error" | "warning" | "deprecation"; // default = "error" + message: string; // description of the problem + studioMessage?: string; // studio-specific message, defaults to message + url?: string; // link with more information about the problem + studioUrl?: string; // studio-specific link +}; + +export function getProperties(values: BarcodeGeneratorPreviewProps, defaultProperties: Properties): Properties { + if (values.codeFormat === "QRCode") { + hidePropertiesIn(defaultProperties, values, ["codeWidth", "codeHeight", "displayValue"]); + } else { + hidePropertiesIn(defaultProperties, values, ["qrSize"]); + } + + if (values.codeFormat !== "Custom") { + hidePropertiesIn(defaultProperties, values, ["customCodeFormat"]); + } + return defaultProperties; +} + +export function check(_values: BarcodeGeneratorPreviewProps): Problem[] { + const errors: Problem[] = []; + + if (!_values.codeWidth || _values.codeWidth < 1) { + errors.push({ + property: `codeWidth`, + severity: "error", + message: `The value of 'Bar width' must be at least 1.` + }); + } + + if (!_values.codeHeight || _values.codeHeight < 20) { + errors.push({ + property: `codeHeight`, + severity: "error", + message: `The value of 'Code height' must be at least 20.` + }); + } + + if (!_values.qrSize || _values.qrSize < 50) { + errors.push({ + property: `codeHeight`, + severity: "error", + message: `The value of 'QR size' must be at least 50.` + }); + } + + return errors; +} diff --git a/packages/pluggableWidgets/barcode-generator-web/src/BarcodeGenerator.editorPreview.tsx b/packages/pluggableWidgets/barcode-generator-web/src/BarcodeGenerator.editorPreview.tsx new file mode 100644 index 0000000000..3d0cebc9ba --- /dev/null +++ b/packages/pluggableWidgets/barcode-generator-web/src/BarcodeGenerator.editorPreview.tsx @@ -0,0 +1,13 @@ +import { ReactElement } from "react"; +import { BarcodeGeneratorPreviewProps } from "../typings/BarcodeGeneratorProps"; +import BarcodePreviewSVG from "./assets/BarcodeGeneratorPreview.svg"; + +export function preview(_props: BarcodeGeneratorPreviewProps): ReactElement { + const doc = decodeURI(BarcodePreviewSVG); + + return ( +
+ +
+ ); +} diff --git a/packages/pluggableWidgets/barcode-generator-web/src/BarcodeGenerator.icon.dark.png b/packages/pluggableWidgets/barcode-generator-web/src/BarcodeGenerator.icon.dark.png new file mode 100644 index 0000000000..b64899dac6 Binary files /dev/null and b/packages/pluggableWidgets/barcode-generator-web/src/BarcodeGenerator.icon.dark.png differ diff --git a/packages/pluggableWidgets/barcode-generator-web/src/BarcodeGenerator.icon.png b/packages/pluggableWidgets/barcode-generator-web/src/BarcodeGenerator.icon.png new file mode 100644 index 0000000000..7f5366d1e9 Binary files /dev/null and b/packages/pluggableWidgets/barcode-generator-web/src/BarcodeGenerator.icon.png differ diff --git a/packages/pluggableWidgets/barcode-generator-web/src/BarcodeGenerator.tile.dark.png b/packages/pluggableWidgets/barcode-generator-web/src/BarcodeGenerator.tile.dark.png new file mode 100644 index 0000000000..863f1f715d Binary files /dev/null and b/packages/pluggableWidgets/barcode-generator-web/src/BarcodeGenerator.tile.dark.png differ diff --git a/packages/pluggableWidgets/barcode-generator-web/src/BarcodeGenerator.tile.png b/packages/pluggableWidgets/barcode-generator-web/src/BarcodeGenerator.tile.png new file mode 100644 index 0000000000..57de645b44 Binary files /dev/null and b/packages/pluggableWidgets/barcode-generator-web/src/BarcodeGenerator.tile.png differ diff --git a/packages/pluggableWidgets/barcode-generator-web/src/BarcodeGenerator.tsx b/packages/pluggableWidgets/barcode-generator-web/src/BarcodeGenerator.tsx new file mode 100644 index 0000000000..a6e3c41aae --- /dev/null +++ b/packages/pluggableWidgets/barcode-generator-web/src/BarcodeGenerator.tsx @@ -0,0 +1,53 @@ +import JsBarcode from "jsbarcode"; +import { QRCodeSVG } from "qrcode.react"; +import { ReactElement, useEffect, useRef } from "react"; +import { BarcodeGeneratorContainerProps } from "../typings/BarcodeGeneratorProps"; + +import "./ui/BarcodeGenerator.scss"; + +export default function BarcodeGenerator({ + codeValue, + codeWidth, + codeHeight, + codeFormat, + codeMargin, + displayValue, + qrSize, + tabIndex +}: BarcodeGeneratorContainerProps): ReactElement { + const svgRef = useRef(null); + + const value = codeValue?.status === "available" ? codeValue.value : ""; + const width = codeWidth ?? 128; + const height = codeHeight ?? 128; + const format = codeFormat ?? "CODE128"; + const margin = codeMargin ?? 2; + const showValue = displayValue ?? false; + const size = qrSize ?? 128; + + useEffect(() => { + if (format !== "QRCode" && svgRef.current && value) { + try { + JsBarcode(svgRef.current, value, { + format, + width, + height, + margin, + displayValue: showValue + }); + } catch (error) { + console.error("Error generating barcode:", error); + } + } + }, [value, width, height, format, margin, showValue]); + + if (!value) { + return No barcode value provided; + } + + return ( +
+ {format === "QRCode" ? : } +
+ ); +} diff --git a/packages/pluggableWidgets/barcode-generator-web/src/BarcodeGenerator.xml b/packages/pluggableWidgets/barcode-generator-web/src/BarcodeGenerator.xml new file mode 100644 index 0000000000..d4ea655914 --- /dev/null +++ b/packages/pluggableWidgets/barcode-generator-web/src/BarcodeGenerator.xml @@ -0,0 +1,72 @@ + + + Barcode Generator + Generate barcodes and QR codes from a string input + Display + Display + + + + + + Dynamic value + String to encode in the QR code + + + + + + Barcode Format + Choose between QR or other barcode types + + Barcode + QR Code + Custom Format + + + + + + Display value + Display the value below the code + + + Bar width + Width of the barcode bars + + + Code height + In pixels + + + QR Size + The size of the QR box + + + Margin size + In pixels + + + + + + Barcode Format + Choose between barcode types format + + CODE128 + EAN-13 + EAN-8 + EAN-5 + EAN-2 + UPC + CODE39 + ITF-14 + MSI + Pharmacode + Codabar + CODE93 + + + + + diff --git a/packages/pluggableWidgets/barcode-generator-web/src/__tests__/BarcodeGenerator.spec.tsx b/packages/pluggableWidgets/barcode-generator-web/src/__tests__/BarcodeGenerator.spec.tsx new file mode 100644 index 0000000000..ff4c4e1464 --- /dev/null +++ b/packages/pluggableWidgets/barcode-generator-web/src/__tests__/BarcodeGenerator.spec.tsx @@ -0,0 +1,213 @@ +import "@testing-library/jest-dom"; +import { render, screen } from "@testing-library/react"; +import { EditableValueBuilder } from "@mendix/widget-plugin-test-utils"; + +// Mock JsBarcode +const mockJsBarcode = jest.fn(); +const barcodeDefaultValue = `default barcode value`; +jest.mock("jsbarcode", () => mockJsBarcode); + +// Mock the QRCodeSVG component +jest.mock("qrcode.react", () => ({ + QRCodeSVG: ({ value, size }: { value: string; size: number }) => ( +
+ QR Code: {value} +
+ ) +})); + +import BarcodeGenerator from "../BarcodeGenerator"; +import { CodeFormatEnum, CustomCodeFormatEnum } from "typings/BarcodeGeneratorProps"; + +describe("BarcodeGenerator", () => { + const defaultProps = { + name: "barcodeGenerator1", + class: "mx-barcode-generator", + tabIndex: -1, + codeFormat: "QRCode" as CodeFormatEnum, + displayValue: false, + codeWidth: 2, + codeHeight: 200, + qrSize: 128, + codeMargin: 4, + customCodeFormat: "CODE128" as CustomCodeFormatEnum, + codeValue: new EditableValueBuilder().withValue(barcodeDefaultValue).build() + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it("renders QR code when value is available", () => { + const props = { + ...defaultProps, + codeValue: { + value: "Hello World", + status: "available" + } as any + }; + + render(); + + expect(screen.getByTestId("qr-code")).toBeInTheDocument(); + expect(screen.getByTestId("qr-code")).toHaveAttribute("data-value", "Hello World"); + expect(screen.getByTestId("qr-code")).toHaveAttribute("data-size", "128"); + }); + + it("shows no barcode message when data is loading", () => { + const props = { + ...defaultProps, + codeValue: { + value: "", + status: "loading" + } as any + }; + + render(); + + expect(screen.queryByTestId("qr-code")).not.toBeInTheDocument(); + expect(screen.getByText("No barcode value provided")).toBeInTheDocument(); + }); + + it("shows no barcode message when data is unavailable", () => { + const props = { + ...defaultProps, + codeValue: { + value: "", + status: "unavailable" + } as any + }; + + render(); + + expect(screen.queryByTestId("qr-code")).not.toBeInTheDocument(); + expect(screen.getByText("No barcode value provided")).toBeInTheDocument(); + }); + + it("renders CODE128 barcode when format is not QR", () => { + const props = { + ...defaultProps, + codeFormat: "CODE128" as const, + codeValue: { + value: "123456789", + status: "available" + } as any + }; + + render(); + + // Should not render QR code + expect(screen.queryByTestId("qr-code")).not.toBeInTheDocument(); + + // Should have called JsBarcode + expect(mockJsBarcode).toHaveBeenCalledWith( + expect.any(Object), // SVG element + "123456789", + { + format: "CODE128", + width: 2, + height: 200, + margin: 4, + displayValue: false + } + ); + }); + + it("renders QR code with custom size", () => { + const props = { + ...defaultProps, + qrSize: 256, + codeValue: { + value: "Custom Size QR", + status: "available" + } as any + }; + + render(); + + expect(screen.getByTestId("qr-code")).toHaveAttribute("data-size", "256"); + }); + + it("passes displayValue option to JSBarcode for non-QR codes", () => { + const props = { + ...defaultProps, + codeFormat: "CODE128" as const, + displayValue: true, + codeValue: { + value: "DISPLAY123", + status: "available" + } as any + }; + + render(); + + expect(mockJsBarcode).toHaveBeenCalledWith( + expect.any(Object), + "DISPLAY123", + expect.objectContaining({ + displayValue: true + }) + ); + }); + + it("handles JSBarcode errors gracefully", () => { + const consoleSpy = jest.spyOn(console, "error").mockImplementation(); + mockJsBarcode.mockImplementation(() => { + throw new Error("Invalid barcode format"); + }); + + const props = { + ...defaultProps, + codeFormat: "CODE128" as const, + codeValue: { + value: "INVALID", + status: "available" + } as any + }; + + render(); + + expect(consoleSpy).toHaveBeenCalledWith("Error generating barcode:", expect.any(Error)); + consoleSpy.mockRestore(); + }); + + it("applies correct CSS class and tabIndex", () => { + const props = { + ...defaultProps, + class: "mx-barcode-generator custom-class", + tabIndex: 5, + codeValue: { + value: "CSS Test", + status: "available" + } as any + }; + + const { container } = render(); + + const widget = container.firstChild as HTMLElement; + expect(widget).toHaveClass("barcode-generator"); + expect(widget).toHaveAttribute("tabIndex", "5"); + }); + + it("uses fallback values when props are missing", () => { + const props = { + ...defaultProps, + codeFormat: "CODE128" as const, + codeValue: { + value: "DEFAULT_TEST", + status: "available" + } as any + }; + + // Component uses nullish coalescing to provide defaults + render(); + + expect(mockJsBarcode).toHaveBeenCalledWith(expect.any(Object), "DEFAULT_TEST", { + format: "CODE128", + width: 2, // from defaultProps + height: 200, // from defaultProps + margin: 4, // from defaultProps + displayValue: false + }); + }); +}); diff --git a/packages/pluggableWidgets/barcode-generator-web/src/assets/BarcodeGeneratorPreview.svg b/packages/pluggableWidgets/barcode-generator-web/src/assets/BarcodeGeneratorPreview.svg new file mode 100644 index 0000000000..aa5601290d --- /dev/null +++ b/packages/pluggableWidgets/barcode-generator-web/src/assets/BarcodeGeneratorPreview.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/packages/pluggableWidgets/barcode-generator-web/src/package.xml b/packages/pluggableWidgets/barcode-generator-web/src/package.xml new file mode 100644 index 0000000000..e32d216db0 --- /dev/null +++ b/packages/pluggableWidgets/barcode-generator-web/src/package.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/packages/pluggableWidgets/barcode-generator-web/src/ui/BarcodeGenerator.scss b/packages/pluggableWidgets/barcode-generator-web/src/ui/BarcodeGenerator.scss new file mode 100644 index 0000000000..e219c30909 --- /dev/null +++ b/packages/pluggableWidgets/barcode-generator-web/src/ui/BarcodeGenerator.scss @@ -0,0 +1,6 @@ +// Barcode Generator Widget Styles +$widget-prefix: "barcode-generator"; + +.#{$widget-prefix} { + display: inline-block; +} diff --git a/packages/pluggableWidgets/barcode-generator-web/src/ui/BarcodeGeneratorPreview.scss b/packages/pluggableWidgets/barcode-generator-web/src/ui/BarcodeGeneratorPreview.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/pluggableWidgets/barcode-generator-web/tsconfig.json b/packages/pluggableWidgets/barcode-generator-web/tsconfig.json new file mode 100644 index 0000000000..7aa60df0c9 --- /dev/null +++ b/packages/pluggableWidgets/barcode-generator-web/tsconfig.json @@ -0,0 +1,30 @@ +{ + "include": ["./src", "./typings"], + "compilerOptions": { + "baseUrl": "./", + "noEmitOnError": true, + "sourceMap": true, + "module": "esnext", + "target": "es6", + "lib": ["esnext", "dom"], + "types": ["jest", "node"], + "moduleResolution": "node", + "declaration": false, + "noLib": false, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "strict": true, + "strictFunctionTypes": false, + "skipLibCheck": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "jsx": "react-jsx", + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "useUnknownInCatchVariables": false, + "exactOptionalPropertyTypes": false, + "paths": { + "react-hot-loader/root": ["./hot-typescript.ts"] + } + } +} diff --git a/packages/pluggableWidgets/barcode-generator-web/typings/BarcodeGeneratorProps.d.ts b/packages/pluggableWidgets/barcode-generator-web/typings/BarcodeGeneratorProps.d.ts new file mode 100644 index 0000000000..ff747ea4cf --- /dev/null +++ b/packages/pluggableWidgets/barcode-generator-web/typings/BarcodeGeneratorProps.d.ts @@ -0,0 +1,47 @@ +/** + * This file was generated from BarcodeGenerator.xml + * WARNING: All changes made to this file will be overwritten + * @author Mendix Widgets Framework Team + */ +import { CSSProperties } from "react"; +import { EditableValue } from "mendix"; + +export type CodeFormatEnum = "CODE128" | "QRCode" | "Custom"; + +export type CustomCodeFormatEnum = "CODE128" | "EAN13" | "EAN8" | "EAN5" | "EAN2" | "UPC" | "CODE39" | "ITF14" | "MSI" | "pharmacode" | "codabar" | "CODE93"; + +export interface BarcodeGeneratorContainerProps { + name: string; + class: string; + style?: CSSProperties; + tabIndex?: number; + codeValue: EditableValue; + codeFormat: CodeFormatEnum; + displayValue: boolean; + codeWidth: number; + codeHeight: number; + qrSize: number; + codeMargin: number; + customCodeFormat: CustomCodeFormatEnum; +} + +export interface BarcodeGeneratorPreviewProps { + /** + * @deprecated Deprecated since version 9.18.0. Please use class property instead. + */ + className: string; + class: string; + style: string; + styleObject?: CSSProperties; + readOnly: boolean; + renderMode: "design" | "xray" | "structure"; + translate: (text: string) => string; + codeValue: string; + codeFormat: CodeFormatEnum; + displayValue: boolean; + codeWidth: number | null; + codeHeight: number | null; + qrSize: number | null; + codeMargin: number | null; + customCodeFormat: CustomCodeFormatEnum; +} diff --git a/packages/pluggableWidgets/barcode-generator-web/typings/declare-svg.ts b/packages/pluggableWidgets/barcode-generator-web/typings/declare-svg.ts new file mode 100644 index 0000000000..e65f2cb5a6 --- /dev/null +++ b/packages/pluggableWidgets/barcode-generator-web/typings/declare-svg.ts @@ -0,0 +1,6 @@ +declare module "*.svg" { + const content: string; + export = content; +} + +declare module '*.css'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5be557a0be..4fb47d0630 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -596,6 +596,46 @@ importers: specifier: workspace:* version: link:../../shared/widget-plugin-test-utils + packages/pluggableWidgets/barcode-generator-web: + dependencies: + classnames: + specifier: ^2.5.1 + version: 2.5.1 + jsbarcode: + specifier: ^3.12.1 + version: 3.12.1 + qrcode.react: + specifier: ^4.2.0 + version: 4.2.0(react@18.3.1) + devDependencies: + '@mendix/automation-utils': + specifier: workspace:* + version: link:../../../automation/utils + '@mendix/eslint-config-web-widgets': + specifier: workspace:* + version: link:../../shared/eslint-config-web-widgets + '@mendix/pluggable-widgets-tools': + specifier: 10.21.2 + version: 10.21.2(@jest/transform@29.7.0)(@jest/types@30.2.0)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@22.14.1)(jest-util@30.2.0)(picomatch@4.0.3)(react-dom@18.3.1(react@18.3.1))(react-native@0.82.0(@babel/core@7.28.4)(@types/react@19.2.2)(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + '@mendix/prettier-config-web-widgets': + specifier: workspace:* + version: link:../../shared/prettier-config-web-widgets + '@mendix/run-e2e': + specifier: workspace:* + version: link:../../../automation/run-e2e + '@mendix/widget-plugin-component-kit': + specifier: workspace:* + version: link:../../shared/widget-plugin-component-kit + '@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 + cross-env: + specifier: ^7.0.3 + version: 7.0.3 + packages/pluggableWidgets/barcode-scanner-web: dependencies: '@zxing/library': @@ -7883,6 +7923,9 @@ packages: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true + jsbarcode@3.12.1: + resolution: {integrity: sha512-QZQSqIknC2Rr/YOUyOkCBqsoiBAOTYK+7yNN3JsqfoUtJtkazxNw1dmPpxuv7VVvqW13kA3/mKiLq+s/e3o9hQ==} + jsc-safe-url@0.2.4: resolution: {integrity: sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q==} @@ -9174,6 +9217,11 @@ packages: pure-rand@6.1.0: resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + qrcode.react@4.2.0: + resolution: {integrity: sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA==} + peerDependencies: + react: '>=18.0.0 <19.0.0' + querystringify@2.2.0: resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} @@ -16985,6 +17033,8 @@ snapshots: dependencies: argparse: 2.0.1 + jsbarcode@3.12.1: {} + jsc-safe-url@0.2.4: {} jsdom@20.0.3: @@ -18494,6 +18544,10 @@ snapshots: pure-rand@6.1.0: {} + qrcode.react@4.2.0(react@18.3.1): + dependencies: + react: 18.3.1 + querystringify@2.2.0: {} queue-microtask@1.2.3: {}