Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require("@mendix/prettier-config-web-widgets");
15 changes: 15 additions & 0 deletions packages/pluggableWidgets/barcode-generator-web/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<!-- Please see [Barcode Generator](https://docs.mendix.com/appstore/widgets/barcode-generator) in the Mendix documentation for details. -->
Original file line number Diff line number Diff line change
@@ -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();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import config from "@mendix/eslint-config-web-widgets/widget-ts.mjs";

export default config;
61 changes: 61 additions & 0 deletions packages/pluggableWidgets/barcode-generator-web/package.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require("@mendix/run-e2e/playwright.config.cjs");
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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 (
<div className="barcode-generator-widget-preview">
<img src={doc} alt="" />
</div>
);
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -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<SVGSVGElement>(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 <span>No barcode value provided</span>;
}

return (
<div className="barcode-generator" tabIndex={tabIndex}>
{format === "QRCode" ? <QRCodeSVG value={value} size={size} /> : <svg ref={svgRef} />}
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8" ?>
<widget id="com.mendix.widget.web.barcodegenerator.BarcodeGenerator" pluginWidget="true" needsEntityContext="true" offlineCapable="true" xmlns="http://www.mendix.com/widget/1.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mendix.com/widget/1.0/ ../node_modules/mendix/custom_widget.xsd">
<name>Barcode Generator</name>
<description>Generate barcodes and QR codes from a string input</description>
<studioProCategory>Display</studioProCategory>
<studioCategory>Display</studioCategory>
<!-- <helpUrl>https://docs.mendix.com/appstore/widgets/barcode-generator</helpUrl> -->
<properties>
<propertyGroup caption="General">
<propertyGroup caption="Data source">
<property key="codeValue" type="attribute" required="true">
<caption>Dynamic value</caption>
<description>String to encode in the QR code</description>
<attributeTypes>
<attributeType name="String" />
</attributeTypes>
</property>
<property key="codeFormat" type="enumeration" required="true" defaultValue="CODE128">
<caption>Barcode Format</caption>
<description>Choose between QR or other barcode types</description>
<enumerationValues>
<enumerationValue key="CODE128">Barcode</enumerationValue>
<enumerationValue key="QRCode">QR Code</enumerationValue>
<enumerationValue key="Custom">Custom Format</enumerationValue>
</enumerationValues>
</property>
</propertyGroup>
<propertyGroup caption="Display">
<property key="displayValue" type="boolean" defaultValue="false">
<caption>Display value</caption>
<description>Display the value below the code</description>
</property>
<property key="codeWidth" type="integer" required="true" defaultValue="2">
<caption>Bar width</caption>
<description>Width of the barcode bars</description>
</property>
<property key="codeHeight" type="integer" required="true" defaultValue="200">
<caption>Code height</caption>
<description>In pixels</description>
</property>
<property key="qrSize" type="integer" required="true" defaultValue="128">
<caption>QR Size</caption>
<description>The size of the QR box</description>
</property>
<property key="codeMargin" type="integer" required="true" defaultValue="2">
<caption>Margin size</caption>
<description>In pixels</description>
</property>
</propertyGroup>
</propertyGroup>
<propertyGroup caption="Advanced">
<property key="customCodeFormat" type="enumeration" required="true" defaultValue="CODE128">
<caption>Barcode Format</caption>
<description>Choose between barcode types format</description>
<enumerationValues>
<enumerationValue key="CODE128">CODE128</enumerationValue>
<enumerationValue key="EAN13">EAN-13</enumerationValue>
<enumerationValue key="EAN8">EAN-8</enumerationValue>
<enumerationValue key="EAN5">EAN-5</enumerationValue>
<enumerationValue key="EAN2">EAN-2</enumerationValue>
<enumerationValue key="UPC">UPC</enumerationValue>
<enumerationValue key="CODE39">CODE39</enumerationValue>
<enumerationValue key="ITF14">ITF-14</enumerationValue>
<enumerationValue key="MSI">MSI</enumerationValue>
<enumerationValue key="pharmacode">Pharmacode</enumerationValue>
<enumerationValue key="codabar">Codabar</enumerationValue>
<enumerationValue key="CODE93">CODE93</enumerationValue>
</enumerationValues>
</property>
</propertyGroup>
</properties>
</widget>
Loading
Loading