Skip to content

Project Requirement Documents #1755

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 14, 2025
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,5 @@ mendixProject
tests
.turbo
automation/run-e2e/ctrf/*.json
.cursor
.windsurf
50 changes: 50 additions & 0 deletions automation/utils/bin/rui-agent-rules.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/usr/bin/env ts-node-script

import { mkdir, readdir, lstat, symlink, unlink } from "fs/promises";
import path from "node:path";

async function ensureSymlink(targetRel: string, linkPath: string): Promise<void> {
try {
const stat = await lstat(linkPath).catch(() => null);
if (stat) {
await unlink(linkPath);
}
await symlink(targetRel, linkPath);
} catch (err) {
console.error(`Failed to create symlink for ${linkPath}:`, err);
}
}

async function main(): Promise<void> {
const repoRoot = path.resolve(process.cwd(), "../..");
const SRC_DIR = path.join(repoRoot, "docs", "requirements");
const CURSOR_DIR = path.join(repoRoot, ".cursor", "rules");
const WINDSURF_DIR = path.join(repoRoot, ".windsurf", "rules");

// Ensure target directories exist
await Promise.all([mkdir(CURSOR_DIR, { recursive: true }), mkdir(WINDSURF_DIR, { recursive: true })]);

const files = (await readdir(SRC_DIR)).filter(f => f.endsWith(".md"));

await Promise.all(
files.map(async file => {
const base = path.basename(file, ".md");
const srcAbsolute = path.join(SRC_DIR, file);

const cursorLink = path.join(CURSOR_DIR, `${base}.mdc`);
const windsurfLink = path.join(WINDSURF_DIR, `${base}.md`);

const relFromCursor = path.relative(path.dirname(cursorLink), srcAbsolute);
const relFromWindsurf = path.relative(path.dirname(windsurfLink), srcAbsolute);

await Promise.all([ensureSymlink(relFromCursor, cursorLink), ensureSymlink(relFromWindsurf, windsurfLink)]);
})
);

console.log("Agent rules links updated.");
}

main().catch(err => {
console.error(err);
process.exit(1);
});
2 changes: 2 additions & 0 deletions automation/utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"copyright": "© Mendix Technology BV 2025. All rights reserved.",
"private": true,
"bin": {
"rui-agent-rules": "bin/rui-agent-rules.ts",
"rui-create-gh-release": "bin/rui-create-gh-release.ts",
"rui-create-translation": "bin/rui-create-translation.ts",
"rui-publish-marketplace": "bin/rui-publish-marketplace.ts",
Expand All @@ -21,6 +22,7 @@
"tsconfig.json"
],
"scripts": {
"agent-rules": "ts-node bin/rui-agent-rules.ts",
"changelog": "ts-node bin/rui-changelog-helper.ts",
"compile:parser:module": "peggy -o ./src/changelog-parser/parser/widget/widget.js ./src/changelog-parser/parser/widget/widget.pegjs",
"compile:parser:widget": "peggy -o ./src/changelog-parser/parser/module/module.js ./src/changelog-parser/parser/module/module.pegjs",
Expand Down
67 changes: 67 additions & 0 deletions docs/requirements/app-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
description:
globs:
alwaysApply: true
---

# Pluggable Widget Lifecycle & Application Flow

Understanding how a pluggable widget moves from development to usage in Mendix Studio Pro is essential. This document breaks down the flow of creating, registering, configuring, building, and integrating a widget into Mendix Studio Pro.

## 1. Scaffold / Creation of a New Widget

Developers typically scaffold a new widget using the Mendix Pluggable Widget Generator. This generator creates:

- A package structure (e.g., `packages/pluggableWidgets/my-widget-web/`).
- Initial files: a TypeScript/TSX file for the React component, a corresponding XML configuration file, a README, and build configuration files.

Using the generator ensures correct conventions in folder layout, naming, and sample code.

## 2. Widget Registration via XML

Every widget has an XML file (e.g., `MyWidget.xml`) that:

- Defines a unique widget ID and name (following the namespace pattern).
- Specifies metadata for categorization in Studio Pro.
- Defines properties (inputs, outputs, events) including types and defaults.
- Includes system properties (e.g., Visibility, Tab index).

Studio Pro reads this XML to register the widget. Errors in the XML (such as duplicate IDs) prevent proper registration.

## 3. Development: Implementing and Configuring the Widget

After scaffolding and XML configuration:

- **Coding:** Implement the widget's functionality in a `.tsx` file using React. The component receives props corresponding to the XML-defined properties.
- **Styling:** Use appropriate CSS classes (preferably Atlas UI classes) rather than inline styles.
- **Configuration:** Test the widget by adding it to a page in Studio Pro and verifying that properties appear and function as defined.

Live reload is used during development to quickly reflect changes.

## 4. Build and Bundle

Using pnpm (or npm), developers:

- Run `pnpm install` to install dependencies.
- Set the `MX_PROJECT_PATH` to point to a Mendix test project.
- You can ask the name of the project, and then set `MX_PROJECT_PATH` dynamically, for example: `export MX_PROJECT_PATH="$HOME/Mendix/${PROJECT_NAME}"`. This way, whether you run `pnpm start` or `pnpm build`, the output is deployed to the correct Studio Pro project and we can immediately see the latest changes.
- Run `pnpm start` to build the widget using Rollup (compiling TypeScript and SCSS). The output is placed in the Mendix project's widget folder.

Rollup handles bundling into an optimized, single JavaScript file.

## 5. Integration into Mendix Studio Pro

Once built:

- **During Development:** The widget appears in Studio Pro's toolbox after synchronization. Developers can drag it onto pages and configure its properties.
- **Packaging:** For distribution, a production build generates an MPK (a ZIP package) that can be imported into any Mendix project.
- **At Runtime:** The Mendix Client instantiates the widget, passes the configured properties (as EditableValue, DynamicValue, ActionValue, etc.), and the widget renders its UI.

## 6. Summary of the Flow

1. **Design/Develop:** Write widget code and define properties in XML.
2. **Register:** XML registration makes the widget available in Studio Pro.
3. **Configure in App:** Drag the widget onto a page and set its properties.
4. **Build:** Compile using pnpm/Rollup.
5. **Run/Integrate:** Mendix client loads the widget with runtime data.
6. **Iteration:** Repeat as needed with live reload and synchronization.
39 changes: 39 additions & 0 deletions docs/requirements/backend-structure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
description:
globs:
alwaysApply: true
---

# Widget Data Flow & Backend Integration

Although pluggable widgets are primarily client-side components, they operate within the Mendix ecosystem. This document explains how data flows between a widget and the Mendix runtime, and how events are handled.

## Widget XML Configuration and Mendix Runtime

- **Property Types and Mendix Data:**
- **attribute:** Provided as an EditableValue object.
- **textTemplate:** Passed as a DynamicValue<string>.
- **objects/object:** Provided as a ListValue or ObjectValue.
- **Simple types:** Passed as plain JS values.
- **action:** Provided as an ActionValue with methods like `execute()`.
- **Data Context:** Widgets may require a context object if placed within a Data View.
- **Offline Capable:** XML can mark widgets as offline capable, meaning they are designed to work without a network connection.

## Data Flow: Reading and Updating Data

- **Initial Data Loading:** On page load, Mendix supplies data via props (e.g., EditableValue, DynamicValue). Widgets should render loading states if data is not yet available.
- **Two-Way Data Binding:** For interactive widgets, changes are pushed back using methods like `EditableValue.setValue()`.
- **Events and Actions:** User-triggered events call ActionValue's `execute()` method (after checking `canExecute`) to run Mendix microflows or nanoflows.

## Mendix Client and Widget Lifecycle

- **Instantiation:** Mendix creates the widget component on page load and passes the required props.
- **Re-rendering:** Changes in Mendix data trigger re-rendering via updated props.
- **Destruction:** Widgets unmount when removed; cleanup (e.g., event listeners) should occur during unmount.
- **Communication with the Server:** Widgets trigger server-side logic indirectly via microflows or nanoflows, using data source properties when needed.

## Example: DataGrid2 Interaction

- A DataGrid widget receives a ListValue for data.
- Sorting or row click events trigger ActionValues that may call microflows.
- Updates to the data source result in re-rendering of the widget.
43 changes: 43 additions & 0 deletions docs/requirements/frontend-guidelines.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
description:
globs:
alwaysApply: true
---

# Frontend Guidelines for Mendix Pluggable Widgets

This guide provides best practices for front-end development in Mendix pluggable widgets, focusing on styling, component structure, naming, and Atlas design system usage. The goal is to ensure consistent, maintainable widget development.

## CSS and SCSS Styling Guidelines

- **Use SCSS for Styles:** Write all widget styles in SCSS files to leverage variables, mixins, and integration with the overall theme.
- **No Inline Styles for Static Design:** Avoid using inline styles. Instead, assign CSS classes defined in SCSS or use Atlas classes.
- **Leverage Atlas UI Classes:** Utilize predefined Atlas classes (e.g., `btn`, `badge`) to ensure a consistent look.
- **Scoped vs Global Styles:** Prefix custom classes with the widget name (e.g., `.widget-combobox`) to avoid conflicts.
- **Responsive Design:** Use relative units and media queries to ensure the widget adapts gracefully to different screen sizes.
- **Avoid Deep Nesting:** Write clear, maintainable CSS without over-nesting or using `!important` unless absolutely necessary.
- **Design Properties and Conditional Classes:** Implement design properties via toggling classes based on user selections in Studio Pro.

## Naming Conventions (Files, Classes, and Components)

- **Component and File Names:** Use PascalCase for React component files (e.g., `ProgressBar.tsx`).
- **Widget Folder Naming:** Use lowercase names with hyphens ending with "web" (e.g., `badge-button-web`).
- **XML Keys and Attribute Names:** Use lowerCamelCase for XML property keys.
- **CSS Class Naming:** Follow a BEM-like convention for custom classes to maintain structure and clarity.

## Component Best Practices

- **Mendix Data API Usage:** Use Mendix API objects (EditableValue, ActionValue, etc.) correctly, checking conditions such as `canExecute` before calling actions.
- **State Management:** Use React state for UI-specific state and rely on Mendix props for persistent data. There are usages of Context and State management tools(mobx)
- **Performance Considerations:** Optimize renders, avoid heavy computations, and consider memoization.
- **Clean Up:** Ensure any event listeners or timers are cleaned up appropriately.
- **No Direct DOM Manipulation:** Use React's state and props to drive UI changes rather than direct DOM queries.
- **Accessibility:** Use semantic HTML, proper ARIA attributes, and ensure keyboard navigation is supported.

## Using Atlas UI and Theming

- **Consistent Look and Feel:** Ensure widgets integrate with Atlas UI by using default Atlas classes for common elements.
- **Custom Themes Support:** Build widgets so that they naturally adopt custom Atlas themes without hard-coded values.
- **Layout and Sizing:** Use flexible layouts (e.g., percentage widths, flexbox) that adapt to container sizes.
- **No Overriding Atlas Core Classes:** Do not override core Atlas classes; wrap widget elements if custom styling is needed.
- **Example – Consistent Button:** Use `<button class="btn btn-secondary" type="button">Click</button>` instead of custom-styled divs.
170 changes: 170 additions & 0 deletions docs/requirements/implementation-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
---
description:
globs:
alwaysApply: true
---

# Implementation Plan for a New Pluggable Widget

This document outlines a step-by-step plan to design, build, test, and integrate a new pluggable widget in Mendix.

## 1. Planning and Requirements

- **Define the Widget's Purpose:** Clearly state what the widget does, its name (e.g., "ProgressCircle"), its category, target data, and required properties (e.g., value, maximum, showLabel, onClick).
- **Check Existing Patterns:** Review similar widgets for best practices and to avoid duplicating functionality.

## 2. Environment Setup

- **Prerequisites:** Install Node.js (using the version specified in the repository), Mendix Studio Pro.
- **Folder Structure:** Create a new package under `packages/pluggableWidgets/` in the monorepo or use a standalone directory.

4. Define Widget Properties in XML
Edit the generated XML file (e.g., ProgressCircle.xml) to define properties and property groups.

Ensure property keys match the TypeScript props.

Do not change the widget's unique ID unless necessary.

5. Implement the React Component
Modify the generated .tsx file to match the XML-defined properties.

Use Mendix API types (e.g., EditableValue, DynamicValue, ActionValue) correctly.

Implement basic rendering and add error/loading states.

Import SCSS for styling and use Atlas UI classes.

6. Build and Run in Studio Pro
Set the MX_PROJECT_PATH environment variable to your test project's directory, you can ask what the project name running in Studio Pro is so that we can se the MX_PROJECT_PATH. The path is: /Users/rahman.unver/Mendix/ProjectName, this path is always same for the macOS version of Studio Pro. For Windows version, our path is /Volumes/[C] Windows11/Users/Rahman.Unver/Documents/ProjectName. example setting:
Windows:

- export MX_PROJECT_PATH=/Volumes/[C] Windows11/Users/Rahman.Unver/Documents/DocumentViewerWidget
- export MX_PROJECT_PATH=/Users/rahman.unver/Mendix/RichTextTest

Run pnpm start (or npm start) to build and watch for changes.

In Studio Pro, synchronize the app directory to load the widget.

Place the widget on a test page and configure properties.

7. Iterative Development
Develop and test iteratively.

Use browser developer tools for debugging.

Adjust XML and TS code as needed when adding new properties or handling edge cases.

8. Testing and Validation
Write unit tests for critical logic using Jest, React Testing Library mainly.

Perform UI/functional testing within a Mendix project.

Test responsiveness, performance, and edge cases.

9. Documentation and Metadata
Update the widget's README with usage instructions and limitations.

Ensure XML descriptions and property captions are clear.

Optionally, add an icon for the widget.

10. Packaging and Sharing
Run npm run build to produce an MPK file.

Test the MPK in a fresh Mendix project.

Update version numbers and changelogs before distribution.

Common Gotchas:

Ensure XML and TypeScript props match exactly.

Install all third-party dependencies.

Maintain case sensitivity in IDs and keys.

Clean up event listeners to avoid memory leaks.

Ensure compatibility with the target Mendix version.

### Github PR Template

<!------STARTS HERE----->
<!--
IMPORTANT: Please read and follow instructions below on how to
open and submit your pull request.

REQUIRED STEPS:
1. Specify correct pull request type.
2. Write meaningful description.
3. Run `pnpm lint` and `pnpm test` in packages you changed and make sure they have no errors.
4. Add new tests (if needed) to cover new functionality.
5. Read checklist below.

CHECKLIST:
- Do you have a JIRA story for your pull request?
- If yes, please format the PR title to match the `[XX-000]: description` pattern.
- If no, please write your PR title using conventional commit rules.
- Does your change require a new version of the widget/module?
- If yes, run `pnpm -w changelog` or update the `CHANGELOG.md` and bump the version manually.
- If no, ignore.
- Do you have related PRs in other Mendix repositories?
- If yes, please link all related pull requests in the description.
- If no, ignore.
- Does your change touch XML, or is it a new feature or behavior?
- If yes, if necessary, please create a PR with updates in the documentation (https://github.com/mendix/docs).
- If no, ignore.
- Is your change a bug fix or a new feature?
- If yes, please add a description (last section in the template) of what should be tested and what steps are needed to test it.
- If no, ignore.
-->

<!--
What type of changes does your PR introduce?
Uncomment relevant sections below by removing `<!--` at the beginning of the line.
-->

### Pull request type

<!-- No code changes (changes to documentation, CI, metadata, etc.)
<!---->

<!-- Dependency changes (any modification to dependencies in `package.json`)
<!---->

<!-- Refactoring (e.g. file rename, variable rename, etc.)
<!---->

<!-- Bug fix (non-breaking change which fixes an issue)
<!---->

<!-- New feature (non-breaking change which adds functionality)
<!---->

<!-- Breaking change (fix or feature that would cause existing functionality to change)
<!---->

<!-- Test related change (New E2E test, test automation, etc.)
<!---->

---

<!---
Describe your changes in detail.
Try to explain WHAT and WHY you change, fix or refactor.
-->

### Description

<!--
Please uncomment and fill in the following section
to describe which part of the package needs to be tested
and how it can be tested.
-->
<!--
### What should be covered while testing?
-->

<!------ENDS HERE----->

Keep this template in mind if I ever ask you to open a PR through command console, Github CLI or similar.
Loading
Loading