From 778d25b18ecd07bfff87414da488e677c956aa71 Mon Sep 17 00:00:00 2001 From: Chris Garcia Date: Mon, 9 Sep 2019 10:59:33 -0500 Subject: [PATCH] Add Responsive.Visible and Responsive.Hidden utilities to Dark Matter --- src/components/Responsive/Hidden/README.md | 6 +++ src/components/Responsive/Hidden/index.js | 53 +++++++++++++++++++++ src/components/Responsive/Hidden/story.js | 29 +++++++++++ src/components/Responsive/Hidden/test.js | 52 ++++++++++++++++++++ src/components/Responsive/Visible/README.md | 6 +++ src/components/Responsive/Visible/index.js | 53 +++++++++++++++++++++ src/components/Responsive/Visible/story.js | 29 +++++++++++ src/components/Responsive/Visible/test.js | 52 ++++++++++++++++++++ src/components/Responsive/index.js | 2 + src/stories/index.js | 2 + 10 files changed, 284 insertions(+) create mode 100644 src/components/Responsive/Hidden/README.md create mode 100644 src/components/Responsive/Hidden/index.js create mode 100644 src/components/Responsive/Hidden/story.js create mode 100644 src/components/Responsive/Hidden/test.js create mode 100644 src/components/Responsive/Visible/README.md create mode 100644 src/components/Responsive/Visible/index.js create mode 100644 src/components/Responsive/Visible/story.js create mode 100644 src/components/Responsive/Visible/test.js diff --git a/src/components/Responsive/Hidden/README.md b/src/components/Responsive/Hidden/README.md new file mode 100644 index 00000000..ebbd2bd3 --- /dev/null +++ b/src/components/Responsive/Hidden/README.md @@ -0,0 +1,6 @@ +# Responsive.Hidden + +**Hide contents based on container width.** + +When you use a `Responsive.Container`, it sets up a container width watcher with pre-defined breakpoints. +You can use those breakpoints to hide this component based on container width. diff --git a/src/components/Responsive/Hidden/index.js b/src/components/Responsive/Hidden/index.js new file mode 100644 index 00000000..439984c5 --- /dev/null +++ b/src/components/Responsive/Hidden/index.js @@ -0,0 +1,53 @@ +import React, { useCallback } from 'react'; +import PropTypes from 'prop-types'; +import intersection from 'lodash/intersection'; + +import Context from '../Context'; + +const Hidden = ({ + children, + responsiveContext: hiddenResponsiveContext, + ...passedProps +}) => { + const getIsHidden = useCallback( + responsiveContext => + !!intersection(hiddenResponsiveContext, responsiveContext).length, + [hiddenResponsiveContext] + ); + + return ( + + {responsiveContext => { + const hidden = getIsHidden(responsiveContext); + + return React.Children.map(children, child => { + if (!child || hidden) { + return null; + } + + if (typeof child === 'string') { + return {child}; + } + + return React.cloneElement(child, { + ...passedProps, + }); + }); + }} + + ); +}; + +Hidden.propTypes = { + children: PropTypes.node, + container: PropTypes.string, + responsiveContext: PropTypes.arrayOf(PropTypes.string), +}; + +Hidden.defaultProps = { + children: null, + container: null, + responsiveContext: [], +}; + +export default Hidden; diff --git a/src/components/Responsive/Hidden/story.js b/src/components/Responsive/Hidden/story.js new file mode 100644 index 00000000..2a0f115e --- /dev/null +++ b/src/components/Responsive/Hidden/story.js @@ -0,0 +1,29 @@ +import React from 'react'; +import { storiesOf } from '@storybook/react'; + +// Rover UI dependencies +import Container from '../Container'; + +// This component's dependencies +import Readme from './README.md'; +import Hidden from './'; + +storiesOf('Dark Matter/Responsive/Hidden', module) + .addParameters({ + readme: { + sidebar: Readme, + }, + }) + .add('Overview', () => ( +
+ +
+ + I'll disappear at certain sizes + +
+
+
+ )); diff --git a/src/components/Responsive/Hidden/test.js b/src/components/Responsive/Hidden/test.js new file mode 100644 index 00000000..4790f9eb --- /dev/null +++ b/src/components/Responsive/Hidden/test.js @@ -0,0 +1,52 @@ +import React from 'react'; +import { mount } from 'enzyme'; +import Context from '../Context'; +import Hidden from './'; + +describe('', () => { + it('renders without error', () => { + mount( + + Boom + + ); + }); + + it('hides by breakpoint', () => { + const wrapper = mount( + + + Boom + + + ); + + expect(wrapper.text()).toEqual(''); + }); + + it("doesn't hide without breakpoint", () => { + const wrapper = mount( + + + Boom + + + ); + + expect(wrapper.text()).toEqual('Boom'); + }); +}); diff --git a/src/components/Responsive/Visible/README.md b/src/components/Responsive/Visible/README.md new file mode 100644 index 00000000..1aaf2866 --- /dev/null +++ b/src/components/Responsive/Visible/README.md @@ -0,0 +1,6 @@ +# Responsive.Visible + +**Shows contents based on container width.** + +When you use a `Responsive.Container`, it sets up a container width watcher with pre-defined breakpoints. +You can use those breakpoints to show this component based on container width. diff --git a/src/components/Responsive/Visible/index.js b/src/components/Responsive/Visible/index.js new file mode 100644 index 00000000..301ee4cd --- /dev/null +++ b/src/components/Responsive/Visible/index.js @@ -0,0 +1,53 @@ +import React, { useCallback } from 'react'; +import PropTypes from 'prop-types'; +import intersection from 'lodash/intersection'; + +import Context from '../Context'; + +const Visible = ({ + children, + responsiveContext: VisibleResponsiveContext, + ...passedProps +}) => { + const getIsVisible = useCallback( + responsiveContext => + !!intersection(VisibleResponsiveContext, responsiveContext).length, + [VisibleResponsiveContext] + ); + + return ( + + {responsiveContext => { + const visible = getIsVisible(responsiveContext); + + return React.Children.map(children, child => { + if (!child || !visible) { + return null; + } + + if (typeof child === 'string') { + return {child}; + } + + return React.cloneElement(child, { + ...passedProps, + }); + }); + }} + + ); +}; + +Visible.propTypes = { + children: PropTypes.node, + container: PropTypes.string, + responsiveContext: PropTypes.arrayOf(PropTypes.string), +}; + +Visible.defaultProps = { + children: null, + container: null, + responsiveContext: [], +}; + +export default Visible; diff --git a/src/components/Responsive/Visible/story.js b/src/components/Responsive/Visible/story.js new file mode 100644 index 00000000..2643039b --- /dev/null +++ b/src/components/Responsive/Visible/story.js @@ -0,0 +1,29 @@ +import React from 'react'; +import { storiesOf } from '@storybook/react'; + +// Rover UI dependencies +import Container from '../Container'; + +// This component's dependencies +import Readme from './README.md'; +import Visible from './'; + +storiesOf('Dark Matter/Responsive/Visible', module) + .addParameters({ + readme: { + sidebar: Readme, + }, + }) + .add('Overview', () => ( +
+ +
+ + I'll appear at certain sizes + +
+
+
+ )); diff --git a/src/components/Responsive/Visible/test.js b/src/components/Responsive/Visible/test.js new file mode 100644 index 00000000..7741fa05 --- /dev/null +++ b/src/components/Responsive/Visible/test.js @@ -0,0 +1,52 @@ +import React from 'react'; +import { mount } from 'enzyme'; +import Context from '../Context'; +import Visible from './'; + +describe('', () => { + it('renders without error', () => { + mount( + + Boom + + ); + }); + + it('shows by breakpoint', () => { + const wrapper = mount( + + + Boom + + + ); + + expect(wrapper.text()).toEqual('Boom'); + }); + + it("doesn't show without breakpoint", () => { + const wrapper = mount( + + + Boom + + + ); + + expect(wrapper.text()).toEqual(''); + }); +}); diff --git a/src/components/Responsive/index.js b/src/components/Responsive/index.js index 810324bc..da80857a 100644 --- a/src/components/Responsive/index.js +++ b/src/components/Responsive/index.js @@ -2,12 +2,14 @@ import Container from './Container'; import Context from './Context'; import Element from './Element'; import Grid from './Grid'; +import Hidden from './Hidden'; const Responsive = { Container, Context, Element, Grid, + Hidden, }; export default Responsive; diff --git a/src/stories/index.js b/src/stories/index.js index ea6a7232..99e24194 100644 --- a/src/stories/index.js +++ b/src/stories/index.js @@ -59,6 +59,8 @@ import '../components/Grid/story'; import '../components/Media/story'; import '../components/Responsive/story'; import '../components/Responsive/Grid/story'; +import '../components/Responsive/Hidden/story'; +import '../components/Responsive/Visible/story'; /* * NEW & UNCATEGORIZED