Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tabs): make component responsive #8616

Merged
merged 133 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
133 commits
Select commit Hold shift + click to select a range
d30b74b
feat(tabs, tab-nav, tab-title, tab): make component responsive
Elijbet Sep 15, 2023
58e56d7
add getOverflowIcons() to detect overflow at right and left edges of …
Elijbet Sep 16, 2023
cc10a03
work out scrollToNextTabTitles callback on right chevron click
Elijbet Sep 18, 2023
9f100f2
work out scrollToPreviousTabTitles callback to onClick for left chevr…
Elijbet Sep 18, 2023
ab01786
Merge branch 'main' into elijbet/6689-tabs-responsive-layout
Elijbet Sep 18, 2023
917bf49
Merge branch 'main' into elijbet/6689-tabs-responsive-layout
Elijbet Sep 20, 2023
fa57cc0
fix overflow logic and only show overflow icons for the inline version
Elijbet Sep 20, 2023
a704b4e
wire up t9n, LocalizedComponent
Elijbet Sep 20, 2023
4242f52
subsitute icon with action button
Elijbet Sep 20, 2023
14136e8
adapt css to action button instead of icon
Elijbet Sep 20, 2023
07728c2
Merge branch 'main' into elijbet/6689-tabs-responsive-layout
Elijbet Sep 20, 2023
fb4d4f6
pas in overflowDirection to scrollDir action callback
Elijbet Sep 21, 2023
2c9f93b
cleanup
Elijbet Sep 21, 2023
0ccac60
add a story and cleanup
Elijbet Sep 22, 2023
19c2fac
slim down scrollToNextTabTitles function
Elijbet Sep 22, 2023
87de476
Merge branch 'main' into elijbet/6689-tabs-responsive-layout
Elijbet Sep 22, 2023
4c1c23d
Merge branch 'main' into elijbet/6689-tabs-responsive-layout
Elijbet Sep 25, 2023
1bae874
Merge branch 'main' into elijbet/6689-tabs-responsive-layout
Elijbet Sep 26, 2023
a849ebf
Merge branch 'main' into elijbet/6689-tabs-responsive-layout
Elijbet Sep 26, 2023
6ad662c
WIP: e2e test for overflowScenarios = [right, left, both]
Elijbet Sep 28, 2023
f505684
WIP: work out leaner combined test for both scenarios
Elijbet Sep 28, 2023
9676f65
Merge branch 'main' into elijbet/6689-tabs-responsive-layout
Elijbet Sep 28, 2023
ffc7704
Merge branch 'main' into elijbet/6689-tabs-responsive-layout
Elijbet Sep 28, 2023
91e0b62
call clientWidth instead of getBoundingClientRect.width, use for..of …
Elijbet Sep 28, 2023
2fc31b1
adapt to tab order change for ltr/rtl bidirecitonal design
Elijbet Sep 29, 2023
043ffe6
Merge branch 'main' into elijbet/6689-tabs-responsive-layout
Elijbet Sep 29, 2023
f35d111
styling of the action button with box-shadow
Elijbet Sep 30, 2023
4a22ae9
simplify arrow navigation logic
Elijbet Oct 1, 2023
1f98581
Merge branch 'main' into elijbet/6689-tabs-responsive-layout
Elijbet Oct 1, 2023
cc4b6df
cleanup
Elijbet Oct 2, 2023
ab70c42
WIP: fix failing tests
Elijbet Oct 2, 2023
e19073a
use Element.scrollIntoView for the horizontal scrolling instead of t…
Elijbet Oct 2, 2023
05ad709
cleanup
Elijbet Oct 2, 2023
f5abd58
Merge branch 'main' into elijbet/6689-tabs-responsive-layout
Elijbet Oct 2, 2023
0d5824c
WIP: fix scroll calculation to apply correctly on RTL
Elijbet Oct 2, 2023
7497b82
revert to ltr only option
Elijbet Oct 2, 2023
817d643
test cleanup
Elijbet Oct 2, 2023
13d7845
Merge branch 'main' into elijbet/6689-tabs-responsive-layout
Elijbet Oct 3, 2023
07b8f69
feat(tab-nav): add support for built-in translations
Elijbet Oct 3, 2023
555f3dd
remove localization and translations and create a seperate PR
Elijbet Oct 3, 2023
128f82b
cleanup
Elijbet Oct 4, 2023
b2ffd3d
fix failing commonTests
Elijbet Oct 4, 2023
7d5d87e
cleanups to fix some failing tests
Elijbet Oct 4, 2023
3bfc5a0
add checks to avoid uncaught errors
Elijbet Oct 4, 2023
925e2c5
Merge branch 'main' into elijbet/6689-tabs-responsive-layout
Elijbet Oct 4, 2023
b255279
cleanup
Elijbet Oct 4, 2023
5474a9f
removed the need for .getBoundingClientRect()
Elijbet Oct 5, 2023
e9bb23c
cleanup
Elijbet Oct 5, 2023
712fb31
apply bordered styling to the action button
Elijbet Oct 5, 2023
aec6472
Merge branch 'main' into elijbet/6689-tabs-responsive-layout
Elijbet Oct 9, 2023
d5ec4ba
Merge branch 'main' into elijbet/6689-tabs-responsive-layout
Elijbet Oct 10, 2023
3e5da4c
calcite appearance based on tab-nav being bordered or not
Elijbet Oct 10, 2023
54ee2b6
Merge branch 'main' into elijbet/6689-tabs-responsive-layout
Elijbet Oct 11, 2023
c9b71b2
WIP: work out tab-title container clipping to accomodate transparent …
Elijbet Oct 11, 2023
aafd27a
Merge branch 'main' into elijbet/6689-add-support-for-built-in-transl…
Elijbet Oct 12, 2023
a6a3b09
merge with resolved conflicts
Elijbet Nov 1, 2023
c1db09f
Merge branch 'main' into elijbet/6689-tabs-responsive-layout
Elijbet Dec 1, 2023
057f6e8
resolve conflicts
Elijbet Dec 1, 2023
a4528ea
Merge branch 'main' into elijbet/6689-add-support-for-built-in-transl…
Elijbet Dec 6, 2023
7db0415
merge update
Elijbet Dec 6, 2023
870f629
alt approach w/ intersection observer
jcfranco Dec 15, 2023
8e3c942
add bound checking logic
jcfranco Dec 18, 2023
e9f1076
merge main
jcfranco Jan 16, 2024
e8fec06
tidy up
jcfranco Jan 16, 2024
406b8e1
fix missing import
jcfranco Jan 16, 2024
cc3b64c
roll back tabs being full width by default
jcfranco Jan 16, 2024
1c9e394
simplify styles
jcfranco Jan 16, 2024
047c52e
merge elijbet/6689-add-support-for-built-in-translations
jcfranco Jan 16, 2024
cf95c28
wire up messages
jcfranco Jan 16, 2024
c0a0614
tidy up
jcfranco Jan 16, 2024
57e48ee
tweak tab-title overflow logic and styling
jcfranco Jan 17, 2024
e80ffb1
add mask to overflowing, visible tab-titles
jcfranco Jan 18, 2024
9e22fce
prevent selected tab-title from shrinking in layout=center
jcfranco Jan 18, 2024
479b638
improve tab-title layout for layout=center
jcfranco Jan 18, 2024
bcd45eb
update mask to latest design
jcfranco Jan 18, 2024
905868d
consider scrolling into active indicator position and width
jcfranco Jan 18, 2024
21ab9d5
tweak breakpoint stories
jcfranco Jan 18, 2024
9fdb6b2
fix mask when tab-titles are overflowing on a single side
jcfranco Jan 18, 2024
8ea8c59
add scroll button class
jcfranco Jan 19, 2024
cc16dfc
tidy up
jcfranco Jan 19, 2024
7177ae3
improve selected tab title scrolling into view
jcfranco Jan 19, 2024
be9a95a
add wheel scrolling behavior
jcfranco Jan 19, 2024
92f1219
improve scrolling behavior
jcfranco Jan 19, 2024
a61ac26
mask active tab-title indicator
jcfranco Jan 19, 2024
c9628b0
tidy up
jcfranco Jan 19, 2024
f649f75
add scroller button dividers
jcfranco Jan 20, 2024
b5a84e5
allow scrolling in center layout
jcfranco Jan 20, 2024
fb14ae4
tweak non-border divider styles
jcfranco Jan 22, 2024
730f407
fix bordered, centered layout spacing
jcfranco Jan 22, 2024
7125bfa
fix forward/backward scrolling
jcfranco Jan 23, 2024
f861b59
fix visual artifact for both start-end mask was applied
jcfranco Jan 23, 2024
a165be4
tidy up
jcfranco Jan 23, 2024
8e5d7a5
update masking for rtl
jcfranco Jan 23, 2024
cb26d11
update scroller buttons for rtl
jcfranco Jan 23, 2024
b23bd25
add placeholders for updated E2E tests
jcfranco Jan 23, 2024
ceb35ca
tidy up
jcfranco Jan 23, 2024
5494687
merge main
jcfranco Jan 24, 2024
fb54422
fix visual regression
jcfranco Jan 24, 2024
8ab7f55
fix spacing
jcfranco Jan 24, 2024
a1bfab6
fix visual regression
jcfranco Jan 24, 2024
452a431
fix spacing
jcfranco Jan 25, 2024
931095a
fix tab-title inline-padding when bordered + scale=s
jcfranco Jan 26, 2024
323014e
restore tab-title bordered + closable spacing
jcfranco Jan 26, 2024
3b91a49
fix scrolling via buttons
jcfranco Jan 27, 2024
ecde23e
tidy up
jcfranco Jan 27, 2024
effb817
add tests
jcfranco Jan 30, 2024
36f61b6
fix incorrect tag used in t9n test helper
jcfranco Jan 30, 2024
46772eb
tidy up
jcfranco Jan 30, 2024
5b5fa24
fix RTL scrolling
jcfranco Feb 13, 2024
00d13e4
initially display selected tab title
jcfranco Feb 13, 2024
aa7656a
tidy up
jcfranco Feb 13, 2024
b352835
bail scrolling to tab title if nonexistent
jcfranco Feb 13, 2024
f0f0bed
fix story typo
jcfranco Feb 13, 2024
3cc4247
improve dom focus util type
jcfranco Feb 17, 2024
de8c447
update scrolling buttons to follow spec
jcfranco Feb 16, 2024
679e4fb
scroll to keyboard-navigated tab
jcfranco Feb 16, 2024
c31177f
fix tab order of scrolling buttons and tab-titles
jcfranco Feb 16, 2024
4143b0d
improve closable tab-title spacing
jcfranco Feb 16, 2024
7257714
fix tab scrolling button focus
jcfranco Feb 17, 2024
834b013
tidy up
jcfranco Feb 17, 2024
e437c53
improve scrolling for mobile
jcfranco Feb 17, 2024
e4255bb
fix tests
jcfranco Feb 17, 2024
536117a
tidy up
jcfranco Feb 20, 2024
3a5a5ab
remove scrolling arrows from tab order
jcfranco Feb 21, 2024
0727b11
update scrolling tests to use both deltaX and deltaY
jcfranco Feb 21, 2024
40c0da8
fix specificity issues
jcfranco Feb 22, 2024
eafdab6
update wheel/scrolling behavior to be device agnostic
jcfranco Feb 22, 2024
85fe4fc
update test config to troubleshoot CI failure
jcfranco Feb 23, 2024
36667f2
add log messaging
jcfranco Feb 23, 2024
3be8717
restore previous scroll values
jcfranco Feb 23, 2024
cd0f5bb
roll back troubleshooting changes
jcfranco Feb 23, 2024
961ecfd
fix indicator position regression
jcfranco Feb 23, 2024
68c9f48
fix layout=center + closable spacing
jcfranco Feb 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/calcite-components/.storybook/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export const filterComponentAttributes = (
/**
* This helper creates a story that captures all breakpoints across all scales for testing.
*
* @param singleStoryHtml – HTML story template with placeholders for `scale` attributes (e.g., `{scale}`).
* @param singleStoryHtml – HTML story template with placeholders for `scale` attributes (e.g., `{scale}`). You can additionally use `.breakpoint-stories-container` and `.breakpoint-story-container` to style breakpoint story containers.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extracted this over to #8739.

* @param [focused] – when specified, creates a single story for the provided breakpoint and scale.
* This should only be used if multiple stories cannot be displayed side-by-side.
*/
Expand Down
18 changes: 18 additions & 0 deletions packages/calcite-components/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ import { StepperMessages } from "./components/stepper/assets/stepper/t9n";
import { StepperItemChangeEventDetail, StepperItemEventDetail, StepperItemKeyEventDetail } from "./components/stepper/interfaces";
import { StepperItemMessages } from "./components/stepper-item/assets/stepper-item/t9n";
import { TabID, TabLayout, TabPosition } from "./components/tabs/interfaces";
import { TabNavMessages } from "./components/tab-nav/assets/tab-nav/t9n";
import { TabChangeEventDetail, TabCloseEventDetail } from "./components/tab/interfaces";
import { TabTitleMessages } from "./components/tab-title/assets/tab-title/t9n";
import { RowType, TableLayout, TableRowFocusEvent } from "./components/table/interfaces";
Expand Down Expand Up @@ -163,6 +164,7 @@ export { StepperMessages } from "./components/stepper/assets/stepper/t9n";
export { StepperItemChangeEventDetail, StepperItemEventDetail, StepperItemKeyEventDetail } from "./components/stepper/interfaces";
export { StepperItemMessages } from "./components/stepper-item/assets/stepper-item/t9n";
export { TabID, TabLayout, TabPosition } from "./components/tabs/interfaces";
export { TabNavMessages } from "./components/tab-nav/assets/tab-nav/t9n";
export { TabChangeEventDetail, TabCloseEventDetail } from "./components/tab/interfaces";
export { TabTitleMessages } from "./components/tab-title/assets/tab-title/t9n";
export { RowType, TableLayout, TableRowFocusEvent } from "./components/table/interfaces";
Expand Down Expand Up @@ -4546,6 +4548,14 @@ export namespace Components {
"indicatorOffset": number;
"indicatorWidth": number;
"layout": TabLayout;
/**
* Use this property to override individual strings used by the component.
*/
"messageOverrides": Partial<TabNavMessages>;
/**
* Made into a prop for testing purposes only.
*/
"messages": TabNavMessages;
/**
* Specifies the position of `calcite-tab-nav` and `calcite-tab-title` components in relation to, and is inherited from the parent `calcite-tabs`, defaults to `top`.
*/
Expand Down Expand Up @@ -12018,6 +12028,14 @@ declare namespace LocalJSX {
"indicatorOffset"?: number;
"indicatorWidth"?: number;
"layout"?: TabLayout;
/**
* Use this property to override individual strings used by the component.
*/
"messageOverrides"?: Partial<TabNavMessages>;
/**
* Made into a prop for testing purposes only.
*/
"messages"?: TabNavMessages;
"onCalciteInternalTabChange"?: (event: CalciteTabNavCustomEvent<TabChangeEventDetail>) => void;
/**
* Emits when the selected `calcite-tab` changes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -648,8 +648,7 @@ describe("calcite-button", () => {
t9n("calcite-button");
});

describe('automatic tooltip', ()=>{

describe("automatic tooltip", () => {
it("shows tooltip for buttons with truncated long text", async () => {
const shortText = "Hi!";
const longText =
Expand Down Expand Up @@ -685,7 +684,6 @@ describe("calcite-button", () => {

expect(button).not.toHaveAttribute("title");
});

});

it("should set aria-expanded attribute on shadowDOM element when used as trigger", async () => {
Expand Down
13 changes: 13 additions & 0 deletions packages/calcite-components/src/components/tab-nav/resources.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
export const ICON = {
chevronRight: "chevron-right",
chevronLeft: "chevron-left",
};

export const CSS = {
activeIndicatorContainer: "tab-nav-active-indicator-container",
container: "tab-nav",
containerHasEndTabTitleOverflow: "tab-nav--end-overflow",
containerHasStartTabTitleOverflow: "tab-nav--start-overflow",
scrollButton: "scroll-button",
scrollButtonContainer: "scroll-button-container",
scrollBackwardContainerButton: "scroll-button-container--backward",
scrollForwardContainerButton: "scroll-button-container--forward",
tabTitleSlotWrapper: "tab-titles-slot-wrapper",
};
175 changes: 173 additions & 2 deletions packages/calcite-components/src/components/tab-nav/tab-nav.e2e.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { newE2EPage } from "@stencil/core/testing";
import { accessible, defaults, renders, hidden } from "../../tests/commonTests";
import { E2EElement, E2EPage, newE2EPage } from "@stencil/core/testing";
import { accessible, defaults, hidden, renders, t9n } from "../../tests/commonTests";
import { html } from "../../../support/formatting";
import { CSS } from "./resources";
import { getElementRect } from "../../tests/utils";

describe("calcite-tab-nav", () => {
describe("defaults", () => {
Expand All @@ -19,6 +21,10 @@ describe("calcite-tab-nav", () => {
accessible("calcite-tab-nav");
});

describe("translation support", () => {
t9n("calcite-tab-nav");
});

it("emits on user interaction", async () => {
const page = await newE2EPage();
await page.setContent(
Expand Down Expand Up @@ -110,4 +116,169 @@ describe("calcite-tab-nav", () => {
await page.keyboard.press("Home");
expect(await page.evaluate(() => document.activeElement.id)).toBe("tab1");
});

describe("responsiveness", () => {
const tabsHTML = html`
<calcite-tabs>
<calcite-tab-nav slot="title-group">
<calcite-tab-title selected>Tab 1 Title</calcite-tab-title>
<calcite-tab-title>Tab 2 Title</calcite-tab-title>
<calcite-tab-title>Tab 3 Title</calcite-tab-title>
<calcite-tab-title>Tab 4 Title</calcite-tab-title>
<calcite-tab-title>Tab 5 Title</calcite-tab-title>
<calcite-tab-title>Tab 6 Title</calcite-tab-title>
<calcite-tab-title>Tab 7 Title</calcite-tab-title>
<calcite-tab-title>Tab 8 Title</calcite-tab-title>
</calcite-tab-nav>
<calcite-tab selected>Tab 1 Content</calcite-tab>
<calcite-tab>Tab 2 Content</calcite-tab>
<calcite-tab>Tab 3 Content</calcite-tab>
<calcite-tab>Tab 4 Content</calcite-tab>
<calcite-tab>Tab 5 Content</calcite-tab>
<calcite-tab>Tab 6 Content</calcite-tab>
<calcite-tab>Tab 7 Content</calcite-tab>
<calcite-tab>Tab 8 Content</calcite-tab>
</calcite-tabs>
`;
const sizeShowingAllTabs = { width: 1200, height: 1200 };
const sizeShowingSomeTabs = { width: 350, height: 1200 };

let page: E2EPage;
let scrollBackButton: E2EElement;
let scrollForwardButton: E2EElement;
let scrollContainer: E2EElement;

async function assertScrollButtonVisibility(
backExpectedVisibility: boolean,
expectedForwardVisibility: boolean,
): Promise<void> {
/* we need to find the scroll buttons to ensure visibility */
expect(await scrollBackButton.isVisible()).toBe(backExpectedVisibility);
expect(await scrollForwardButton.isVisible()).toBe(expectedForwardVisibility);
}

beforeEach(async () => {
page = await newE2EPage();
await page.setContent(tabsHTML);
await page.setViewport(sizeShowingSomeTabs);
await page.waitForChanges();
scrollBackButton = await page.find(`calcite-tab-nav >>> .${CSS.scrollBackwardContainerButton}`);
scrollForwardButton = await page.find(`calcite-tab-nav >>> .${CSS.scrollForwardContainerButton}`);
scrollContainer = await page.find(`calcite-tab-nav >>> .${CSS.tabTitleSlotWrapper}`);
});

it("shows scrolling buttons if tab-titles overflow", async () => {
await assertScrollButtonVisibility(false, true);

await page.click("calcite-tab-title:nth-child(4)");
await page.waitForChanges();

await assertScrollButtonVisibility(true, true);

await page.setViewport(sizeShowingAllTabs);
await page.waitForChanges();

await assertScrollButtonVisibility(false, false);

await page.setViewport(sizeShowingSomeTabs);
await page.waitForChanges();

await assertScrollButtonVisibility(false, true);

await page.click("calcite-tab-title:nth-child(4)");
await page.waitForChanges();

await assertScrollButtonVisibility(true, true);
});

it("scrolling tabs via buttons", async () => {
await assertScrollButtonVisibility(false, true);

let scrollEnd = scrollContainer.waitForEvent("scrollend");
await scrollForwardButton.click();
await page.waitForChanges();
await scrollEnd;

await assertScrollButtonVisibility(true, true);

scrollEnd = scrollContainer.waitForEvent("scrollend");
await scrollForwardButton.click();
await page.waitForChanges();
await scrollEnd;

await assertScrollButtonVisibility(true, false);

scrollEnd = scrollContainer.waitForEvent("scrollend");
await scrollBackButton.click();
await page.waitForChanges();
await scrollEnd;

await assertScrollButtonVisibility(true, true);

scrollEnd = scrollContainer.waitForEvent("scrollend");
await scrollBackButton.click();
await page.waitForChanges();
await scrollEnd;

await assertScrollButtonVisibility(false, true);
});

it("scrolling tabs via mouse wheel", async () => {
await assertScrollButtonVisibility(false, true);

const tabNavBounds = await getElementRect(page, "calcite-tab-nav");
await page.mouse.move(tabNavBounds.x + tabNavBounds.width / 2, tabNavBounds.y + tabNavBounds.height / 2);
await page.mouse.wheel({ deltaY: 200 });
await page.waitForChanges();

await assertScrollButtonVisibility(true, true);

await page.mouse.wheel({ deltaY: 200 });
await page.waitForChanges();

await assertScrollButtonVisibility(true, false);

await page.mouse.wheel({ deltaY: -200 });
await page.waitForChanges();

await assertScrollButtonVisibility(true, true);

await page.mouse.wheel({ deltaY: -200 });
await page.waitForChanges();

await assertScrollButtonVisibility(false, true);
});

it("scrolls into view clipped start or end tab-title when selected", async () => {
const tabNavBounds = await getElementRect(page, "calcite-tab-nav");
await page.mouse.move(tabNavBounds.x + tabNavBounds.width / 2, tabNavBounds.y + tabNavBounds.height / 2);
await page.waitForChanges();

await page.mouse.wheel({ deltaY: 1 });
await page.waitForChanges();

await assertScrollButtonVisibility(true, true);

let scrollEnd = scrollContainer.waitForEvent("scrollend");
const firstTab = await page.find("calcite-tab-title:first-child");
await firstTab.callMethod("click"); // we call method to avoid having E2E click element in the middle, which would hit the scroll button
await page.waitForChanges();
await scrollEnd;

await assertScrollButtonVisibility(false, true);

await page.mouse.wheel({ deltaY: 180 });
await page.waitForChanges();

await assertScrollButtonVisibility(true, true);

scrollEnd = scrollContainer.waitForEvent("scrollend");
const lastTab = await page.find("calcite-tab-title:last-child");
await lastTab.callMethod("click"); // we call method to avoid having E2E click element in the middle, which would hit the scroll button
await page.waitForChanges();
await scrollEnd;

await assertScrollButtonVisibility(true, false);
});
});
});
Loading
Loading