From 5e125af229a54e826076e6928e5e59a0058d5ecb Mon Sep 17 00:00:00 2001 From: Ed Leeks Date: Tue, 6 Apr 2021 15:24:57 +0100 Subject: [PATCH] feat(tabs, tab): surface styled system margin interface in tabs and padding interface in tab Adds styled-system/margin interface to `Tabs`. Adds styled-system/padding interface to `Tab`. --- .../tabs-header/tabs-header.spec.js | 8 +- .../tabs-header/tabs-header.style.js | 10 +-- src/components/tabs/tab/tab.component.js | 9 ++ src/components/tabs/tab/tab.d.ts | 3 +- src/components/tabs/tab/tab.spec.js | 30 ++++++- src/components/tabs/tab/tab.style.js | 5 ++ src/components/tabs/tabs.component.js | 20 +++-- src/components/tabs/tabs.d.ts | 3 +- src/components/tabs/tabs.spec.js | 41 ++++++++- src/components/tabs/tabs.stories.mdx | 88 ++++++++++++++++++- src/components/tabs/tabs.style.js | 6 +- 11 files changed, 197 insertions(+), 26 deletions(-) diff --git a/src/components/tabs/__internal__/tabs-header/tabs-header.spec.js b/src/components/tabs/__internal__/tabs-header/tabs-header.spec.js index 64af3a5885..20bca71dbf 100644 --- a/src/components/tabs/__internal__/tabs-header/tabs-header.spec.js +++ b/src/components/tabs/__internal__/tabs-header/tabs-header.spec.js @@ -30,7 +30,7 @@ describe("TabsHeader", () => { boxShadow: `inset 0px -2px 0px 0px ${baseTheme.tab.background}`, cursor: "pointer", listStyle: "none", - margin: "0 0 10px", + margin: "0", padding: "0", }, renderStyles() @@ -59,14 +59,14 @@ describe("TabsHeader", () => { { flexDirection: "column", boxShadow: `inset -2px 0px 0px 0px ${baseTheme.tab.background}`, - margin: "0 10px 0", + margin: "0 0 0 10px", }, wrapper.find(StyledTabsHeaderList) ); assertStyleMatch( { - width: "20%", + minWidth: "20%", overflowY: "auto", padding: "2px", }, @@ -146,7 +146,7 @@ describe("TabsHeader", () => { ); assertStyleMatch( { - width: "100%", + minWidth: "100%", }, wrapper.find(StyledTabsHeaderWrapper) ); 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 092353532d..a123eceea4 100644 --- a/src/components/tabs/__internal__/tabs-header/tabs-header.style.js +++ b/src/components/tabs/__internal__/tabs-header/tabs-header.style.js @@ -19,13 +19,13 @@ const StyledTabsHeaderWrapper = styled.div` ${!isInSidebar && css` - width: 20%; - margin: 0 10px 0; + min-width: 20%; + margin: 0 0 0 10px; `} ${isInSidebar && css` - width: 100%; + min-width: 100%; margin: auto; padding: 0px; `} @@ -43,7 +43,7 @@ const StyledTabsHeaderList = styled.ul` `} cursor: pointer; list-style: none; - margin: 0 0 10px; + margin: 0; padding: 0; ${({ align }) => @@ -66,7 +66,7 @@ const StyledTabsHeaderList = styled.ul` ${!isInSidebar && css` - margin: 0 10px 0; + margin: 0 0 0 10px; `} ${isInSidebar && diff --git a/src/components/tabs/tab/tab.component.js b/src/components/tabs/tab/tab.component.js index 8c8035af96..37ae3a9060 100644 --- a/src/components/tabs/tab/tab.component.js +++ b/src/components/tabs/tab/tab.component.js @@ -1,10 +1,16 @@ import React, { useCallback, useEffect, useState } from "react"; import PropTypes from "prop-types"; +import styledSystemPropTypes from "@styled-system/prop-types"; import StyledTab from "./tab.style"; import tagComponent from "../../../utils/helpers/tags/tags"; +import { filterStyledSystemPaddingProps } from "../../../style/utils"; const TabContext = React.createContext({}); +const paddingPropTypes = filterStyledSystemPaddingProps( + styledSystemPropTypes.space +); + const Tab = ({ ariaLabelledby, className, @@ -77,6 +83,8 @@ const Tab = ({ aria-labelledby={ariaLabelledby} position={position} {...tagComponent("tab", rest)} + {...(position === "top" ? { pt: "10px" } : { pl: "10px" })} + {...rest} > {!href && children} @@ -85,6 +93,7 @@ const Tab = ({ }; Tab.propTypes = { + ...paddingPropTypes, title: PropTypes.string, /** A unique ID to identify this specific tab. */ tabId: PropTypes.string.isRequired, diff --git a/src/components/tabs/tab/tab.d.ts b/src/components/tabs/tab/tab.d.ts index 960bddb833..d68553563a 100644 --- a/src/components/tabs/tab/tab.d.ts +++ b/src/components/tabs/tab/tab.d.ts @@ -1,6 +1,7 @@ import * as React from 'react'; +import { PaddingSpacingProps } from "../../../utils/helpers/options-helper"; -export interface TabProps { +export interface TabProps extends PaddingSpacingProps { title?: string; tabId: string; className?: string; diff --git a/src/components/tabs/tab/tab.spec.js b/src/components/tabs/tab/tab.spec.js index cc714f6788..ed5540943e 100644 --- a/src/components/tabs/tab/tab.spec.js +++ b/src/components/tabs/tab/tab.spec.js @@ -4,7 +4,10 @@ import { mount } from "enzyme"; import Tab from "./tab.component"; import Textbox from "../../../__experimental__/components/textbox"; import StyledTab from "./tab.style"; -import { assertStyleMatch } from "../../../__spec_helper__/test-utils"; +import { + assertStyleMatch, + testStyledSystemPadding, +} from "../../../__spec_helper__/test-utils"; const updateErrors = jest.fn(); const updateWarnings = jest.fn(); @@ -53,6 +56,31 @@ function renderStyles(props) { describe("Tab", () => { let wrapper; + + testStyledSystemPadding( + (props) => ( + + TabContent + + ), + { pt: "10px" } + ); + + testStyledSystemPadding( + (props) => ( + + TabContent + + ), + { pl: "10px" } + ); + it("has display property equals to none", () => { wrapper = renderStyles(); assertStyleMatch( diff --git a/src/components/tabs/tab/tab.style.js b/src/components/tabs/tab/tab.style.js index a63af7e80a..0873e2f837 100644 --- a/src/components/tabs/tab/tab.style.js +++ b/src/components/tabs/tab/tab.style.js @@ -1,5 +1,7 @@ import styled, { css } from "styled-components"; import propTypes from "prop-types"; +import { padding } from "styled-system"; +import BaseTheme from "../../../style/themes/base"; const StyledTab = styled.div` display: none; @@ -13,11 +15,14 @@ const StyledTab = styled.div` css` width: 80%; `} + + ${padding} `} `; StyledTab.defaultProps = { position: "top", + theme: BaseTheme, }; StyledTab.propTypes = { diff --git a/src/components/tabs/tabs.component.js b/src/components/tabs/tabs.component.js index 1c69012ac9..1b4a7429b3 100644 --- a/src/components/tabs/tabs.component.js +++ b/src/components/tabs/tabs.component.js @@ -7,6 +7,7 @@ import React, { useState, } from "react"; import PropTypes from "prop-types"; +import styledSystemPropTypes from "@styled-system/prop-types"; import Tab from "./tab"; import Event from "../../utils/helpers/events/events"; import tagComponent from "../../utils/helpers/tags/tags"; @@ -15,6 +16,11 @@ import StyledTabs from "./tabs.style"; import TabsHeader from "./__internal__/tabs-header"; import TabTitle from "./__internal__/tab-title"; import { SidebarContext } from "../drawer"; +import { filterStyledSystemMarginProps } from "../../style/utils"; + +const marginPropTypes = filterStyledSystemMarginProps( + styledSystemPropTypes.space +); const Tabs = ({ align = "left", @@ -26,7 +32,7 @@ const Tabs = ({ position = "top", setLocation = true, extendedLine = true, - size = "default", + size, borders = "off", variant = "default", validationStatusOverride, @@ -41,6 +47,7 @@ const Tabs = ({ const [tabsWarnings, setTabsWarnings] = useState({}); const [tabsInfos, setTabsInfos] = useState({}); const _window = Browser.getWindow(); + const isInSidebar = !!(sidebarContext && sidebarContext.isInSidebar); useLayoutEffect(() => { if (firstRender.current) { @@ -258,7 +265,7 @@ const Tabs = ({ error={tabHasError} warning={tabHasWarning} info={tabHasInfo} - size={size} + size={size || "default"} borders={borders !== "off"} siblings={siblings} titlePosition={titlePosition} @@ -269,7 +276,7 @@ const Tabs = ({ noLeftBorder={["no left side", "no sides"].includes(borders)} noRightBorder={["no right side", "no sides"].includes(borders)} customLayout={customLayout} - isInSidebar={Boolean(sidebarContext && sidebarContext.isInSidebar)} + isInSidebar={isInSidebar} /> ); @@ -284,7 +291,7 @@ const Tabs = ({ extendedLine={extendedLine} alternateStyling={variant === "alternate" || !!sidebarContext} noRightBorder={["no right side", "no sides"].includes(borders)} - isInSidebar={Boolean(sidebarContext && sidebarContext.isInSidebar)} + isInSidebar={isInSidebar} > {tabTitles} @@ -354,7 +361,9 @@ const Tabs = ({ updateErrors={updateErrors} updateWarnings={updateWarnings} {...tagComponent("tabs", rest)} - isInSidebar={Boolean(sidebarContext && sidebarContext.isInSidebar)} + isInSidebar={isInSidebar} + mt={position === "left" || isInSidebar ? "0px" : "15px"} + {...rest} > {renderTabHeaders()} {renderTabs()} @@ -363,6 +372,7 @@ const Tabs = ({ }; Tabs.propTypes = { + ...marginPropTypes, /** @ignore @private */ className: PropTypes.string, /** Prevent rendering of hidden tabs, by default this is set to true and therefore all tabs will be rendered */ diff --git a/src/components/tabs/tabs.d.ts b/src/components/tabs/tabs.d.ts index fa587c7336..7435485951 100644 --- a/src/components/tabs/tabs.d.ts +++ b/src/components/tabs/tabs.d.ts @@ -1,7 +1,8 @@ import * as React from 'react'; import Tab from './tab'; +import { MarginSpacingProps } from "../../utils/helpers/options-helper"; -export interface TabsProps { +export interface TabsProps extends MarginSpacingProps { className?: string; renderHiddenTabs: boolean; selectedTabId: string; diff --git a/src/components/tabs/tabs.spec.js b/src/components/tabs/tabs.spec.js index 7d7b50ecca..c8274b284f 100644 --- a/src/components/tabs/tabs.spec.js +++ b/src/components/tabs/tabs.spec.js @@ -7,7 +7,11 @@ 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 { assertStyleMatch, simulate } from "../../__spec_helper__/test-utils"; +import { + assertStyleMatch, + simulate, + testStyledSystemMargin, +} from "../../__spec_helper__/test-utils"; import { SidebarContext } from "../drawer"; function render(props) { @@ -117,6 +121,40 @@ const MockWrapper = ({ }; describe("Tabs", () => { + testStyledSystemMargin( + (props) => ( + + + TabContent + + + ), + { mt: "15px" } + ); + + testStyledSystemMargin( + (props) => ( + + + TabContent + + + ), + { mt: "0px" } + ); + describe("when passing custom className as a prop", () => { it("adds it to the classList", () => { const wrapper = render({ className: "class" }); @@ -131,7 +169,6 @@ describe("Tabs", () => { { display: "flex", width: "100%", - marginTop: "0", }, wrapper ); diff --git a/src/components/tabs/tabs.stories.mdx b/src/components/tabs/tabs.stories.mdx index e023a4e19f..1e44b27e30 100644 --- a/src/components/tabs/tabs.stories.mdx +++ b/src/components/tabs/tabs.stories.mdx @@ -6,6 +6,7 @@ import Icon from '../icon'; import Pill from '../pill'; import { Row, Column } from '../row'; import { ActionPopover, ActionPopoverItem } from '../action-popover'; +import StyledSystemProps from '../../../.storybook/utils/styled-system-props'; @@ -22,7 +23,7 @@ Switch between content panes or filtered views of tables. ## Quick Start ```javascript -import {Tabs, Tab} from "carbon-react/lib/components/tabs; +import {Tabs, Tab} from "carbon-react/lib/components/tabs"; ``` ## Designer Notes @@ -1354,7 +1355,7 @@ can be used to render the `title` "before" (default) or "after" the additional e -### Custom layout +### With custom layout It is possible to override the static layout of the `TabTitle` by passing in your own custom layouts to the `customLayout` prop to the `Tab` component. @@ -1518,10 +1519,89 @@ By setting the `variant` prop to "alternate" it is possible to apply some additi +### 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 +CSS string. + + + + {() => { + return ( +
+ + + Content for tab 1 + + + Content for tab 2 + + + Content for tab 3 + + + Content for tab 4 + + + Content for tab 5 + + +
+ ); + }} +
+
+ ## Props ### Tabs - + + ### Tab - + + diff --git a/src/components/tabs/tabs.style.js b/src/components/tabs/tabs.style.js index 9fe10a9ab8..5f7481183c 100644 --- a/src/components/tabs/tabs.style.js +++ b/src/components/tabs/tabs.style.js @@ -1,10 +1,9 @@ import styled, { css } from "styled-components"; +import { margin } from "styled-system"; import PropTypes from "prop-types"; import BaseTheme from "../../style/themes/base"; const StyledTabs = styled.div` - margin-top: 15px; - ${({ position, inSidebar, theme }) => css` color: ${theme.text.color}; @@ -16,9 +15,10 @@ const StyledTabs = styled.div` `} width: 100%; - margin-top: 0; `} `} + + ${margin} `; StyledTabs.defaultProps = {