xmlui 0.10.24 → 0.10.26

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 (47) hide show
  1. package/dist/lib/{index-DoIVkz5T.mjs → index-DHXWMb-6.mjs} +12924 -12882
  2. package/dist/lib/index.css +1 -1
  3. package/dist/lib/{initMock-CSGEd746.mjs → initMock-TxnkId6_.mjs} +1 -1
  4. package/dist/lib/language-server-web-worker.mjs +1 -1
  5. package/dist/lib/language-server.mjs +1 -1
  6. package/dist/lib/{metadata-utils-Dx-2qZBh.mjs → metadata-utils-DXUdlyja.mjs} +6 -6
  7. package/dist/lib/{server-common--BHVvP1o.mjs → server-common-CtpN0Z4h.mjs} +1 -1
  8. package/dist/lib/xmlui.d.ts +1 -0
  9. package/dist/lib/xmlui.mjs +1 -1
  10. package/dist/metadata/{collectedComponentMetadata-B3Hs8_cV.mjs → collectedComponentMetadata-BgHIc2Iu.mjs} +12871 -12829
  11. package/dist/metadata/{initMock-DQrGwkya.mjs → initMock-B3UDa-rw.mjs} +1 -1
  12. package/dist/metadata/style.css +1 -1
  13. package/dist/metadata/xmlui-metadata.mjs +1 -1
  14. package/dist/metadata/xmlui-metadata.umd.js +3 -3
  15. package/dist/scripts/package.json +1 -1
  16. package/dist/scripts/src/components/App/App.spec.js +100 -0
  17. package/dist/scripts/src/components/App/AppNative.js +13 -2
  18. package/dist/scripts/src/components/AppHeader/AppHeader.js +1 -6
  19. package/dist/scripts/src/components/AppHeader/AppHeaderNative.js +7 -7
  20. package/dist/scripts/src/components/AutoComplete/AutoComplete.spec.js +1 -1
  21. package/dist/scripts/src/components/AutoComplete/AutoCompleteNative.js +30 -5
  22. package/dist/scripts/src/components/Form/Form.spec.js +2 -2
  23. package/dist/scripts/src/components/FormItem/FormItemNative.js +5 -1
  24. package/dist/scripts/src/components/Heading/Heading.js +45 -5
  25. package/dist/scripts/src/components/Heading/Heading.spec.js +82 -0
  26. package/dist/scripts/src/components/ModalDialog/ModalDialog.js +4 -6
  27. package/dist/scripts/src/components/ModalDialog/ModalDialog.spec.js +19 -0
  28. package/dist/scripts/src/components/NavGroup/NavGroup.spec.js +103 -11
  29. package/dist/scripts/src/components/NavGroup/NavGroupNative.js +6 -1
  30. package/dist/scripts/src/components/Option/Option.spec.js +3 -1
  31. package/dist/scripts/src/components/Select/HiddenOption.js +3 -3
  32. package/dist/scripts/src/components/Select/Select.js +2 -3
  33. package/dist/scripts/src/components/Select/Select.spec.js +4 -6
  34. package/dist/scripts/src/components/Select/SelectNative.js +187 -47
  35. package/dist/scripts/src/components-core/rendering/ComponentAdapter.js +11 -0
  36. package/dist/scripts/src/components-core/rendering/Container.js +3 -4
  37. package/dist/scripts/src/components-core/rendering/StateContainer.js +16 -18
  38. package/dist/scripts/src/components-core/rendering/reducer.js +6 -3
  39. package/dist/scripts/src/components-core/rendering/valueExtractor.js +9 -1
  40. package/dist/scripts/src/components-core/utils/extractParam.js +2 -1
  41. package/dist/scripts/src/testing/ComponentDrivers.js +1 -1
  42. package/dist/standalone/xmlui-standalone.es.d.ts +1 -0
  43. package/dist/standalone/xmlui-standalone.umd.js +35 -35
  44. package/package.json +1 -1
  45. package/dist/scripts/src/components/Select/MultiSelectOption.js +0 -42
  46. package/dist/scripts/src/components/Select/SelectOption.js +0 -34
  47. package/dist/scripts/src/components/Select/SimpleSelect.js +0 -57
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xmlui",
3
- "version": "0.10.24",
3
+ "version": "0.10.26",
4
4
  "sideEffects": false,
5
5
  "scripts": {
6
6
  "start-test-bed": "cd src/testing/infrastructure && xmlui start",
@@ -217,3 +217,103 @@ fixtures_1.test.describe("Event Handling", () => {
217
217
  yield fixtures_1.expect.poll(testStateDriver.testState).toEqual(testData);
218
218
  }));
219
219
  });
