Skip to content

Commit 7eba835

Browse files
committed
fix: update appNumber to -1 in marketplace configuration
1 parent f269137 commit 7eba835

File tree

8 files changed

+228
-488
lines changed

8 files changed

+228
-488
lines changed

packages/pluggableWidgets/qrcode-generator-web/package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@
1515
"type": "widget",
1616
"mpkName": "QRCodeGenerator.mpk"
1717
},
18-
"packagePath": "com.mendix.widget.custom",
18+
"packagePath": "com.mendix.widget.web",
1919
"marketplace": {
2020
"minimumMXVersion": "9.6.0",
21-
"appNumber": 0,
21+
"appNumber": -1,
2222
"appName": "QR Code Generator",
2323
"reactReady": true
2424
},
@@ -43,7 +43,8 @@
4343
"verify": "rui-verify-package-format"
4444
},
4545
"dependencies": {
46-
"classnames": "^2.5.1"
46+
"classnames": "^2.5.1",
47+
"qrcode.react": "^4.2.0"
4748
},
4849
"devDependencies": {
4950
"@mendix/automation-utils": "workspace:*",
Lines changed: 10 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,5 @@
11
import { QRCodeGeneratorPreviewProps } from "../typings/QRCodeGeneratorProps";
22

3-
export type Platform = "web" | "desktop";
4-
5-
export type Properties = PropertyGroup[];
6-
7-
type PropertyGroup = {
8-
caption: string;
9-
propertyGroups?: PropertyGroup[];
10-
properties?: Property[];
11-
};
12-
13-
type Property = {
14-
key: string;
15-
caption: string;
16-
description?: string;
17-
objectHeaders?: string[]; // used for customizing object grids
18-
objects?: ObjectProperties[];
19-
properties?: Properties[];
20-
};
21-
22-
type ObjectProperties = {
23-
properties: PropertyGroup[];
24-
captions?: string[]; // used for customizing object grids
25-
};
26-
273
export type Problem = {
284
property?: string; // key of the property, at which the problem exists
295
severity?: "error" | "warning" | "deprecation"; // default = "error"
@@ -33,108 +9,16 @@ export type Problem = {
339
studioUrl?: string; // studio-specific link
3410
};
3511

36-
type BaseProps = {
37-
type: "Image" | "Container" | "RowLayout" | "Text" | "DropZone" | "Selectable" | "Datasource";
38-
grow?: number; // optionally sets a growth factor if used in a layout (default = 1)
39-
};
40-
41-
type ImageProps = BaseProps & {
42-
type: "Image";
43-
document?: string; // svg image
44-
data?: string; // base64 image
45-
property?: object; // widget image property object from Values API
46-
width?: number; // sets a fixed maximum width
47-
height?: number; // sets a fixed maximum height
48-
};
49-
50-
type ContainerProps = BaseProps & {
51-
type: "Container" | "RowLayout";
52-
children: PreviewProps[]; // any other preview element
53-
borders?: boolean; // sets borders around the layout to visually group its children
54-
borderRadius?: number; // integer. Can be used to create rounded borders
55-
backgroundColor?: string; // HTML color, formatted #RRGGBB
56-
borderWidth?: number; // sets the border width
57-
padding?: number; // integer. adds padding around the container
58-
};
59-
60-
type RowLayoutProps = ContainerProps & {
61-
type: "RowLayout";
62-
columnSize?: "fixed" | "grow"; // default is fixed
63-
};
64-
65-
type TextProps = BaseProps & {
66-
type: "Text";
67-
content: string; // text that should be shown
68-
fontSize?: number; // sets the font size
69-
fontColor?: string; // HTML color, formatted #RRGGBB
70-
bold?: boolean;
71-
italic?: boolean;
72-
};
73-
74-
type DropZoneProps = BaseProps & {
75-
type: "DropZone";
76-
property: object; // widgets property object from Values API
77-
placeholder: string; // text to be shown inside the dropzone when empty
78-
showDataSourceHeader?: boolean; // true by default. Toggles whether to show a header containing information about the datasource
79-
};
80-
81-
type SelectableProps = BaseProps & {
82-
type: "Selectable";
83-
object: object; // object property instance from the Value API
84-
child: PreviewProps; // any type of preview property to visualize the object instance
85-
};
12+
export function check(_values: QRCodeGeneratorPreviewProps): Problem[] {
13+
const errors: Problem[] = [];
8614

87-
type DatasourceProps = BaseProps & {
88-
type: "Datasource";
89-
property: object | null; // datasource property object from Values API
90-
child?: PreviewProps; // any type of preview property component (optional)
91-
};
92-
93-
export type PreviewProps =
94-
| ImageProps
95-
| ContainerProps
96-
| RowLayoutProps
97-
| TextProps
98-
| DropZoneProps
99-
| SelectableProps
100-
| DatasourceProps;
101-
102-
export function getProperties(
103-
_values: QRCodeGeneratorPreviewProps,
104-
defaultProperties: Properties /* , target: Platform*/
105-
): Properties {
106-
// Do the values manipulation here to control the visibility of properties in Studio and Studio Pro conditionally.
107-
/* Example
108-
if (values.myProperty === "custom") {
109-
delete defaultProperties.properties.myOtherProperty;
15+
if (!_values.qrSize || _values.qrSize < 50) {
16+
errors.push({
17+
property: `qrSize`,
18+
severity: "error",
19+
message: `The value of 'QR Code Size' may not be smaller than 50px.`
20+
});
11021
}
111-
*/
112-
return defaultProperties;
113-
}
11422

115-
// export function check(_values: QRCodeGeneratorPreviewProps): Problem[] {
116-
// const errors: Problem[] = [];
117-
// // Add errors to the above array to throw errors in Studio and Studio Pro.
118-
// /* Example
119-
// if (values.myProperty !== "custom") {
120-
// errors.push({
121-
// property: `myProperty`,
122-
// message: `The value of 'myProperty' is different of 'custom'.`,
123-
// url: "https://github.com/myrepo/mywidget"
124-
// });
125-
// }
126-
// */
127-
// return errors;
128-
// }
129-
130-
// export function getPreview(values: QRCodeGeneratorPreviewProps, isDarkMode: boolean, version: number[]): PreviewProps {
131-
// // Customize your pluggable widget appearance for Studio Pro.
132-
// return {
133-
// type: "Container",
134-
// children: []
135-
// }
136-
// }
137-
138-
// export function getCustomCaption(values: QRCodeGeneratorPreviewProps, platform: Platform): string {
139-
// return "Qrcodegeneratorweb";
140-
// }
23+
return errors;
24+
}
Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,40 @@
1-
import { createElement, ReactElement } from "react";
2-
1+
import { createElement, ReactElement, useMemo } from "react";
32
import { QRCodeGeneratorContainerProps } from "../typings/QRCodeGeneratorProps";
4-
3+
import { QRCodeSVG } from "qrcode.react"; // Changed to named import QRCodeSVG
54
import "./ui/QRCodeGenerator.scss";
65

7-
export default function QRCodeGenerator(props: QRCodeGeneratorContainerProps): ReactElement {
8-
const value = props.valueAttribute?.value ?? "No value";
6+
export default function QRCodeGenerator({
7+
qrValue,
8+
qrSize,
9+
qrMargin,
10+
tabIndex
11+
}: QRCodeGeneratorContainerProps): ReactElement {
12+
// Handle Mendix data binding
13+
const value = qrValue?.status === "available" ? qrValue.value : "";
14+
const size = qrSize ?? 128;
15+
const margin = qrMargin ?? 2;
16+
17+
// Memoize QR code rendering
18+
const qrCode = useMemo(() => {
19+
if (!value) return null;
20+
return (
21+
<QRCodeSVG
22+
value={value}
23+
size={size}
24+
level="M" // Error correction level
25+
marginSize={margin ?? 2}
26+
aria-label={`QR code for: ${value}`}
27+
/>
28+
);
29+
}, [value, size, margin]);
930

1031
return (
11-
<div className={props.class} style={props.style} tabIndex={props.tabIndex}>
12-
<div className="qr-code-generator">
13-
<p>QR Code Generator</p>
14-
<p>Value: {value}</p>
15-
</div>
32+
<div className="qr-code-widget" tabIndex={tabIndex}>
33+
{qrCode ?? (
34+
<span className="text-muted">
35+
{qrValue?.status === "loading" ? "Loading..." : "No QR code to display"}
36+
</span>
37+
)}
1638
</div>
1739
);
1840
}

packages/pluggableWidgets/qrcode-generator-web/src/QRCodeGenerator.xml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,21 @@
88
<properties>
99
<propertyGroup caption="General">
1010
<propertyGroup caption="Data source">
11-
<property key="valueAttribute" type="attribute" required="true">
11+
<property key="qrValue" type="attribute" required="true">
1212
<caption>Dynamic value</caption>
13-
<description>The attribute that contains the qrcodegeneratorweb value</description>
13+
<description>String to encode in the QR code</description>
1414
<attributeTypes>
1515
<attributeType name="String" />
1616
</attributeTypes>
1717
</property>
18+
<property key="qrSize" type="integer" required="true" defaultValue="128">
19+
<caption>QR Code Size</caption>
20+
<description>Size of the QR code in pixels</description>
21+
</property>
22+
<property key="qrMargin" type="integer" required="true" defaultValue="2">
23+
<caption>QR Margin Size</caption>
24+
<description>Size of the QR margin in pixels</description>
25+
</property>
1826
</propertyGroup>
1927
</propertyGroup>
2028
</properties>

packages/pluggableWidgets/qrcode-generator-web/src/__tests__/QRCodeGenerator.spec.tsx

Lines changed: 74 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,51 +4,108 @@ import { createElement } from "react";
44

55
import QRCodeGenerator from "../QRCodeGenerator";
66

7+
// Mock the QRCodeSVG component
8+
jest.mock("qrcode.react", () => ({
9+
QRCodeSVG: ({ value, size, marginSize }: { value: string; size: number; marginSize: number }) => (
10+
<div data-testid="qr-code" data-value={value} data-size={size} data-margin={marginSize}>
11+
QR Code: {value}
12+
</div>
13+
)
14+
}));
15+
716
describe("QRCodeGenerator", () => {
8-
it("renders the basic structure", () => {
17+
it("renders QR code when value is available", () => {
18+
const props = {
19+
name: "qrCodeGenerator1",
20+
class: "mx-qrcode-generator",
21+
tabIndex: -1,
22+
qrValue: {
23+
value: "Hello World",
24+
status: "available"
25+
} as any,
26+
qrSize: 128,
27+
qrMargin: 8
28+
};
29+
30+
render(<QRCodeGenerator {...props} />);
31+
32+
expect(screen.getByTestId("qr-code")).toBeInTheDocument();
33+
expect(screen.getByTestId("qr-code")).toHaveAttribute("data-value", "Hello World");
34+
expect(screen.getByTestId("qr-code")).toHaveAttribute("data-size", "128");
35+
});
36+
37+
it("shows loading message when data is loading", () => {
38+
const props = {
39+
name: "qrCodeGenerator1",
40+
class: "mx-qrcode-generator",
41+
tabIndex: -1,
42+
qrValue: {
43+
value: "",
44+
status: "loading"
45+
} as any,
46+
qrSize: 128,
47+
qrMargin: 8
48+
};
49+
50+
render(<QRCodeGenerator {...props} />);
51+
52+
expect(screen.getByText("Loading...")).toBeInTheDocument();
53+
expect(screen.queryByTestId("qr-code")).not.toBeInTheDocument();
54+
});
55+
56+
it("shows no data message when value is empty", () => {
957
const props = {
1058
name: "qrCodeGenerator1",
1159
class: "mx-qrcode-generator",
1260
tabIndex: -1,
13-
valueAttribute: {
14-
value: "Hello World"
15-
} as any
61+
qrValue: {
62+
value: "",
63+
status: "available"
64+
} as any,
65+
qrSize: 128,
66+
qrMargin: 8
1667
};
1768

1869
render(<QRCodeGenerator {...props} />);
1970

20-
expect(screen.getByText("QR Code Generator")).toBeInTheDocument();
21-
expect(screen.getByText("Value: Hello World")).toBeInTheDocument();
71+
expect(screen.getByText("No QR code to display")).toBeInTheDocument();
72+
expect(screen.queryByTestId("qr-code")).not.toBeInTheDocument();
2273
});
2374

24-
it("handles empty value", () => {
75+
it("uses default size when qrSize is not provided", () => {
2576
const props = {
2677
name: "qrCodeGenerator1",
2778
class: "mx-qrcode-generator",
2879
tabIndex: -1,
29-
valueAttribute: {
30-
value: undefined
31-
} as any
80+
qrValue: {
81+
value: "test",
82+
status: "available"
83+
} as any,
84+
qrSize: 256, // Test with different size
85+
qrMargin: 8
3286
};
3387

3488
render(<QRCodeGenerator {...props} />);
3589

36-
expect(screen.getByText("Value: No value")).toBeInTheDocument();
90+
expect(screen.getByTestId("qr-code")).toHaveAttribute("data-size", "256");
3791
});
3892

39-
it("applies CSS classes correctly", () => {
93+
it("applies the correct CSS structure", () => {
4094
const props = {
4195
name: "qrCodeGenerator1",
4296
class: "mx-qrcode-generator",
4397
tabIndex: -1,
44-
valueAttribute: {
45-
value: "test"
46-
} as any
98+
qrValue: {
99+
value: "test",
100+
status: "available"
101+
} as any,
102+
qrSize: 128,
103+
qrMargin: 8
47104
};
48105

49106
const { container } = render(<QRCodeGenerator {...props} />);
50107

51-
expect(container.firstChild).toHaveClass("mx-qrcode-generator");
52-
expect(container.querySelector(".qr-code-generator")).toBeInTheDocument();
108+
expect(container.firstChild).toHaveClass("qr-code-widget");
109+
expect(container.firstChild).toHaveAttribute("tabindex", "-1");
53110
});
54111
});

0 commit comments

Comments
 (0)