+
+ {children.map((child, index) => (
+
+ {child}
+
+ ))}
+
+
+ );
+};
+
+export default withIsland(OverloadTabs, {
+ name: 'OverloadTabs',
+ on: { interaction: 'pointerover,focusin,touchstart' },
+});
diff --git a/packages/react/src/html/ui/components/OverloadTabs/index.module.css b/packages/react/src/html/ui/components/OverloadTabs/index.module.css
new file mode 100644
index 000000000..b23fe518c
--- /dev/null
+++ b/packages/react/src/html/ui/components/OverloadTabs/index.module.css
@@ -0,0 +1,21 @@
+.panelContainer {
+ display: grid;
+ grid-template-columns: 1fr;
+ grid-template-rows: 1fr;
+}
+
+.panel {
+ grid-column: 1;
+ grid-row: 1;
+ opacity: 1;
+ visibility: visible;
+ pointer-events: auto;
+ transition: opacity 0.2s ease;
+ margin-top: calc(var(--spacing, 0.25rem) * 2);
+}
+
+.panel[data-state='inactive'] {
+ opacity: 0;
+ visibility: hidden;
+ pointer-events: none;
+}
diff --git a/packages/react/src/html/ui/index.css b/packages/react/src/html/ui/index.css
index e385a3eb2..7d97506de 100644
--- a/packages/react/src/html/ui/index.css
+++ b/packages/react/src/html/ui/index.css
@@ -78,6 +78,12 @@ main {
}
}
+ .overload-panel {
+ display: flex;
+ flex-direction: column;
+ gap: calc(var(--spacing) * 6);
+ }
+
table {
td {
word-break: break-all;
diff --git a/packages/react/src/jsx-ast/utils/__tests__/buildContent.test.mjs b/packages/react/src/jsx-ast/utils/__tests__/buildContent.test.mjs
index 225f8b857..2a5ac9da5 100644
--- a/packages/react/src/jsx-ast/utils/__tests__/buildContent.test.mjs
+++ b/packages/react/src/jsx-ast/utils/__tests__/buildContent.test.mjs
@@ -3,7 +3,11 @@ import { describe, it } from 'node:test';
import { setConfig } from '@doc-kit/core/utils/configuration/index.mjs';
-import { transformHeadingNode, gatherChangeEntries } from '../buildContent.mjs';
+import {
+ transformHeadingNode,
+ gatherChangeEntries,
+ groupOverloadsIntoTabs,
+} from '../buildContent.mjs';
const heading = {
type: 'heading',
@@ -142,3 +146,68 @@ describe('gatherChangeEntries', () => {
assert.equal(result[1].label, 'Added new feature.');
});
});
+
+describe('groupOverloadsIntoTabs', () => {
+ it('groups consecutive overloads into a single OverloadTabs component', () => {
+ const originalEntries = [
+ { heading: { data: { name: 'funcA', isOverload: false } } },
+ { heading: { depth: 3, data: { name: 'funcB', isOverload: false } } },
+ { heading: { depth: 3, data: { name: 'funcB', isOverload: true } } },
+ { heading: { depth: 3, data: { name: 'funcB', isOverload: true } } },
+ { heading: { data: { name: 'funcC', isOverload: false } } },
+ ];
+
+ const makeNode = (className, bodyText) => ({
+ type: 'element',
+ tagName: 'div',
+ properties: { className },
+ children: [
+ { type: 'element', tagName: 'h3', depth: 3 }, // The heading to be stripped
+ { type: 'text', value: bodyText },
+ ],
+ });
+
+ const processedChildren = [
+ makeNode('entry-a', 'body a'),
+ makeNode('entry-b1', 'body b1'),
+ makeNode('entry-b2', 'body b2'),
+ makeNode('entry-b3', 'body b3'),
+ makeNode('entry-c', 'body c'),
+ ];
+
+ const result = groupOverloadsIntoTabs(processedChildren, originalEntries);
+
+ // 0: funcA, 1: funcB-heading, 2: Overloads-heading, 3: OverloadTabs(funcB), 4: funcC
+ assert.equal(result.length, 5);
+
+ // First element is untouched
+ assert.equal(result[0].properties.className, 'entry-a');
+
+ // Second element is the extracted heading
+ assert.equal(result[1].tagName, 'h3');
+
+ // Third element is the "Overloads" heading
+ assert.equal(result[2].children[0].value, 'Overloads');
+
+ // Fourth element is the OverloadTabs component
+ const tabsComponent = result[3];
+ assert.equal(tabsComponent.name, 'OverloadTabs');
+ assert.equal(tabsComponent.children.length, 3); // 3 tab panels
+
+ // Check that the h3 was removed from the overloads and they are wrapped in overload-panel
+ const panel1 = tabsComponent.children[0];
+ const classAttr1 = panel1.attributes.find(a => a.name === 'className');
+ assert.equal(classAttr1.value, 'overload-panel');
+ assert.equal(panel1.children[0].type, 'text');
+ assert.equal(panel1.children[0].value, 'body b1');
+
+ const panel2 = tabsComponent.children[1];
+ const classAttr2 = panel2.attributes.find(a => a.name === 'className');
+ assert.equal(classAttr2.value, 'overload-panel');
+ assert.equal(panel2.children[0].type, 'text');
+ assert.equal(panel2.children[0].value, 'body b2');
+
+ // Fifth element is untouched
+ assert.equal(result[4].properties.className, 'entry-c');
+ });
+});
diff --git a/packages/react/src/jsx-ast/utils/buildContent.mjs b/packages/react/src/jsx-ast/utils/buildContent.mjs
index 7b9a7dda4..ac1cd1451 100644
--- a/packages/react/src/jsx-ast/utils/buildContent.mjs
+++ b/packages/react/src/jsx-ast/utils/buildContent.mjs
@@ -310,6 +310,62 @@ export const processEntry = entry => {
return entry.content;
};
+/**
+ * Groups consecutive overloaded function API entries into a single OverloadTabs component.
+ * @param {Array