diff --git a/catalog/pages/icons/index.js b/catalog/pages/icons/index.js index f50907159..8c30f4b41 100644 --- a/catalog/pages/icons/index.js +++ b/catalog/pages/icons/index.js @@ -10,7 +10,13 @@ import { VenueIcon, VipIcon, CrossIcon, - StarIcon + StarIcon, + TwitterIcon, + InstagramIcon, + YoutubeIcon, + FacebookIcon, + USIcon, + CAIcon } from "../../../src/components/Icons/index"; import { Container, Row, Column } from "../../../src/components/Grid"; import colors from "../../../src/theme/colors"; @@ -42,6 +48,12 @@ export default { VipIcon, CrossIcon, StarIcon, + TwitterIcon, + InstagramIcon, + FacebookIcon, + YoutubeIcon, + USIcon, + CAIcon, Container, Row, Column, diff --git a/catalog/pages/icons/index.md b/catalog/pages/icons/index.md index 95a044a4f..fcb46c160 100644 --- a/catalog/pages/icons/index.md +++ b/catalog/pages/icons/index.md @@ -107,6 +107,39 @@ rows:
Parking Icon
Parking Icon
+ + +
Twitter Icon
+
Twitter Icon
+
+ + +
Facebook Icon
+
Facebook Icon
+
+ + +
Instagram Icon
+
Instagram Icon
+
+ + + +
Youtube Icon
+
Youtube Icon
+
+ + +
US Icon
+
US Icon
+
+ + +
CA Icon
+
CA Icon
+
+
+ ``` diff --git a/src/components/Icons/CA.js b/src/components/Icons/CA.js new file mode 100644 index 000000000..407ef08fe --- /dev/null +++ b/src/components/Icons/CA.js @@ -0,0 +1,29 @@ +import React from "react"; +import { PropTypes } from "prop-types"; + +const CAIcon = ({ height, width, children, ...props }) => ( + + {children} + + + + + +); + +CAIcon.displayName = "CAIcon"; + +CAIcon.defaultProps = { + children: null +}; + +CAIcon.propTypes = { + height: PropTypes.number.isRequired, + width: PropTypes.number.isRequired, + children: PropTypes.node +}; + +export default CAIcon; diff --git a/src/components/Icons/Checkmark.js b/src/components/Icons/Checkmark.js index 8623c21e3..e49b34dff 100644 --- a/src/components/Icons/Checkmark.js +++ b/src/components/Icons/Checkmark.js @@ -1,7 +1,7 @@ import React from "react"; import PropTypes from "prop-types"; -const Checkmark = ({ size, color, ...props }) => ( +const Checkmark = ({ size, color, children, ...props }) => ( ( fill="none" fillRule="evenodd" > + {children} ); @@ -22,12 +23,14 @@ Checkmark.displayName = "Checkmark"; Checkmark.defaultProps = { size: 24, - color: "currentcolor" + color: "currentcolor", + children: null }; Checkmark.propTypes = { size: PropTypes.number, - color: PropTypes.string + color: PropTypes.string, + children: PropTypes.node }; export default Checkmark; diff --git a/src/components/Icons/Cross.js b/src/components/Icons/Cross.js index ef87eeecc..166928d39 100644 --- a/src/components/Icons/Cross.js +++ b/src/components/Icons/Cross.js @@ -1,8 +1,9 @@ import React from "react"; import PropTypes from "prop-types"; -const CrossIcon = ({ size, color, ...props }) => ( +const CrossIcon = ({ size, color, children, ...props }) => ( + {children} ); @@ -10,12 +11,14 @@ const CrossIcon = ({ size, color, ...props }) => ( CrossIcon.displayName = "CrossIcon"; CrossIcon.defaultProps = { - color: "currentColor" + color: "currentColor", + children: null }; CrossIcon.propTypes = { size: PropTypes.number.isRequired, - color: PropTypes.string + color: PropTypes.string, + children: PropTypes.node }; export default CrossIcon; diff --git a/src/components/Icons/Facebook.js b/src/components/Icons/Facebook.js new file mode 100644 index 000000000..854c9006c --- /dev/null +++ b/src/components/Icons/Facebook.js @@ -0,0 +1,24 @@ +import React from "react"; +import { PropTypes } from "prop-types"; + +const FacebookIcon = ({ size, color, children, ...props }) => ( + + {children} + + +); + +FacebookIcon.displayName = "FacebookIcon"; + +FacebookIcon.defaultProps = { + color: "currentcolor", + children: null +}; + +FacebookIcon.propTypes = { + size: PropTypes.number.isRequired, + color: PropTypes.string, + children: PropTypes.node +}; + +export default FacebookIcon; diff --git a/src/components/Icons/Instagram.js b/src/components/Icons/Instagram.js new file mode 100644 index 000000000..1a2b7d345 --- /dev/null +++ b/src/components/Icons/Instagram.js @@ -0,0 +1,28 @@ +import React from "react"; +import { PropTypes } from "prop-types"; + +const InstagramIcon = ({ size, color, children, ...props }) => ( + + {children} + + + + + + +); + +InstagramIcon.displayName = "InstagramIcon"; + +InstagramIcon.defaultProps = { + color: "currentcolor", + children: null +}; + +InstagramIcon.propTypes = { + size: PropTypes.number.isRequired, + color: PropTypes.string, + children: PropTypes.node +}; + +export default InstagramIcon; diff --git a/src/components/Icons/Parking.js b/src/components/Icons/Parking.js index 357ad5e8c..2f788ecbc 100644 --- a/src/components/Icons/Parking.js +++ b/src/components/Icons/Parking.js @@ -1,8 +1,9 @@ import React from "react"; import PropTypes from "prop-types"; -const ParkingIcon = ({ size, color, ...props }) => ( +const ParkingIcon = ({ size, color, children, ...props }) => ( + {children} ( ParkingIcon.displayName = "ParkingIcon"; ParkingIcon.defaultProps = { - color: "currentColor" + color: "currentColor", + children: null }; ParkingIcon.propTypes = { size: PropTypes.number.isRequired, - color: PropTypes.string + color: PropTypes.string, + children: PropTypes.node }; export default ParkingIcon; diff --git a/src/components/Icons/Ticket.js b/src/components/Icons/Ticket.js index c2b2cc49d..1ef0c6294 100644 --- a/src/components/Icons/Ticket.js +++ b/src/components/Icons/Ticket.js @@ -1,8 +1,9 @@ import React from "react"; import PropTypes from "prop-types"; -const TicketIcon = ({ size, color, ...props }) => ( +const TicketIcon = ({ size, color, children, ...props }) => ( + {children} ( TicketIcon.displayName = "TicketIcon"; TicketIcon.defaultProps = { - color: "currentColor" + color: "currentColor", + children: null }; TicketIcon.propTypes = { size: PropTypes.number.isRequired, - color: PropTypes.string + color: PropTypes.string, + children: PropTypes.node }; export default TicketIcon; diff --git a/src/components/Icons/Twitter.js b/src/components/Icons/Twitter.js new file mode 100644 index 000000000..de1897ca5 --- /dev/null +++ b/src/components/Icons/Twitter.js @@ -0,0 +1,24 @@ +import React from "react"; +import { PropTypes } from "prop-types"; + +const TwitterIcon = ({ size, color, children, ...props }) => ( + + {children} + + +); + +TwitterIcon.displayName = "TwitterIcon"; + +TwitterIcon.defaultProps = { + color: "currentcolor", + children: null +}; + +TwitterIcon.propTypes = { + size: PropTypes.number.isRequired, + color: PropTypes.string, + children: PropTypes.node +}; + +export default TwitterIcon; diff --git a/src/components/Icons/US.js b/src/components/Icons/US.js new file mode 100644 index 000000000..2ca49d15d --- /dev/null +++ b/src/components/Icons/US.js @@ -0,0 +1,44 @@ +import React from "react"; +import { PropTypes } from "prop-types"; + +const USIcon = ({ width, height, children, ...props }) => ( + + {children} + + + + + + + + + + + + + + + + + +); + +USIcon.displayName = "USIcon"; + +USIcon.defaultProps = { + children: null +}; + +USIcon.propTypes = { + width: PropTypes.number.isRequired, + height: PropTypes.number.isRequired, + children: PropTypes.node +}; + +export default USIcon; diff --git a/src/components/Icons/Venue.js b/src/components/Icons/Venue.js index 3e890e9fc..17e5aff18 100644 --- a/src/components/Icons/Venue.js +++ b/src/components/Icons/Venue.js @@ -1,8 +1,9 @@ import React from "react"; import PropTypes from "prop-types"; -const VenueIcon = ({ size, color, ...props }) => ( +const VenueIcon = ({ size, color, children, ...props }) => ( + {children} ( VenueIcon.displayName = "VenueIcon"; VenueIcon.defaultProps = { - color: "currentColor" + color: "currentColor", + children: null }; VenueIcon.propTypes = { size: PropTypes.number.isRequired, - color: PropTypes.string + color: PropTypes.string, + children: PropTypes.node }; export default VenueIcon; diff --git a/src/components/Icons/Vip.js b/src/components/Icons/Vip.js index 858f21490..4ff284a41 100644 --- a/src/components/Icons/Vip.js +++ b/src/components/Icons/Vip.js @@ -1,8 +1,9 @@ import React from "react"; import PropTypes from "prop-types"; -const VipIcon = ({ size, color, ...props }) => ( +const VipIcon = ({ size, color, children, ...props }) => ( + {children} ( VipIcon.displayName = "VipIcon"; VipIcon.defaultProps = { - color: "currentColor" + color: "currentColor", + children: null }; VipIcon.propTypes = { size: PropTypes.number.isRequired, - color: PropTypes.string + color: PropTypes.string, + children: PropTypes.node }; export default VipIcon; diff --git a/src/components/Icons/Youtube.js b/src/components/Icons/Youtube.js new file mode 100644 index 000000000..9365ba9e4 --- /dev/null +++ b/src/components/Icons/Youtube.js @@ -0,0 +1,24 @@ +import React from "react"; +import { PropTypes } from "prop-types"; + +const YoutubeIcon = ({ size, color, children, ...props }) => ( + + {children} + + +); + +YoutubeIcon.displayName = "YoutubeIcon"; + +YoutubeIcon.defaultProps = { + color: "currentcolor", + children: null +}; + +YoutubeIcon.propTypes = { + size: PropTypes.number.isRequired, + color: PropTypes.string, + children: PropTypes.node +}; + +export default YoutubeIcon; diff --git a/src/components/Icons/__tests__/CA.spec.js b/src/components/Icons/__tests__/CA.spec.js new file mode 100644 index 000000000..376c2ffc2 --- /dev/null +++ b/src/components/Icons/__tests__/CA.spec.js @@ -0,0 +1,18 @@ +import React from "react"; +import renderer from "react-test-renderer"; + +import CAIcon from "../CA"; + +describe("CAIcon", () => { + const children = CA Icon; + + it("renders correctly", () => { + const component = renderer.create( + + {children} + + ); + + expect(component.toJSON()).toMatchSnapshot(); + }); +}); diff --git a/src/components/Icons/__tests__/Facebook.spec.js b/src/components/Icons/__tests__/Facebook.spec.js new file mode 100644 index 000000000..00170f252 --- /dev/null +++ b/src/components/Icons/__tests__/Facebook.spec.js @@ -0,0 +1,18 @@ +import React from "react"; +import renderer from "react-test-renderer"; + +import FacebookIcon from "../Facebook"; + +describe("FacebookIcon", () => { + const children = Facebook Icon; + + it("renders correctly", () => { + const component = renderer.create( + + {children} + + ); + + expect(component.toJSON()).toMatchSnapshot(); + }); +}); diff --git a/src/components/Icons/__tests__/Instagram.spec.js b/src/components/Icons/__tests__/Instagram.spec.js new file mode 100644 index 000000000..3371b4492 --- /dev/null +++ b/src/components/Icons/__tests__/Instagram.spec.js @@ -0,0 +1,18 @@ +import React from "react"; +import renderer from "react-test-renderer"; + +import InstagramIcon from "../Instagram"; + +describe("InstagramIcon", () => { + const children = Instagram Icon; + + it("renders correctly", () => { + const component = renderer.create( + + {children} + + ); + + expect(component.toJSON()).toMatchSnapshot(); + }); +}); diff --git a/src/components/Icons/__tests__/Twitter.spec.js b/src/components/Icons/__tests__/Twitter.spec.js new file mode 100644 index 000000000..7fc75eb37 --- /dev/null +++ b/src/components/Icons/__tests__/Twitter.spec.js @@ -0,0 +1,18 @@ +import React from "react"; +import renderer from "react-test-renderer"; + +import TwitterIcon from "../Twitter"; + +describe("TwitterIcon", () => { + const children = Twitter Icon; + + it("renders correctly", () => { + const component = renderer.create( + + {children} + + ); + + expect(component.toJSON()).toMatchSnapshot(); + }); +}); diff --git a/src/components/Icons/__tests__/US.spec.js b/src/components/Icons/__tests__/US.spec.js new file mode 100644 index 000000000..5bff87c4d --- /dev/null +++ b/src/components/Icons/__tests__/US.spec.js @@ -0,0 +1,18 @@ +import React from "react"; +import renderer from "react-test-renderer"; + +import USIcon from "../US"; + +describe("USIcon", () => { + const children = US Icon; + + it("renders correctly", () => { + const component = renderer.create( + + {children} + + ); + + expect(component.toJSON()).toMatchSnapshot(); + }); +}); diff --git a/src/components/Icons/__tests__/Youtube.spec.js b/src/components/Icons/__tests__/Youtube.spec.js new file mode 100644 index 000000000..2a2df05e1 --- /dev/null +++ b/src/components/Icons/__tests__/Youtube.spec.js @@ -0,0 +1,18 @@ +import React from "react"; +import renderer from "react-test-renderer"; + +import YoutubeIcon from "../Youtube"; + +describe("FacebookIcon", () => { + const children = Youtube Icon; + + it("renders correctly", () => { + const component = renderer.create( + + {children} + + ); + + expect(component.toJSON()).toMatchSnapshot(); + }); +}); diff --git a/src/components/Icons/__tests__/__snapshots__/CA.spec.js.snap b/src/components/Icons/__tests__/__snapshots__/CA.spec.js.snap new file mode 100644 index 000000000..671259590 --- /dev/null +++ b/src/components/Icons/__tests__/__snapshots__/CA.spec.js.snap @@ -0,0 +1,26 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`CAIcon renders correctly 1`] = ` + + + CA Icon + + + + + + +`; diff --git a/src/components/Icons/__tests__/__snapshots__/Facebook.spec.js.snap b/src/components/Icons/__tests__/__snapshots__/Facebook.spec.js.snap new file mode 100644 index 000000000..da2eb87f6 --- /dev/null +++ b/src/components/Icons/__tests__/__snapshots__/Facebook.spec.js.snap @@ -0,0 +1,17 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`FacebookIcon renders correctly 1`] = ` + + + Facebook Icon + + + +`; diff --git a/src/components/Icons/__tests__/__snapshots__/Instagram.spec.js.snap b/src/components/Icons/__tests__/__snapshots__/Instagram.spec.js.snap new file mode 100644 index 000000000..4bb338cd7 --- /dev/null +++ b/src/components/Icons/__tests__/__snapshots__/Instagram.spec.js.snap @@ -0,0 +1,31 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`InstagramIcon renders correctly 1`] = ` + + + Instagram Icon + + + + + + + +`; diff --git a/src/components/Icons/__tests__/__snapshots__/Twitter.spec.js.snap b/src/components/Icons/__tests__/__snapshots__/Twitter.spec.js.snap new file mode 100644 index 000000000..809f4288e --- /dev/null +++ b/src/components/Icons/__tests__/__snapshots__/Twitter.spec.js.snap @@ -0,0 +1,17 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`TwitterIcon renders correctly 1`] = ` + + + Twitter Icon + + + +`; diff --git a/src/components/Icons/__tests__/__snapshots__/US.spec.js.snap b/src/components/Icons/__tests__/__snapshots__/US.spec.js.snap new file mode 100644 index 000000000..63876c51a --- /dev/null +++ b/src/components/Icons/__tests__/__snapshots__/US.spec.js.snap @@ -0,0 +1,52 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`USIcon renders correctly 1`] = ` + + + US Icon + + + + + + + + + + + + + + + + + + +`; diff --git a/src/components/Icons/__tests__/__snapshots__/Youtube.spec.js.snap b/src/components/Icons/__tests__/__snapshots__/Youtube.spec.js.snap new file mode 100644 index 000000000..474622307 --- /dev/null +++ b/src/components/Icons/__tests__/__snapshots__/Youtube.spec.js.snap @@ -0,0 +1,17 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`FacebookIcon renders correctly 1`] = ` + + + Youtube Icon + + + +`; diff --git a/src/components/Icons/index.js b/src/components/Icons/index.js index cbd6a1a2e..e6127572f 100644 --- a/src/components/Icons/index.js +++ b/src/components/Icons/index.js @@ -8,3 +8,9 @@ export { default as VenueIcon } from "./Venue"; export { default as VipIcon } from "./Vip"; export { default as CrossIcon } from "./Cross"; export { default as StarIcon } from "./Star"; +export { default as TwitterIcon } from "./Twitter"; +export { default as FacebookIcon } from "./Facebook"; +export { default as InstagramIcon } from "./Instagram"; +export { default as YoutubeIcon } from "./Youtube"; +export { default as USIcon } from "./US"; +export { default as CAIcon } from "./CA"; diff --git a/src/components/Link/Context.js b/src/components/Link/Context.js new file mode 100644 index 000000000..9f3c5c6a5 --- /dev/null +++ b/src/components/Link/Context.js @@ -0,0 +1,6 @@ +import React from "react"; + +const { Provider, Consumer } = React.createContext(); + +export { Provider as LinkListProvider }; +export { Consumer as LinkListConsumer }; diff --git a/src/components/Link/List.js b/src/components/Link/List.js index f1e7c6731..4917b0be0 100644 --- a/src/components/Link/List.js +++ b/src/components/Link/List.js @@ -1,27 +1,71 @@ +import React, { Component } from "react"; +import PropTypes from "prop-types"; import styled from "styled-components"; import constants from "../../theme/constants"; import colors from "../../theme/colors"; -import typography from "../../theme/typography"; +import spacing from "../../theme/spacing"; -const LinkList = styled.div.attrs({ - className: "links links__list", - role: "menu" +import { LinkListProvider } from "./Context"; + +const Container = styled.div.attrs({ + className: "links links__list" })` display: none; - background-color: ${colors.white.base}; - color: ${colors.onyx.base}; - font-weight: ${typography.weight.regular}; - box-shadow: 0 4px 4px 0 rgba(0, 0, 0, 0.12); - border-radius: ${constants.borderRadius.large}; - overflow: hidden; + flex-direction: column; position: absolute; - left: 0; - top: 0; + width: 205px; + overflow: hidden; + background-color: ${colors.white.base}; + padding: ${spacing.cozy}; + border-radius: ${constants.borderRadius.small}; .list-container--open & { display: block; } + + &.links__list--open { + display: block; + } `; +class LinkList extends Component { + state = { + onItemClick: () => {}, // eslint-disable-line + selectedIndex: this.props.selectedIndex // eslint-disable-line + }; + + componentWillMount() { + this.setState({ + onItemClick: this.onItemClick // eslint-disable-line + }); + } + + onItemClick = ({ index }) => { + this.setState({ selectedIndex: index }); // eslint-disable-line + this.props.onItemClick({ selectedIndex: index }); + }; + + render() { + const { children, ...rest } = this.props; + + return ( + + {children} + + ); + } +} + +LinkList.defaultProps = { + selectedIndex: -1, + onItemClick: () => {} +}; + +LinkList.propTypes = { + children: PropTypes.node.isRequired, + onItemClick: PropTypes.func, + selectedIndex: PropTypes.number +}; + export default LinkList; diff --git a/src/components/Link/ListItem.js b/src/components/Link/ListItem.js new file mode 100644 index 000000000..bd77c9ee9 --- /dev/null +++ b/src/components/Link/ListItem.js @@ -0,0 +1,79 @@ +import React from "react"; +import PropTypes from "prop-types"; +import styled from "styled-components"; +import classNames from "classnames"; + +import colors from "../../theme/colors"; +import constants from "../../theme/constants"; +import { LinkListConsumer } from "./Context"; +import spacing from "../../theme/spacing"; +import typography from "../../theme/typography"; +import composeEventHandlers from "../../utils/composeEventHandlers"; + +const ItemContainer = styled.div.attrs({ + className: "links__list__item" +})` + padding: 0 12px; + height: 36px; + display: flex; + color: ${colors.blackPearl}; + align-items: center; + font-size: ${typography.size.kilo}; + font-weight: ${typography.weight.regular}; + border-radius: ${constants.borderRadius.small}; + + &.links__list__item--selected { + background-color: ${colors.shale}; + } + + &:hover { + background-color: ${colors.azure.base}; + color: ${colors.white.base}; + } + + &:not(:last-of-type) { + margin-bottom: ${spacing.slim}; + } +`; + +const LinkItemContainer = ItemContainer.withComponent("a").extend` +text-decoration: none; +`; + +const ListItem = ({ children, index, onClick, href, ...rest }) => ( + + {({ onItemClick, selectedIndex }) => + href ? ( + + {children} + + ) : ( + onItemClick({ index }), onClick)} + {...rest} + > + {children} + + ) + } + +); + +ListItem.defaultProps = { + index: 0, + onClick: () => {}, + href: null, + children: null +}; + +ListItem.propTypes = { + children: PropTypes.node, + index: PropTypes.number, + onClick: PropTypes.func, + href: PropTypes.string +}; + +export default ListItem; diff --git a/src/components/Link/__tests__/List.spec.js b/src/components/Link/__tests__/List.spec.js new file mode 100644 index 000000000..01dc82950 --- /dev/null +++ b/src/components/Link/__tests__/List.spec.js @@ -0,0 +1,74 @@ +import React from "react"; +import { render, Simulate } from "react-testing-library"; + +import LinkList from "../List"; +import LinkListItem from "../ListItem"; + +const mockFn = jest.fn(); + +const countries = [ + { + value: "en-US", + text: "USA (English)", + label: "United States" + }, + { + value: "en-CA", + text: "Canada (English)", + label: "Canada" + }, + { + value: "fr-CA", + text: "Canada (Français)", + label: "Canada" + } +]; + +describe("", () => { + it("renders LinkList correctly when its open", () => { + const { container } = render( + + {countries.map((option, index) => ( + + {option.text} + + ))} + + ); + expect(container.firstChild).toMatchSnapshot(); + }); + + it("renders LinkList correctly with list items as links", () => { + const { container } = render( + + {countries.map((option, index) => ( + + {option.text} + + ))} + + ); + expect(container.firstChild).toMatchSnapshot(); + }); + + it("onItemClick in LinkList is called when a list item is clicked", () => { + const onOptionSelected = mockFn; + const { container } = render( + + {countries.map((option, index) => ( + + {option.text} + + ))} + + ); + + Simulate.click(container.querySelector(".links__list__item")); + expect(onOptionSelected).toHaveBeenCalled(); + }); +}); diff --git a/src/components/Link/__tests__/__snapshots__/List.spec.js.snap b/src/components/Link/__tests__/__snapshots__/List.spec.js.snap new file mode 100644 index 000000000..1dacc462e --- /dev/null +++ b/src/components/Link/__tests__/__snapshots__/List.spec.js.snap @@ -0,0 +1,48 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders LinkList correctly when its open 1`] = ` + +`; + +exports[` renders LinkList correctly with list items as links 1`] = ` + +`; diff --git a/src/components/Link/__tests__/__snapshots__/index.spec.js.snap b/src/components/Link/__tests__/__snapshots__/index.spec.js.snap index e305a9429..5fa6210e0 100644 --- a/src/components/Link/__tests__/__snapshots__/index.spec.js.snap +++ b/src/components/Link/__tests__/__snapshots__/index.spec.js.snap @@ -17,8 +17,7 @@ exports[` closes on mouse leave 1`] = `