{children().map((child, index) => {
const disabled = tabs[index].disabled;
@@ -218,7 +245,12 @@ export const TabContainer = factory(function TabContainer({
{...formatAriaProperties(aria)}
key="root"
aria-orientation={orientation}
- classes={[theme.variant(), alignClass || null, themeCss.root]}
+ classes={[
+ theme.variant(),
+ alignClass || null,
+ themeCss.root,
+ fixed ? themeCss.fixed : themeCss.scroller
+ ]}
role="tablist"
>
{...content}
diff --git a/src/tab-container/tests/TabContainer.spec.tsx b/src/tab-container/tests/TabContainer.spec.tsx
index d75b0163ca..28ff58a066 100644
--- a/src/tab-container/tests/TabContainer.spec.tsx
+++ b/src/tab-container/tests/TabContainer.spec.tsx
@@ -1,11 +1,12 @@
-import { AriaAttributes } from '@dojo/framework/core/interfaces';
+import { AriaAttributes, RenderResult } from '@dojo/framework/core/interfaces';
const { registerSuite } = intern.getInterface('object');
const { assert } = intern.getPlugin('chai');
import * as sinon from 'sinon';
-import { tsx, v, w } from '@dojo/framework/core/vdom';
+import { create, tsx, v, w } from '@dojo/framework/core/vdom';
+import assertionTemplate from '@dojo/framework/testing/harness/assertionTemplate';
import { Keys } from '../../common/util';
import Icon from '../../icon';
@@ -18,7 +19,7 @@ import {
isStringComparator,
noop
} from '../../common/tests/support/test-helpers';
-import assertionTemplate from '@dojo/framework/testing/harness/assertionTemplate';
+import offscreen from '../../middleware/offscreen';
const compareLabelledBy = { selector: '*', property: 'labelledBy', comparator: isStringComparator };
const compareControls = { selector: '*', property: 'controls', comparator: isStringComparator };
@@ -41,7 +42,7 @@ const baseTemplate = assertionTemplate(() => (
@@ -49,11 +50,29 @@ const baseTemplate = assertionTemplate(() => (
));
+const scrollableBaseTemplate = assertionTemplate(() => (
+
+));
+
const reverseOrientationTemplate = assertionTemplate(() => (
@@ -427,6 +446,77 @@ registerSuite('TabContainer', {
h.trigger('@1-tabbutton', 'onclick');
h.expect(disabledTemplate);
assert.isTrue(onActiveIndexStub.notCalled);
+ },
+
+ 'variable width tabs'() {
+ const tabs = [{ name: 'tab0' }, { name: 'tab1' }];
+
+ let offscreenRenderResult: RenderResult;
+
+ const factory = create();
+ const offscreenMock = factory(function offscreen() {
+ return (
+ renderFunction: () => RenderResult,
+ predicate: (node: HTMLDivElement) => number
+ ): number => {
+ offscreenRenderResult = renderFunction();
+ return predicate({ offsetHeight: 10, clientHeight: 5 } as any);
+ };
+ });
+
+ const h = harness(
+ () => (
+
+ tab0
+ tab1
+
+ ),
+ {
+ middleware: [[offscreen, offscreenMock]]
+ }
+ );
+
+ const template = scrollableBaseTemplate
+ .setChildren('@buttons', () => [
+
+
+ tab0
+
+
+
+
+
,
+
+
+ tab1
+
+
+
+
+
+ ])
+ .setChildren('@tabs', () => [
+
,
+
+ ]);
+
+ h.expect(template);
+
+ assert.deepEqual(offscreenRenderResult,
);
}
}
});
diff --git a/src/theme/default/tab-container.m.css b/src/theme/default/tab-container.m.css
index 6482dffb1e..ffa06c0649 100644
--- a/src/theme/default/tab-container.m.css
+++ b/src/theme/default/tab-container.m.css
@@ -62,6 +62,46 @@
box-sizing: border-box;
}
+/* Added to an TabButton */
+.fixed {
+}
+
+.scroller {
+ overflow-y: hidden;
+}
+
+.scrollArea {
+ -webkit-overflow-scrolling: touch;
+ display: flex;
+ overflow-x: hidden;
+}
+
+.scroll {
+ overflow-x: scroll;
+}
+
+.scrollTest {
+ position: absolute;
+ top: -9999px;
+ width: 100px;
+ height: 100px;
+ overflow-x: scroll;
+}
+
+.scrollArea::-webkit-scrollbar,
+.scrollTest::-webkit-scrollbar {
+ display: none;
+}
+
+.scrollContent {
+ position: relative;
+ display: flex;
+ flex: 1 0 auto;
+ -webkit-transform: none;
+ transform: none;
+ will-change: transform;
+}
+
/* The root class of the TabButton */
.tabButton {
border: 1px solid transparent;
@@ -78,6 +118,12 @@
top: 1px;
}
+.scroller .tabButton {
+ min-width: 90px;
+ max-width: 360px;
+ flex-grow: 0;
+}
+
/* Focus for a TabButton when not disabled */
.tabButton:focus:not(.disabledTabButton) {
font-weight: bold;
diff --git a/src/theme/default/tab-container.m.css.d.ts b/src/theme/default/tab-container.m.css.d.ts
index 863161b570..0ad4432e1e 100644
--- a/src/theme/default/tab-container.m.css.d.ts
+++ b/src/theme/default/tab-container.m.css.d.ts
@@ -8,6 +8,12 @@ export const tabButtonContent: string;
export const activeTabButtonLabel: string;
export const activeTabButton: string;
export const root: string;
+export const fixed: string;
+export const scroller: string;
+export const scrollArea: string;
+export const scroll: string;
+export const scrollTest: string;
+export const scrollContent: string;
export const tabButton: string;
export const disabledTabButton: string;
export const alignLeft: string;
diff --git a/src/theme/dojo/tab-container.m.css b/src/theme/dojo/tab-container.m.css
index 94684d2cdb..c03b780b67 100644
--- a/src/theme/dojo/tab-container.m.css
+++ b/src/theme/dojo/tab-container.m.css
@@ -6,10 +6,21 @@
font-family: var(--font-family);
}
+.root:not(.fixed) .tabButton {
+ min-width: 90px;
+ max-width: 360px;
+ flex-grow: 0;
+}
+
.tabButtons {
display: flex;
}
+.root.fixed .tabButton {
+ width: var(--tab-width);
+ flex: 1;
+}
+
.tabButton {
border-bottom: var(--border-width) solid var(--tab-button-disabled-color);
border-left: var(--border-width) solid transparent;
@@ -17,21 +28,25 @@
border-top: var(--border-width) solid transparent;
color: var(--tab-button-color);
cursor: pointer;
- display: inline-block;
- flex: 1;
+ display: flex;
outline: none;
overflow: hidden;
padding: calc(var(--grid-base) * 2) calc(var(--grid-base) / 2);
position: relative;
- text-align: center;
+ justify-content: center;
text-overflow: ellipsis;
vertical-align: top;
white-space: nowrap;
- width: var(--tab-width);
margin: 0;
background-color: var(--tab-button-background);
}
+.tabButtonContent {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
.tabButton:hover:not(.disabledTabButton):not(.activeTabButton) {
background-color: var(--color-background-faded);
border-color: var(--color-background-faded);
diff --git a/src/theme/dojo/tab-container.m.css.d.ts b/src/theme/dojo/tab-container.m.css.d.ts
index aa20c2bb68..eab9050770 100644
--- a/src/theme/dojo/tab-container.m.css.d.ts
+++ b/src/theme/dojo/tab-container.m.css.d.ts
@@ -1,6 +1,8 @@
export const root: string;
-export const tabButtons: string;
+export const fixed: string;
export const tabButton: string;
+export const tabButtons: string;
+export const tabButtonContent: string;
export const disabledTabButton: string;
export const activeTabButton: string;
export const activeTabButtonLabel: string;
diff --git a/src/theme/material/tab-container.m.css b/src/theme/material/tab-container.m.css
index 2392677f7d..310685d605 100644
--- a/src/theme/material/tab-container.m.css
+++ b/src/theme/material/tab-container.m.css
@@ -9,12 +9,21 @@
pointer-events: all;
}
+.root:not(.fixed) .tabButton {
+ min-width: 90px;
+ max-width: 360px;
+ flex-grow: 0;
+}
+
.tabButton:not(.activeTabButton) .tabButtonContent {
color: var(--mdc-tab-button-text-color);
}
.tabButtonContent {
composes: mdc-tab__text-label from '@material/tab/dist/mdc.tab.css';
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
}
.activeTabButton {
diff --git a/src/theme/material/tab-container.m.css.d.ts b/src/theme/material/tab-container.m.css.d.ts
index 0de48c66aa..c42b9ce725 100644
--- a/src/theme/material/tab-container.m.css.d.ts
+++ b/src/theme/material/tab-container.m.css.d.ts
@@ -1,8 +1,9 @@
export const tabButtons: string;
export const tabButton: string;
+export const root: string;
+export const fixed: string;
export const activeTabButton: string;
export const tabButtonContent: string;
-export const root: string;
export const activeTabButtonLabel: string;
export const disabledTabButton: string;
export const close: string;
@@ -14,3 +15,6 @@ export const alignRight: string;
export const alignLeft: string;
export const tabs: string;
export const alignBottom: string;
+export const scroller: string;
+export const scrollArea: string;
+export const scrollContent: string;