From e1f3d260d2687542a43dbf9c50781afa710ce86f Mon Sep 17 00:00:00 2001 From: Misha Moroshko Date: Tue, 4 Feb 2020 13:08:18 +1100 Subject: [PATCH] Add social links to Footer and other extra props to components --- src/components/Button.js | 33 ++- src/components/Checkbox.js | 27 ++- src/components/Container.js | 52 +++-- src/components/Container.test.js | 29 +++ src/components/DatePicker.js | 27 ++- src/components/Flex.js | 50 +++-- src/components/Footer.js | 190 +++++++++++++----- src/components/Footer.test.js | 31 +++ src/components/Frequency.js | 37 ++-- src/components/Header.js | 10 +- src/components/Icon.js | 16 +- src/components/Input.js | 29 ++- src/components/Link.js | 58 ++++-- src/components/List.js | 59 +++--- src/components/RadioGroup.js | 30 ++- src/components/Select.js | 30 ++- src/components/Text.js | 32 +-- src/components/TimeSpan.js | 27 ++- src/components/internal/Logo.js | 2 +- src/hooks/useBackground.js | 18 ++ src/hooks/useContainer.js | 27 --- src/hooks/useListType.js | 15 ++ src/hooks/useResponsivePropsCSS.test.js | 39 ++-- src/icons/facebook.js | 39 ++++ src/icons/instagram.js | 39 ++++ src/icons/linkedin.js | 39 ++++ src/icons/twitter.js | 39 ++++ src/logos/gem.js | 30 +-- src/logos/latitude.js | 30 +-- src/themes/default/icon.js | 22 -- src/themes/default/index.js | 7 +- src/themes/default/link.js | 35 ++-- src/themes/tokens.js | 3 +- src/utils/css.js | 77 ++++--- website/src/components/kitchen-sink/Footer.js | 94 ++++++++- website/src/components/kitchen-sink/List.js | 2 +- website/src/pages/components/flex/index.js | 21 ++ website/src/pages/components/footer/index.js | 75 ++++++- website/src/pages/components/list/index.js | 12 +- website/src/themes/website/link.js | 12 +- 40 files changed, 1039 insertions(+), 405 deletions(-) create mode 100644 src/hooks/useBackground.js delete mode 100644 src/hooks/useContainer.js create mode 100644 src/hooks/useListType.js create mode 100644 src/icons/facebook.js create mode 100644 src/icons/instagram.js create mode 100644 src/icons/linkedin.js create mode 100644 src/icons/twitter.js delete mode 100644 src/themes/default/icon.js diff --git a/src/components/Button.js b/src/components/Button.js index 3696babc..ef9e9769 100644 --- a/src/components/Button.js +++ b/src/components/Button.js @@ -1,10 +1,11 @@ import React from "react"; import PropTypes from "prop-types"; import useTheme from "../hooks/useTheme"; -import useContainer from "../hooks/useContainer"; -import { responsiveMargin } from "../utils/css"; -import { responsiveMarginType } from "../hooks/useResponsiveProp"; +import useBackground from "../hooks/useBackground"; import useResponsivePropsCSS from "../hooks/useResponsivePropsCSS"; +import { responsiveMarginType } from "../hooks/useResponsiveProp"; +import { responsiveMargin } from "../utils/css"; +import { mergeProps } from "../utils/component"; const VARIANTS = ["primary", "secondary", "icon"]; const COLORS = ["highlight.blue.t100", "white"]; @@ -23,10 +24,24 @@ Button.COLORS = COLORS; Button.TYPES = TYPES; Button.DEFAULT_PROPS = DEFAULT_PROPS; -function Button(_props) { - const props = { ...DEFAULT_PROPS, ..._props }; +function Button(props) { + const theme = useTheme(); + const { background } = useBackground(); + const inheritedColor = + background === "primary.blue.t100" ? "white" : "highlight.blue.t100"; + const inheritedProps = { + color: inheritedColor + }; + const mergedProps = mergeProps(props, DEFAULT_PROPS, inheritedProps, { + variant: variant => VARIANTS.includes(variant), + color: color => COLORS.includes(color), + isFullWidth: isFullWidth => typeof isFullWidth === "boolean", + isDisabled: isDisabled => typeof isDisabled === "boolean", + type: type => TYPES.includes(type) + }); const { variant, + color, isFullWidth, isDisabled, type, @@ -36,14 +51,10 @@ function Button(_props) { __internal__keyboardFocus, __internal__hover, __internal__active - } = props; - const theme = useTheme(); - const { buttonColor } = useContainer(); - const responsivePropsCSS = useResponsivePropsCSS(props, DEFAULT_PROPS, { + } = mergedProps; + const responsivePropsCSS = useResponsivePropsCSS(mergedProps, DEFAULT_PROPS, { margin: responsiveMargin }); - const color = - !COLORS.includes(_props.color) && buttonColor ? buttonColor : props.color; const colorStr = color === DEFAULT_PROPS.color ? "default" : color; const css = { ...theme.button, diff --git a/src/components/Checkbox.js b/src/components/Checkbox.js index 2812c0d7..63db0dcf 100644 --- a/src/components/Checkbox.js +++ b/src/components/Checkbox.js @@ -1,11 +1,12 @@ import React, { useState } from "react"; import PropTypes from "prop-types"; import nanoid from "nanoid"; -import Field from "./internal/Field"; -import VisuallyHidden from "./VisuallyHidden"; -import useContainer from "../hooks/useContainer"; import useTheme from "../hooks/useTheme"; +import useBackground from "../hooks/useBackground"; import useValidation from "../hooks/useValidation"; +import { mergeProps } from "../utils/component"; +import Field from "./internal/Field"; +import VisuallyHidden from "./VisuallyHidden"; const COLORS = ["grey.t05", "white"]; @@ -69,10 +70,19 @@ CheckboxIcon.propTypes = { isChecked: PropTypes.bool.isRequired }; -function Checkbox(_props) { - const props = { ...DEFAULT_PROPS, ..._props }; +function Checkbox(props) { + const { inputColor } = useBackground(); + const inheritedProps = { + color: inputColor + }; + const mergedProps = mergeProps(props, DEFAULT_PROPS, inheritedProps, { + color: color => COLORS.includes(color), + isOptional: isOptional => typeof isOptional === "boolean", + isDisabled: isDisabled => typeof isDisabled === "boolean" + }); const { label, + color, isOptional, helpText, isDisabled, @@ -81,18 +91,15 @@ function Checkbox(_props) { children, testId, __internal__keyboardFocus - } = props; + } = mergedProps; const theme = useTheme(); - const { inputColor } = useContainer(); - const color = - !COLORS.includes(_props.color) && inputColor ? inputColor : props.color; const [labelId] = useState(() => `radio-group-label-${nanoid()}`); const [inputId] = useState(() => `checkbox-${nanoid()}`); const [auxId] = useState(() => `checkbox-aux-${nanoid()}`); const [isTouched, setIsTouched] = useState(false); const { value: isChecked, errors } = data; const validate = useValidation({ - props, + props: mergedProps, extraData: { isTouched } diff --git a/src/components/Container.js b/src/components/Container.js index ca4088cc..7986059c 100644 --- a/src/components/Container.js +++ b/src/components/Container.js @@ -2,7 +2,8 @@ import React from "react"; import PropTypes from "prop-types"; import Text from "./Text"; import useTheme from "../hooks/useTheme"; -import { ContainerProvider } from "../hooks/useContainer"; +import { TextStyleProvider } from "../hooks/useTextStyle"; +import { BackgroundProvider } from "../hooks/useBackground"; import { responsiveMarginType, responsivePaddingType, @@ -42,7 +43,14 @@ Container.DEFAULT_PROPS = DEFAULT_PROPS; function Container(_props) { const props = { ...DEFAULT_PROPS, ..._props }; - const { bg, boxShadow, hasBreakpointWidth, children, testId } = props; + const { + bg, + boxShadow, + hasBreakpointWidth, + textStyle, + children, + testId + } = props; const theme = useTheme(); const responsivePropsCSS = useResponsivePropsCSS(props, DEFAULT_PROPS, { margin: responsiveMargin, @@ -88,22 +96,31 @@ function Container(_props) { : { boxShadow: tokens.shadows[boxShadow] || null }; - - return ( - -
- {children} -
-
+ let container = ( +
+ {children} +
); + + if (textStyle) { + container = ( + {container} + ); + } + + if (bg) { + container = {container}; + } + + return container; } Container.propTypes = { @@ -127,6 +144,7 @@ Container.propTypes = { ...responsivePaddingType, ...responsiveWidthType, ...responsiveHeightType, + ...responsivePropType("textStyle", PropTypes.oneOf(Text.TEXT_STYLES)), ...responsivePropType("textAlign", PropTypes.oneOf(Text.ALIGNS)), hasBreakpointWidth: PropTypes.bool, children: PropTypes.node, diff --git a/src/components/Container.test.js b/src/components/Container.test.js index e35dfdad..45520578 100644 --- a/src/components/Container.test.js +++ b/src/components/Container.test.js @@ -2,6 +2,7 @@ import React from "react"; import { render } from "../utils/test"; import "@testing-library/jest-dom/extend-expect"; import Container from "./Container"; +import Text from "./Text"; describe("Container", () => { it("no props", () => { @@ -53,6 +54,34 @@ describe("Container", () => { `); }); + it("with textStyle parent", () => { + const { getByText } = render( + + Hello World + + ); + const text = getByText("Hello World"); + + expect(text).toHaveStyle(` + font-size: 14px; + `); + }); + + it("with textStyle grandparent", () => { + const { getByText } = render( + + + Hello World + + + ); + const text = getByText("Hello World"); + + expect(text).toHaveStyle(` + font-size: 104px; + `); + }); + it("with textAlign", () => { const { getByText } = render( Hello World diff --git a/src/components/DatePicker.js b/src/components/DatePicker.js index 18963528..3db69b51 100644 --- a/src/components/DatePicker.js +++ b/src/components/DatePicker.js @@ -6,11 +6,12 @@ import { format as formatDate } from "date-fns"; import nanoid from "nanoid"; +import useBackground from "../hooks/useBackground"; +import useValidation from "../hooks/useValidation"; +import { mergeProps } from "../utils/component"; import Field from "./internal/Field"; -import useContainer from "../hooks/useContainer"; -import Grid from "./Grid"; import Input from "./Input"; -import useValidation from "../hooks/useValidation"; +import Grid from "./Grid"; const COLORS = ["grey.t05", "white"]; @@ -118,9 +119,18 @@ function getHelpText(day, month, year, defaultHelpText) { return formatDate(new Date(yearInt, monthInt - 1, dayInt), "d MMMM, yyyy"); } -function DatePicker(_props) { - const props = { ...DEFAULT_PROPS, ..._props }; +function DatePicker(props) { + const { inputColor } = useBackground(); + const inheritedProps = { + color: inputColor + }; + const mergedProps = mergeProps(props, DEFAULT_PROPS, inheritedProps, { + color: color => COLORS.includes(color), + isOptional: isOptional => typeof isOptional === "boolean", + isDisabled: isDisabled => typeof isDisabled === "boolean" + }); const { + color, label, isOptional, helpText: helpTextProp, @@ -128,10 +138,7 @@ function DatePicker(_props) { data, onChange, testId - } = props; - const { inputColor } = useContainer(); - const color = - !COLORS.includes(_props.color) && inputColor ? inputColor : props.color; + } = mergedProps; const [labelId] = useState(() => `date-picker-${nanoid()}`); const [auxId] = useState(() => `date-picker-aux-${nanoid()}`); const [isTouched, setIsTouched] = useState({ @@ -145,7 +152,7 @@ function DatePicker(_props) { [value.day, value.month, value.year, helpTextProp] ); const validate = useValidation({ - props, + props: mergedProps, extraData: { isTouched } diff --git a/src/components/Flex.js b/src/components/Flex.js index 8c08c670..31b1d79b 100644 --- a/src/components/Flex.js +++ b/src/components/Flex.js @@ -2,10 +2,14 @@ import React from "react"; import PropTypes from "prop-types"; import useResponsivePropsCSS from "../hooks/useResponsivePropsCSS"; import { + responsiveMarginType, + responsiveWidthType, responsiveHeightType, responsivePropType } from "../hooks/useResponsiveProp"; import { + responsiveMargin, + responsiveWidth, responsiveHeight, responsiveFlexDirection, responsiveFlexGutter, @@ -39,6 +43,7 @@ const PLACE_ITEMS = [ const DEFAULT_PROPS = { direction: "row", + wrap: false, placeItems: "top left" }; @@ -48,44 +53,61 @@ Flex.DEFAULT_PROPS = DEFAULT_PROPS; function Flex(_props) { const props = { ...DEFAULT_PROPS, ..._props }; - const { children, testId } = props; + const { wrap, children, testId } = props; const childrenArray = React.Children.toArray(children); + const wrapperCSS = useResponsivePropsCSS(props, DEFAULT_PROPS, { + margin: responsiveMargin, + width: responsiveWidth, + height: responsiveHeight + }); const flexCSS = useResponsivePropsCSS(props, DEFAULT_PROPS, { - height: responsiveHeight, + gutter: responsiveFlexGutter("items-container"), placeItems: responsiveFlexPlaceItems, direction: responsiveFlexDirection }); const flexItemCSS = useResponsivePropsCSS(props, DEFAULT_PROPS, { - gutter: responsiveFlexGutter + gutter: responsiveFlexGutter("item") }); return (
- {isObjectEmpty(flexItemCSS) - ? childrenArray - : childrenArray.map((child, index) => ( -
- {child} -
- ))} +
+ {isObjectEmpty(flexItemCSS) + ? childrenArray + : childrenArray.map((child, index) => ( +
+ {child} +
+ ))} +
); } Flex.propTypes = { - ...responsivePropType("direction", PropTypes.oneOf(DIRECTIONS)), + ...responsiveMarginType, + ...responsiveWidthType, ...responsiveHeightType, + ...responsivePropType("direction", PropTypes.oneOf(DIRECTIONS)), ...responsivePropType( "gutter", PropTypes.oneOfType([PropTypes.number, PropTypes.string]) ), + wrap: PropTypes.bool, ...responsivePropType("placeItems", PropTypes.oneOf(PLACE_ITEMS)), children: PropTypes.node.isRequired, testId: PropTypes.string diff --git a/src/components/Footer.js b/src/components/Footer.js index d4bdd022..2b63b582 100644 --- a/src/components/Footer.js +++ b/src/components/Footer.js @@ -1,17 +1,13 @@ import React from "react"; import PropTypes from "prop-types"; import Container from "./Container"; -import Flex from "./Flex"; -import Text from "./Text"; -import Link from "./Link"; -import useTheme from "../hooks/useTheme"; +import { Flex, Text, Link, Icon } from ".."; import Logo from "./internal/Logo"; -import tokens from "../themes/tokens"; function HeaderLogo({ name, testId }) { return ( - - + + ); } @@ -21,11 +17,130 @@ HeaderLogo.propTypes = { testId: PropTypes.string }; +function HeaderSocial({ children, testId }) { + return ( + + + Connect with us + + + {children} + + + ); +} + +HeaderSocial.propTypes = { + children: PropTypes.node.isRequired, + testId: PropTypes.string +}; + +function SocialFacebook({ href, title, testId }) { + return ( + + + + ); +} + +SocialFacebook.propTypes = { + href: PropTypes.string.isRequired, + title: PropTypes.string.isRequired, + testId: PropTypes.string +}; + +function SocialTwitter({ href, title, testId }) { + return ( + + + + ); +} + +SocialTwitter.propTypes = { + href: PropTypes.string.isRequired, + title: PropTypes.string.isRequired, + testId: PropTypes.string +}; + +function SocialInstagram({ href, title, testId }) { + return ( + + + + ); +} + +SocialInstagram.propTypes = { + href: PropTypes.string.isRequired, + title: PropTypes.string.isRequired, + testId: PropTypes.string +}; + +function SocialLinkedIn({ href, title, testId }) { + return ( + + + + ); +} + +SocialLinkedIn.propTypes = { + href: PropTypes.string.isRequired, + title: PropTypes.string.isRequired, + testId: PropTypes.string +}; + function Header({ children, testId }) { return ( - {children} + + {children} + ); @@ -37,40 +152,15 @@ Header.propTypes = { }; function LegalLinks({ children, testId }) { - const theme = useTheme(); const links = React.Children.toArray(children).filter( // Ignore all children that aren't a Link child => child.type === Link ); - const linksContainerCSS = { - display: "flex", - flexDirection: "column", - flexWrap: "wrap", - justifyContent: "center", - [theme.minMediaQueries.md]: { - flexDirection: "row" - } - }; return ( -
- {links.map((link, index) => { - const linkItemCSS = { - whiteSpace: "nowrap", - marginTop: index > 0 && tokens.space[3], - [theme.minMediaQueries.md]: { - marginTop: 0, - marginLeft: index > 0 && tokens.space[4] - } - }; - - return ( -
- {link} -
- ); - })} -
+ + {links} + ); } @@ -81,15 +171,9 @@ LegalLinks.propTypes = { function LegalCopy({ children, testId }) { return ( - + {children} - + ); } @@ -101,11 +185,7 @@ LegalCopy.propTypes = { function Legal({ children, testId }) { return ( - + {children} @@ -126,10 +206,16 @@ Footer.propTypes = { testId: PropTypes.string }; -Header.Logo = HeaderLogo; Footer.Header = Header; -Legal.Links = LegalLinks; -Legal.Copy = LegalCopy; +Footer.Header.Logo = HeaderLogo; +Footer.Header.Social = HeaderSocial; +Footer.Header.Social.Facebook = SocialFacebook; +Footer.Header.Social.Twitter = SocialTwitter; +Footer.Header.Social.Instagram = SocialInstagram; +Footer.Header.Social.LinkedIn = SocialLinkedIn; + Footer.Legal = Legal; +Footer.Legal.Links = LegalLinks; +Footer.Legal.Copy = LegalCopy; export default Footer; diff --git a/src/components/Footer.test.js b/src/components/Footer.test.js index e2e121a4..adf4baed 100644 --- a/src/components/Footer.test.js +++ b/src/components/Footer.test.js @@ -11,6 +11,28 @@ describe("Footer", () => {