220
+ // =============================================================================
221
+ // Drawer HANDLING TESTS
222
+ // =============================================================================
223
+ fixtures_1.test.describe("Drawer Handling", () => {
224
+ (0, fixtures_1.test)("Drawer displayed if NavPanel has no 'when'", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page, }) {
225
+ // Set small viewport to trigger drawer mode
226
+ yield page.setViewportSize({ width: 400, height: 600 });
227
+ yield initTestBed(`
228
+ <App layout="condensed">
229
+ <AppHeader testId="appHeader"/>
230
+ <NavPanel>
231
+ <NavGroup label="Pages">
232
+ <NavLink label="Page 1" to="/page1"/>
233
+ <NavLink label="Page 2" to="/page2"/>
234
+ </NavGroup>
235
+ </NavPanel>
236
+ </App>
237
+ `);
238
+ // Open drawer by clicking hamburger button
239
+ const appHeader = page.getByTestId("appHeader");
240
+ const hamburgerButton = appHeader.locator('[data-part-id="hamburger"]').first();
241
+ yield (0, fixtures_1.expect)(hamburgerButton).toBeVisible();
242
+ }));
243
+ (0, fixtures_1.test)("Drawer displayed if NavPanel has when='true'", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page, }) {
244
+ // Set small viewport to trigger drawer mode
245
+ yield page.setViewportSize({ width: 400, height: 600 });
246
+ yield initTestBed(`
247
+ <App layout="condensed">
248
+ <AppHeader testId="appHeader"/>
249
+ <NavPanel when="true">
250
+ <NavGroup label="Pages">
251
+ <NavLink label="Page 1" to="/page1"/>
252
+ <NavLink label="Page 2" to="/page2"/>
253
+ </NavGroup>
254
+ </NavPanel>
255
+ </App>
256
+ `);
257
+ // Open drawer by clicking hamburger button
258
+ const appHeader = page.getByTestId("appHeader");
259
+ const hamburgerButton = appHeader.locator('[data-part-id="hamburger"]').first();
260
+ yield (0, fixtures_1.expect)(hamburgerButton).toBeVisible();
261
+ }));
262
+ (0, fixtures_1.test)("Drawer displayed if NavPanel has when='{true}'", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page, }) {
263
+ // Set small viewport to trigger drawer mode
264
+ yield page.setViewportSize({ width: 400, height: 600 });
265
+ yield initTestBed(`
266
+ <App layout="condensed">
267
+ <AppHeader testId="appHeader"/>
268
+ <NavPanel when="{true}">
269
+ <NavGroup label="Pages">
270
+ <NavLink label="Page 1" to="/page1"/>
271
+ <NavLink label="Page 2" to="/page2"/>
272
+ </NavGroup>
273
+ </NavPanel>
274
+ </App>
275
+ `);
276
+ // Open drawer by clicking hamburger button
277
+ const appHeader = page.getByTestId("appHeader");
278
+ const hamburgerButton = appHeader.locator('[data-part-id="hamburger"]').first();
279
+ yield (0, fixtures_1.expect)(hamburgerButton).toBeVisible();
280
+ }));
281
+ (0, fixtures_1.test)("Drawer not displayed if NavPanel has when='false'", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page, }) {
282
+ // Set small viewport to trigger drawer mode
283
+ yield page.setViewportSize({ width: 400, height: 600 });
284
+ yield initTestBed(`
285
+ <App layout="condensed">
286
+ <AppHeader testId="appHeader"/>
287
+ <NavPanel when="false">
288
+ <NavGroup label="Pages">
289
+ <NavLink label="Page 1" to="/page1"/>
290
+ <NavLink label="Page 2" to="/page2"/>
291
+ </NavGroup>
292
+ </NavPanel>
293
+ </App>
294
+ `);
295
+ // Open drawer by clicking hamburger button
296
+ const appHeader = page.getByTestId("appHeader");
297
+ const hamburgerButton = appHeader.locator('[data-part-id="hamburger"]').first();
298
+ yield (0, fixtures_1.expect)(hamburgerButton).not.toBeVisible();
299
+ }));
300
+ (0, fixtures_1.test)("Drawer not displayed if NavPanel has when='{false}'", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page, }) {
301
+ // Set small viewport to trigger drawer mode
302
+ yield page.setViewportSize({ width: 400, height: 600 });
303
+ yield initTestBed(`
304
+ <App layout="condensed">
305
+ <AppHeader testId="appHeader"/>
306
+ <NavPanel when="{false}">
307
+ <NavGroup label="Pages">
308
+ <NavLink label="Page 1" to="/page1"/>
309
+ <NavLink label="Page 2" to="/page2"/>
310
+ </NavGroup>
311
+ </NavPanel>
312
+ </App>
313
+ `);
314
+ // Open drawer by clicking hamburger button
315
+ const appHeader = page.getByTestId("appHeader");
316
+ const hamburgerButton = appHeader.locator('[data-part-id="hamburger"]').first();
317
+ yield (0, fixtures_1.expect)(hamburgerButton).not.toBeVisible();
318
+ }));
319
+ });
@@ -34,6 +34,7 @@ const AppLayoutContext_1 = require("./AppLayoutContext");
34
34
  const SearchContext_1 = require("./SearchContext");
35
35
  const LinkInfoContext_1 = require("./LinkInfoContext");
36
36
  const constants_1 = require("../../components-core/constants");
