xmlui 0.10.25 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/lib/{index-CCEPGw_x.mjs → index-CEq6OdjV.js} +6186 -6523
  2. package/dist/lib/index.css +1 -1
  3. package/dist/lib/{initMock-DFcCR7ey.mjs → initMock-DhUnLKrR.js} +1 -1
  4. package/dist/lib/{language-server-web-worker.mjs → language-server-web-worker.js} +1 -1
  5. package/dist/lib/{language-server.mjs → language-server.js} +1 -1
  6. package/dist/lib/{metadata-utils-Dx-2qZBh.mjs → metadata-utils-D27cn-XB.js} +7 -7
  7. package/dist/lib/{server-common--BHVvP1o.mjs → server-common-2DaoOOL5.js} +625 -616
  8. package/dist/lib/testing.d.ts +2011 -0
  9. package/dist/lib/testing.js +2386 -0
  10. package/dist/lib/vite-xmlui-plugin/index.js +13968 -0
  11. package/dist/lib/vite-xmlui-plugin/package.json +3 -0
  12. package/dist/lib/xmlui-parser-BZZ430Wm.js +523 -0
  13. package/dist/lib/xmlui-parser.d.ts +2 -1
  14. package/dist/lib/{xmlui-parser.mjs → xmlui-parser.js} +2 -2
  15. package/dist/lib/{xmlui-serializer-uCYa8_tZ.mjs → xmlui-serializer-D9D2mQ8m.js} +1 -1
  16. package/dist/lib/xmlui.d.ts +1 -0
  17. package/dist/lib/{xmlui.mjs → xmlui.js} +24 -23
  18. package/dist/metadata/{collectedComponentMetadata-mwkNkxN_.mjs → collectedComponentMetadata-BAI5eK2v.js} +13057 -12883
  19. package/dist/metadata/{initMock-BVxHA6wu.mjs → initMock-CekNG5Ax.js} +1 -1
  20. package/dist/metadata/style.css +1 -1
  21. package/dist/metadata/{xmlui-metadata.mjs → xmlui-metadata.js} +1 -1
  22. package/dist/metadata/xmlui-metadata.umd.cjs +207 -0
  23. package/dist/scripts/bin/bootstrap.cjs +4 -0
  24. package/dist/scripts/bin/index.js +85 -13
  25. package/dist/scripts/package.json +30 -22
  26. package/dist/scripts/src/components/App/App.spec.js +127 -15
  27. package/dist/scripts/src/components/App/AppNative.js +13 -2
  28. package/dist/scripts/src/components/AppHeader/AppHeader.js +1 -6
  29. package/dist/scripts/src/components/AppHeader/AppHeaderNative.js +6 -7
  30. package/dist/scripts/src/components/Avatar/Avatar.spec.js +0 -29
  31. package/dist/scripts/src/components/Button/Button.spec.js +0 -29
  32. package/dist/scripts/src/components/Charts/BarChart/BarChartNative.js +2 -0
  33. package/dist/scripts/src/components/Charts/LineChart/LineChartNative.js +2 -2
  34. package/dist/scripts/src/components/Charts/Tooltip/TooltipContent.spec.js +8 -6
  35. package/dist/scripts/src/components/Form/Form.js +19 -0
  36. package/dist/scripts/src/components/Form/Form.spec.js +444 -0
  37. package/dist/scripts/src/components/Form/FormNative.js +46 -15
  38. package/dist/scripts/src/components/Form/formActions.js +3 -2
  39. package/dist/scripts/src/components/FormItem/FormItem.js +10 -2
  40. package/dist/scripts/src/components/FormItem/FormItem.spec.js +159 -0
  41. package/dist/scripts/src/components/FormItem/FormItemNative.js +6 -5
  42. package/dist/scripts/src/components/Heading/Heading.js +45 -5
  43. package/dist/scripts/src/components/Heading/Heading.spec.js +116 -47
  44. package/dist/scripts/src/components/Queue/Queue.js +1 -16
  45. package/dist/scripts/src/components/Queue/QueueNative.js +60 -2
  46. package/dist/scripts/src/components/TableOfContents/TableOfContents.js +7 -5
  47. package/dist/scripts/src/components-core/appContext/misc-utils.js +2 -1
  48. package/dist/scripts/src/components-core/devtools/InspectorDialog.js +2 -2
  49. package/dist/scripts/src/components-core/rendering/valueExtractor.js +9 -1
  50. package/dist/scripts/src/components-core/script-runner/eval-tree-async.js +2 -0
  51. package/dist/scripts/src/components-core/utils/base64-utils.js +2 -0
  52. package/dist/scripts/src/components-core/utils/extractParam.js +2 -1
  53. package/dist/scripts/src/components-core/utils/misc.js +44 -0
  54. package/dist/scripts/src/language-server/server-common.js +2 -2
  55. package/dist/scripts/src/language-server/{xmlui-metadata-generated.mjs → xmlui-metadata-generated.js} +625 -615
  56. package/dist/scripts/src/testing/drivers/index.js +9 -0
  57. package/dist/scripts/src/testing/index.js +69 -0
  58. package/dist/standalone/xmlui-standalone.es.d.ts +32 -16
  59. package/dist/standalone/xmlui-standalone.umd.js +36 -36
  60. package/package.json +45 -37
  61. package/dist/metadata/xmlui-metadata.umd.js +0 -207
  62. package/dist/scripts/bin/bootstrap.js +0 -11
  63. /package/dist/lib/{apiInterceptorWorker-QiltRtq1.mjs → apiInterceptorWorker-QiltRtq1.js} +0 -0
  64. /package/dist/lib/{syntax-monaco.mjs → syntax-monaco.js} +0 -0
  65. /package/dist/lib/{syntax-textmate.mjs → syntax-textmate.js} +0 -0
  66. /package/dist/lib/{transform-Tooy42EB.mjs → transform-Tooy42EB.js} +0 -0
  67. /package/dist/metadata/{apiInterceptorWorker-Dql7QGw2.mjs → apiInterceptorWorker-Dql7QGw2.js} +0 -0
