Skip to content

Commit f608c42

Browse files
bpas247jquense
authored andcommitted
feat: modulate Popover Components (react-bootstrap#3811)
* decouple the Popover components from the base component start work on decoupling and modulating the popover components to their own respective components. This will allow the users to choose how they want the popovers to be displayed, whether they want a title, no title, or if they want to inject their own custom title. also start work on changing the popover docs to reflect this new behavior. also start work on updating the popover unit tests to reflect this new behavior * update graphql queries to properly grab the Popover components * fix: compilation issues due to not binding the Popover components this issue was caused by the HOC function not carrying over the properties of the function, which were the modulated popover components. * add shorthand syntax for Popover, and fix bsPrefix issue add a shorthand syntax for declaring a popover with no title. also fix bsPrefix issues with PopoverTitle and PopoverContent * fix: very simple mistake of passing the string 'children' and not the actual children prop i don't know how i missed this 👀 * update all of the Popover example docs to reflect new api * fix: applying header styling to PopoverTitle this should be applied based on the user's preference, via className * Add 'as' prop to PopoverContent and PopoverTitle this will allow users to define a custom element type for their Popover components (such as setting PopoverTitle to use an 'h3' element) * Apply suggestions from code review Update Popover examples to utilize new `as` prop on `PopoverTitle` Co-Authored-By: Jimmy Jia <[email protected]> BREAKING CHANGE: Popovers now expose sub components Content, Title for building up popovers
1 parent e1f230b commit f608c42

File tree

11 files changed

+153
-57
lines changed

11 files changed

+153
-57
lines changed

src/Popover.js

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import React from 'react';
33
import PropTypes from 'prop-types';
44
import isRequiredForA11y from 'prop-types-extra/lib/isRequiredForA11y';
55
import { createBootstrapComponent } from './ThemeProvider';
6+
import PopoverTitle from './PopoverTitle';
7+
import PopoverContent from './PopoverContent';
68

79
const propTypes = {
810
/**
@@ -52,17 +54,19 @@ const propTypes = {
5254
style: PropTypes.object,
5355
}),
5456

57+
/**
58+
* When this prop is set, it creates a Popover with a Popover.Content inside
59+
* passing the children directly to it
60+
*/
61+
content: PropTypes.bool,
62+
5563
/** @private */
5664
innerRef: PropTypes.any,
5765

5866
/** @private */
5967
scheduleUpdate: PropTypes.func,
6068
/** @private */
6169
outOfBoundaries: PropTypes.bool,
62-
/**
63-
* Title content
64-
*/
65-
title: PropTypes.node,
6670
};
6771

6872
const defaultProps = {
@@ -75,8 +79,8 @@ function Popover({
7579
placement,
7680
className,
7781
style,
78-
title,
7982
children,
83+
content,
8084
arrowProps,
8185
scheduleUpdate: _,
8286
outOfBoundaries: _1,
@@ -92,15 +96,17 @@ function Popover({
9296
{...props}
9397
>
9498
<div className="arrow" {...arrowProps} />
95-
96-
{title && <div className={`${bsPrefix}-header h3`}>{title}</div>}
97-
98-
<div className={`${bsPrefix}-body`}>{children}</div>
99+
{content ? <PopoverContent>{children}</PopoverContent> : children}
99100
</div>
100101
);
101102
}
102103

103104
Popover.propTypes = propTypes;
104105
Popover.defaultProps = defaultProps;
105106

106-
export default createBootstrapComponent(Popover, 'popover');
107+
const DecoratedPopover = createBootstrapComponent(Popover, 'popover');
108+
109+
DecoratedPopover.Title = PopoverTitle;
110+
DecoratedPopover.Content = PopoverContent;
111+
112+
export default DecoratedPopover;

src/PopoverContent.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import classNames from 'classnames';
2+
import React from 'react';
3+
import PropTypes from 'prop-types';
4+
import { useBootstrapPrefix } from './ThemeProvider';
5+
6+
const propTypes = {
7+
/** Set a custom element for this component */
8+
as: PropTypes.elementType,
9+
10+
/** @default 'popover-body' */
11+
bsPrefix: PropTypes.string,
12+
};
13+
14+
const PopoverContent = React.forwardRef(
15+
(
16+
{
17+
// Need to define the default "as" during prop destructuring to be compatible with styled-components github.com/react-bootstrap/react-bootstrap/issues/3595
18+
as: Component = 'div',
19+
bsPrefix,
20+
className,
21+
children,
22+
...props
23+
},
24+
ref,
25+
) => {
26+
bsPrefix = useBootstrapPrefix(bsPrefix, 'popover-body');
27+
28+
return (
29+
<Component
30+
ref={ref}
31+
{...props}
32+
className={classNames(className, bsPrefix)}
33+
>
34+
{children}
35+
</Component>
36+
);
37+
},
38+
);
39+
40+
PopoverContent.propTypes = propTypes;
41+
42+
export default PopoverContent;

src/PopoverTitle.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import classNames from 'classnames';
2+
import React from 'react';
3+
import PropTypes from 'prop-types';
4+
import { useBootstrapPrefix } from './ThemeProvider';
5+
6+
const propTypes = {
7+
/** Set a custom element for this component */
8+
as: PropTypes.elementType,
9+
10+
/** @default 'popover-header' */
11+
bsPrefix: PropTypes.string,
12+
};
13+
14+
const PopoverTitle = React.forwardRef(
15+
(
16+
{
17+
// Need to define the default "as" during prop destructuring to be compatible with styled-components github.com/react-bootstrap/react-bootstrap/issues/3595
18+
as: Component = 'div',
19+
bsPrefix,
20+
className,
21+
children,
22+
...props
23+
},
24+
ref,
25+
) => {
26+
bsPrefix = useBootstrapPrefix(bsPrefix, 'popover-header');
27+
28+
return (
29+
<Component
30+
ref={ref}
31+
{...props}
32+
className={classNames(bsPrefix, className)}
33+
>
34+
{children}
35+
</Component>
36+
);
37+
},
38+
);
39+
40+
PopoverTitle.propTypes = propTypes;
41+
42+
export default PopoverTitle;

src/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ export OverlayTrigger from './OverlayTrigger';
5151
export PageItem from './PageItem';
5252
export Pagination from './Pagination';
5353
export Popover from './Popover';
54+
export PopoverContent from './PopoverContent';
55+
export PopoverTitle from './PopoverTitle';
5456
export ProgressBar from './ProgressBar';
5557
export ResponsiveEmbed from './ResponsiveEmbed';
5658
export Row from './Row';

test/PopoverSpec.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@ import Popover from '../src/Popover';
66
describe('Popover', () => {
77
it('Should output a popover title and content', () => {
88
mount(
9-
<Popover id="test-popover" title="Popover title">
10-
<strong>Popover Content</strong>
9+
<Popover id="test-popover">
10+
<Popover.Title>Popover title</Popover.Title>
11+
<Popover.Content>
12+
<strong>Popover Content</strong>
13+
</Popover.Content>
1114
</Popover>,
1215
).assertSingle(
1316
'.popover[x-placement="right"][role="tooltip"].bs-popover-right strong',

www/src/examples/Overlays/PopoverBasic.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
const popover = (
2-
<Popover id="popover-basic" title="Popover right">
3-
And here's some <strong>amazing</strong> content. It's very engaging. right?
2+
<Popover id="popover-basic">
3+
<Popover.Title as="h3">Popover right</Popover.Title>
4+
<Popover.Content>
5+
And here's some <strong>amazing</strong> content. It's very engaging.
6+
right?
7+
</Popover.Content>
48
</Popover>
59
);
610

www/src/examples/Overlays/PopoverContained.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,11 @@ class Example extends React.Component {
2323
container={this}
2424
containerPadding={20}
2525
>
26-
<Popover id="popover-contained" title="Popover bottom">
27-
<strong>Holy guacamole!</strong> Check this info.
26+
<Popover id="popover-contained">
27+
<Popover.Title as="h3">Popover bottom</Popover.Title>
28+
<Popover.Content>
29+
<strong>Holy guacamole!</strong> Check this info.
30+
</Popover.Content>
2831
</Popover>
2932
</Overlay>
3033
</ButtonToolbar>

www/src/examples/Overlays/PopoverPositioned.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
key={placement}
66
placement={placement}
77
overlay={
8-
<Popover
9-
id={`popover-positioned-${placement}`}
10-
title={`Popover ${placement}`}
11-
>
12-
<strong>Holy guacamole!</strong> Check this info.
8+
<Popover id={`popover-positioned-${placement}`}>
9+
<Popover.Title as="h3">{`Popover ${placement}`}</Popover.Title>
10+
<Popover.Content>
11+
<strong>Holy guacamole!</strong> Check this info.
12+
</Popover.Content>
1313
</Popover>
1414
}
1515
>

www/src/examples/Overlays/PopoverPositionedScrolling.js

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,19 @@
1-
const popoverLeft = (
2-
<Popover id="popover-positioned-scrolling-left" title="Popover left">
3-
<strong>Holy guacamole!</strong> Check this info.
1+
const popover = position => (
2+
<Popover id={`popover-positioned-scrolling-${position}`}>
3+
<Popover.Title as="h3">{`Popover ${position}`}</Popover.Title>
4+
<Popover.Content>
5+
<strong>Holy guacamole!</strong> Check this info.
6+
</Popover.Content>
47
</Popover>
58
);
69

7-
const popoverTop = (
8-
<Popover id="popover-positioned-scrolling-top" title="Popover top">
9-
<strong>Holy guacamole!</strong> Check this info.
10-
</Popover>
11-
);
10+
const popoverLeft = <popover position="left" />;
1211

13-
const popoverBottom = (
14-
<Popover id="popover-positioned-scrolling-bottom" title="Popover bottom">
15-
<strong>Holy guacamole!</strong> Check this info.
16-
</Popover>
17-
);
12+
const popoverTop = <popover position="top" />;
1813

19-
const popoverRight = (
20-
<Popover id="popover-positioned-scrolling-right" title="Popover right">
21-
<strong>Holy guacamole!</strong> Check this info.
22-
</Popover>
23-
);
14+
const popoverBottom = <popover position="bottom" />;
15+
16+
const popoverRight = <popover position="right" />;
2417

2518
class Positioner extends React.Component {
2619
render() {

www/src/examples/Overlays/PopoverTriggerBehaviors.js

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,19 @@
1-
const popoverClick = (
2-
<Popover id="popover-trigger-click" title="Popover bottom">
3-
<strong>Holy guacamole!</strong> Check this info.
1+
const popover = triggerBehavior => (
2+
<Popover id={`popover-trigger-${triggerBehavior}`}>
3+
<Popover.Title as="h3">Popover bottom</Popover.Title>
4+
<Popover.Content>
5+
<strong>Holy guacamole!</strong> Check this info.
6+
</Popover.Content>
47
</Popover>
58
);
69

7-
const popoverHoverFocus = (
8-
<Popover id="popover-trigger-hover-focus" title="Popover bottom">
9-
<strong>Holy guacamole!</strong> Check this info.
10-
</Popover>
11-
);
10+
const popoverClick = <popover triggerBehavior="click" />;
1211

13-
const popoverFocus = (
14-
<Popover id="popover-trigger-focus" title="Popover bottom">
15-
<strong>Holy guacamole!</strong> Check this info.
16-
</Popover>
17-
);
12+
const popoverHoverFocus = <popover triggerBehavior="hover-focus" />;
1813

19-
const popoverClickRootClose = (
20-
<Popover id="popover-trigger-click-root-close" title="Popover bottom">
21-
<strong>Holy guacamole!</strong> Check this info.
22-
</Popover>
23-
);
14+
const popoverFocus = <popover triggerBehavior="focus" />;
15+
16+
const popoverClickRootClose = <popover triggerBehavior="click-root-close" />;
2417

2518
render(
2619
<ButtonToolbar>

www/src/pages/components/overlays.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,8 @@ export default withLayout(function TooltipSection({ data }) {
212212
<ComponentApi metadata={data.OverlayTrigger} />
213213
<ComponentApi metadata={data.Tooltip} />
214214
<ComponentApi metadata={data.Popover} />
215+
<ComponentApi metadata={data.PopoverContent} />
216+
<ComponentApi metadata={data.PopoverTitle} />
215217
</>
216218
);
217219
});
@@ -224,6 +226,12 @@ export const query = graphql`
224226
Popover: componentMetadata(displayName: { eq: "Popover" }) {
225227
...ComponentApi_metadata
226228
}
229+
PopoverContent: componentMetadata(displayName: { eq: "PopoverContent" }) {
230+
...ComponentApi_metadata
231+
}
232+
PopoverTitle: componentMetadata(displayName: { eq: "PopoverTitle" }) {
233+
...ComponentApi_metadata
234+
}
227235
Overlay: componentMetadata(displayName: { eq: "Overlay" }) {
228236
...ComponentApi_metadata
229237
}

0 commit comments

Comments
 (0)