Skip to content

Commit 57c0b38

Browse files
committed
feat: initial selection controls version
1 parent 3db100b commit 57c0b38

53 files changed

Lines changed: 2877 additions & 325 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require("@mendix/prettier-config-web-widgets");
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Changelog
2+
3+
All notable changes to this widget will be documented in this file.
4+
5+
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).
6+
7+
## [Unreleased]
8+
9+
## [1.0.0] - 2025-01-20
10+
11+
### Added
12+
13+
- Initial release of Selection Controls widget
14+
- Support for radio button lists (single selection)
15+
- Support for checkbox lists (multiple selection)
16+
- Context data source support (associations)
17+
- Database data source support
18+
- Static data source support
19+
- Custom content options
20+
- Accessibility features with ARIA labels
21+
- Keyboard navigation support
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Selection Controls
2+
3+
A widget for displaying radio button lists (single selection) and checkbox lists (multiple selection) based on different data sources.
4+
5+
## Features
6+
7+
- **Single Selection**: Radio button list for exclusive selection
8+
- **Multiple Selection**: Checkbox list for multiple selection
9+
- **Data Sources**: Support for context (association), database, and static data
10+
- **Custom Content**: Ability to add custom content for options
11+
- **Accessibility**: Full accessibility support with ARIA labels and keyboard navigation
12+
13+
## Configuration
14+
15+
The widget supports various data source types:
16+
17+
- **Context**: Use associations from your entity
18+
- **Database**: Query database for selectable objects
19+
- **Static**: Define static values directly in the widget
20+
21+
## Usage
22+
23+
1. Add the Selection Controls widget to your page
24+
2. Configure the data source (Context, Database, or Static)
25+
3. Set up caption and value attributes
26+
4. Configure selection method (single or multiple)
27+
5. Customize styling and accessibility options
28+
29+
For detailed configuration options, please refer to the widget properties in Studio Pro.
30+
31+
## Browser Support
32+
33+
- Modern browsers supporting ES6+
34+
- Internet Explorer 11+ (with polyfills)
35+
36+
## Development
37+
38+
This widget is built using:
39+
40+
- React 18+
41+
- TypeScript
42+
- Mendix Pluggable Widgets API
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { test, expect } from "@playwright/test";
2+
3+
test.afterEach("Cleanup session", async ({ page }) => {
4+
// 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.
5+
await page.evaluate(() => window.mx.session.logout());
6+
});
7+
8+
test.describe("selection-controls-web", () => {
9+
test.beforeEach(async ({ page }) => {
10+
await page.goto("/p/selectioncontrols");
11+
await page.waitForLoadState("networkidle");
12+
});
13+
14+
test.describe("data source types", () => {
15+
test("renders selection controls using association", async ({ page }) => {
16+
const selectionControls = page.locator(".mx-name-selectionControls1");
17+
await expect(selectionControls).toBeVisible({ timeout: 10000 });
18+
await expect(selectionControls).toHaveScreenshot(`selectionControlsAssociation.png`);
19+
});
20+
21+
test("renders selection controls using enum", async ({ page }) => {
22+
const selectionControls = page.locator(".mx-name-selectionControls2");
23+
await expect(selectionControls).toBeVisible({ timeout: 10000 });
24+
await expect(selectionControls).toHaveScreenshot(`selectionControlsEnum.png`);
25+
});
26+
27+
test("renders selection controls using boolean", async ({ page }) => {
28+
const selectionControls = page.locator(".mx-name-selectionControls3");
29+
await expect(selectionControls).toBeVisible({ timeout: 10000 });
30+
await expect(selectionControls).toHaveScreenshot(`selectionControlsBoolean.png`);
31+
});
32+
33+
test("renders selection controls using static values", async ({ page }) => {
34+
const selectionControls = page.locator(".mx-name-selectionControls4");
35+
await expect(selectionControls).toBeVisible({ timeout: 10000 });
36+
await expect(selectionControls).toHaveScreenshot(`selectionControlsStatic.png`);
37+
});
38+
39+
test("renders selection controls using database", async ({ page }) => {
40+
const selectionControls = page.locator(".mx-name-selectionControls5");
41+
await expect(selectionControls).toBeVisible({ timeout: 10000 });
42+
await expect(selectionControls).toHaveScreenshot(`selectionControlsDatabase.png`);
43+
});
44+
45+
test.describe("selection behavior", () => {
46+
test("handles radio button selection", async ({ page }) => {
47+
const selectionControls = page.locator(".mx-name-selectionControls1");
48+
await expect(selectionControls).toBeVisible({ timeout: 10000 });
49+
50+
const radioOption = selectionControls.locator('input[type="radio"]').first();
51+
await radioOption.click();
52+
await expect(radioOption).toBeChecked();
53+
});
54+
55+
test("handles checkbox selection", async ({ page }) => {
56+
const selectionControls = page.locator(".mx-name-selectionControls6"); // multi selection
57+
await expect(selectionControls).toBeVisible({ timeout: 10000 });
58+
59+
const checkboxOption = selectionControls.locator('input[type="checkbox"]').first();
60+
await checkboxOption.click();
61+
await expect(checkboxOption).toBeChecked();
62+
});
63+
});
64+
});
65+
});
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from "@mendix/eslint-config-web-widgets";
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
{
2+
"name": "@mendix/selection-controls-web",
3+
"widgetName": "SelectionControls",
4+
"version": "1.0.0",
5+
"description": "Configurable radio buttons and check box widget",
6+
"copyright": "© Mendix Technology BV 2025. All rights reserved.",
7+
"license": "Apache-2.0",
8+
"repository": {
9+
"type": "git",
10+
"url": "https://github.com/mendix/web-widgets.git"
11+
},
12+
"config": {
13+
"developmentPort": 3000,
14+
"mendixHost": "http://localhost:8080"
15+
},
16+
"mxpackage": {
17+
"name": "SelectionControls",
18+
"type": "widget",
19+
"mpkName": "com.mendix.widget.web.SelectionControls.mpk"
20+
},
21+
"packagePath": "com.mendix.widget.web",
22+
"marketplace": {
23+
"minimumMXVersion": "10.7.0",
24+
"appNumber": 219304,
25+
"appName": "Selection Controls",
26+
"reactReady": true
27+
},
28+
"testProject": {
29+
"githubUrl": "https://github.com/mendix/testProjects",
30+
"branchName": "selection-controls-web"
31+
},
32+
"scripts": {
33+
"prebuild": "rui-create-translation",
34+
"build": "pluggable-widgets-tools build:web",
35+
"create-gh-release": "rui-create-gh-release",
36+
"create-translation": "rui-create-translation",
37+
"dev": "pluggable-widgets-tools start:web",
38+
"e2e": "run-e2e ci",
39+
"e2edev": "run-e2e dev --with-preps",
40+
"format": "prettier --ignore-path ./node_modules/@mendix/prettier-config-web-widgets/global-prettierignore --write .",
41+
"lint": "eslint src/ package.json",
42+
"publish-marketplace": "rui-publish-marketplace",
43+
"release": "pluggable-widgets-tools release:web",
44+
"start": "pluggable-widgets-tools start:server",
45+
"test": "jest --projects jest.config.js",
46+
"update-changelog": "rui-update-changelog-widget",
47+
"verify": "rui-verify-package-format"
48+
},
49+
"dependencies": {
50+
"classnames": "^2.3.2"
51+
},
52+
"devDependencies": {
53+
"@mendix/automation-utils": "workspace:*",
54+
"@mendix/eslint-config-web-widgets": "workspace:*",
55+
"@mendix/pluggable-widgets-tools": "*",
56+
"@mendix/prettier-config-web-widgets": "workspace:*",
57+
"@mendix/run-e2e": "workspace:^*",
58+
"@mendix/widget-plugin-component-kit": "workspace:*",
59+
"@mendix/widget-plugin-grid": "workspace:*",
60+
"@mendix/widget-plugin-hooks": "workspace:*",
61+
"@mendix/widget-plugin-platform": "workspace:*",
62+
"@mendix/widget-plugin-test-utils": "workspace:*",
63+
"cross-env": "^7.0.3"
64+
}
65+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require("@mendix/run-e2e/playwright.config.cjs");
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
const { join } = require("path");
2+
const { cp, mkdir, rm } = require("shelljs");
3+
4+
const sourcePath = process.cwd();
5+
const outDir = join(sourcePath, "/dist/tmp/widgets/");
6+
7+
module.exports = args => {
8+
const result = args.configDefaultConfig;
9+
10+
const localesDir = join(outDir, "locales/");
11+
mkdir("-p", localesDir);
12+
13+
const translationFiles = join(sourcePath, "dist/locales/**/*");
14+
// copy everything under dist/locales to dist/tmp/widgets/locales for the widget mpk
15+
cp("-r", translationFiles, localesDir);
16+
// remove root level *.json locales files (duplicate with language specific files (e.g. en-US/*.json))
17+
rm("-f", join(outDir, "locales/*.json"), localesDir);
18+
19+
return result;
20+
};
Lines changed: 2 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)