From b76b8a746ec1c0233ce21c62f93ebb12676468a6 Mon Sep 17 00:00:00 2001 From: Dawid Date: Mon, 28 Jun 2021 13:45:12 +0200 Subject: [PATCH] feat(tabs): add tabsHeaderWidth prop `tabsHeaderWidth` prop will set a width to `TabsHeader` component. It will allow users with very long title to make well aligned `Tabs` if there will be more of tabs group than one fixes: #4024 --- .../tabs-header/tabs-header.style.js | 1 + src/components/tabs/tabs.component.js | 14 +++++ src/components/tabs/tabs.d.ts | 4 ++ src/components/tabs/tabs.spec.js | 63 +++++++++++++++++++ src/components/tabs/tabs.stories.js | 3 +- src/components/tabs/tabs.stories.mdx | 58 +++++++++++++++++ src/components/tabs/tabs.style.js | 15 ++++- 7 files changed, 156 insertions(+), 2 deletions(-) diff --git a/src/components/tabs/__internal__/tabs-header/tabs-header.style.js b/src/components/tabs/__internal__/tabs-header/tabs-header.style.js index a123eceea4..fd80524377 100644 --- a/src/components/tabs/__internal__/tabs-header/tabs-header.style.js +++ b/src/components/tabs/__internal__/tabs-header/tabs-header.style.js @@ -16,6 +16,7 @@ const StyledTabsHeaderWrapper = styled.div` css` overflow-y: auto; padding: 2px; + box-sizing: border-box; ${!isInSidebar && css` diff --git a/src/components/tabs/tabs.component.js b/src/components/tabs/tabs.component.js index 5ff238cd4c..a0f6369344 100644 --- a/src/components/tabs/tabs.component.js +++ b/src/components/tabs/tabs.component.js @@ -34,6 +34,7 @@ const Tabs = ({ borders = "off", variant = "default", validationStatusOverride, + headerWidth, ...rest }) => { const tabRefs = useRef([]); @@ -319,6 +320,7 @@ const Tabs = ({ {...tagComponent("tabs", rest)} isInSidebar={isInSidebar} mt={position === "left" || isInSidebar ? "0px" : "15px"} + headerWidth={headerWidth} {...rest} > {renderTabHeaders()} @@ -356,6 +358,18 @@ Tabs.propTypes = { "no right side", "no sides", ]), + /** sets width to the tab headers. Can be any valid CSS string. + * The headerWidth prop works only for `position="left"` + */ + headerWidth: (props, propName, componentName) => { + if (props.position !== "left" && props[propName] !== undefined) { + return new Error( + `Invalid usage of prop ${propName} in ${componentName}. The ${propName} can be used only if position is set to left` + ); + } + + return null; + }, /** Adds an alternate styling variant to the tab titles. */ variant: PropTypes.oneOf(["default", "alternate"]), /** An object to support overriding validation statuses, when the Tabs have custom targets for example. diff --git a/src/components/tabs/tabs.d.ts b/src/components/tabs/tabs.d.ts index fb334c5853..d82a9c7a9e 100644 --- a/src/components/tabs/tabs.d.ts +++ b/src/components/tabs/tabs.d.ts @@ -26,6 +26,10 @@ export interface TabsProps extends MarginProps { borders?: "off" | "on" | "no left side" | "no right side" | "no sides"; /** Adds an alternate styling variant to the tab titles. */ variant?: "default" | "alternate"; + /** sets width to the tab headers. Can be any valid CSS string. + * The headerWidth prop works only for `position="left"` + */ + headerWidth?: string; /** An object to support overriding validation statuses, when the Tabs have custom targets for example. * The `id` property should match the `tabId`s for the rendered Tabs. */ diff --git a/src/components/tabs/tabs.spec.js b/src/components/tabs/tabs.spec.js index 7cf11f0050..c8799f3e55 100644 --- a/src/components/tabs/tabs.spec.js +++ b/src/components/tabs/tabs.spec.js @@ -2,16 +2,19 @@ import React, { useEffect, useContext } from "react"; import { mount, shallow } from "enzyme"; import { act } from "react-dom/test-utils"; +import { css } from "styled-components"; import TabTitle from "./__internal__/tab-title/tab-title.component"; import { Tabs, Tab } from "./tabs.component"; import { TabContext } from "./tab/index"; import { rootTagTest } from "../../utils/helpers/tags/tags-specs/tags-specs"; import StyledTabs from "./tabs.style"; +import StyledTab from "./tab/tab.style"; import { assertStyleMatch, simulate, testStyledSystemMargin, } from "../../__spec_helper__/test-utils"; +import { StyledTabsHeaderWrapper } from "./__internal__/tabs-header/tabs-header.style"; import { SidebarContext } from "../drawer"; function render(props) { @@ -155,6 +158,66 @@ describe("Tabs", () => { { mt: "0px" } ); + describe("when `headerWidth` is provided", () => { + describe.each(["35%", "100px", "5em"])( + "and value of %s is provided", + (headerWidth) => { + it("should render correct `width` in `TabsHeader` component, and `Tab` `width` should be `auto`", () => { + const wrapper = mount( + + + TabContent + + + ); + + assertStyleMatch( + { + width: headerWidth, + }, + wrapper, + { + modifier: css` + ${StyledTabsHeaderWrapper} + `, + } + ); + + assertStyleMatch( + { + width: "auto", + }, + wrapper, + { + modifier: css` + ${StyledTab} + `, + } + ); + }); + } + ); + }); + + describe('when `headerWidth` is provided, and `position="top"`', () => { + it(" should render console error", () => { + jest.spyOn(global.console, "error").mockImplementation(() => {}); + + mount( + + + TabContent + + + ); + + // eslint-disable-next-line no-console + expect(console.error).toHaveBeenCalledWith( + "Warning: Failed prop type: Invalid usage of prop headerWidth in Tabs. The headerWidth can be used only if position is set to left\n in Tabs" + ); + }); + }); + describe("when passing custom className as a prop", () => { it("adds it to the classList", () => { const wrapper = render({ className: "class" }); diff --git a/src/components/tabs/tabs.stories.js b/src/components/tabs/tabs.stories.js index 1aa4f9d06e..3a9024495e 100644 --- a/src/components/tabs/tabs.stories.js +++ b/src/components/tabs/tabs.stories.js @@ -1,5 +1,5 @@ import React from "react"; -import { select, withKnobs } from "@storybook/addon-knobs"; +import { select, withKnobs, text } from "@storybook/addon-knobs"; import { Tabs, Tab } from "./tabs.component"; export default { @@ -19,6 +19,7 @@ export default { export const Default = () => { return ( +### With headerWidth +The `headerWidth` prop works only if prop `position` is set to `left`. + + + + {() => { + return ( +
+ + + Content for tab 1 + + + Content for tab 2 + + + + + Content for tab 1 + + + Content for tab 2 + + +
+ ); + }} +
+
+ ### With custom spacing The `Tabs`component also allows you to pass custom margin spacing, whilst `Tab` components support custom padding spacings. The spacing modifiers support being passed either a number between 1 and 8 that is then multiplied by `8px` or any valid diff --git a/src/components/tabs/tabs.style.js b/src/components/tabs/tabs.style.js index 5f7481183c..48cc5e6286 100644 --- a/src/components/tabs/tabs.style.js +++ b/src/components/tabs/tabs.style.js @@ -2,13 +2,26 @@ import styled, { css } from "styled-components"; import { margin } from "styled-system"; import PropTypes from "prop-types"; import BaseTheme from "../../style/themes/base"; +import { StyledTabsHeaderWrapper } from "./__internal__/tabs-header/tabs-header.style"; +import StyledTab from "./tab/tab.style"; const StyledTabs = styled.div` - ${({ position, inSidebar, theme }) => css` + ${({ position, inSidebar, theme, headerWidth }) => css` color: ${theme.text.color}; ${position === "left" && css` + ${headerWidth && + css` + ${StyledTabsHeaderWrapper} { + width: ${headerWidth}; + } + + ${StyledTab} { + width: auto; + } + `} + ${!inSidebar && css` display: flex;