xmlui 0.10.21 → 0.10.22
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.
- package/dist/lib/{index-BiS4wEuu.mjs → index-BaUZuvtZ.mjs} +736 -595
- package/dist/lib/index.css +1 -1
- package/dist/lib/{initMock-CB_cMi6U.mjs → initMock-BVLjWJxZ.mjs} +1 -1
- package/dist/lib/xmlui.d.ts +32 -0
- package/dist/lib/xmlui.mjs +1 -1
- package/dist/metadata/{collectedComponentMetadata-MFUg6aSX.mjs → collectedComponentMetadata-MZbCI1Ki.mjs} +734 -593
- package/dist/metadata/{initMock-Dw9wrVkQ.mjs → initMock-BkgwDrCY.mjs} +1 -1
- package/dist/metadata/style.css +1 -1
- package/dist/metadata/xmlui-metadata.mjs +1 -1
- package/dist/metadata/xmlui-metadata.umd.js +3 -3
- package/dist/scripts/package.json +1 -1
- package/dist/scripts/src/components/ComponentProvider.js +30 -2
- package/dist/scripts/src/components/Tabs/TabContext.js +26 -17
- package/dist/scripts/src/components/Tabs/TabItemNative.js +7 -2
- package/dist/scripts/src/components/Tabs/Tabs.js +22 -3
- package/dist/scripts/src/components/Tabs/Tabs.spec.js +362 -0
- package/dist/scripts/src/components/Tabs/TabsNative.js +28 -4
- package/dist/scripts/src/components-core/rendering/ComponentAdapter.js +2 -3
- package/dist/standalone/xmlui-standalone.es.d.ts +34 -0
- package/dist/standalone/xmlui-standalone.umd.js +14 -14
- package/package.json +1 -1
|
@@ -74,6 +74,7 @@ const FileDownloadAction_1 = require("../components-core/action/FileDownloadActi
|
|
|
74
74
|
const FileUploadAction_1 = require("../components-core/action/FileUploadAction");
|
|
75
75
|
const NavigateAction_1 = require("../components-core/action/NavigateAction");
|
|
76
76
|
const TimedAction_1 = require("../components-core/action/TimedAction");
|
|
77
|
+
const CoreBehaviors_1 = require("../components-core/behaviors/CoreBehaviors");
|
|
77
78
|
const ApiLoader_1 = require("../components-core/loader/ApiLoader");
|
|
78
79
|
const ExternalDataLoader_1 = require("../components-core/loader/ExternalDataLoader");
|
|
79
80
|
const MockLoaderRenderer_1 = require("../components-core/loader/MockLoaderRenderer");
|
|
@@ -149,7 +150,7 @@ class ComponentRegistry {
|
|
|
149
150
|
* about component registrations
|
|
150
151
|
*/
|
|
151
152
|
constructor(contributes = {}, extensionManager) {
|
|
152
|
-
var _a, _b, _c;
|
|
153
|
+
var _a, _b, _c, _d;
|
|
153
154
|
this.extensionManager = extensionManager;
|
|
154
155
|
// --- The pool of available components
|
|
155
156
|
this.pool = new Map(); // namespace -> name -> ComponentRegistryEntry;
|
|
@@ -162,6 +163,8 @@ class ComponentRegistry {
|
|
|
162
163
|
this.actionFns = new Map();
|
|
163
164
|
// --- The pool of available loader renderers
|
|
164
165
|
this.loaders = new Map();
|
|
166
|
+
// --- The pool of available behaviors
|
|
167
|
+
this.behaviors = [];
|
|
165
168
|
this.extensionRegistered = (extension) => {
|
|
166
169
|
var _a;
|
|
167
170
|
(_a = extension.components) === null || _a === void 0 ? void 0 : _a.forEach((c) => {
|
|
@@ -656,7 +659,14 @@ class ComponentRegistry {
|
|
|
656
659
|
this.registerLoaderRenderer(ExternalDataLoader_1.externalDataLoaderRenderer);
|
|
657
660
|
this.registerLoaderRenderer(MockLoaderRenderer_1.mockLoaderRenderer);
|
|
658
661
|
this.registerLoaderRenderer(DataLoader_1.dataLoaderRenderer);
|
|
659
|
-
|
|
662
|
+
this.registerBehavior(CoreBehaviors_1.tooltipBehavior);
|
|
663
|
+
this.registerBehavior(CoreBehaviors_1.animationBehavior);
|
|
664
|
+
this.registerBehavior(CoreBehaviors_1.labelBehavior);
|
|
665
|
+
// Register external behaviors from contributes
|
|
666
|
+
(_c = contributes.behaviors) === null || _c === void 0 ? void 0 : _c.forEach((behavior) => {
|
|
667
|
+
this.registerBehavior(behavior);
|
|
668
|
+
});
|
|
669
|
+
(_d = this.extensionManager) === null || _d === void 0 ? void 0 : _d.subscribeToRegistrations(this.extensionRegistered);
|
|
660
670
|
}
|
|
661
671
|
/**
|
|
662
672
|
* All theme variables used by the registered components.
|
|
@@ -761,6 +771,24 @@ class ComponentRegistry {
|
|
|
761
771
|
registerActionFn({ actionName: functionName, actionFn }) {
|
|
762
772
|
this.actionFns.set(functionName, actionFn);
|
|
763
773
|
}
|
|
774
|
+
// --- Registers a behavior
|
|
775
|
+
registerBehavior(behavior, location = "after", position) {
|
|
776
|
+
// If position is specified, insert relative to that behavior
|
|
777
|
+
if (position) {
|
|
778
|
+
const targetIndex = this.behaviors.findIndex(b => b.name === position);
|
|
779
|
+
if (targetIndex !== -1) {
|
|
780
|
+
const insertIndex = location === "before" ? targetIndex : targetIndex + 1;
|
|
781
|
+
this.behaviors.splice(insertIndex, 0, behavior);
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
// Default: append to the end
|
|
786
|
+
this.behaviors.push(behavior);
|
|
787
|
+
}
|
|
788
|
+
// --- Returns all registered behaviors
|
|
789
|
+
getBehaviors() {
|
|
790
|
+
return this.behaviors;
|
|
791
|
+
}
|
|
764
792
|
}
|
|
765
793
|
exports.ComponentRegistry = ComponentRegistry;
|
|
766
794
|
/**
|
|
@@ -13,32 +13,41 @@ exports.TabContext = (0, react_1.createContext)({
|
|
|
13
13
|
register: (tabItem) => { },
|
|
14
14
|
unRegister: (innerId) => { },
|
|
15
15
|
activeTabId: "",
|
|
16
|
+
getTabItems: () => [],
|
|
16
17
|
});
|
|
17
18
|
function useTabContextValue() {
|
|
18
19
|
const [tabItems, setTabItems] = (0, react_1.useState)(constants_1.EMPTY_ARRAY);
|
|
20
|
+
const tabItemsRef = (0, react_1.useRef)(constants_1.EMPTY_ARRAY);
|
|
21
|
+
tabItemsRef.current = tabItems;
|
|
19
22
|
const [activeTabId, setActiveTabId] = (0, react_1.useState)("");
|
|
23
|
+
const register = (0, react_1.useCallback)((tabItem) => {
|
|
24
|
+
setTabItems((0, immer_1.default)((draft) => {
|
|
25
|
+
const existing = draft.findIndex((col) => col.innerId === tabItem.innerId);
|
|
26
|
+
if (existing < 0) {
|
|
27
|
+
draft.push(tabItem);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
draft[existing] = tabItem;
|
|
31
|
+
}
|
|
32
|
+
}));
|
|
33
|
+
}, []);
|
|
34
|
+
const unRegister = (0, react_1.useCallback)((innerId) => {
|
|
35
|
+
setTabItems((0, immer_1.default)((draft) => {
|
|
36
|
+
return draft.filter((col) => col.innerId !== innerId);
|
|
37
|
+
}));
|
|
38
|
+
}, []);
|
|
39
|
+
const getTabItems = (0, react_1.useCallback)(() => {
|
|
40
|
+
return tabItemsRef.current;
|
|
41
|
+
}, []);
|
|
20
42
|
const tabContextValue = (0, react_1.useMemo)(() => {
|
|
21
43
|
return {
|
|
22
|
-
register
|
|
23
|
-
|
|
24
|
-
const existing = draft.findIndex((col) => col.innerId === tabItem.innerId);
|
|
25
|
-
if (existing < 0) {
|
|
26
|
-
draft.push(tabItem);
|
|
27
|
-
}
|
|
28
|
-
else {
|
|
29
|
-
draft[existing] = tabItem;
|
|
30
|
-
}
|
|
31
|
-
}));
|
|
32
|
-
},
|
|
33
|
-
unRegister: (innerId) => {
|
|
34
|
-
setTabItems((0, immer_1.default)((draft) => {
|
|
35
|
-
return draft.filter((col) => col.innerId !== innerId);
|
|
36
|
-
}));
|
|
37
|
-
},
|
|
44
|
+
register,
|
|
45
|
+
unRegister,
|
|
38
46
|
activeTabId,
|
|
39
47
|
setActiveTabId,
|
|
48
|
+
getTabItems,
|
|
40
49
|
};
|
|
41
|
-
}, [activeTabId]);
|
|
50
|
+
}, [register, unRegister, activeTabId, getTabItems]);
|
|
42
51
|
return {
|
|
43
52
|
tabItems,
|
|
44
53
|
tabContextValue,
|
|
@@ -21,9 +21,10 @@ const react_tabs_1 = require("@radix-ui/react-tabs");
|
|
|
21
21
|
const Tabs_module_scss_1 = __importDefault(require("../Tabs/Tabs.module.scss"));
|
|
22
22
|
const TabContext_1 = require("./TabContext");
|
|
23
23
|
exports.TabItemComponent = (0, react_2.forwardRef)(function TabItemComponent(_a, forwardedRef) {
|
|
24
|
+
var _b;
|
|
24
25
|
var { children, label, headerRenderer, style, id, activated } = _a, rest = __rest(_a, ["children", "label", "headerRenderer", "style", "id", "activated"]);
|
|
25
26
|
const innerId = (0, react_2.useId)();
|
|
26
|
-
const { register, unRegister, activeTabId } = (0, TabContext_1.useTabContext)();
|
|
27
|
+
const { register, unRegister, activeTabId, getTabItems } = (0, TabContext_1.useTabContext)();
|
|
27
28
|
(0, react_2.useEffect)(() => {
|
|
28
29
|
register({
|
|
29
30
|
label,
|
|
@@ -44,5 +45,9 @@ exports.TabItemComponent = (0, react_2.forwardRef)(function TabItemComponent(_a,
|
|
|
44
45
|
}, [activeTabId, innerId, activated]);
|
|
45
46
|
if (activeTabId !== innerId)
|
|
46
47
|
return null;
|
|
47
|
-
|
|
48
|
+
// Find the index of this tab for ordering
|
|
49
|
+
const tabItems = getTabItems();
|
|
50
|
+
const tabIndex = (_b = tabItems === null || tabItems === void 0 ? void 0 : tabItems.findIndex(item => item.innerId === innerId)) !== null && _b !== void 0 ? _b : 0;
|
|
51
|
+
const contentOrder = tabIndex * 2 + 1;
|
|
52
|
+
return ((0, react_1.createElement)(react_tabs_1.Content, Object.assign({}, rest, { key: innerId, value: innerId, className: Tabs_module_scss_1.default.tabsContent, ref: forwardedRef, style: Object.assign(Object.assign({}, style), { order: contentOrder }) }), children));
|
|
48
53
|
});
|
|
@@ -24,11 +24,30 @@ exports.TabsMd = (0, metadata_helpers_1.createMetadata)({
|
|
|
24
24
|
orientation: {
|
|
25
25
|
description: `This property indicates the orientation of the component. In horizontal orientation, ` +
|
|
26
26
|
`the tab sections are laid out on the left side of the content panel, while in vertical ` +
|
|
27
|
-
`orientation, the buttons are at the top
|
|
27
|
+
`orientation, the buttons are at the top. Note: This property is ignored when ` +
|
|
28
|
+
`accordionView is set to true.`,
|
|
28
29
|
availableValues: ["horizontal", "vertical"],
|
|
29
30
|
defaultValue: TabsNative_1.defaultProps.orientation,
|
|
30
31
|
valueType: "string",
|
|
31
32
|
},
|
|
33
|
+
tabAlignment: {
|
|
34
|
+
description: `This property controls how tabs are aligned within the tab header container in ` +
|
|
35
|
+
`horizontal orientation. Use 'start' to align tabs to the left, 'end' to align to the ` +
|
|
36
|
+
`right, 'center' to center the tabs, and 'stretch' to make tabs fill the full width ` +
|
|
37
|
+
`of the header. Note: This property is ignored when orientation is set to 'vertical' ` +
|
|
38
|
+
`or when accordionView is enabled.`,
|
|
39
|
+
availableValues: ["start", "end", "center", "stretch"],
|
|
40
|
+
defaultValue: TabsNative_1.defaultProps.tabAlignment,
|
|
41
|
+
valueType: "string",
|
|
42
|
+
},
|
|
43
|
+
accordionView: {
|
|
44
|
+
description: `When enabled, displays tabs in an accordion-like view where tab headers are stacked ` +
|
|
45
|
+
`vertically and only the active tab's content is visible. Each tab header remains visible ` +
|
|
46
|
+
`and clicking a header opens its content while closing others. When enabled, the ` +
|
|
47
|
+
`orientation property is ignored.`,
|
|
48
|
+
defaultValue: TabsNative_1.defaultProps.accordionView,
|
|
49
|
+
valueType: "boolean",
|
|
50
|
+
},
|
|
32
51
|
headerTemplate: Object.assign({}, (0, metadata_helpers_1.dComponent)(`This property declares the template for the clickable tab area.`)),
|
|
33
52
|
},
|
|
34
53
|
events: {
|
|
@@ -67,10 +86,10 @@ exports.TabsMd = (0, metadata_helpers_1.createMetadata)({
|
|
|
67
86
|
},
|
|
68
87
|
});
|
|
69
88
|
exports.tabsComponentRenderer = (0, renderers_1.createComponentRenderer)(COMP, exports.TabsMd, ({ extractValue, node, renderChild, className, registerComponentApi, lookupEventHandler }) => {
|
|
70
|
-
var _a, _b, _c;
|
|
89
|
+
var _a, _b, _c, _d, _e;
|
|
71
90
|
return ((0, jsx_runtime_1.jsx)(TabsNative_1.Tabs, { id: node === null || node === void 0 ? void 0 : node.uid, className: className, headerRenderer: ((_a = node === null || node === void 0 ? void 0 : node.props) === null || _a === void 0 ? void 0 : _a.headerTemplate)
|
|
72
91
|
? (item) => ((0, jsx_runtime_1.jsx)(container_helpers_1.MemoizedItem, { node: node.props.headerTemplate, itemKey: "$header", contextVars: {
|
|
73
92
|
$header: item,
|
|
74
93
|
}, renderChild: renderChild }))
|
|
75
|
-
: undefined, activeTab: extractValue((_b = node.props) === null || _b === void 0 ? void 0 : _b.activeTab), orientation: extractValue((_c = node.props) === null || _c === void 0 ? void 0 : _c.orientation), onDidChange: lookupEventHandler("didChange"), registerComponentApi: registerComponentApi, children: renderChild(node.children) }));
|
|
94
|
+
: undefined, activeTab: extractValue((_b = node.props) === null || _b === void 0 ? void 0 : _b.activeTab), orientation: extractValue((_c = node.props) === null || _c === void 0 ? void 0 : _c.orientation), tabAlignment: extractValue((_d = node.props) === null || _d === void 0 ? void 0 : _d.tabAlignment), accordionView: extractValue((_e = node.props) === null || _e === void 0 ? void 0 : _e.accordionView), onDidChange: lookupEventHandler("didChange"), registerComponentApi: registerComponentApi, children: renderChild(node.children) }));
|
|
76
95
|
});
|
|
@@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const component_test_helpers_1 = require("../../testing/component-test-helpers");
|
|
12
13
|
const fixtures_1 = require("../../testing/fixtures");
|
|
13
14
|
// =============================================================================
|
|
14
15
|
// SMOKE TESTS
|
|
@@ -105,6 +106,367 @@ fixtures_1.test.describe("basic functionality", () => {
|
|
|
105
106
|
const tabsRoot = page.getByTestId("vertical-tabs");
|
|
106
107
|
yield (0, fixtures_1.expect)(tabsRoot).toHaveAttribute("data-orientation", "vertical");
|
|
107
108
|
}));
|
|
109
|
+
fixtures_1.test.describe("tabAlignment property", () => {
|
|
110
|
+
(0, fixtures_1.test)("tabAlignment='start' positions tabs at the start of container (horizontal)", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
|
|
111
|
+
yield initTestBed(`
|
|
112
|
+
<Tabs tabAlignment="start" testId="tabs">
|
|
113
|
+
<TabItem label="Tab 1">Content 1</TabItem>
|
|
114
|
+
<TabItem label="Tab 2">Content 2</TabItem>
|
|
115
|
+
<TabItem label="Tab 3">Content 3</TabItem>
|
|
116
|
+
</Tabs>
|
|
117
|
+
`);
|
|
118
|
+
const tabsContainer = page.getByTestId("tabs");
|
|
119
|
+
const tab1 = page.getByRole("tab", { name: "Tab 1" });
|
|
120
|
+
const { left: containerLeft } = yield (0, component_test_helpers_1.getBounds)(tabsContainer);
|
|
121
|
+
const { left: tab1Left } = yield (0, component_test_helpers_1.getBounds)(tab1);
|
|
122
|
+
// Tab should be near the start of the container (within a small margin for padding)
|
|
123
|
+
(0, fixtures_1.expect)(tab1Left - containerLeft).toBeLessThan(50);
|
|
124
|
+
}));
|
|
125
|
+
(0, fixtures_1.test)("tabAlignment='end' positions tabs at the end of container (horizontal)", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
|
|
126
|
+
yield initTestBed(`
|
|
127
|
+
<Tabs tabAlignment="end" testId="tabs">
|
|
128
|
+
<TabItem label="Tab 1">Content 1</TabItem>
|
|
129
|
+
<TabItem label="Tab 2">Content 2</TabItem>
|
|
130
|
+
<TabItem label="Tab 3">Content 3</TabItem>
|
|
131
|
+
</Tabs>
|
|
132
|
+
`);
|
|
133
|
+
const tabsContainer = page.getByTestId("tabs");
|
|
134
|
+
const tab3 = page.getByRole("tab", { name: "Tab 3" });
|
|
135
|
+
const { right: containerRight } = yield (0, component_test_helpers_1.getBounds)(tabsContainer);
|
|
136
|
+
const { right: tab3Right } = yield (0, component_test_helpers_1.getBounds)(tab3);
|
|
137
|
+
// Last tab should be near the end of the container (within a small margin for padding)
|
|
138
|
+
(0, fixtures_1.expect)(containerRight - tab3Right).toBeLessThan(50);
|
|
139
|
+
}));
|
|
140
|
+
(0, fixtures_1.test)("tabAlignment='center' positions tabs in center of container (horizontal)", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
|
|
141
|
+
yield initTestBed(`
|
|
142
|
+
<Tabs tabAlignment="center" testId="tabs" style="width: 800px">
|
|
143
|
+
<TabItem label="Tab 1">Content 1</TabItem>
|
|
144
|
+
<TabItem label="Tab 2">Content 2</TabItem>
|
|
145
|
+
<TabItem label="Tab 3">Content 3</TabItem>
|
|
146
|
+
</Tabs>
|
|
147
|
+
`);
|
|
148
|
+
const tabsContainer = page.getByTestId("tabs");
|
|
149
|
+
const tab1 = page.getByRole("tab", { name: "Tab 1" });
|
|
150
|
+
const tab3 = page.getByRole("tab", { name: "Tab 3" });
|
|
151
|
+
const { left: containerLeft, right: containerRight, width: containerWidth } = yield (0, component_test_helpers_1.getBounds)(tabsContainer);
|
|
152
|
+
const { left: tab1Left } = yield (0, component_test_helpers_1.getBounds)(tab1);
|
|
153
|
+
const { right: tab3Right } = yield (0, component_test_helpers_1.getBounds)(tab3);
|
|
154
|
+
const containerCenter = containerLeft + containerWidth / 2;
|
|
155
|
+
const tabsCenter = tab1Left + (tab3Right - tab1Left) / 2;
|
|
156
|
+
// Tabs should be centered (within a reasonable margin)
|
|
157
|
+
(0, fixtures_1.expect)(Math.abs(tabsCenter - containerCenter)).toBeLessThan(50);
|
|
158
|
+
}));
|
|
159
|
+
(0, fixtures_1.test)("tabAlignment='stretch' makes tabs fill container width (horizontal)", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
|
|
160
|
+
yield initTestBed(`
|
|
161
|
+
<Tabs tabAlignment="stretch" testId="tabs" style="width: 600px">
|
|
162
|
+
<TabItem label="Tab 1">Content 1</TabItem>
|
|
163
|
+
<TabItem label="Tab 2">Content 2</TabItem>
|
|
164
|
+
<TabItem label="Tab 3">Content 3</TabItem>
|
|
165
|
+
</Tabs>
|
|
166
|
+
`);
|
|
167
|
+
const tabsContainer = page.getByTestId("tabs");
|
|
168
|
+
const tab1 = page.getByRole("tab", { name: "Tab 1" });
|
|
169
|
+
const tab3 = page.getByRole("tab", { name: "Tab 3" });
|
|
170
|
+
const { left: containerLeft, right: containerRight } = yield (0, component_test_helpers_1.getBounds)(tabsContainer);
|
|
171
|
+
const { left: tab1Left } = yield (0, component_test_helpers_1.getBounds)(tab1);
|
|
172
|
+
const { right: tab3Right } = yield (0, component_test_helpers_1.getBounds)(tab3);
|
|
173
|
+
// First tab should start near container start
|
|
174
|
+
(0, fixtures_1.expect)(tab1Left - containerLeft).toBeLessThan(50);
|
|
175
|
+
// Last tab should end near container end
|
|
176
|
+
(0, fixtures_1.expect)(containerRight - tab3Right).toBeLessThan(50);
|
|
177
|
+
}));
|
|
178
|
+
(0, fixtures_1.test)("tabAlignment can be dynamically changed", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
|
|
179
|
+
const { testStateDriver } = yield initTestBed(`
|
|
180
|
+
<Fragment>
|
|
181
|
+
<Tabs tabAlignment="{testState ?? 'start'}" testId="tabs" style="width: 700px">
|
|
182
|
+
<TabItem label="Tab 1">Content 1</TabItem>
|
|
183
|
+
<TabItem label="Tab 2">Content 2</TabItem>
|
|
184
|
+
<TabItem label="Tab 3">Content 3</TabItem>
|
|
185
|
+
</Tabs>
|
|
186
|
+
<Button testId="changeBtn" onClick="testState = 'end'" />
|
|
187
|
+
</Fragment>
|
|
188
|
+
`);
|
|
189
|
+
const tabsContainer = page.getByTestId("tabs");
|
|
190
|
+
const tab1 = page.getByRole("tab", { name: "Tab 1" });
|
|
191
|
+
// Initially with 'start' alignment
|
|
192
|
+
const { left: containerLeft } = yield (0, component_test_helpers_1.getBounds)(tabsContainer);
|
|
193
|
+
const { left: initialTab1Left } = yield (0, component_test_helpers_1.getBounds)(tab1);
|
|
194
|
+
(0, fixtures_1.expect)(initialTab1Left - containerLeft).toBeLessThan(50);
|
|
195
|
+
// Change to 'end' alignment
|
|
196
|
+
yield page.getByTestId("changeBtn").click();
|
|
197
|
+
yield fixtures_1.expect.poll(testStateDriver.testState).toEqual("end");
|
|
198
|
+
// After changing to 'end', the last tab should be near the end
|
|
199
|
+
const tab3 = page.getByRole("tab", { name: "Tab 3" });
|
|
200
|
+
const { right: containerRight } = yield (0, component_test_helpers_1.getBounds)(tabsContainer);
|
|
201
|
+
const { right: tab3Right } = yield (0, component_test_helpers_1.getBounds)(tab3);
|
|
202
|
+
(0, fixtures_1.expect)(containerRight - tab3Right).toBeLessThan(50);
|
|
203
|
+
}));
|
|
204
|
+
(0, fixtures_1.test)("tabAlignment works with single tab", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
|
|
205
|
+
yield initTestBed(`
|
|
206
|
+
<Tabs tabAlignment="center" testId="tabs" style="width: 600px">
|
|
207
|
+
<TabItem label="Only Tab">Only Content</TabItem>
|
|
208
|
+
</Tabs>
|
|
209
|
+
`);
|
|
210
|
+
const tab = page.getByRole("tab", { name: "Only Tab" });
|
|
211
|
+
yield (0, fixtures_1.expect)(tab).toBeVisible();
|
|
212
|
+
// Verify the tab renders and is functional
|
|
213
|
+
yield (0, fixtures_1.expect)(page.getByText("Only Content")).toBeVisible();
|
|
214
|
+
}));
|
|
215
|
+
(0, fixtures_1.test)("tabAlignment='stretch' distributes multiple tabs evenly", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
|
|
216
|
+
yield initTestBed(`
|
|
217
|
+
<Tabs tabAlignment="stretch" testId="tabs" style="width: 600px">
|
|
218
|
+
<TabItem label="Tab 1">Content 1</TabItem>
|
|
219
|
+
<TabItem label="Tab 2">Content 2</TabItem>
|
|
220
|
+
<TabItem label="Tab 3">Content 3</TabItem>
|
|
221
|
+
</Tabs>
|
|
222
|
+
`);
|
|
223
|
+
const tab1 = page.getByRole("tab", { name: "Tab 1" });
|
|
224
|
+
const tab2 = page.getByRole("tab", { name: "Tab 2" });
|
|
225
|
+
const tab3 = page.getByRole("tab", { name: "Tab 3" });
|
|
226
|
+
const { width: tab1Width } = yield (0, component_test_helpers_1.getBounds)(tab1);
|
|
227
|
+
const { width: tab2Width } = yield (0, component_test_helpers_1.getBounds)(tab2);
|
|
228
|
+
const { width: tab3Width } = yield (0, component_test_helpers_1.getBounds)(tab3);
|
|
229
|
+
// All tabs should have similar widths when stretched (within 20px tolerance for text differences)
|
|
230
|
+
(0, fixtures_1.expect)(Math.abs(tab1Width - tab2Width)).toBeLessThan(20);
|
|
231
|
+
(0, fixtures_1.expect)(Math.abs(tab2Width - tab3Width)).toBeLessThan(20);
|
|
232
|
+
}));
|
|
233
|
+
(0, fixtures_1.test)("tabAlignment handles null value gracefully", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
|
|
234
|
+
yield initTestBed(`
|
|
235
|
+
<Tabs tabAlignment="{null}" testId="tabs">
|
|
236
|
+
<TabItem label="Tab 1">Content 1</TabItem>
|
|
237
|
+
<TabItem label="Tab 2">Content 2</TabItem>
|
|
238
|
+
</Tabs>
|
|
239
|
+
`);
|
|
240
|
+
// Should fall back to default 'start' alignment
|
|
241
|
+
const tabsContainer = page.getByTestId("tabs");
|
|
242
|
+
const tab1 = page.getByRole("tab", { name: "Tab 1" });
|
|
243
|
+
yield (0, fixtures_1.expect)(tab1).toBeVisible();
|
|
244
|
+
const { left: containerLeft } = yield (0, component_test_helpers_1.getBounds)(tabsContainer);
|
|
245
|
+
const { left: tab1Left } = yield (0, component_test_helpers_1.getBounds)(tab1);
|
|
246
|
+
// Should behave like 'start' alignment
|
|
247
|
+
(0, fixtures_1.expect)(tab1Left - containerLeft).toBeLessThan(50);
|
|
248
|
+
}));
|
|
249
|
+
(0, fixtures_1.test)("tabAlignment handles undefined value gracefully", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
|
|
250
|
+
yield initTestBed(`
|
|
251
|
+
<Tabs tabAlignment="{undefined}" testId="tabs">
|
|
252
|
+
<TabItem label="Tab 1">Content 1</TabItem>
|
|
253
|
+
<TabItem label="Tab 2">Content 2</TabItem>
|
|
254
|
+
</Tabs>
|
|
255
|
+
`);
|
|
256
|
+
// Should fall back to default 'start' alignment
|
|
257
|
+
const tabsContainer = page.getByTestId("tabs");
|
|
258
|
+
const tab1 = page.getByRole("tab", { name: "Tab 1" });
|
|
259
|
+
yield (0, fixtures_1.expect)(tab1).toBeVisible();
|
|
260
|
+
const { left: containerLeft } = yield (0, component_test_helpers_1.getBounds)(tabsContainer);
|
|
261
|
+
const { left: tab1Left } = yield (0, component_test_helpers_1.getBounds)(tab1);
|
|
262
|
+
// Should behave like 'start' alignment
|
|
263
|
+
(0, fixtures_1.expect)(tab1Left - containerLeft).toBeLessThan(50);
|
|
264
|
+
}));
|
|
265
|
+
(0, fixtures_1.test)("tabAlignment maintains functionality when tabs are clicked", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
|
|
266
|
+
yield initTestBed(`
|
|
267
|
+
<Tabs tabAlignment="center" testId="tabs" style="width: 700px">
|
|
268
|
+
<TabItem label="Tab 1">Content 1</TabItem>
|
|
269
|
+
<TabItem label="Tab 2">Content 2</TabItem>
|
|
270
|
+
<TabItem label="Tab 3">Content 3</TabItem>
|
|
271
|
+
</Tabs>
|
|
272
|
+
`);
|
|
273
|
+
// Verify all tabs are visible
|
|
274
|
+
yield (0, fixtures_1.expect)(page.getByRole("tab", { name: "Tab 1" })).toBeVisible();
|
|
275
|
+
yield (0, fixtures_1.expect)(page.getByRole("tab", { name: "Tab 2" })).toBeVisible();
|
|
276
|
+
yield (0, fixtures_1.expect)(page.getByRole("tab", { name: "Tab 3" })).toBeVisible();
|
|
277
|
+
// Click different tabs and verify content changes
|
|
278
|
+
yield page.getByRole("tab", { name: "Tab 2" }).click();
|
|
279
|
+
yield (0, fixtures_1.expect)(page.getByText("Content 2")).toBeVisible();
|
|
280
|
+
yield (0, fixtures_1.expect)(page.getByText("Content 1")).not.toBeVisible();
|
|
281
|
+
yield page.getByRole("tab", { name: "Tab 3" }).click();
|
|
282
|
+
yield (0, fixtures_1.expect)(page.getByText("Content 3")).toBeVisible();
|
|
283
|
+
yield (0, fixtures_1.expect)(page.getByText("Content 2")).not.toBeVisible();
|
|
284
|
+
yield page.getByRole("tab", { name: "Tab 1" }).click();
|
|
285
|
+
yield (0, fixtures_1.expect)(page.getByText("Content 1")).toBeVisible();
|
|
286
|
+
yield (0, fixtures_1.expect)(page.getByText("Content 3")).not.toBeVisible();
|
|
287
|
+
}));
|
|
288
|
+
});
|
|
289
|
+
fixtures_1.test.describe("accordionView property", () => {
|
|
290
|
+
(0, fixtures_1.test)("accordionView renders all tab headers when true", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
|
|
291
|
+
yield initTestBed(`
|
|
292
|
+
<Tabs accordionView="true" testId="tabs">
|
|
293
|
+
<TabItem label="Tab 1">Content 1</TabItem>
|
|
294
|
+
<TabItem label="Tab 2">Content 2</TabItem>
|
|
295
|
+
<TabItem label="Tab 3">Content 3</TabItem>
|
|
296
|
+
</Tabs>
|
|
297
|
+
`);
|
|
298
|
+
// All tab headers should be visible
|
|
299
|
+
yield (0, fixtures_1.expect)(page.getByRole("tab", { name: "Tab 1" })).toBeVisible();
|
|
300
|
+
yield (0, fixtures_1.expect)(page.getByRole("tab", { name: "Tab 2" })).toBeVisible();
|
|
301
|
+
yield (0, fixtures_1.expect)(page.getByRole("tab", { name: "Tab 3" })).toBeVisible();
|
|
302
|
+
}));
|
|
303
|
+
(0, fixtures_1.test)("accordionView shows only active tab content", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
|
|
304
|
+
yield initTestBed(`
|
|
305
|
+
<Tabs accordionView="true" testId="tabs">
|
|
306
|
+
<TabItem label="Tab 1">Content 1</TabItem>
|
|
307
|
+
<TabItem label="Tab 2">Content 2</TabItem>
|
|
308
|
+
<TabItem label="Tab 3">Content 3</TabItem>
|
|
309
|
+
</Tabs>
|
|
310
|
+
`);
|
|
311
|
+
// Only first tab content should be visible (default active)
|
|
312
|
+
yield (0, fixtures_1.expect)(page.getByText("Content 1")).toBeVisible();
|
|
313
|
+
yield (0, fixtures_1.expect)(page.getByText("Content 2")).not.toBeVisible();
|
|
314
|
+
yield (0, fixtures_1.expect)(page.getByText("Content 3")).not.toBeVisible();
|
|
315
|
+
}));
|
|
316
|
+
(0, fixtures_1.test)("accordionView positions active tab header above its content", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
|
|
317
|
+
yield initTestBed(`
|
|
318
|
+
<Tabs accordionView="true" testId="tabs">
|
|
319
|
+
<TabItem label="Tab 1">Content 1</TabItem>
|
|
320
|
+
<TabItem label="Tab 2">Content 2</TabItem>
|
|
321
|
+
<TabItem label="Tab 3">Content 3</TabItem>
|
|
322
|
+
</Tabs>
|
|
323
|
+
`);
|
|
324
|
+
const tab1Header = page.getByRole("tab", { name: "Tab 1" });
|
|
325
|
+
const tab1Content = page.getByText("Content 1");
|
|
326
|
+
const { bottom: headerBottom } = yield (0, component_test_helpers_1.getBounds)(tab1Header);
|
|
327
|
+
const { top: contentTop } = yield (0, component_test_helpers_1.getBounds)(tab1Content);
|
|
328
|
+
// Header should be above content (header bottom should be less than or near content top)
|
|
329
|
+
(0, fixtures_1.expect)(headerBottom).toBeLessThanOrEqual(contentTop + 5);
|
|
330
|
+
}));
|
|
331
|
+
(0, fixtures_1.test)("accordionView positions active tab content above next tab header", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
|
|
332
|
+
yield initTestBed(`
|
|
333
|
+
<Tabs accordionView="true" testId="tabs">
|
|
334
|
+
<TabItem label="Tab 1">Content 1</TabItem>
|
|
335
|
+
<TabItem label="Tab 2">Content 2</TabItem>
|
|
336
|
+
<TabItem label="Tab 3">Content 3</TabItem>
|
|
337
|
+
</Tabs>
|
|
338
|
+
`);
|
|
339
|
+
const tab1Content = page.getByText("Content 1");
|
|
340
|
+
const tab2Header = page.getByRole("tab", { name: "Tab 2" });
|
|
341
|
+
const { bottom: contentBottom } = yield (0, component_test_helpers_1.getBounds)(tab1Content);
|
|
342
|
+
const { top: nextHeaderTop } = yield (0, component_test_helpers_1.getBounds)(tab2Header);
|
|
343
|
+
// Content should be above next header (content bottom should be less than or near next header top)
|
|
344
|
+
(0, fixtures_1.expect)(contentBottom).toBeLessThanOrEqual(nextHeaderTop + 5);
|
|
345
|
+
}));
|
|
346
|
+
(0, fixtures_1.test)("accordionView maintains order: header1 -> content1 -> header2 -> content2 (when tab2 active)", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
|
|
347
|
+
yield initTestBed(`
|
|
348
|
+
<Tabs accordionView="true" activeTab="1" testId="tabs">
|
|
349
|
+
<TabItem label="Tab 1">Content 1</TabItem>
|
|
350
|
+
<TabItem label="Tab 2">Content 2</TabItem>
|
|
351
|
+
<TabItem label="Tab 3">Content 3</TabItem>
|
|
352
|
+
</Tabs>
|
|
353
|
+
`);
|
|
354
|
+
const tab1Header = page.getByRole("tab", { name: "Tab 1" });
|
|
355
|
+
const tab2Header = page.getByRole("tab", { name: "Tab 2" });
|
|
356
|
+
const tab2Content = page.getByText("Content 2");
|
|
357
|
+
const tab3Header = page.getByRole("tab", { name: "Tab 3" });
|
|
358
|
+
const { top: tab1Top } = yield (0, component_test_helpers_1.getBounds)(tab1Header);
|
|
359
|
+
const { top: tab2Top } = yield (0, component_test_helpers_1.getBounds)(tab2Header);
|
|
360
|
+
const { top: content2Top } = yield (0, component_test_helpers_1.getBounds)(tab2Content);
|
|
361
|
+
const { top: tab3Top } = yield (0, component_test_helpers_1.getBounds)(tab3Header);
|
|
362
|
+
// Verify vertical ordering
|
|
363
|
+
(0, fixtures_1.expect)(tab1Top).toBeLessThan(tab2Top);
|
|
364
|
+
(0, fixtures_1.expect)(tab2Top).toBeLessThan(content2Top);
|
|
365
|
+
(0, fixtures_1.expect)(content2Top).toBeLessThan(tab3Top);
|
|
366
|
+
}));
|
|
367
|
+
(0, fixtures_1.test)("accordionView switches content when different tab is clicked", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
|
|
368
|
+
yield initTestBed(`
|
|
369
|
+
<Tabs accordionView="true" testId="tabs">
|
|
370
|
+
<TabItem label="Tab 1">Content 1</TabItem>
|
|
371
|
+
<TabItem label="Tab 2">Content 2</TabItem>
|
|
372
|
+
<TabItem label="Tab 3">Content 3</TabItem>
|
|
373
|
+
</Tabs>
|
|
374
|
+
`);
|
|
375
|
+
// Initially Tab 1 is active
|
|
376
|
+
yield (0, fixtures_1.expect)(page.getByText("Content 1")).toBeVisible();
|
|
377
|
+
yield (0, fixtures_1.expect)(page.getByText("Content 2")).not.toBeVisible();
|
|
378
|
+
// Click Tab 2
|
|
379
|
+
yield page.getByRole("tab", { name: "Tab 2" }).click();
|
|
380
|
+
yield (0, fixtures_1.expect)(page.getByText("Content 2")).toBeVisible();
|
|
381
|
+
yield (0, fixtures_1.expect)(page.getByText("Content 1")).not.toBeVisible();
|
|
382
|
+
// Verify Tab 2 content is below Tab 2 header
|
|
383
|
+
const tab2Header = page.getByRole("tab", { name: "Tab 2" });
|
|
384
|
+
const tab2Content = page.getByText("Content 2");
|
|
385
|
+
const { bottom: headerBottom } = yield (0, component_test_helpers_1.getBounds)(tab2Header);
|
|
386
|
+
const { top: contentTop } = yield (0, component_test_helpers_1.getBounds)(tab2Content);
|
|
387
|
+
(0, fixtures_1.expect)(headerBottom).toBeLessThanOrEqual(contentTop + 5);
|
|
388
|
+
}));
|
|
389
|
+
(0, fixtures_1.test)("accordionView works with dynamic activeTab changes", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
|
|
390
|
+
const { testStateDriver } = yield initTestBed(`
|
|
391
|
+
<Fragment>
|
|
392
|
+
<Tabs accordionView="true" activeTab="{testState ?? 0}" testId="tabs">
|
|
393
|
+
<TabItem label="Tab 1">Content 1</TabItem>
|
|
394
|
+
<TabItem label="Tab 2">Content 2</TabItem>
|
|
395
|
+
<TabItem label="Tab 3">Content 3</TabItem>
|
|
396
|
+
</Tabs>
|
|
397
|
+
<Button testId="tab2Btn" onClick="testState = 1" />
|
|
398
|
+
<Button testId="tab3Btn" onClick="testState = 2" />
|
|
399
|
+
</Fragment>
|
|
400
|
+
`);
|
|
401
|
+
// Initially Content 1 is visible
|
|
402
|
+
yield (0, fixtures_1.expect)(page.getByText("Content 1")).toBeVisible();
|
|
403
|
+
yield (0, fixtures_1.expect)(page.getByText("Content 2")).not.toBeVisible();
|
|
404
|
+
yield (0, fixtures_1.expect)(page.getByText("Content 3")).not.toBeVisible();
|
|
405
|
+
// Switch to Tab 2
|
|
406
|
+
yield page.getByTestId("tab2Btn").click();
|
|
407
|
+
yield (0, fixtures_1.expect)(page.getByText("Content 2")).toBeVisible();
|
|
408
|
+
yield (0, fixtures_1.expect)(page.getByText("Content 1")).not.toBeVisible();
|
|
409
|
+
yield (0, fixtures_1.expect)(page.getByText("Content 3")).not.toBeVisible();
|
|
410
|
+
// Verify ordering for Tab 2
|
|
411
|
+
const tab2Header = page.getByRole("tab", { name: "Tab 2" });
|
|
412
|
+
const tab2Content = page.getByText("Content 2");
|
|
413
|
+
const tab3Header = page.getByRole("tab", { name: "Tab 3" });
|
|
414
|
+
const { top: tab2Top } = yield (0, component_test_helpers_1.getBounds)(tab2Header);
|
|
415
|
+
const { top: content2Top } = yield (0, component_test_helpers_1.getBounds)(tab2Content);
|
|
416
|
+
const { top: tab3Top } = yield (0, component_test_helpers_1.getBounds)(tab3Header);
|
|
417
|
+
(0, fixtures_1.expect)(tab2Top).toBeLessThan(content2Top);
|
|
418
|
+
(0, fixtures_1.expect)(content2Top).toBeLessThan(tab3Top);
|
|
419
|
+
// Switch to Tab 3
|
|
420
|
+
yield page.getByTestId("tab3Btn").click();
|
|
421
|
+
yield (0, fixtures_1.expect)(page.getByText("Content 3")).toBeVisible();
|
|
422
|
+
yield (0, fixtures_1.expect)(page.getByText("Content 1")).not.toBeVisible();
|
|
423
|
+
yield (0, fixtures_1.expect)(page.getByText("Content 2")).not.toBeVisible();
|
|
424
|
+
}));
|
|
425
|
+
(0, fixtures_1.test)("accordionView=false renders standard tabs (non-accordion)", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
|
|
426
|
+
yield initTestBed(`
|
|
427
|
+
<Tabs accordionView="false" testId="tabs">
|
|
428
|
+
<TabItem label="Tab 1">Content 1</TabItem>
|
|
429
|
+
<TabItem label="Tab 2">Content 2</TabItem>
|
|
430
|
+
<TabItem label="Tab 3">Content 3</TabItem>
|
|
431
|
+
</Tabs>
|
|
432
|
+
`);
|
|
433
|
+
const tab1Header = page.getByRole("tab", { name: "Tab 1" });
|
|
434
|
+
const tab2Header = page.getByRole("tab", { name: "Tab 2" });
|
|
435
|
+
const tab1Content = page.getByText("Content 1");
|
|
436
|
+
// In standard mode (non-accordion), all headers should be visible
|
|
437
|
+
yield (0, fixtures_1.expect)(tab1Header).toBeVisible();
|
|
438
|
+
yield (0, fixtures_1.expect)(tab2Header).toBeVisible();
|
|
439
|
+
// Only active tab content should be visible
|
|
440
|
+
yield (0, fixtures_1.expect)(tab1Content).toBeVisible();
|
|
441
|
+
yield (0, fixtures_1.expect)(page.getByText("Content 2")).not.toBeVisible();
|
|
442
|
+
// Tab content should NOT be interleaved between headers
|
|
443
|
+
// (unlike accordion view where content appears between headers)
|
|
444
|
+
const { bottom: tab1Bottom } = yield (0, component_test_helpers_1.getBounds)(tab1Header);
|
|
445
|
+
const { top: tab2Top } = yield (0, component_test_helpers_1.getBounds)(tab2Header);
|
|
446
|
+
const { top: contentTop } = yield (0, component_test_helpers_1.getBounds)(tab1Content);
|
|
447
|
+
// Content should NOT be between the two headers
|
|
448
|
+
// Either tab2 is next to tab1 (horizontal) or below tab1 but before content (vertical)
|
|
449
|
+
const isContentBetweenHeaders = contentTop > tab1Bottom && contentTop < tab2Top;
|
|
450
|
+
(0, fixtures_1.expect)(isContentBetweenHeaders).toBe(false);
|
|
451
|
+
}));
|
|
452
|
+
(0, fixtures_1.test)("accordionView handles null value gracefully (defaults to false)", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
|
|
453
|
+
yield initTestBed(`
|
|
454
|
+
<Tabs accordionView="{null}" testId="tabs">
|
|
455
|
+
<TabItem label="Tab 1">Content 1</TabItem>
|
|
456
|
+
<TabItem label="Tab 2">Content 2</TabItem>
|
|
457
|
+
</Tabs>
|
|
458
|
+
`);
|
|
459
|
+
// Should render as standard horizontal tabs
|
|
460
|
+
const tab1Header = page.getByRole("tab", { name: "Tab 1" });
|
|
461
|
+
const tab2Header = page.getByRole("tab", { name: "Tab 2" });
|
|
462
|
+
yield (0, fixtures_1.expect)(tab1Header).toBeVisible();
|
|
463
|
+
yield (0, fixtures_1.expect)(tab2Header).toBeVisible();
|
|
464
|
+
// Headers should be horizontally aligned
|
|
465
|
+
const { top: tab1Top } = yield (0, component_test_helpers_1.getBounds)(tab1Header);
|
|
466
|
+
const { top: tab2Top } = yield (0, component_test_helpers_1.getBounds)(tab2Header);
|
|
467
|
+
(0, fixtures_1.expect)(Math.abs(tab1Top - tab2Top)).toBeLessThan(10);
|
|
468
|
+
}));
|
|
469
|
+
});
|
|
108
470
|
});
|
|
109
471
|
// =============================================================================
|
|
110
472
|
// HEADER TEMPLATE TESTS
|