Skip to content

Commit

Permalink
feat: new API with Function as Child Components
Browse files Browse the repository at this point in the history
BREAKING CHANGE: `<Drawer />` now expects a function as its child. This
makes the library much more flexible.
  • Loading branch information
damusnet committed Sep 5, 2017
1 parent c4ab5aa commit ba9437b
Show file tree
Hide file tree
Showing 15 changed files with 356 additions and 344 deletions.
5 changes: 1 addition & 4 deletions examples/config/webpack.config.dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,7 @@ module.exports = {
// Process JS with Babel.
{
test: /\.(js|jsx)$/,
include: [
paths.appSrc,
path.resolve(paths.appSrc, "../../src/Drawer"),
],
include: [paths.appSrc, path.resolve(paths.appSrc, "../../src")],
loader: require.resolve("babel-loader"),
options: {
// This is a feature of `babel-loader` for webpack (not Babel itself).
Expand Down
5 changes: 1 addition & 4 deletions examples/config/webpack.config.prod.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,7 @@ module.exports = {
// Process JS with Babel.
{
test: /\.(js|jsx)$/,
include: [
paths.appSrc,
path.resolve(paths.appSrc, "../../src/Drawer"),
],
include: [paths.appSrc, path.resolve(paths.appSrc, "../../src")],
loader: require.resolve("babel-loader"),
options: {
compact: true,
Expand Down
51 changes: 47 additions & 4 deletions examples/src/App.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,56 @@
import React from "react";
import FaBars from "react-icons/lib/fa/bars";

import MainContent from "./MainContent";
import Drawer from "../../src/Drawer";
import CupcakeIpsum from "./CupcakeIpsum";

import "./MainContent.css";

import Drawer, {
DrawerContainer,
MainContentContainer,
} from "../../src/Drawer";

import DrawerContent from "./DrawerContent";

const App = () => (
<Drawer width={80} content={<DrawerContent />}>
<MainContent />
<Drawer width={80}>
{({
width,
swiping,
translateX,
mainContentScroll,
toggleDrawer,
handleTouchStart,
handleTouchMove,
handleTouchEnd,
}) => (
<div>
<DrawerContainer
width={width}
swiping={swiping}
translateX={translateX}
toggleDrawer={toggleDrawer}
handleTouchStart={handleTouchStart}
handleTouchMove={handleTouchMove}
handleTouchEnd={handleTouchEnd}
drawerContent={<DrawerContent />}
/>
<MainContentContainer
translateX={translateX}
mainContentScroll={mainContentScroll}
>
<div className="MainContent">
<div className="MainContent-navbar">
<FaBars size={24} color="white" onClick={toggleDrawer} />
<h1 className="MainContent-navbar-title">
React Swipeable Drawer
</h1>
</div>
<CupcakeIpsum />
</div>
</MainContentContainer>
</div>
)}
</Drawer>
);

Expand Down
59 changes: 16 additions & 43 deletions src/Drawer.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import React, { Component } from "react";
import { Component } from "react";
import PropTypes from "prop-types";

import DrawerOverlay from "./DrawerOverlay";
import DrawerContent from "./DrawerContent";

class Drawer extends Component {
static propTypes = {
width: PropTypes.number.isRequired,
content: PropTypes.element.isRequired,
children: PropTypes.element.isRequired,
children: PropTypes.func.isRequired,
};

state = {
Expand Down Expand Up @@ -92,48 +88,25 @@ class Drawer extends Component {
};

render() {
const { width, content: drawerContent, children } = this.props;
const { width, children } = this.props;
const { swiping, translateX } = this.state;

const open = translateX > 0;

const mainContentOpenStyle = {
position: "fixed",
top: -this.mainContentScroll,
};

const mainContent = React.cloneElement(children, {
return children({
width,
swiping,
translateX,
mainContentScroll: this.mainContentScroll,
toggleDrawer: this.toggleDrawer,
style: open ? mainContentOpenStyle : {},
handleTouchStart: this.handleTouchStart,
handleTouchMove: this.handleTouchMove,
handleTouchEnd: this.handleTouchEnd,
});

return (
<div>
<div className="DrawerContainer">
<DrawerOverlay
open={open}
swiping={swiping}
translateX={translateX}
toggleDrawer={this.toggleDrawer}
handleTouchStart={this.handleTouchStart}
handleTouchMove={this.handleTouchMove}
handleTouchEnd={this.handleTouchEnd}
/>
<DrawerContent
width={width}
swiping={swiping}
translateX={translateX}
toggleDrawer={this.toggleDrawer}
handleTouchStart={this.handleTouchStart}
handleTouchMove={this.handleTouchMove}
handleTouchEnd={this.handleTouchEnd}
drawerContent={drawerContent}
/>
</div>
{mainContent}
</div>
);
}
}

export default Drawer;

export { default as DrawerContainer } from "./DrawerContainer";
export { default as DrawerOverlay } from "./DrawerOverlay";
export { default as DrawerContentContainer } from "./DrawerContentContainer";
export { default as MainContentContainer } from "./MainContentContainer";
147 changes: 78 additions & 69 deletions src/Drawer.test.js
Original file line number Diff line number Diff line change
@@ -1,137 +1,146 @@
import React from "react";
import renderer from "react-test-renderer";
import { shallow } from "enzyme";
import { shallow, mount } from "enzyme";

import Drawer from "./Drawer";

jest.mock("./DrawerOverlay", () => "DrawerOverlay");
jest.mock("./DrawerContent", () => "DrawerContent");
const noop = () => <div />;

describe("<Drawer />", () => {
it("toggles open", () => {
const component = renderer.create(
const drawer = shallow(
<Drawer width={80} content={<div />}>
<div />
{noop}
</Drawer>
);
let tree = component.toJSON();
tree.children[0].children[0].props.toggleDrawer();
tree = component.toJSON();
expect(tree).toMatchSnapshot();
).instance();

drawer.toggleDrawer();

expect(drawer.state.translateX).toEqual(100);
});

it("toggles close", () => {
const component = renderer.create(
const drawer = shallow(
<Drawer width={80} content={<div />}>
<div />
{noop}
</Drawer>
);
let tree = component.toJSON();
tree.children[0].children[0].props.toggleDrawer();
tree.children[0].children[0].props.toggleDrawer();
tree = component.toJSON();
expect(tree).toMatchSnapshot();
).instance();

drawer.toggleDrawer();
drawer.toggleDrawer();

expect(drawer.state.translateX).toEqual(0);
});

it("swipes horizontally", () => {
const component = renderer.create(
const drawer = shallow(
<Drawer width={80} content={<div />}>
<div />
{noop}
</Drawer>
);
let tree = component.toJSON();
tree.children[0].children[0].props.handleTouchStart({
).instance();

drawer.handleTouchStart({
targetTouches: [{ clientX: 100, clientY: 100 }],
});
tree.children[0].children[0].props.handleTouchMove(80)({
targetTouches: [{ clientX: 200, clientY: 100 }],

drawer.handleTouchMove(80)({
targetTouches: [{ clientX: 900, clientY: 100 }],
});
tree = component.toJSON();
expect(tree).toMatchSnapshot();

expect(drawer.state.translateX).toEqual(100);
});

it("swipes vertically", () => {
const component = renderer.create(
const drawer = shallow(
<Drawer width={80} content={<div />}>
<div />
{noop}
</Drawer>
);
let tree = component.toJSON();
tree.children[0].children[0].props.handleTouchStart({
).instance();

drawer.handleTouchStart({
targetTouches: [{ clientX: 100, clientY: 100 }],
});
tree.children[0].children[0].props.handleTouchMove(80)({
targetTouches: [{ clientX: 100, clientY: 200 }],

drawer.handleTouchMove(80)({
targetTouches: [{ clientX: 100, clientY: 900 }],
});
tree = component.toJSON();
expect(tree).toMatchSnapshot();

expect(drawer.state.translateX).toEqual(0);
});

it("closes when swiping a little", () => {
const component = renderer.create(
const drawer = shallow(
<Drawer width={80} content={<div />}>
<div />
{noop}
</Drawer>
);
let tree = component.toJSON();
tree.children[0].children[0].props.handleTouchStart({
).instance();

drawer.handleTouchStart({
targetTouches: [{ clientX: 100, clientY: 100 }],
});
tree.children[0].children[0].props.handleTouchMove(80)({

drawer.handleTouchMove(80)({
targetTouches: [{ clientX: 200, clientY: 100 }],
});
tree.children[0].children[0].props.handleTouchEnd();
tree = component.toJSON();
expect(tree).toMatchSnapshot();

drawer.handleTouchEnd();

expect(drawer.state.translateX).toEqual(0);
});

it("opens when swiping enough", () => {
const component = renderer.create(
const drawer = shallow(
<Drawer width={80} content={<div />}>
<div />
{noop}
</Drawer>
);
let tree = component.toJSON();
tree.children[0].children[0].props.handleTouchStart({
).instance();

drawer.handleTouchStart({
targetTouches: [{ clientX: 100, clientY: 100 }],
});
tree.children[0].children[0].props.handleTouchMove(80)({

drawer.handleTouchMove(80)({
targetTouches: [{ clientX: 500, clientY: 100 }],
});
tree.children[0].children[0].props.handleTouchEnd();
tree = component.toJSON();
expect(tree).toMatchSnapshot();

drawer.handleTouchEnd();

expect(drawer.state.translateX).toEqual(100);
});

it("watches scroll events", () => {
const wrapper = shallow(
global.addEventListener = jest.fn();

const drawer = mount(
<Drawer width={80} content={<div />}>
<div />
{noop}
</Drawer>
);
const instance = wrapper.instance();
).instance();

window.pageYOffset = 100;
instance.onScroll();
expect(instance.mainContentScroll).toEqual(100);
drawer.onScroll();

expect(drawer.mainContentScroll).toEqual(100);

instance.toggleDrawer();
drawer.toggleDrawer();
window.pageYOffset = 0;
instance.onScroll();
expect(instance.mainContentScroll).toEqual(100);
drawer.onScroll();

expect(drawer.mainContentScroll).toEqual(100);

expect(global.addEventListener).toHaveBeenCalledTimes(2);
});

it("removes the scroll event listener", () => {
global.removeEventListener = jest.fn();

const wrapper = shallow(
const drawer = shallow(
<Drawer width={80} content={<div />}>
<div />
{noop}
</Drawer>
);
const instance = wrapper.instance();
).instance();

drawer.componentWillUnmount();

instance.componentWillUnmount();
expect(global.removeEventListener).toHaveBeenCalledTimes(1);
});
});
Loading

0 comments on commit ba9437b

Please sign in to comment.