Skip to content

Commit 5f0bb10

Browse files
authored
Update Carousel (react-bootstrap#3201)
* Update Carousel * WIP * fix tests * remove transition * docs: code mod graphic imports * fix tests
1 parent a4e9b83 commit 5f0bb10

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+672
-681
lines changed

src/Carousel.js

Lines changed: 281 additions & 191 deletions
Large diffs are not rendered by default.

src/CarouselCaption.js

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import classNames from 'classnames';
2+
import PropTypes from 'prop-types';
23
import React from 'react';
34
import elementType from 'prop-types-extra/lib/elementType';
4-
5-
import { bsClass, getClassSet, splitBsProps } from './utils/bootstrapUtils';
5+
import { createBootstrapComponent } from './ThemeProvider';
66

77
const propTypes = {
8+
/**
9+
* @default 'carousel-caption'
10+
*/
11+
bsPrefix: PropTypes.string,
812
as: elementType,
913
};
1014

@@ -14,18 +18,13 @@ const defaultProps = {
1418

1519
class CarouselCaption extends React.Component {
1620
render() {
17-
const { as: Component, className, ...props } = this.props;
18-
const [bsProps, elementProps] = splitBsProps(props);
19-
20-
const classes = getClassSet(bsProps);
21+
const { as: Component, className, bsPrefix, ...props } = this.props;
2122

22-
return (
23-
<Component {...elementProps} className={classNames(className, classes)} />
24-
);
23+
return <Component {...props} className={classNames(className, bsPrefix)} />;
2524
}
2625
}
2726

2827
CarouselCaption.propTypes = propTypes;
2928
CarouselCaption.defaultProps = defaultProps;
3029

31-
export default bsClass('carousel-caption', CarouselCaption);
30+
export default createBootstrapComponent(CarouselCaption, 'carousel-caption');

src/CarouselItem.js

Lines changed: 12 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,109 +1,27 @@
11
import classNames from 'classnames';
22
import React from 'react';
33
import PropTypes from 'prop-types';
4-
import ReactDOM from 'react-dom';
5-
import transition from 'dom-helpers/transition';
4+
import { createBootstrapComponent } from './ThemeProvider';
65

76
const propTypes = {
8-
direction: PropTypes.oneOf(['prev', 'next']),
9-
onAnimateOutEnd: PropTypes.func,
10-
active: PropTypes.bool,
11-
animateIn: PropTypes.bool,
12-
animateOut: PropTypes.bool,
13-
index: PropTypes.number,
14-
};
15-
16-
const defaultProps = {
17-
active: false,
18-
animateIn: false,
19-
animateOut: false,
7+
/**
8+
* @default 'carousel-item'
9+
*/
10+
bsPrefix: PropTypes.string,
2011
};
2112

2213
class CarouselItem extends React.Component {
23-
constructor(props, context) {
24-
super(props, context);
25-
26-
this.handleAnimateOutEnd = this.handleAnimateOutEnd.bind(this);
27-
28-
this.state = {
29-
direction: null,
30-
};
31-
32-
this.isUnmounted = false;
33-
}
34-
35-
componentWillReceiveProps(nextProps) {
36-
if (this.props.active !== nextProps.active) {
37-
this.setState({ direction: null });
38-
}
39-
}
40-
41-
componentDidUpdate(prevProps) {
42-
const { active } = this.props;
43-
const prevActive = prevProps.active;
44-
45-
if (!active && prevActive) {
46-
transition.end(ReactDOM.findDOMNode(this), this.handleAnimateOutEnd);
47-
}
48-
49-
if (active !== prevActive) {
50-
setTimeout(() => this.startAnimation(), 20);
51-
}
52-
}
53-
54-
componentWillUnmount() {
55-
this.isUnmounted = true;
56-
}
57-
58-
handleAnimateOutEnd() {
59-
if (this.isUnmounted) {
60-
return;
61-
}
62-
63-
if (this.props.onAnimateOutEnd) {
64-
this.props.onAnimateOutEnd(this.props.index);
65-
}
66-
}
67-
68-
startAnimation() {
69-
if (this.isUnmounted) {
70-
return;
71-
}
72-
73-
this.setState({
74-
direction: this.props.direction === 'prev' ? 'right' : 'left',
75-
});
76-
}
77-
7814
render() {
79-
const {
80-
direction,
81-
active,
82-
animateIn,
83-
animateOut,
84-
className,
85-
...props
86-
} = this.props;
87-
88-
delete props.onAnimateOutEnd;
89-
delete props.index;
90-
91-
const classes = {
92-
item: true,
93-
active: (active && !animateIn) || animateOut,
94-
};
95-
if (direction && active && animateIn) {
96-
classes[direction] = true;
97-
}
98-
if (this.state.direction && (animateIn || animateOut)) {
99-
classes[this.state.direction] = true;
100-
}
15+
const { bsPrefix, children, className, ...props } = this.props;
10116

102-
return <div {...props} className={classNames(className, classes)} />;
17+
return (
18+
<div {...props} className={classNames(className, bsPrefix)}>
19+
{children}
20+
</div>
21+
);
10322
}
10423
}
10524

10625
CarouselItem.propTypes = propTypes;
107-
CarouselItem.defaultProps = defaultProps;
10826

109-
export default CarouselItem;
27+
export default createBootstrapComponent(CarouselItem, 'carousel-item');

src/Collapse.js

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,14 @@ import Transition, {
1010
EXITING,
1111
} from 'react-transition-group/Transition';
1212

13+
import triggerBrowserReflow from './utils/triggerBrowserReflow';
1314
import createChainedFunction from './utils/createChainedFunction';
1415

1516
const MARGINS = {
1617
height: ['marginTop', 'marginBottom'],
1718
width: ['marginLeft', 'marginRight'],
1819
};
1920

20-
// reading a dimension prop will cause the browser to recalculate,
21-
// which will let our animations work
22-
function triggerBrowserReflow(node) {
23-
node.offsetHeight; // eslint-disable-line no-unused-expressions
24-
}
25-
2621
function getDimensionValue(dimension, elem) {
2722
let offset = `offset${dimension[0].toUpperCase()}${dimension.slice(1)}`;
2823
let value = elem[offset];

src/DropdownMenu.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import classNames from 'classnames';
22
import React from 'react';
33
import PropTypes from 'prop-types';
44
import elementType from 'prop-types-extra/lib/elementType';
5-
import BaseDropdownMenu from 'react-overlays//DropdownMenu';
5+
import BaseDropdownMenu from 'react-overlays/DropdownMenu';
66
import NavbarContext from './NavbarContext';
77

88
import { createBootstrapComponent } from './ThemeProvider';

src/Fade.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Transition, {
66
ENTERING,
77
} from 'react-transition-group/Transition';
88
import onEnd from 'dom-helpers/transition/end';
9+
import triggerBrowserReflow from './utils/triggerBrowserReflow';
910

1011
const propTypes = {
1112
/**
@@ -75,10 +76,6 @@ const fadeStyles = {
7576
[ENTERED]: 'show',
7677
};
7778

78-
function triggerBrowserReflow(node) {
79-
node.offsetHeight; // eslint-disable-line no-unused-expressions
80-
}
81-
8279
class Fade extends React.Component {
8380
handleEnter = node => {
8481
triggerBrowserReflow(node);

src/Modal.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import canUseDOM from 'dom-helpers/util/inDOM';
66
import getScrollbarSize from 'dom-helpers/util/scrollbarSize';
77
import React from 'react';
88
import PropTypes from 'prop-types';
9-
import BaseModal from 'react-overlays//Modal';
9+
import BaseModal from 'react-overlays/Modal';
1010
import elementType from 'prop-types-extra/lib/elementType';
1111

1212
import Fade from './Fade';

src/Overlay.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import classNames from 'classnames';
22
import React, { cloneElement } from 'react';
33
import PropTypes from 'prop-types';
4-
import BaseOverlay from 'react-overlays//Overlay';
4+
import BaseOverlay from 'react-overlays/Overlay';
55
import elementType from 'prop-types-extra/lib/elementType';
66

77
import Fade from './Fade';

src/utils/BootstrapModalManager.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import qsa from 'dom-helpers/query/querySelectorAll';
22
import css from 'dom-helpers/style';
33
import getScrollbarSize from 'dom-helpers/util/scrollbarSize';
4-
import ModalManager from 'react-overlays//ModalManager';
4+
import ModalManager from 'react-overlays/ModalManager';
55

66
const Selector = {
77
FIXED_CONTENT: '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top',

src/utils/triggerBrowserReflow.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// reading a dimension prop will cause the browser to recalculate,
2+
// which will let our animations work
3+
export default function triggerBrowserReflow(node) {
4+
node.offsetHeight; // eslint-disable-line no-unused-expressions
5+
}

test/CarouselCaptionSpec.js

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,20 @@
11
import React from 'react';
2-
import ReactDOM from 'react-dom';
3-
import ReactTestUtils from 'react-dom/test-utils';
2+
import { mount } from 'enzyme';
43

54
import Carousel from '../src/Carousel';
65

76
describe('<Carousel.Caption>', () => {
87
it('uses "div" by default', () => {
9-
let instance = ReactTestUtils.renderIntoDocument(<Carousel.Caption />);
10-
11-
assert.equal(ReactDOM.findDOMNode(instance).nodeName, 'DIV');
8+
mount(
9+
<Carousel.Caption className="custom-class">
10+
<strong>Children</strong>
11+
</Carousel.Caption>,
12+
).assertSingle('div.carousel-caption.custom-class strong');
1213
});
1314

14-
it('has "carousel-caption" class', () => {
15-
let instance = ReactTestUtils.renderIntoDocument(
16-
<Carousel.Caption>Carousel.Caption content</Carousel.Caption>,
17-
);
18-
assert.equal(ReactDOM.findDOMNode(instance).className, 'carousel-caption');
19-
});
20-
21-
it('Should merge additional classes passed in', () => {
22-
let instance = ReactTestUtils.renderIntoDocument(
23-
<Carousel.Caption className="bob" />,
24-
);
25-
assert.ok(ReactDOM.findDOMNode(instance).className.match(/\bbob\b/));
26-
assert.ok(
27-
ReactDOM.findDOMNode(instance).className.match(/\bcarousel-caption\b/),
15+
it('should allow custom elements instead of "div"', () => {
16+
mount(<Carousel.Caption as="section" />).assertSingle(
17+
'section.carousel-caption',
2818
);
2919
});
30-
31-
it('allows custom elements instead of "div"', () => {
32-
let instance = ReactTestUtils.renderIntoDocument(
33-
<Carousel.Caption as="section" />,
34-
);
35-
36-
assert.equal(ReactDOM.findDOMNode(instance).nodeName, 'SECTION');
37-
});
3820
});

0 commit comments

Comments
 (0)