Skip to content

Commit

Permalink
feat(Dropdown): Add support for dropleft and dropright (#813)
Browse files Browse the repository at this point in the history
Closes #785
  • Loading branch information
supergibbs authored and TheSharpieOne committed Feb 7, 2018
1 parent b35cdcb commit 2b71fd6
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 37 deletions.
54 changes: 47 additions & 7 deletions docs/lib/Components/ButtonDropdownPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import React from 'react';
import { PrismCode } from 'react-prism';
import Helmet from 'react-helmet';
import {
Button,
ButtonDropdown,
DropdownToggle,
DropdownItem,
DropdownMenu } from 'reactstrap';
import Example from '../examples/ButtonDropdownMulti';
import ExampleSplit from '../examples/ButtonDropdownMultiSplit';
import ButtonDropdownExample from '../examples/ButtonDropdown';

const ButtonDropdownExampleSource = require('!!raw!../examples/ButtonDropdown');

export default class ButtonDropdownPage extends React.Component {
Expand Down Expand Up @@ -47,7 +47,7 @@ export default class ButtonDropdownPage extends React.Component {
<PrismCode className="language-jsx">
{`ButtonDropdown.propTypes = {
disabled: PropTypes.bool,
dropup: PropTypes.bool,
direction: PropTypes.oneOf(['up', 'down', 'left', 'right']),
group: PropTypes.bool,
isOpen: PropTypes.bool,
tag: PropTypes.string,
Expand Down Expand Up @@ -165,30 +165,70 @@ DropdownToggle.propTypes = {
</ButtonDropdown>`}
</PrismCode>
</pre>
<h3>Dropup variation</h3>
<h3>Drop direction variations</h3>
<div className="docs-example">
<div>
<ButtonDropdown dropup isOpen={this.state.btnDropup} toggle={() => { this.setState({ btnDropup: !this.state.btnDropup }); }}>
<DropdownToggle caret size="lg">
<ButtonDropdown direction="up" isOpen={this.state.btnDropup} toggle={() => { this.setState({ btnDropup: !this.state.btnDropup }); }}>
<DropdownToggle caret>
Dropup
</DropdownToggle>
<DropdownMenu>
<DropdownItem>Another Action</DropdownItem>
<DropdownItem>Another Action</DropdownItem>
</DropdownMenu>
</ButtonDropdown>
{' '}
<ButtonDropdown direction="left" isOpen={this.state.btnDropleft} toggle={() => { this.setState({ btnDropleft: !this.state.btnDropleft }); }}>
<DropdownToggle caret>
Dropleft
</DropdownToggle>
<DropdownMenu>
<DropdownItem>Another Action</DropdownItem>
<DropdownItem>Another Action</DropdownItem>
</DropdownMenu>
</ButtonDropdown>
{' '}
<ButtonDropdown direction="right" isOpen={this.state.btnDropright} toggle={() => { this.setState({ btnDropright: !this.state.btnDropright }); }}>
<DropdownToggle caret>
Dropright
</DropdownToggle>
<DropdownMenu>
<DropdownItem>Another Action</DropdownItem>
<DropdownItem>Another Action</DropdownItem>
</DropdownMenu>
</ButtonDropdown>
</div>
</div>
<pre>
<PrismCode className="language-jsx">
{`<ButtonDropdown isOpen={isOpen} toggle={toggle} dropup>
<DropdownToggle caret caret size="lg">
{`<ButtonDropdown direction="up" isOpen={isOpen} toggle={toggle}>
<DropdownToggle caret>
Dropup
</DropdownToggle>
<DropdownMenu>
<DropdownItem>Another Action</DropdownItem>
<DropdownItem>Another Action</DropdownItem>
</DropdownMenu>
</ButtonDropdown>
<ButtonDropdown direction="left" isOpen={this.state.btnDropleft} toggle={() => { this.setState({ btnDropleft: !this.state.btnDropleft }); }}>
<DropdownToggle caret>
Dropleft
</DropdownToggle>
<DropdownMenu>
<DropdownItem>Another Action</DropdownItem>
<DropdownItem>Another Action</DropdownItem>
</DropdownMenu>
</ButtonDropdown>
<ButtonDropdown direction="right" isOpen={this.state.btnDropright} toggle={() => { this.setState({ btnDropright: !this.state.btnDropright }); }}>
<DropdownToggle caret>
Dropright
</DropdownToggle>
<DropdownMenu>
<DropdownItem>Another Action</DropdownItem>
<DropdownItem>Another Action</DropdownItem>
</DropdownMenu>
</ButtonDropdown>`}
</PrismCode>
</pre>
Expand Down
75 changes: 74 additions & 1 deletion docs/lib/Components/DropdownsPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { Link } from 'react-router';
import { PrismCode } from 'react-prism';
import Helmet from 'react-helmet';
import {
Row,
Col,
Dropdown,
DropdownToggle,
DropdownItem,
Expand Down Expand Up @@ -54,7 +56,7 @@ export default class DropdownPage extends React.Component {
<PrismCode className="language-jsx">
{`Dropdown.propTypes = {
disabled: PropTypes.bool,
dropup: PropTypes.bool,
direction: PropTypes.oneOf(['up', 'down', 'left', 'right']),
group: PropTypes.bool,
isOpen: PropTypes.bool,
// For Dropdown usage inside a Nav
Expand Down Expand Up @@ -220,6 +222,77 @@ DropdownItem.propTypes = {
{DropdownUncontrolledExampleSource}
</PrismCode>
</pre>
<h3>Drop direction variations</h3>
<div className="docs-example">
<Row>
<Col>
<Dropdown direction="up" isOpen={this.state.ddDropup} toggle={() => { this.setState({ ddDropup: !this.state.ddDropup }); }}>
<DropdownToggle caret>
Dropup
</DropdownToggle>
<DropdownMenu>
<DropdownItem>Another Action</DropdownItem>
<DropdownItem>Another Action</DropdownItem>
</DropdownMenu>
</Dropdown>
</Col>
<Col>
<Dropdown direction="left" isOpen={this.state.ddDropleft} toggle={() => { this.setState({ ddDropleft: !this.state.ddDropleft }); }}>
<DropdownToggle caret>
Dropleft
</DropdownToggle>
<DropdownMenu>
<DropdownItem>Another Action</DropdownItem>
<DropdownItem>Another Action</DropdownItem>
</DropdownMenu>
</Dropdown>
</Col>
<Col>
<Dropdown direction="right" isOpen={this.state.ddDropright} toggle={() => { this.setState({ ddDropright: !this.state.ddDropright }); }}>
<DropdownToggle caret>
Dropright
</DropdownToggle>
<DropdownMenu>
<DropdownItem>Another Action</DropdownItem>
<DropdownItem>Another Action</DropdownItem>
</DropdownMenu>
</Dropdown>
</Col>
</Row>
</div>
<pre>
<PrismCode className="language-jsx">
{`<Dropdown direction="up" isOpen={isOpen} toggle={toggle}>
<DropdownToggle caret>
Dropup
</DropdownToggle>
<DropdownMenu>
<DropdownItem>Another Action</DropdownItem>
<DropdownItem>Another Action</DropdownItem>
</DropdownMenu>
</Dropdown>
<Dropdown direction="left" isOpen={this.state.btnDropleft} toggle={() => { this.setState({ btnDropleft: !this.state.btnDropleft }); }}>
<DropdownToggle caret>
Dropleft
</DropdownToggle>
<DropdownMenu>
<DropdownItem>Another Action</DropdownItem>
<DropdownItem>Another Action</DropdownItem>
</DropdownMenu>
</Dropdown>
<Dropdown direction="right" isOpen={this.state.btnDropright} toggle={() => { this.setState({ btnDropright: !this.state.btnDropright }); }}>
<DropdownToggle caret>
Dropright
</DropdownToggle>
<DropdownMenu>
<DropdownItem>Another Action</DropdownItem>
<DropdownItem>Another Action</DropdownItem>
</DropdownMenu>
</Dropdown>`}
</PrismCode>
</pre>
</div>
);
}
Expand Down
17 changes: 10 additions & 7 deletions src/Dropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';
import { Manager } from 'react-popper';
import classNames from 'classnames';
import { mapToCssModules, omit, keyCodes } from './utils';
import { mapToCssModules, omit, keyCodes, deprecated } from './utils';

const propTypes = {
disabled: PropTypes.bool,
dropup: PropTypes.bool,
dropup: deprecated(PropTypes.bool, 'Please use the prop "direction" with the value "up".'),
direction: PropTypes.oneOf(['up', 'down', 'left', 'right']),
group: PropTypes.bool,
isOpen: PropTypes.bool,
nav: PropTypes.bool,
Expand All @@ -26,7 +27,7 @@ const propTypes = {

const defaultProps = {
isOpen: false,
dropup: false,
direction: 'down',
nav: false,
addonType: false,
inNavbar: false,
Expand All @@ -35,7 +36,7 @@ const defaultProps = {
const childContextTypes = {
toggle: PropTypes.func.isRequired,
isOpen: PropTypes.bool.isRequired,
dropup: PropTypes.bool.isRequired,
direction: PropTypes.oneOf(['up', 'down', 'left', 'right']).isRequired,
inNavbar: PropTypes.bool.isRequired,
};

Expand All @@ -54,7 +55,7 @@ class Dropdown extends React.Component {
return {
toggle: this.props.toggle,
isOpen: this.props.isOpen,
dropup: this.props.dropup,
direction: (this.props.direction === 'down' && this.props.dropup) ? 'up' : this.props.direction,
inNavbar: this.props.inNavbar,
};
}
Expand Down Expand Up @@ -180,19 +181,21 @@ class Dropdown extends React.Component {
nav,
addonType,
...attrs
} = omit(this.props, ['toggle', 'disabled', 'inNavbar']);
} = omit(this.props, ['toggle', 'disabled', 'inNavbar', 'direction']);

const direction = (this.props.direction === 'down' && dropup) ? 'up' : this.props.direction;

attrs.tag = attrs.tag || (nav ? 'li' : 'div');

const classes = mapToCssModules(classNames(
className,
direction !== 'down' && `drop${direction}`,
{
[`input-group-${addonType}`]: addonType,
'btn-group': group,
[`btn-group-${size}`]: !!size,
dropdown: !group && !addonType,
show: isOpen,
dropup: dropup,
'nav-item': nav
}
), cssModule);
Expand Down
12 changes: 10 additions & 2 deletions src/DropdownMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,19 @@ const defaultProps = {

const contextTypes = {
isOpen: PropTypes.bool.isRequired,
dropup: PropTypes.bool.isRequired,
direction: PropTypes.oneOf(['up', 'down', 'left', 'right']).isRequired,
inNavbar: PropTypes.bool.isRequired,
};

const noFlipModifier = { flip: { enabled: false } };

const directionPositionMap = {
up: 'top',
left: 'left',
right: 'right',
down: 'bottom',
};

const DropdownMenu = (props, context) => {
const { className, cssModule, right, tag, flip, ...attrs } = props;
const classes = mapToCssModules(classNames(
Expand All @@ -41,7 +48,8 @@ const DropdownMenu = (props, context) => {

if (context.isOpen && !context.inNavbar) {
Tag = Popper;
const position1 = context.dropup ? 'top' : 'bottom';

const position1 = directionPositionMap[context.direction] || 'bottom';
const position2 = right ? 'end' : 'start';
attrs.placement = `${position1}-${position2}`;
attrs.component = tag;
Expand Down
14 changes: 13 additions & 1 deletion src/__tests__/Dropdown.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { mount } from 'enzyme';
import { mount, shallow } from 'enzyme';
import { Popper, Target } from 'react-popper';
import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from '../';
import { keyCodes } from '../utils';
Expand Down Expand Up @@ -805,4 +805,16 @@ describe('Dropdown', () => {
expect(wrapper.find('.dropdown-menu').first().type()).toEqual(Popper);
});
});

it('should render with correct class when direction is set', () => {
const dropup = shallow(<Dropdown direction="up" />);
const dropupProp = shallow(<Dropdown dropup />);
const dropleft = shallow(<Dropdown direction="left" />);
const dropright = shallow(<Dropdown direction="right" />);

expect(dropup.hasClass('dropup')).toBe(true);
expect(dropupProp.hasClass('dropup')).toBe(true);
expect(dropleft.hasClass('dropleft')).toBe(true);
expect(dropright.hasClass('dropright')).toBe(true);
});
});
Loading

0 comments on commit 2b71fd6

Please sign in to comment.