@@ -260,6 +260,7 @@ function BarChart({ data = [], layout = exports.defaultProps.layout, nameKey, st
260
260
  height: miniMode || hideX ? 0 : xAxisHeight,
261
261
  tick: miniMode || hideTickX ? false : { fill: "currentColor", fontSize },
262
262
  tickFormatter: miniMode || hideTickX ? undefined : tickFormatterX,
263
+ domain: [0, (dataMax) => dataMax * 1.05],
263
264
  }
264
265
  : {
265
266
  type: "category",
@@ -292,6 +293,7 @@ function BarChart({ data = [], layout = exports.defaultProps.layout, nameKey, st
292
293
  tickCount: yTickCount,
293
294
  tickFormatter: miniMode || hideTickY ? undefined : tickFormatterY,
294
295
  width: miniMode || hideY ? 0 : yAxisWidth,
296
+ domain: [0, (dataMax) => dataMax * 1.1],
295
297
  };
296
298
  return ((0, jsx_runtime_1.jsxs)(ChartProvider_1.default, { value: chartContextValue, children: [children, (0, jsx_runtime_1.jsx)("div", { ref: labelsRef, style: { position: "absolute", visibility: "hidden", height: 0, overflow: "hidden" }, children: validData
297
299
  .map((d) => d[nameKey])
@@ -123,7 +123,7 @@ exports.LineChart = (0, react_1.forwardRef)(function LineChart({ data, dataKeys
123
123
  payloadArray.push({
124
124
  label: dataKey,
125
125
  value: originalPayload[dataKey],
126
- color: colorValues[index] || colorValues[0]
126
+ color: colorValues[index] || colorValues[0],
127
127
  });
128
128
  }
129
129
  });
@@ -181,7 +181,7 @@ exports.LineChart = (0, react_1.forwardRef)(function LineChart({ data, dataKeys
181
181
  ? safeData
182
182
  .map((d) => d === null || d === void 0 ? void 0 : d[nameKey])
183
183
  .map((label, idx) => ((0, jsx_runtime_1.jsx)("span", { style: { fontSize: 12, whiteSpace: "nowrap" }, children: label }, idx)))
184
- : null }), (0, jsx_runtime_1.jsx)("div", { ref: forwardedRef, className: (0, classnames_1.default)(className, LineChart_module_scss_1.default.wrapper), style: Object.assign({ flexGrow: 1 }, style), children: (0, jsx_runtime_1.jsx)(recharts_1.ResponsiveContainer, { ref: containerRef, width: "100%", height: "100%", minWidth: 60, minHeight: 30, debounce: 100, children: (0, jsx_runtime_1.jsxs)(recharts_1.LineChart, { accessibilityLayer: true, data: data, margin: miniMode ? { left: 0, right: 0, top: 0, bottom: 0 } : chartMargin, children: [(0, jsx_runtime_1.jsx)(recharts_1.XAxis, { interval: interval, tickLine: false, dataKey: nameKey, angle: tickAngle, textAnchor: tickAnchor, tick: miniMode ? false : !hideTickX && { fill: "currentColor", fontSize }, tickFormatter: miniMode ? undefined : tickFormatterX, height: miniMode || hideX ? 0 : xAxisHeight, hide: miniMode || hideX }), (0, jsx_runtime_1.jsx)(recharts_1.YAxis, { hide: miniMode || hideY, tickLine: false, tickFormatter: miniMode ? undefined : tickFormatterY, tick: miniMode ? false : !hideTickY && { fill: "currentColor", fontSize } }), !miniMode && !hideTooltip && (0, jsx_runtime_1.jsx)(recharts_1.Tooltip, { content: safeTooltipRenderer }), dataKeys.map((dataKey, i) => ((0, jsx_runtime_1.jsx)(recharts_1.Line, { type: "monotone", dataKey: dataKey, name: dataKey, stroke: colorValues[i], strokeWidth: strokeWidth, dot: false }, dataKey))), showLegend && ((0, jsx_runtime_1.jsx)(recharts_1.Legend, { wrapperStyle: {
184
+ : null }), (0, jsx_runtime_1.jsx)("div", { ref: forwardedRef, className: (0, classnames_1.default)(className, LineChart_module_scss_1.default.wrapper), style: Object.assign({ flexGrow: 1 }, style), children: (0, jsx_runtime_1.jsx)(recharts_1.ResponsiveContainer, { ref: containerRef, width: "100%", height: "100%", minWidth: 60, minHeight: 30, debounce: 100, children: (0, jsx_runtime_1.jsxs)(recharts_1.LineChart, { accessibilityLayer: true, data: data, margin: miniMode ? { left: 0, right: 0, top: 0, bottom: 0 } : chartMargin, children: [(0, jsx_runtime_1.jsx)(recharts_1.XAxis, { interval: interval, tickLine: false, dataKey: nameKey, angle: tickAngle, textAnchor: tickAnchor, tick: miniMode ? false : !hideTickX && { fill: "currentColor", fontSize }, tickFormatter: miniMode ? undefined : tickFormatterX, height: miniMode || hideX ? 0 : xAxisHeight, hide: miniMode || hideX }), (0, jsx_runtime_1.jsx)(recharts_1.YAxis, { hide: miniMode || hideY, tickLine: false, tickFormatter: miniMode ? undefined : tickFormatterY, tick: miniMode ? false : !hideTickY && { fill: "currentColor", fontSize }, domain: [(dataMin) => dataMin * 0.95, (dataMax) => dataMax * 1.05] }), !miniMode && !hideTooltip && (0, jsx_runtime_1.jsx)(recharts_1.Tooltip, { content: safeTooltipRenderer }), dataKeys.map((dataKey, i) => ((0, jsx_runtime_1.jsx)(recharts_1.Line, { type: "monotone", dataKey: dataKey, name: dataKey, stroke: colorValues[i], strokeWidth: strokeWidth, dot: false }, dataKey))), showLegend && ((0, jsx_runtime_1.jsx)(recharts_1.Legend, { wrapperStyle: {
185
185
  bottom: 0,
186
186
  left: 0,
187
187
  right: 0,
@@ -266,10 +266,10 @@ fixtures_1.test.describe("Basic Functionality", () => {
266
266
  const indicator = page.locator(tooltipIndicatorSelector);
267
267
  yield (0, fixtures_1.expect)(indicator).toBeVisible();
268
268
  // Indicator should have background color
269
- const backgroundColor = yield indicator.evaluate(el => window.getComputedStyle(el).backgroundColor);
269
+ const backgroundColor = yield indicator.evaluate((el) => window.getComputedStyle(el).backgroundColor);
270
270
  (0, fixtures_1.expect)(backgroundColor).not.toBe("rgba(0, 0, 0, 0)"); // Not transparent
271
271
  }));
272
- (0, fixtures_1.test)("applies consistent styling across different chart types", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
272
+ (0, fixtures_1.test)("applies consistent styling across different chart types", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page, }) {
273
273
  // Test in LineChart context
274
274
  yield initTestBed(`
275
275
  <LineChart
@@ -281,9 +281,11 @@ fixtures_1.test.describe("Basic Functionality", () => {
281
281
  />
282
282
  `);
283
283
  yield page.waitForSelector(chartRoot, { timeout: 10000 });
284
- // Hover over line to trigger tooltip
285
- const line = page.locator(".recharts-line-curve");
286
- yield line.hover();
284
+ // Hover over chart area to trigger tooltip (Recharts tooltip activates on chart area, not just line)
285
+ const chartSvg = page.locator(".recharts-surface").first();
286
+ yield chartSvg.hover({ position: { x: 200, y: 200 } });
287
+ // Wait for tooltip to appear
288
+ yield page.waitForTimeout(500);
287
289
  yield (0, fixtures_1.expect)(page.locator(tooltipContentSelector)).toBeVisible();
288
290
  // Should have consistent styling
289
291
  const indicator = page.locator(tooltipIndicatorSelector);
@@ -337,7 +339,7 @@ fixtures_1.test.describe("Accessibility", () => {
337
339
  yield (0, fixtures_1.expect)(valueText).toHaveCSS("font-size", /\d+px/);
338
340
  // Should have proper contrast (background vs text)
339
341
  const tooltipContainer = page.locator(tooltipContentSelector);
340
- const backgroundColor = yield tooltipContainer.evaluate(el => window.getComputedStyle(el).backgroundColor);
342
+ const backgroundColor = yield tooltipContainer.evaluate((el) => window.getComputedStyle(el).backgroundColor);
341
343
  (0, fixtures_1.expect)(backgroundColor).not.toBe("transparent");
342
344
  }));
343
345
  (0, fixtures_1.test)("tooltip appears on keyboard navigation", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
@@ -78,6 +78,17 @@ exports.FormMd = (0, metadata_helpers_1.createMetadata)({
78
78
  type: "boolean",
79
79
  defaultValue: FormNative_1.defaultProps.hideButtonRowUntilDirty,
80
80
  },
81
+ hideButtonRow: {
82
+ description: `This property hides the button row entirely when set to true.`,
83
+ type: "boolean",
84
+ defaultValue: FormNative_1.defaultProps.hideButtonRow,
85
+ },
86
+ enableSubmit: {
87
+ description: `This property controls whether the submit button is enabled. When set to false, ` +
88
+ `the submit button is disabled and the form cannot be submitted.`,
89
+ type: "boolean",
90
+ defaultValue: FormNative_1.defaultProps.enableSubmit,
91
+ },
81
92
  submitUrl: (0, metadata_helpers_1.d)(`URL to submit the form data.`),
82
93
  submitMethod: {
83
94
  description: "This property sets the HTTP method to use when submitting the form data. If not " +
@@ -117,6 +128,14 @@ exports.FormMd = (0, metadata_helpers_1.createMetadata)({
117
128
  data: "An object containing the form data to update.",
118
129
  },
119
130
  },
131
+ validate: {
132
+ description: "This method triggers validation on all form fields without submitting the form. " +
133
+ "It displays validation errors and returns the validation result along with the cleaned form data. " +
134
+ "This is useful for implementing custom submit buttons or performing operations that require " +
135
+ "validated data without actually submitting the form.",
136
+ signature: "validate(): Promise<{ isValid: boolean, data: Record<string, any>, errors: ValidationResult[], warnings: ValidationResult[], validationResults: Record<string, ValidationResult> }>",
137
+ returns: "A promise that resolves to an object containing validation status, cleaned data, and detailed validation results.",
138
+ },
120
139
  },
121
140
  themeVars: (0, themeVars_1.parseScssVar)(Form_module_scss_1.default.themeVars),
122
141
  defaultThemeVars: {
@@ -97,6 +97,334 @@ fixtures_1.test.describe("Basic Functionality", () => {
97
97
  yield (0, fixtures_1.expect)(buttons.last()).toHaveText("Cancel");
98
98
  }));
99
99
  // =============================================================================
100
+ // HIDE BUTTON ROW TESTS
101
+ // =============================================================================
102
+ fixtures_1.test.describe("hideButtonRow property", () => {
103
+ (0, fixtures_1.test)("hides button row when set to true", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
104
+ yield initTestBed(`<Form hideButtonRow="true"/>`);
105
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Cancel" })).not.toBeVisible();
106
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Save" })).not.toBeVisible();
107
+ }));
108
+ (0, fixtures_1.test)("shows button row when set to false", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
109
+ yield initTestBed(`<Form hideButtonRow="false"/>`);
110
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Cancel" })).toBeVisible();
111
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Save" })).toBeVisible();
112
+ }));
113
+ (0, fixtures_1.test)("shows button row by default when property not set", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
114
+ yield initTestBed(`<Form/>`);
115
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Cancel" })).toBeVisible();
116
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Save" })).toBeVisible();
117
+ }));
118
+ (0, fixtures_1.test)("hides custom button row template when set to true", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
119
+ yield initTestBed(`
120
+ <Form hideButtonRow="true">
121
+ <property name="buttonRowTemplate">
122
+ <Button label="Custom Save" type="submit" testId="customSave" />
123
+ <Button label="Custom Cancel" type="button" testId="customCancel" />
124
+ </property>
125
+ </Form>
126
+ `);
127
+ yield (0, fixtures_1.expect)(page.getByTestId("customSave")).not.toBeVisible();
128
+ yield (0, fixtures_1.expect)(page.getByTestId("customCancel")).not.toBeVisible();
129
+ }));
130
+ (0, fixtures_1.test)("overrides hideButtonRowUntilDirty when both are set", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page, createFormItemDriver, createTextBoxDriver, }) {
131
+ yield initTestBed(`
132
+ <Form hideButtonRow="true" hideButtonRowUntilDirty="true">
133
+ <FormItem label="Name" bindTo="name" testId="nameField" />
134
+ </Form>
135
+ `);
136
+ // Button row should be hidden even before making changes
137
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Save" })).not.toBeVisible();
138
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Cancel" })).not.toBeVisible();
139
+ // Make the form dirty
140
+ const driver = yield createFormItemDriver("nameField");
141
+ const input = yield createTextBoxDriver(driver.input);
142
+ yield input.field.fill("John");
143
+ // Button row should still be hidden even after making changes
144
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Save" })).not.toBeVisible();
145
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Cancel" })).not.toBeVisible();
146
+ }));
147
+ (0, fixtures_1.test)("handles null value gracefully", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
148
+ yield initTestBed(`<Form hideButtonRow="{null}"/>`);
149
+ // Should show button row (default behavior)
150
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Cancel" })).toBeVisible();
151
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Save" })).toBeVisible();
152
+ }));
153
+ (0, fixtures_1.test)("handles undefined value gracefully", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
154
+ yield initTestBed(`<Form hideButtonRow="{undefined}"/>`);
155
+ // Should show button row (default behavior)
156
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Cancel" })).toBeVisible();
157
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Save" })).toBeVisible();
158
+ }));
159
+ (0, fixtures_1.test)("handles string 'true' value", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
160
+ yield initTestBed(`<Form hideButtonRow="true"/>`);
161
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Cancel" })).not.toBeVisible();
162
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Save" })).not.toBeVisible();
163
+ }));
164
+ (0, fixtures_1.test)("handles string 'false' value", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
165
+ yield initTestBed(`<Form hideButtonRow="false"/>`);
166
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Cancel" })).toBeVisible();
167
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Save" })).toBeVisible();
168
+ }));
169
+ (0, fixtures_1.test)("form submission still works with hidden button row via external submit", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page, createFormItemDriver, createTextBoxDriver, }) {
170
+ const { testStateDriver } = yield initTestBed(`
171
+ <Fragment>
172
+ <Form id="testForm" hideButtonRow="true" onSubmit="arg => testState = arg">
173
+ <FormItem label="Name" bindTo="name" testId="nameField" />
174
+ <Button type="submit" label="External Submit" testId="externalSubmit" />
175
+ </Form>
176
+ </Fragment>
177
+ `);
178
+ const driver = yield createFormItemDriver("nameField");
179
+ const input = yield createTextBoxDriver(driver.input);
180
+ yield input.field.fill("John Doe");
181
+ yield page.getByTestId("externalSubmit").click();
182
+ const submittedData = yield testStateDriver.testState();
183
+ (0, fixtures_1.expect)(submittedData).toEqual({ name: "John Doe" });
184
+ }));
185
+ });
186
+ // =============================================================================
187
+ // HIDE BUTTON ROW UNTIL DIRTY TESTS
188
+ // =============================================================================
189
+ fixtures_1.test.describe("hideButtonRowUntilDirty property", () => {
190
+ (0, fixtures_1.test)("hides button row initially when form is not dirty", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
191
+ yield initTestBed(`
192
+ <Form hideButtonRowUntilDirty="true">
193
+ <FormItem label="Name" bindTo="name" testId="nameField" />
194
+ </Form>
195
+ `);
196
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Cancel" })).not.toBeVisible();
197
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Save" })).not.toBeVisible();
198
+ }));
199
+ (0, fixtures_1.test)("shows button row when form becomes dirty", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page, createFormItemDriver, createTextBoxDriver, }) {
200
+ yield initTestBed(`
201
+ <Form hideButtonRowUntilDirty="true">
202
+ <FormItem label="Name" bindTo="name" testId="nameField" />
203
+ </Form>
204
+ `);
205
+ // Initially hidden
206
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Save" })).not.toBeVisible();
207
+ // Make form dirty
208
+ const driver = yield createFormItemDriver("nameField");
209
+ const input = yield createTextBoxDriver(driver.input);
210
+ yield input.field.fill("John");
211
+ // Now visible
212
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Cancel" })).toBeVisible();
213
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Save" })).toBeVisible();
214
+ }));
215
+ (0, fixtures_1.test)("keeps button row visible after form becomes dirty", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page, createFormItemDriver, createTextBoxDriver, }) {
216
+ yield initTestBed(`
217
+ <Form hideButtonRowUntilDirty="true">
218
+ <FormItem label="Name" bindTo="name" testId="nameField" />
219
+ </Form>
220
+ `);
221
+ const driver = yield createFormItemDriver("nameField");
222
+ const input = yield createTextBoxDriver(driver.input);
223
+ // Make form dirty
224
+ yield input.field.fill("John");
225
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Save" })).toBeVisible();
226
+ // Clear the input (form is still dirty)
227
+ yield input.field.clear();
228
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Save" })).toBeVisible();
229
+ }));
230
+ (0, fixtures_1.test)("shows button row by default when property set to false", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page, }) {
231
+ yield initTestBed(`
232
+ <Form hideButtonRowUntilDirty="false">
233
+ <FormItem label="Name" bindTo="name" testId="nameField" />
234
+ </Form>
235
+ `);
236
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Cancel" })).toBeVisible();
237
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Save" })).toBeVisible();
238
+ }));
239
+ (0, fixtures_1.test)("shows button row by default when property not set", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
240
+ yield initTestBed(`
241
+ <Form>
242
+ <FormItem label="Name" bindTo="name" testId="nameField" />
243
+ </Form>
244
+ `);
245
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Cancel" })).toBeVisible();
246
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Save" })).toBeVisible();
247
+ }));
248
+ (0, fixtures_1.test)("works with multiple form items", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page, createFormItemDriver, createTextBoxDriver, }) {
249
+ yield initTestBed(`
250
+ <Form hideButtonRowUntilDirty="true">
251
+ <FormItem label="Name" bindTo="name" testId="nameField" />
252
+ <FormItem label="Email" bindTo="email" testId="emailField" />
253
+ </Form>
254
+ `);
255
+ // Initially hidden
256
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Save" })).not.toBeVisible();
257
+ // Modify second field
258
+ const emailDriver = yield createFormItemDriver("emailField");
259
+ const emailInput = yield createTextBoxDriver(emailDriver.input);
260
+ yield emailInput.field.fill("test@example.com");
261
+ // Now visible
262
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Save" })).toBeVisible();
263
+ }));
264
+ (0, fixtures_1.test)("hides custom button row template until dirty", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page, createFormItemDriver, createTextBoxDriver, }) {
265
+ yield initTestBed(`
266
+ <Form hideButtonRowUntilDirty="true">
267
+ <FormItem label="Name" bindTo="name" testId="nameField" />
268
+ <property name="buttonRowTemplate">
269
+ <Button label="Custom Save" type="submit" testId="customSave" />
270
+ </property>
271
+ </Form>
272
+ `);
273
+ // Initially hidden
274
+ yield (0, fixtures_1.expect)(page.getByTestId("customSave")).not.toBeVisible();
275
+ // Make form dirty
276
+ const driver = yield createFormItemDriver("nameField");
277
+ const input = yield createTextBoxDriver(driver.input);
278
+ yield input.field.fill("John");
279
+ // Now visible
280
+ yield (0, fixtures_1.expect)(page.getByTestId("customSave")).toBeVisible();
281
+ }));
282
+ (0, fixtures_1.test)("handles null value gracefully", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
283
+ yield initTestBed(`
284
+ <Form hideButtonRowUntilDirty="{null}">
285
+ <FormItem label="Name" bindTo="name" testId="nameField" />
286
+ </Form>
287
+ `);
288
+ // Should show button row (default behavior)
289
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Cancel" })).toBeVisible();
290
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Save" })).toBeVisible();
291
+ }));
292
+ (0, fixtures_1.test)("handles undefined value gracefully", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
293
+ yield initTestBed(`
294
+ <Form hideButtonRowUntilDirty="{undefined}">
295
+ <FormItem label="Name" bindTo="name" testId="nameField" />
296
+ </Form>
297
+ `);
298
+ // Should show button row (default behavior)
299
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Cancel" })).toBeVisible();
300
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Save" })).toBeVisible();
301
+ }));
302
+ (0, fixtures_1.test)("works with form initialized with data", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page, createFormItemDriver, createTextBoxDriver, }) {
303
+ yield initTestBed(`
304
+ <Form hideButtonRowUntilDirty="true" data="{{ name: 'Initial' }}">
305
+ <FormItem label="Name" bindTo="name" testId="nameField" />
306
+ </Form>
307
+ `);
308
+ // Initially hidden (form has data but is not dirty)
309
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Save" })).not.toBeVisible();
310
+ // Make form dirty
311
+ const driver = yield createFormItemDriver("nameField");
312
+ const input = yield createTextBoxDriver(driver.input);
313
+ yield input.field.fill("Modified");
314
+ // Now visible
315
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Save" })).toBeVisible();
316
+ }));
317
+ (0, fixtures_1.test)("button row appears when checkbox is checked", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
318
+ yield initTestBed(`
319
+ <Form hideButtonRowUntilDirty="true">
320
+ <FormItem label="Accept Terms" bindTo="terms" type="checkbox" />
321
+ </Form>
322
+ `);
323
+ // Initially hidden
324
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Save" })).not.toBeVisible();
325
+ // Check the checkbox
326
+ const checkbox = page.getByRole("checkbox");
327
+ yield checkbox.check();
328
+ // Now visible
329
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Save" })).toBeVisible();
330
+ }));
331
+ (0, fixtures_1.test)("button row appears when slider value changes", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
332
+ yield initTestBed(`
333
+ <Form hideButtonRowUntilDirty="true">
334
+ <FormItem label="Volume" bindTo="volume" type="slider" testId="volumeField" />
335
+ </Form>
336
+ `);
337
+ // Initially hidden
338
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Save" })).not.toBeVisible();
339
+ // Move the slider using keyboard
340
+ const slider = page.getByRole("slider");
341
+ yield slider.press("ArrowRight");
342
+ // Now visible
343
+ yield (0, fixtures_1.expect)(page.getByRole("button", { name: "Save" })).toBeVisible();
344
+ }));
345
+ });
346
+ // =============================================================================
347
+ // ENABLE SUBMIT PROPERTY TESTS
348
+ // =============================================================================
349
+ fixtures_1.test.describe("enableSubmit property", () => {
350
+ (0, fixtures_1.test)("disables submit button when set to false", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
351
+ yield initTestBed(`<Form enableSubmit="false"/>`);
352
+ const saveButton = page.getByRole("button", { name: "Save" });
353
+ yield (0, fixtures_1.expect)(saveButton).toBeVisible();
354
+ yield (0, fixtures_1.expect)(saveButton).toBeDisabled();
355
+ }));
356
+ (0, fixtures_1.test)("enables submit button when set to true", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
357
+ yield initTestBed(`<Form enableSubmit="true"/>`);
358
+ const saveButton = page.getByRole("button", { name: "Save" });
359
+ yield (0, fixtures_1.expect)(saveButton).toBeVisible();
360
+ yield (0, fixtures_1.expect)(saveButton).toBeEnabled();
361
+ }));
362
+ (0, fixtures_1.test)("submit button is enabled by default when property not set", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page, }) {
363
+ yield initTestBed(`<Form/>`);
364
+ const saveButton = page.getByRole("button", { name: "Save" });
365
+ yield (0, fixtures_1.expect)(saveButton).toBeEnabled();
366
+ }));
367
+ (0, fixtures_1.test)("prevents form submission when set to false", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
368
+ const { testStateDriver } = yield initTestBed(`
369
+ <Form enableSubmit="false" onSubmit="arg => testState = arg">
370
+ <FormItem label="Name" bindTo="name" testId="nameField" />
371
+ </Form>
372
+ `);
373
+ const saveButton = page.getByRole("button", { name: "Save" });
374
+ yield (0, fixtures_1.expect)(saveButton).toBeDisabled();
375
+ // Verify form does not submit (button is disabled, so click won't work)
376
+ yield saveButton.click({ force: true }); // Force click on disabled button
377
+ // testState should remain null since submit was prevented
378
+ yield fixtures_1.expect.poll(testStateDriver.testState).toBeNull();
379
+ }));
380
+ (0, fixtures_1.test)("allows form submission when set to true", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page, createFormItemDriver, createTextBoxDriver, }) {
381
+ const { testStateDriver } = yield initTestBed(`
382
+ <Form enableSubmit="true" onSubmit="arg => testState = arg">
383
+ <FormItem label="Name" bindTo="name" testId="nameField" />
384
+ </Form>
385
+ `);
386
+ const driver = yield createFormItemDriver("nameField");
387
+ const input = yield createTextBoxDriver(driver.input);
388
+ yield input.field.fill("John Doe");
389
+ const saveButton = page.getByRole("button", { name: "Save" });
390
+ yield (0, fixtures_1.expect)(saveButton).toBeEnabled();
391
+ yield saveButton.click();
392
+ const submittedData = yield testStateDriver.testState();
393
+ (0, fixtures_1.expect)(submittedData).toEqual({ name: "John Doe" });
394
+ }));
395
+ (0, fixtures_1.test)("handles null value gracefully (defaults to enabled)", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
396
+ yield initTestBed(`<Form enableSubmit="{null}"/>`);
397
+ const saveButton = page.getByRole("button", { name: "Save" });
398
+ yield (0, fixtures_1.expect)(saveButton).toBeEnabled();
399
+ }));
400
+ (0, fixtures_1.test)("handles string 'true' value", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
401
+ yield initTestBed(`<Form enableSubmit="true"/>`);
402
+ const saveButton = page.getByRole("button", { name: "Save" });
403
+ yield (0, fixtures_1.expect)(saveButton).toBeEnabled();
404
+ }));
405
+ (0, fixtures_1.test)("handles string 'false' value", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
406
+ yield initTestBed(`<Form enableSubmit="false"/>`);
407
+ const saveButton = page.getByRole("button", { name: "Save" });
408
+ yield (0, fixtures_1.expect)(saveButton).toBeDisabled();
409
+ }));
410
+ (0, fixtures_1.test)("does not affect cancel button", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
411
+ yield initTestBed(`<Form enableSubmit="false"/>`);
412
+ const cancelButton = page.getByRole("button", { name: "Cancel" });
413
+ yield (0, fixtures_1.expect)(cancelButton).toBeEnabled();
414
+ }));
415
+ (0, fixtures_1.test)("works with custom submit button label", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
416
+ yield initTestBed(`<Form enableSubmit="false" saveLabel="Submit Now"/>`);
417
+ const submitButton = page.getByRole("button", { name: "Submit Now" });
418
+ yield (0, fixtures_1.expect)(submitButton).toBeDisabled();
419
+ }));
420
+ (0, fixtures_1.test)("works together with form disabled state", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
421
+ yield initTestBed(`<Form enabled="false" enableSubmit="true"/>`);
422
+ const saveButton = page.getByRole("button", { name: "Save" });
423
+ // Form disabled takes precedence
424
+ yield (0, fixtures_1.expect)(saveButton).toBeDisabled();
425
+ }));
426
+ });
427
+ // =============================================================================
100
428
  // DATA PROPERTY TESTS