37
+ const extractParam_1 = require("../../components-core/utils/extractParam");
37
38
  exports.defaultProps = {
38
39
  scrollWholePage: true,
39
40
  noScrollbarGutters: false,
@@ -50,9 +51,19 @@ function App(_a) {
50
51
  const mounted = (0, react_1.useRef)(false);
51
52
  const layoutWithDefaultValue = layout || getThemeVar("layout-App") || "condensed-sticky";
52
53
  const safeLayout = layoutWithDefaultValue === null || layoutWithDefaultValue === void 0 ? void 0 : layoutWithDefaultValue.trim().replace(/[\u2013\u2014\u2011]/g, "-"); //It replaces all &ndash; (–) and &mdash; (—) and non-breaking hyphen '‑' symbols with simple dashes (-).
53
- const { setLoggedInUser, mediaSize, forceRefreshAnchorScroll, appGlobals } = (0, AppContext_1.useAppContext)();
54
+ const appContext = (0, AppContext_1.useAppContext)();
55
+ const { setLoggedInUser, mediaSize, forceRefreshAnchorScroll, appGlobals } = appContext;
54
56
  const hasRegisteredHeader = header !== undefined;
55
- const hasRegisteredNavPanel = navPanelDef !== undefined;
57
+ // --- Check if NavPanel's "when" condition allows it to be rendered
58
+ // --- This ensures the drawer and hamburger menu are hidden when NavPanel.when evaluates to false
59
+ const navPanelShouldRender = (0, react_1.useMemo)(() => {
60
+ if (!navPanelDef)
61
+ return false;
62
+ // Use shouldKeep to evaluate the NavPanel's when condition
63
+ // Pass empty state {} and appContext since we're evaluating at the App level
64
+ return (0, extractParam_1.shouldKeep)(navPanelDef.when, {}, appContext);
65
+ }, [navPanelDef, appContext]);
66
+ const hasRegisteredNavPanel = navPanelDef !== undefined && navPanelShouldRender;
56
67
  (0, react_1.useEffect)(() => {
57
68
  setLoggedInUser(loggedInUser);
58
69
  }, [loggedInUser, setLoggedInUser]);
@@ -34,11 +34,6 @@ exports.AppHeaderMd = (0, metadata_helpers_1.createMetadata)({
34
34
  valueType: "boolean",
35
35
  defaultValue: AppHeaderNative_1.defaultProps.showLogo,
36
36
  },
37
- showNavPanelIf: {
38
- description: "Determines if the navigation panel should be displayed",
39
- valueType: "boolean",
40
- defaultValue: AppHeaderNative_1.defaultProps.showNavPanelIf,
41
- },
42
37
  },
43
38
  themeVars: (0, themeVars_1.parseScssVar)(AppHeader_module_scss_1.default.themeVars),
44
39
  themeVarDescriptions: {
@@ -53,7 +48,7 @@ exports.appHeaderComponentRenderer = (0, renderers_1.createComponentRenderer)(CO
53
48
  // --- Convert the plain (text) logo template into component definition
54
49
  const logoTemplate = node.props.logoTemplate || ((_a = node.slots) === null || _a === void 0 ? void 0 : _a.logoSlot);
55
50
  const titleTemplate = node.props.titleTemplate || ((_b = node.slots) === null || _b === void 0 ? void 0 : _b.titleSlot);
56
- return ((0, jsx_runtime_1.jsx)(AppHeaderNative_1.AppContextAwareAppHeader, { profileMenu: renderChild(extractValue(node.props.profileMenuTemplate, true)), title: extractValue(node.props.title), showLogo: extractValue.asOptionalBoolean(node.props.showLogo), showNavPanelIf: extractValue.asOptionalBoolean(node.props.showNavPanelIf, AppHeaderNative_1.defaultProps.showNavPanelIf), titleContent: titleTemplate && ((0, jsx_runtime_1.jsx)(SlotItem_1.SlotItem, { node: titleTemplate, renderChild: renderChild, slotProps: { title: extractValue(node.props.title) } })), logoContent: renderChild(logoTemplate, {
51
+ return ((0, jsx_runtime_1.jsx)(AppHeaderNative_1.AppContextAwareAppHeader, { profileMenu: renderChild(extractValue(node.props.profileMenuTemplate, true)), title: extractValue(node.props.title), showLogo: extractValue.asOptionalBoolean(node.props.showLogo), titleContent: titleTemplate && ((0, jsx_runtime_1.jsx)(SlotItem_1.SlotItem, { node: titleTemplate, renderChild: renderChild, slotProps: { title: extractValue(node.props.title) } })), logoContent: renderChild(logoTemplate, {
57
52
  type: "Stack",
58
53
  orientation: "horizontal",
59
54
  }), className: (0, classnames_1.default)(layoutContext === null || layoutContext === void 0 ? void 0 : layoutContext.themeClassName, className), renderChild: renderChild, children: renderChild(node.children, {
@@ -30,9 +30,9 @@ const AppLayoutContext_1 = require("../../components/App/AppLayoutContext");
30
30
  const ButtonNative_1 = require("../../components/Button/ButtonNative");
31
31
  const NavLinkNative_1 = require("../../components/NavLink/NavLinkNative");
32
32
  const hooks_1 = require("../../components-core/utils/hooks");
33
+ const PART_HAMBURGER = "hamburger";
33
34
  exports.defaultProps = {
34
35
  showLogo: true,
35
- showNavPanelIf: true,
36
36
  };
37
37
  function useLogoUrl() {
38
38
  const { logo, logoLight, logoDark } = (0, AppLayoutContext_1.useAppLayoutContext)() || {};
@@ -46,27 +46,27 @@ function useLogoUrl() {
46
46
  return toneLogoUrl || baseLogoUrl;
47
47
  }
48
48
  const AppHeader = (_a) => {
49
- var { children, profileMenu, style = constants_1.EMPTY_OBJECT, logoContent, className, canRestrictContentWidth, navPanelVisible = true, toggleDrawer, showLogo = exports.defaultProps.showLogo, showNavPanelIf = exports.defaultProps.showNavPanelIf, hasRegisteredNavPanel, title, titleContent, registerSubNavPanelSlot } = _a, rest = __rest(_a, ["children", "profileMenu", "style", "logoContent", "className", "canRestrictContentWidth", "navPanelVisible", "toggleDrawer", "showLogo", "showNavPanelIf", "hasRegisteredNavPanel", "title", "titleContent", "registerSubNavPanelSlot"]);
49
+ var { children, profileMenu, style = constants_1.EMPTY_OBJECT, logoContent, className, canRestrictContentWidth, navPanelVisible = true, toggleDrawer, showLogo = exports.defaultProps.showLogo, hasRegisteredNavPanel, title, titleContent, registerSubNavPanelSlot } = _a, rest = __rest(_a, ["children", "profileMenu", "style", "logoContent", "className", "canRestrictContentWidth", "navPanelVisible", "toggleDrawer", "showLogo", "hasRegisteredNavPanel", "title", "titleContent", "registerSubNavPanelSlot"]);
50
50
  const { mediaSize } = (0, AppContext_1.useAppContext)();
51
51
  const logoUrl = useLogoUrl();
52
52
  const subNavPanelSlot = (0, react_1.useRef)(null);
53
- const effectiveNavPanelVisible = navPanelVisible && showNavPanelIf;
53
+ const effectiveNavPanelVisible = navPanelVisible;
54
54
  const safeLogoTitle = mediaSize.sizeIndex < 2 ? null : !titleContent && title ? ((0, jsx_runtime_1.jsx)(NavLinkNative_1.NavLink, { to: "/", displayActive: false, style: { paddingLeft: 0 }, children: title })) : (titleContent);
55
55
  (0, hooks_1.useIsomorphicLayoutEffect)(() => {
56
56
  registerSubNavPanelSlot === null || registerSubNavPanelSlot === void 0 ? void 0 : registerSubNavPanelSlot(subNavPanelSlot.current);
57
57
  }, []);
58
58
  return ((0, jsx_runtime_1.jsx)("div", Object.assign({}, rest, { className: (0, classnames_1.default)(AppHeader_module_scss_1.default.header, className), style: style, role: "banner", children: (0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)(AppHeader_module_scss_1.default.headerInner, {
59
59
  [AppHeader_module_scss_1.default.full]: !canRestrictContentWidth,
60
- }), children: [hasRegisteredNavPanel && showNavPanelIf && ((0, jsx_runtime_1.jsx)(ButtonNative_1.Button, { onClick: toggleDrawer, icon: (0, jsx_runtime_1.jsx)(IconNative_1.Icon, { name: "hamburger" }), variant: "ghost", className: AppHeader_module_scss_1.default.drawerToggle, style: { color: "inherit", flexShrink: 0 } })), (0, jsx_runtime_1.jsx)("div", { className: AppHeader_module_scss_1.default.logoAndTitle, children: (showLogo || !effectiveNavPanelVisible) &&
60
+ }), children: [hasRegisteredNavPanel && ((0, jsx_runtime_1.jsx)(ButtonNative_1.Button, { "data-part-id": PART_HAMBURGER, onClick: toggleDrawer, icon: (0, jsx_runtime_1.jsx)(IconNative_1.Icon, { name: "hamburger" }), variant: "ghost", className: AppHeader_module_scss_1.default.drawerToggle, style: { color: "inherit", flexShrink: 0 } })), (0, jsx_runtime_1.jsx)("div", { className: AppHeader_module_scss_1.default.logoAndTitle, children: (showLogo || !effectiveNavPanelVisible) &&
61
61
  (logoContent ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: AppHeader_module_scss_1.default.customLogoContainer, children: logoContent }), safeLogoTitle] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [!!logoUrl && ((0, jsx_runtime_1.jsx)("div", { className: AppHeader_module_scss_1.default.logoContainer, children: (0, jsx_runtime_1.jsx)(NavLinkNative_1.NavLink, { to: "/", displayActive: false, className: AppHeader_module_scss_1.default.logoLink, children: (0, jsx_runtime_1.jsx)(LogoNative_1.Logo, {}) }) })), safeLogoTitle] }))) }), (0, jsx_runtime_1.jsx)("div", { ref: subNavPanelSlot, className: AppHeader_module_scss_1.default.subNavPanelSlot }), (0, jsx_runtime_1.jsx)("div", { className: AppHeader_module_scss_1.default.childrenWrapper, children: children }), profileMenu && (0, jsx_runtime_1.jsx)("div", { className: AppHeader_module_scss_1.default.rightItems, children: profileMenu })] }) })));
62
62
  };
63
63
  exports.AppHeader = AppHeader;
64
- function AppContextAwareAppHeader({ children, logoContent, profileMenu, style, className, title, titleContent, showLogo = true, showNavPanelIf = exports.defaultProps.showNavPanelIf, renderChild, }) {
64
+ function AppContextAwareAppHeader({ children, logoContent, profileMenu, style, className, title, titleContent, showLogo = true, renderChild, }) {
65
65
  const appLayoutContext = (0, AppLayoutContext_1.useAppLayoutContext)();
66
66
  const { navPanelVisible, toggleDrawer, layout, hasRegisteredNavPanel, navPanelDef, logoContentDef, registerSubNavPanelSlot, } = appLayoutContext || {};
67
67
  // console.log("APP LAYOUT CONTEXT", appLayoutContext);
68
68
  const displayLogo = layout !== "vertical" && layout !== "vertical-sticky" && showLogo;
69
69
  const canRestrictContentWidth = layout !== "vertical-full-header";
70
- const effectiveNavPanelVisible = navPanelVisible && showNavPanelIf;
71
- return ((0, jsx_runtime_1.jsxs)(exports.AppHeader, { hasRegisteredNavPanel: hasRegisteredNavPanel, navPanelVisible: effectiveNavPanelVisible, toggleDrawer: toggleDrawer, canRestrictContentWidth: canRestrictContentWidth, showLogo: displayLogo, showNavPanelIf: showNavPanelIf, logoContent: logoContent || renderChild(logoContentDef), profileMenu: profileMenu, style: style, className: className, title: title, titleContent: titleContent, registerSubNavPanelSlot: registerSubNavPanelSlot, children: [(layout === null || layout === void 0 ? void 0 : layout.startsWith("condensed")) && effectiveNavPanelVisible && ((0, jsx_runtime_1.jsx)("div", { style: { minWidth: 0 }, children: renderChild(navPanelDef) })), children] }));
70
+ const effectiveNavPanelVisible = navPanelVisible;
71
+ return ((0, jsx_runtime_1.jsxs)(exports.AppHeader, { hasRegisteredNavPanel: hasRegisteredNavPanel, navPanelVisible: effectiveNavPanelVisible, toggleDrawer: toggleDrawer, canRestrictContentWidth: canRestrictContentWidth, showLogo: displayLogo, logoContent: logoContent || renderChild(logoContentDef), profileMenu: profileMenu, style: style, className: className, title: title, titleContent: titleContent, registerSubNavPanelSlot: registerSubNavPanelSlot, children: [(layout === null || layout === void 0 ? void 0 : layout.startsWith("condensed")) && effectiveNavPanelVisible && ((0, jsx_runtime_1.jsx)("div", { style: { minWidth: 0 }, children: renderChild(navPanelDef) })), children] }));
72
72
  }
@@ -115,10 +115,10 @@ const fixtures_1 = require("../../testing/fixtures");
115
115
  // First option should be selected
116
116
  yield (0, fixtures_1.expect)(page.getByTestId("text")).toHaveText("Selected values: 1");
117
117
  // Click another option
118
- yield driver.click();
119
118
  yield driver.selectLabel("Diana Prince");
120
119
  // Both options should be selected
121
120
  yield (0, fixtures_1.expect)(page.getByTestId("text")).toHaveText("Selected values: 1,3");
121
+ yield driver.click();
122
122
  // Both selected options should be visible as badges
123
123
  yield (0, fixtures_1.expect)(page.getByText("Bruce Wayne")).toBeVisible();
124
124
  yield (0, fixtures_1.expect)(page.getByText("Diana Prince")).toBeVisible();
@@ -162,7 +162,16 @@ exports.AutoComplete = (0, react_1.forwardRef)(function AutoComplete(_a, forward
162
162
  onDidChange(newValue);
163
163
  }, [multi, updateState, onDidChange]);
164
164
  const onOptionAdd = (0, react_1.useCallback)((option) => {
165
- setOptions((prev) => new Set(prev).add(option));
165
+ setOptions((prev) => {
166
+ const newSet = new Set(prev);
167
+ // Remove old version if exists, then add the new one to ensure updates
168
+ const existing = Array.from(prev).find((opt) => opt.value === option.value);
169
+ if (existing) {
170
+ newSet.delete(existing);
171
+ }
172
+ newSet.add(option);
173
+ return newSet;
174
+ });
166
175
  }, []);
167
176
  const onOptionRemove = (0, react_1.useCallback)((option) => {
168
177
  setOptions((prev) => {
@@ -363,7 +372,17 @@ exports.AutoComplete = (0, react_1.forwardRef)(function AutoComplete(_a, forward
363
372
  }, modal: false, children: [(0, jsx_runtime_1.jsx)(react_popover_1.PopoverTrigger, { asChild: true, ref: setReferenceElement, children: (0, jsx_runtime_1.jsxs)("div", { ref: forwardedRef, style: style, "data-part-id": PART_LIST_WRAPPER, className: (0, classnames_1.default)(className, AutoComplete_module_scss_1.default.badgeListWrapper, AutoComplete_module_scss_1.default[validationStatus], {
364
373
  [AutoComplete_module_scss_1.default.disabled]: !enabled,
365
374
  [AutoComplete_module_scss_1.default.focused]: isFocused,
366
- }), "aria-expanded": open, children: [Array.isArray(selectedValue) && selectedValue.length > 0 && ((0, jsx_runtime_1.jsx)("div", { className: AutoComplete_module_scss_1.default.badgeList, children: selectedValue.map((v, index) => ((0, jsx_runtime_1.jsxs)("span", { className: AutoComplete_module_scss_1.default.badge, children: [v === null || v === void 0 ? void 0 : v.label, !readOnly && ((0, jsx_runtime_1.jsx)(IconNative_1.default, { name: "close", size: "sm", onClick: (event) => {
375
+ }), "aria-expanded": open, onClick: (event) => {
376
+ if (readOnly)
377
+ return;
378
+ // In multi mode, only open the dropdown, don't toggle
379
+ // In single mode, toggle as usual
380
+ if (multi && open) {
381
+ return; // Already open, don't close
382
+ }
383
+ event.stopPropagation();
384
+ setOpen((prev) => !prev);
385
+ }, children: [Array.isArray(selectedValue) && selectedValue.length > 0 && ((0, jsx_runtime_1.jsx)("div", { className: AutoComplete_module_scss_1.default.badgeList, children: selectedValue.map((v, index) => ((0, jsx_runtime_1.jsxs)("span", { className: AutoComplete_module_scss_1.default.badge, children: [v === null || v === void 0 ? void 0 : v.label, !readOnly && ((0, jsx_runtime_1.jsx)(IconNative_1.default, { name: "close", size: "sm", onClick: (event) => {
367
386
  event.stopPropagation();
368
387
  toggleOption(v.value);
369
388
  } }))] }, index))) })), (0, jsx_runtime_1.jsxs)("div", { className: AutoComplete_module_scss_1.default.inputWrapper, children: [(0, jsx_runtime_1.jsx)("input", Object.assign({}, rest, { role: "combobox", id: id, ref: inputRef, onFocus: (ev) => {
@@ -418,7 +437,7 @@ exports.AutoComplete = (0, react_1.forwardRef)(function AutoComplete(_a, forward
418
437
  }) })] }) }) }))] }), children] }) }) }));
419
438
  });
420
439
  function CreatableItem({ onNewItem, isHighlighted = false }) {
421
- const { value, options, searchTerm, onChange, setOpen, setSelectedIndex } = (0, AutoCompleteContext_1.useAutoComplete)();
440
+ const { value, options, searchTerm, onChange, setOpen, setSelectedIndex, multi } = (0, AutoCompleteContext_1.useAutoComplete)();
422
441
  const { onOptionAdd } = (0, OptionContext_1.useOption)();
423
442
  if (isOptionsExist(options, [{ value: searchTerm, label: searchTerm }]) ||
424
443
  (Array.isArray(value) && (value === null || value === void 0 ? void 0 : value.find((s) => s === searchTerm))) ||
@@ -430,7 +449,10 @@ function CreatableItem({ onNewItem, isHighlighted = false }) {
430
449
  onOptionAdd(newOption);
431
450
  onNewItem === null || onNewItem === void 0 ? void 0 : onNewItem(searchTerm);
432
451
  onChange(searchTerm);
433
- setOpen(false);
452
+ // Only close dropdown for single select mode
453
+ if (!multi) {
454
+ setOpen(false);
455
+ }
434
456
  };
435
457
  const Item = ((0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)(AutoComplete_module_scss_1.default.autoCompleteOption, {
436
458
  [AutoComplete_module_scss_1.default.highlighted]: isHighlighted,
@@ -456,7 +478,10 @@ function AutoCompleteOption(option) {
456
478
  const handleClick = () => {
457
479
  if (!readOnly && enabled) {
458
480
  onChange(value);
459
- setOpen(false);
481
+ // Only close dropdown for single select mode
482
+ if (!multi) {
483
+ setOpen(false);
484
+ }
460
485
  }
461
486
  };
462
487
  return ((0, jsx_runtime_1.jsx)("div", { id: id, role: "option", "aria-disabled": !enabled, "aria-selected": selected, className: (0, classnames_1.default)(AutoComplete_module_scss_1.default.autoCompleteOption, {
@@ -674,7 +674,7 @@ fixtures_1.test.describe("Edge Cases", () => {
674
674
  const textfieldElement = (yield createFormItemDriver("name2")).input;
675
675
  const textfieldDriver = yield createTextBoxDriver(textfieldElement);
676
676
  yield selectDriver.component.click();
677
- yield page.getByLabel("Public Key").click();
677
+ yield page.getByText("Public Key").click();
678
678
  yield textfieldDriver.field.fill("John");
679
679
  yield formDriver.submitForm();
680
680
  yield fixtures_1.expect.poll(testStateDriver.testState).toEqual(true);
@@ -1242,7 +1242,7 @@ const smartCrudInterceptor = {
1242
1242
  const textfieldElement = (yield createFormItemDriver("name2")).input;
1243
1243
  const textfieldDriver = yield createTextBoxDriver(textfieldElement);
1244
1244
  yield selectDriver.component.click();
1245
- yield page.getByLabel("Public Key").click();
1245
+ yield page.getByText("Public Key").click();
1246
1246
  yield textfieldDriver.field.fill("John");
1247
1247
  yield formDriver.submitForm();
1248
1248
  yield fixtures_1.expect.poll(testStateDriver.testState).toEqual(true);
@@ -10,6 +10,9 @@ var __rest = (this && this.__rest) || function (s, e) {
10
10
  }
11
11
  return t;
12
12
  };
13
+ var __importDefault = (this && this.__importDefault) || function (mod) {
14
+ return (mod && mod.__esModule) ? mod : { "default": mod };
15
+ };
13
16
  Object.defineProperty(exports, "__esModule", { value: true });
14
17
  exports.FormItem = exports.defaultProps = void 0;
15
18
  exports.CustomFormItem = CustomFormItem;
@@ -38,6 +41,7 @@ const HelperText_1 = require("./HelperText");
38
41
  const ItemsNative_1 = require("../Items/ItemsNative");
39
42
  const constants_1 = require("../../components-core/constants");
40
43
  const hooks_1 = require("../../components-core/utils/hooks");
44
+ const FormItem_module_scss_1 = __importDefault(require("./FormItem.module.scss"));
41
45
  const DEFAULT_LABEL_POSITIONS = {
42
46
  checkbox: "end",
43
47
  };
@@ -221,7 +225,7 @@ exports.FormItem = (0, react_1.memo)(function FormItem(_a) {
221
225
  if (!isInsideForm) {
222
226
  throw new Error("FormItem must be used inside a Form");
223
227
  }
224
- return ((0, jsx_runtime_1.jsx)(ItemWithLabel_1.ItemWithLabel, { labelPosition: labelPositionValue, label: label, labelWidth: labelWidthValue, labelBreak: labelBreakValue, enabled: isEnabled, required: validations.required, validationInProgress: validationResult === null || validationResult === void 0 ? void 0 : validationResult.partial, onFocus: onFocus, onBlur: onBlur, style: style, className: className, validationResult: (0, jsx_runtime_1.jsx)("div", { ref: animateContainerRef, children: isHelperTextShown &&
228
+ return ((0, jsx_runtime_1.jsx)(ItemWithLabel_1.ItemWithLabel, { labelPosition: labelPositionValue, label: label, labelWidth: labelWidthValue, labelBreak: labelBreakValue, enabled: isEnabled, required: validations.required, validationInProgress: validationResult === null || validationResult === void 0 ? void 0 : validationResult.partial, onFocus: onFocus, onBlur: onBlur, style: style, className: className, validationResult: (0, jsx_runtime_1.jsx)("div", { ref: animateContainerRef, className: FormItem_module_scss_1.default.helperTextContainer, children: isHelperTextShown &&
225
229
  (validationResult === null || validationResult === void 0 ? void 0 : validationResult.validations.map((singleValidation, i) => ((0, jsx_runtime_1.jsxs)(react_1.Fragment, { children: [singleValidation.isValid && !!singleValidation.validMessage && ((0, jsx_runtime_1.jsx)(HelperText_1.HelperText, { text: singleValidation.validMessage, status: "valid", style: { opacity: singleValidation.stale ? 0.5 : undefined } })), !singleValidation.isValid && !!singleValidation.invalidMessage && ((0, jsx_runtime_1.jsx)(HelperText_1.HelperText, { text: singleValidation.invalidMessage, status: singleValidation.severity, style: { opacity: singleValidation.stale ? 0.5 : undefined } }))] }, i)))) }), children: formControl }));
226
230
  });
227
231
  function CustomFormItem({ renderChild, node, bindTo, }) {
@@ -23,6 +23,37 @@ const HeadingNative_1 = require("./HeadingNative");
23
23
  const extractParam_1 = require("../../components-core/utils/extractParam");
24
24
  const metadata_helpers_1 = require("../metadata-helpers");
25
25
  const COMP = "Heading";
26
+ /**
27
+ * Normalizes the level value to a valid HeadingLevel (h1-h6).
28
+ * Accepts: 1-6, "1"-"6", "h1"-"h6", "H1"-"H6"
29
+ * Returns "h1" for any invalid value.
30
+ */
31
+ function normalizeHeadingLevel(value) {
32
+ if (value === null || value === undefined) {
33
+ return "h1";
34
+ }
35
+ // Handle numeric values (1-6)
36
+ if (typeof value === "number") {
37
+ if (value >= 1 && value <= 6) {
38
+ return `h${value}`;
39
+ }
40
+ return "h1";
41
+ }
42
+ // Handle string values
43
+ if (typeof value === "string") {
44
+ const trimmed = value.trim().toLowerCase();
45
+ // Handle "h1"-"h6" (case insensitive)
46
+ if (/^h[1-6]$/.test(trimmed)) {
47
+ return trimmed;
48
+ }
49
+ // Handle "1"-"6"
50
+ if (/^[1-6]$/.test(trimmed)) {
51
+ return `h${trimmed}`;
52
+ }
53
+ }
54
+ // Default fallback
55
+ return "h1";
56
+ }
26
57
  const VALUE_DESC = (0, metadata_helpers_1.d)(`This property determines the text displayed in the heading. \`${COMP}\` also accepts nested ` +
27
58
  `text instead of specifying the \`value\`. If both \`value\` and a nested text are used, ` +
28
59
  `the \`value\` will be displayed.`);
@@ -68,8 +99,14 @@ exports.HeadingMd = (0, metadata_helpers_1.createMetadata)({
68
99
  props: {
69
100
  value: VALUE_DESC,
70
101
  level: {
71
- description: "This property sets the visual significance (level) of the heading.",
72
- availableValues: ["h1", "h2", "h3", "h4", "h5", "h6"],
102
+ description: "This property sets the visual significance (level) of the heading. " +
103
+ "Accepts multiple formats: `h1`-`h6`, `H1`-`H6`, or `1`-`6`." +
104
+ "Invalid values default to `h1`.",
105
+ availableValues: [
106
+ "h1", "h2", "h3", "h4", "h5", "h6",
107
+ "H1", "H2", "H3", "H4", "H5", "H6",
108
+ "1", "2", "3", "4", "5", "6",
109
+ ],
73
110
  defaultValue: HeadingNative_1.defaultProps.level,
74
111
  },
75
112
  maxLines: MAX_LINES_DESC,
@@ -278,11 +315,14 @@ exports.H6Md = (0, metadata_helpers_1.createMetadata)({
278
315
  },
279
316
  });
280
317
  function renderHeading({ node, extractValue, className, level, renderChild, registerComponentApi, }) {
281
- var _a, _b, _c;
282
- const _d = node.props, { maxLines, preserveLinebreaks, ellipses, showAnchor } = _d, restProps = __rest(_d, ["maxLines", "preserveLinebreaks", "ellipses", "showAnchor"]);
318
+ var _a, _b;
319
+ const _c = node.props, { maxLines, preserveLinebreaks, ellipses, showAnchor } = _c, restProps = __rest(_c, ["maxLines", "preserveLinebreaks", "ellipses", "showAnchor"]);
283
320
  delete restProps.level; // Remove level from restProps as it is handled separately
284
321
  const showAnchorValue = extractValue.asOptionalBoolean((_a = node.props) === null || _a === void 0 ? void 0 : _a.showAnchor);
285
- return ((0, jsx_runtime_1.jsx)(HeadingNative_1.Heading, Object.assign({ uid: node.uid, level: ((_b = extractValue.asOptionalString(level)) !== null && _b !== void 0 ? _b : "h1"), maxLines: extractValue.asOptionalNumber(maxLines), preserveLinebreaks: extractValue.asOptionalBoolean(preserveLinebreaks, false), ellipses: extractValue.asOptionalBoolean(ellipses, true), showAnchor: showAnchorValue, className: className, omitFromToc: extractValue.asOptionalBoolean((_c = node.props) === null || _c === void 0 ? void 0 : _c.omitFromToc), registerComponentApi: registerComponentApi }, (0, extractParam_1.resolveAndCleanProps)(restProps, extractValue), { children: extractValue.asDisplayText(node.props.value) || renderChild(node.children) })));
322
+ // Extract and normalize the level value
323
+ const extractedLevel = extractValue(level);
324
+ const normalizedLevel = normalizeHeadingLevel(extractedLevel);
325
+ return ((0, jsx_runtime_1.jsx)(HeadingNative_1.Heading, Object.assign({ uid: node.uid, level: normalizedLevel, maxLines: extractValue.asOptionalNumber(maxLines), preserveLinebreaks: extractValue.asOptionalBoolean(preserveLinebreaks, false), ellipses: extractValue.asOptionalBoolean(ellipses, true), showAnchor: showAnchorValue, className: className, omitFromToc: extractValue.asOptionalBoolean((_b = node.props) === null || _b === void 0 ? void 0 : _b.omitFromToc), registerComponentApi: registerComponentApi }, (0, extractParam_1.resolveAndCleanProps)(restProps, extractValue), { children: extractValue.asDisplayText(node.props.value) || renderChild(node.children) })));
286
326
  }
287
327
  exports.headingComponentRenderer = (0, renderers_1.createComponentRenderer)(COMP, exports.HeadingMd, ({ node, extractValue, className, renderChild, registerComponentApi }) => {
288
328
  return renderHeading({
@@ -336,6 +336,88 @@ fixtures_1.test.describe("Basic Functionality", () => {
336
336
  const driver = yield createHeadingDriver();
337
337
  yield (0, fixtures_1.expect)(driver.component).toHaveText("Child content text");
338
338
  }));
339
+ fixtures_1.test.describe("level property accepts numeric values", () => {
340
+ [1, 2, 3, 4, 5, 6].forEach((level) => {
341
+ (0, fixtures_1.test)(`level="{${level}}" renders as h${level}`, (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, createHeadingDriver }) {
342
+ yield initTestBed(`<Heading level="{${level}}">Numeric Level ${level}</Heading>`);
343
+ const driver = yield createHeadingDriver();
344
+ yield (0, fixtures_1.expect)(driver.component).toBeVisible();
345
+ const tagName = yield driver.getComponentTagName();
346
+ (0, fixtures_1.expect)(tagName.toLowerCase()).toBe(`h${level}`);
347
+ yield (0, fixtures_1.expect)(driver.component).toHaveText(`Numeric Level ${level}`);
348
+ }));
349
+ });
350
+ });
351
+ fixtures_1.test.describe("level property accepts string numeric values", () => {
352
+ ["1", "2", "3", "4", "5", "6"].forEach((level) => {
353
+ (0, fixtures_1.test)(`level="${level}" renders as h${level}`, (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, createHeadingDriver }) {
354
+ yield initTestBed(`<Heading level="${level}">String Level ${level}</Heading>`);
355
+ const driver = yield createHeadingDriver();
356
+ yield (0, fixtures_1.expect)(driver.component).toBeVisible();
357
+ const tagName = yield driver.getComponentTagName();
358
+ (0, fixtures_1.expect)(tagName.toLowerCase()).toBe(`h${level}`);
359
+ yield (0, fixtures_1.expect)(driver.component).toHaveText(`String Level ${level}`);
360
+ }));
361
+ });
362
+ });
363
+ fixtures_1.test.describe("level property accepts uppercase H format", () => {
364
+ ["H1", "H2", "H3", "H4", "H5", "H6"].forEach((level) => {
365
+ (0, fixtures_1.test)(`level="${level}" renders as ${level.toLowerCase()}`, (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, createHeadingDriver }) {
366
+ yield initTestBed(`<Heading level="${level}">Uppercase Level ${level}</Heading>`);
367
+ const driver = yield createHeadingDriver();
368
+ yield (0, fixtures_1.expect)(driver.component).toBeVisible();
369
+ const tagName = yield driver.getComponentTagName();
370
+ (0, fixtures_1.expect)(tagName.toLowerCase()).toBe(level.toLowerCase());
371
+ yield (0, fixtures_1.expect)(driver.component).toHaveText(`Uppercase Level ${level}`);
372
+ }));
373
+ });
374
+ });
375
+ fixtures_1.test.describe("level property handles invalid values", () => {
376
+ [
377
+ { value: '"invalid"', label: "invalid string" },
378
+ { value: '"{0}"', label: "zero" },
379
+ { value: '"{7}"', label: "out of range number (7)" },
380
+ { value: '"{-1}"', label: "negative number" },
381
+ { value: '"{999}"', label: "large number" },
382
+ { value: '"h7"', label: "invalid h-format (h7)" },
383
+ { value: '"h0"', label: "invalid h-format (h0)" },
384
+ { value: '"7"', label: "out of range string (7)" },
385
+ { value: '"{null}"', label: "null" },
386
+ { value: '"{undefined}"', label: "undefined" },
387
+ { value: '"{{}}"', label: "empty object" },
388
+ { value: '"{[]}"', label: "empty array" },
389
+ ].forEach(({ value, label }) => {
390
+ (0, fixtures_1.test)(`level=${value} (${label}) defaults to h1`, (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, createHeadingDriver }) {
391
+ yield initTestBed(`<Heading level=${value}>Invalid Level Fallback</Heading>`);
392
+ const driver = yield createHeadingDriver();
393
+ yield (0, fixtures_1.expect)(driver.component).toBeVisible();
394
+ const tagName = yield driver.getComponentTagName();
395
+ (0, fixtures_1.expect)(tagName.toLowerCase()).toBe("h1");
396
+ yield (0, fixtures_1.expect)(driver.component).toHaveText("Invalid Level Fallback");
397
+ }));
398
+ });
399
+ });
400
+ (0, fixtures_1.test)("level property with mixed case string formats", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, createHeadingDriver }) {
401
+ const testCases = [
402
+ { input: "H3", expected: "h3" },
403
+ { input: "h3", expected: "h3" },
404
+ { input: "h3", expected: "h3" },
405
+ ];
406
+ for (const { input, expected } of testCases) {
407
+ yield initTestBed(`<Heading level="${input}">Mixed Case ${input}</Heading>`);
408
+ const driver = yield createHeadingDriver();
409
+ yield (0, fixtures_1.expect)(driver.component).toBeVisible();
410
+ const tagName = yield driver.getComponentTagName();
411
+ (0, fixtures_1.expect)(tagName.toLowerCase()).toBe(expected);
412
+ }
413
+ }));
414
+ (0, fixtures_1.test)("level property handles whitespace in string values", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, createHeadingDriver }) {
415
+ yield initTestBed(`<Heading level=" h2 ">Whitespace Level</Heading>`);
416
+ const driver = yield createHeadingDriver();
417
+ yield (0, fixtures_1.expect)(driver.component).toBeVisible();
418
+ const tagName = yield driver.getComponentTagName();
419
+ (0, fixtures_1.expect)(tagName.toLowerCase()).toBe("h2");
420
+ }));
339
421
  });
340
422
  // =============================================================================
341
423
  // ACCESSIBILITY TESTS (REQUIRED)
@@ -70,15 +70,13 @@ exports.ModalDialogMd = (0, metadata_helpers_1.createMetadata)({
70
70
  themeVars: (0, themeVars_1.parseScssVar)(ModalDialog_module_scss_1.default.themeVars),
71
71
  defaultThemeVars: Object.assign(Object.assign({}, (0, base_utils_1.paddingSubject)(COMP, { all: "$space-7" })), { [`backgroundColor-${COMP}`]: "$backgroundColor-primary", [`backgroundColor-overlay-${COMP}`]: "$backgroundColor-overlay", [`textColor-${COMP}`]: "$textColor-primary", [`borderRadius-${COMP}`]: "$borderRadius", [`fontFamily-${COMP}`]: "$fontFamily", [`maxWidth-${COMP}`]: "450px", [`marginBottom-title-${COMP}`]: "0" }),
72
72
  });
73
- exports.modalViewComponentRenderer = (0, renderers_1.createComponentRenderer)(COMP, exports.ModalDialogMd, ({ node, extractValue, className, renderChild, lookupEventHandler, registerComponentApi, layoutContext, }) => {
73
+ exports.modalViewComponentRenderer = (0, renderers_1.createComponentRenderer)(COMP, exports.ModalDialogMd, ({ node, contextVars, extractValue, className, renderChild, lookupEventHandler, registerComponentApi, layoutContext, }) => {
74
74
  var _a, _b;
75
- // gigantic hack: If the ModalDialog is not inside a ModalDialogFrame, wrap it in one
76
- // we do this through the layout context, render it through another render loop with the extra $param context var
77
- // (note the layoutContext and node on the MemoizedItem)
78
- // one solution would be to have a renderChild that can take a contextVars argument
75
+ // --- If the ModalDialog is not inside a ModalDialogFrame, wrap it in one.
79
76
  if (!(layoutContext === null || layoutContext === void 0 ? void 0 : layoutContext._insideModalFrame)) {
77
+ // --- Context variables are now directly available via contextVars parameter
80
78
  return ((0, jsx_runtime_1.jsx)(ModalDialogNative_1.ModalDialogFrame, { isInitiallyOpen: extractValue(node.when) !== undefined, registerComponentApi: registerComponentApi, onClose: lookupEventHandler("close"), onOpen: lookupEventHandler("open"), renderDialog: ({ openParams, ref }) => {
81
- return ((0, jsx_runtime_1.jsx)(container_helpers_1.MemoizedItem, { node: node, renderChild: renderChild, layoutContext: { _insideModalFrame: true }, contextVars: { $param: openParams === null || openParams === void 0 ? void 0 : openParams[0], $params: openParams } }));
79
+ return ((0, jsx_runtime_1.jsx)(container_helpers_1.MemoizedItem, { node: node, renderChild: renderChild, layoutContext: { _insideModalFrame: true }, contextVars: Object.assign(Object.assign({}, contextVars), { $param: openParams === null || openParams === void 0 ? void 0 : openParams[0], $params: openParams }) }));
82
80
  } }));
83
81
  }
84
82
  return ((0, jsx_runtime_1.jsx)(ModalDialogNative_1.ModalDialog, { className: className, fullScreen: extractValue.asOptionalBoolean((_a = node.props) === null || _a === void 0 ? void 0 : _a.fullScreen), title: extractValue((_b = node.props) === null || _b === void 0 ? void 0 : _b.title), closeButtonVisible: extractValue.asOptionalBoolean(node.props.closeButtonVisible), externalAnimation: extractValue.asOptionalBoolean(node.props.externalAnimation), children: renderChild(node.children, { type: "Stack" }) }));
@@ -63,6 +63,25 @@ fixtures_1.test.describe("Open/Close", () => {
63
63
  yield page.getByTestId("button").click();
64
64
  yield (0, fixtures_1.expect)(page.getByTestId("modal").getByRole("heading")).toHaveText("PARAM_VALUE");
65
65
  }));
66
+ (0, fixtures_1.test)("Preserves $item context variable from Table Column", (_a) => __awaiter(void 0, [_a], void 0, function* ({ page, initTestBed }) {
67
+ yield initTestBed(`
68
+ <Table data='{[
69
+ {id: 1, company: "Acme Corp", order: 1},
70
+ ]}'>
71
+ <Column>
72
+ <ModalDialog id="dialog" testId="dialog" title="{$item.company}">
73
+ <Text testId="modal-text">{JSON.stringify($item)}</Text>
74
+ </ModalDialog>
75
+ <Button testId="btn-{$itemIndex}" onClick="dialog.open()">{$item.company}</Button>
76
+ </Column>
77
+ </Table>
78
+ `);
79
+ // Test first row
80
+ yield page.getByTestId("btn-0").click();
81
+ const modal = page.getByTestId("dialog");
82
+ yield (0, fixtures_1.expect)(modal).toBeVisible();
83
+ yield (0, fixtures_1.expect)(modal).toContainText("Acme Corp");
84
+ }));
66
85
  (0, fixtures_1.test)("Declarative open/close", (_a) => __awaiter(void 0, [_a], void 0, function* ({ page, initTestBed }) {
67
86
  yield initTestBed(`
68
87
  <Fragment var.isOpen="{false}">