Skip to content

Commit

Permalink
Merge pull request #4202 from Sage/FE-4161-tabs-changing-width
Browse files Browse the repository at this point in the history
feat(tabs): add headerWidth prop (FE-4161)
  • Loading branch information
Dawid Zarzycki authored Jul 6, 2021
2 parents d611f65 + bfd85df commit 4626892
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const StyledTabsHeaderWrapper = styled.div`
css`
overflow-y: auto;
padding: 2px;
box-sizing: border-box;
${!isInSidebar &&
css`
Expand Down
14 changes: 14 additions & 0 deletions src/components/tabs/tabs.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const Tabs = ({
borders = "off",
variant = "default",
validationStatusOverride,
headerWidth,
...rest
}) => {
const tabRefs = useRef([]);
Expand Down Expand Up @@ -319,6 +320,7 @@ const Tabs = ({
{...tagComponent("tabs", rest)}
isInSidebar={isInSidebar}
mt={position === "left" || isInSidebar ? "0px" : "15px"}
headerWidth={headerWidth}
{...rest}
>
{renderTabHeaders()}
Expand Down Expand Up @@ -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.
Expand Down
4 changes: 4 additions & 0 deletions src/components/tabs/tabs.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down
63 changes: 63 additions & 0 deletions src/components/tabs/tabs.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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(
<Tabs position="left" headerWidth={headerWidth}>
<Tab title="Tab Title 1" tabId="uniqueid1">
TabContent
</Tab>
</Tabs>
);

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(
<Tabs position="top" headerWidth="500px">
<Tab title="Tab Title 1" tabId="uniqueid1">
TabContent
</Tab>
</Tabs>
);

// 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" });
Expand Down
3 changes: 2 additions & 1 deletion src/components/tabs/tabs.stories.js
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -19,6 +19,7 @@ export default {
export const Default = () => {
return (
<Tabs
headerWidth={text("headerWidth", "")}
align={select("align", ["left", "right"], "left")}
position={select("position", ["top", "left"], "top")}
borders={select(
Expand Down
58 changes: 58 additions & 0 deletions src/components/tabs/tabs.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1520,6 +1520,64 @@ By setting the `variant` prop to "alternate" it is possible to apply some additi
</Story>
</Preview>

### With headerWidth
The `headerWidth` prop works only if prop `position` is set to `left`.

<Preview>
<Story name="with headerWidth">
{() => {
return (
<div style={{ padding: "4px" }}>
<Tabs headerWidth="400px" align="left" position="left">
<Tab
errorMessage="error"
warningMessage="warning"
infoMessage="info"
tabId="tab-1"
title="Very long title for Tab 1 without with prop it would be not well aligned with the second Tabs group"
key="tab-1"
>
Content for tab 1
</Tab>
<Tab
errorMessage="error"
warningMessage="warning"
infoMessage="info"
tabId="tab-2"
title="Tab 2"
key="tab-2"
>
Content for tab 2
</Tab>
</Tabs>
<Tabs headerWidth="400px" align="left" position="left">
<Tab
errorMessage="error"
warningMessage="warning"
infoMessage="info"
tabId="tab-1"
title="Tab 1"
key="tab-1"
>
Content for tab 1
</Tab>
<Tab
errorMessage="error"
warningMessage="warning"
infoMessage="info"
tabId="tab-2"
title="Tab 2"
key="tab-2"
>
Content for tab 2
</Tab>
</Tabs>
</div>
);
}}
</Story>
</Preview>

### 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
Expand Down
15 changes: 14 additions & 1 deletion src/components/tabs/tabs.style.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 4626892

Please sign in to comment.