101
429
  // =============================================================================
102
430
  fixtures_1.test.describe("data property", () => {
@@ -366,6 +694,122 @@ fixtures_1.test.describe("Basic Functionality", () => {
366
694
  yield page.getByRole("button", { name: "Reset" }).click();
367
695
  yield (0, fixtures_1.expect)(nameInput.field).toHaveValue("Initial");
368
696
  }));
697
+ (0, fixtures_1.test)("validate method returns validation results without submitting", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page, createFormItemDriver, createTextBoxDriver, }) {
698
+ const { testStateDriver } = yield initTestBed(`
699
+ <Form id="testForm">
700
+ <FormItem label="Name" bindTo="name" required="true" testId="nameField" />
701
+ <FormItem label="Email" bindTo="email" testId="emailField" />
702
+ <Button onClick="testState = testForm.validate()" label="Validate" testId="validateBtn" />
703
+ </Form>
704
+ `);
705
+ // Click validate button without filling required field
706
+ yield page.getByTestId("validateBtn").click();
707
+ // Wait for validation to complete
708
+ yield page.waitForTimeout(100);
709
+ const result = yield testStateDriver.testState();
710
+ (0, fixtures_1.expect)(result).toBeTruthy();
711
+ (0, fixtures_1.expect)(result.isValid).toBe(false);
712
+ (0, fixtures_1.expect)(result.errors).toBeDefined();
713
+ (0, fixtures_1.expect)(result.errors.length).toBeGreaterThan(0);
714
+ }));
715
+ (0, fixtures_1.test)("validate method returns isValid true when all validations pass", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page, createFormItemDriver, createTextBoxDriver, }) {
716
+ const { testStateDriver } = yield initTestBed(`
717
+ <Form id="testForm">
718
+ <FormItem label="Name" bindTo="name" required="true" testId="nameField" />
719
+ <Button onClick="testState = testForm.validate()" label="Validate" testId="validateBtn" />
720
+ </Form>
721
+ `);
722
+ // Fill the required field
723
+ const nameDriver = yield createFormItemDriver("nameField");
724
+ const nameInput = yield createTextBoxDriver(nameDriver.input);
725
+ yield nameInput.field.fill("John Doe");
726
+ // Click validate button
727
+ yield page.getByTestId("validateBtn").click();
728
+ // Wait for validation to complete
729
+ yield page.waitForTimeout(100);
730
+ const result = yield testStateDriver.testState();
731
+ (0, fixtures_1.expect)(result).toBeTruthy();
732
+ (0, fixtures_1.expect)(result.isValid).toBe(true);
733
+ (0, fixtures_1.expect)(result.errors.length).toBe(0);
734
+ }));
735
+ (0, fixtures_1.test)("validate method returns cleaned form data", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page, createFormItemDriver, createTextBoxDriver, }) {
736
+ const { testStateDriver } = yield initTestBed(`
737
+ <Form id="testForm">
738
+ <FormItem label="Name" bindTo="name" testId="nameField" />
739
+ <FormItem label="Age" bindTo="age" type="integer" testId="ageField" />
740
+ <Button onClick="testState = testForm.validate()" label="Validate" testId="validateBtn" />
741
+ </Form>
742
+ `);
743
+ // Fill form fields
744
+ const nameDriver = yield createFormItemDriver("nameField");
745
+ const nameInput = yield createTextBoxDriver(nameDriver.input);
746
+ yield nameInput.field.fill("John Doe");
747
+ const ageDriver = yield createFormItemDriver("ageField");
748
+ const ageInput = yield createTextBoxDriver(ageDriver.input);
749
+ yield ageInput.field.fill("30");
750
+ // Click validate button
751
+ yield page.getByTestId("validateBtn").click();
752
+ // Wait for validation to complete
753
+ yield page.waitForTimeout(100);
754
+ const result = yield testStateDriver.testState();
755
+ (0, fixtures_1.expect)(result).toBeTruthy();
756
+ (0, fixtures_1.expect)(result.data).toEqual({ name: "John Doe", age: "30" });
757
+ }));
758
+ (0, fixtures_1.test)("validate method displays validation errors on form", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page, createFormItemDriver, }) {
759
+ yield initTestBed(`
760
+ <Form id="testForm">
761
+ <FormItem label="Name" bindTo="name" required="true" testId="nameField" />
762
+ <Button onClick="testForm.validate()" label="Validate" testId="validateBtn" />
763
+ </Form>
764
+ `);
765
+ // Click validate without filling required field
766
+ yield page.getByTestId("validateBtn").click();
767
+ // Validation error should be displayed
768
+ const nameField = page.getByTestId("nameField");
769
+ yield (0, fixtures_1.expect)(nameField).toContainText("This field is required");
770
+ }));
771
+ (0, fixtures_1.test)("validate method does not trigger form submission", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page, createFormItemDriver, createTextBoxDriver, }) {
772
+ const { testStateDriver } = yield initTestBed(`
773
+ <Form id="testForm" onSubmit="testState = 'submitted'">
774
+ <FormItem label="Name" bindTo="name" testId="nameField" />
775
+ <Button onClick="testForm.validate()" label="Validate" testId="validateBtn" />
776
+ </Form>
777
+ `);
778
+ // Fill form
779
+ const nameDriver = yield createFormItemDriver("nameField");
780
+ const nameInput = yield createTextBoxDriver(nameDriver.input);
781
+ yield nameInput.field.fill("John");
782
+ // Click validate button
783
+ yield page.getByTestId("validateBtn").click();
784
+ // Wait a bit
785
+ yield page.waitForTimeout(200);
786
+ // testState should remain null (not 'submitted')
787
+ yield fixtures_1.expect.poll(testStateDriver.testState).toBeNull();
788
+ }));
789
+ (0, fixtures_1.test)("validate method returns complete validation results object", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page, createFormItemDriver, createTextBoxDriver, }) {
790
+ const { testStateDriver } = yield initTestBed(`
791
+ <Form id="testForm">
792
+ <FormItem label="Name" bindTo="name" required="true" testId="nameField" />
793
+ <FormItem label="Email" bindTo="email" testId="emailField" />
794
+ <Button onClick="testState = testForm.validate()" label="Validate" testId="validateBtn" />
795
+ </Form>
796
+ `);
797
+ // Fill only email (name is required)
798
+ const emailDriver = yield createFormItemDriver("emailField");
799
+ const emailInput = yield createTextBoxDriver(emailDriver.input);
800
+ yield emailInput.field.fill("test@example.com");
801
+ // Click validate button
802
+ yield page.getByTestId("validateBtn").click();
803
+ // Wait for validation to complete
804
+ yield page.waitForTimeout(100);
805
+ const result = yield testStateDriver.testState();
806
+ (0, fixtures_1.expect)(result).toBeTruthy();
807
+ (0, fixtures_1.expect)(result.isValid).toBeDefined();
808
+ (0, fixtures_1.expect)(result.data).toBeDefined();
809
+ (0, fixtures_1.expect)(result.errors).toBeDefined();
810
+ (0, fixtures_1.expect)(result.warnings).toBeDefined();
811
+ (0, fixtures_1.expect)(result.validationResults).toBeDefined();
812
+ }));
369
813
  });
370
814
  // =============================================================================
371
815
  // CONTEXT VARIABLE TESTS