Skip to content
Permalink
Browse files

feat: modulate Popover Components (#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 <tesrin@gmail.com>

BREAKING CHANGE: Popovers now expose sub components Content, Title for building up popovers
  • Loading branch information
bpas247 authored and jquense committed Jun 21, 2019
1 parent e1f230b commit f608c42c40639fcd6e28f384e5206e66c25d6115
@@ -3,6 +3,8 @@ import React from 'react';
import PropTypes from 'prop-types';
import isRequiredForA11y from 'prop-types-extra/lib/isRequiredForA11y';
import { createBootstrapComponent } from './ThemeProvider';
import PopoverTitle from './PopoverTitle';
import PopoverContent from './PopoverContent';

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

/**
* When this prop is set, it creates a Popover with a Popover.Content inside
* passing the children directly to it
*/
content: PropTypes.bool,

/** @private */
innerRef: PropTypes.any,

/** @private */
scheduleUpdate: PropTypes.func,
/** @private */
outOfBoundaries: PropTypes.bool,
/**
* Title content
*/
title: PropTypes.node,
};

const defaultProps = {
@@ -75,8 +79,8 @@ function Popover({
placement,
className,
style,
title,
children,
content,
arrowProps,
scheduleUpdate: _,
outOfBoundaries: _1,
@@ -92,15 +96,17 @@ function Popover({
{...props}
>
<div className="arrow" {...arrowProps} />

{title && <div className={`${bsPrefix}-header h3`}>{title}</div>}

<div className={`${bsPrefix}-body`}>{children}</div>
{content ? <PopoverContent>{children}</PopoverContent> : children}
</div>
);
}

Popover.propTypes = propTypes;
Popover.defaultProps = defaultProps;

export default createBootstrapComponent(Popover, 'popover');
const DecoratedPopover = createBootstrapComponent(Popover, 'popover');

DecoratedPopover.Title = PopoverTitle;
DecoratedPopover.Content = PopoverContent;

export default DecoratedPopover;
@@ -0,0 +1,42 @@
import classNames from 'classnames';
import React from 'react';
import PropTypes from 'prop-types';
import { useBootstrapPrefix } from './ThemeProvider';

const propTypes = {
/** Set a custom element for this component */
as: PropTypes.elementType,

/** @default 'popover-body' */
bsPrefix: PropTypes.string,
};

const PopoverContent = React.forwardRef(
(
{
// Need to define the default "as" during prop destructuring to be compatible with styled-components github.com/react-bootstrap/react-bootstrap/issues/3595
as: Component = 'div',
bsPrefix,
className,
children,
...props
},
ref,
) => {
bsPrefix = useBootstrapPrefix(bsPrefix, 'popover-body');

return (
<Component
ref={ref}
{...props}
className={classNames(className, bsPrefix)}
>
{children}
</Component>
);
},
);

PopoverContent.propTypes = propTypes;

export default PopoverContent;
@@ -0,0 +1,42 @@
import classNames from 'classnames';
import React from 'react';
import PropTypes from 'prop-types';
import { useBootstrapPrefix } from './ThemeProvider';

const propTypes = {
/** Set a custom element for this component */
as: PropTypes.elementType,

/** @default 'popover-header' */
bsPrefix: PropTypes.string,
};

const PopoverTitle = React.forwardRef(
(
{
// Need to define the default "as" during prop destructuring to be compatible with styled-components github.com/react-bootstrap/react-bootstrap/issues/3595
as: Component = 'div',
bsPrefix,
className,
children,
...props
},
ref,
) => {
bsPrefix = useBootstrapPrefix(bsPrefix, 'popover-header');

return (
<Component
ref={ref}
{...props}
className={classNames(bsPrefix, className)}
>
{children}
</Component>
);
},
);

PopoverTitle.propTypes = propTypes;

export default PopoverTitle;
@@ -51,6 +51,8 @@ export OverlayTrigger from './OverlayTrigger';
export PageItem from './PageItem';
export Pagination from './Pagination';
export Popover from './Popover';
export PopoverContent from './PopoverContent';
export PopoverTitle from './PopoverTitle';
export ProgressBar from './ProgressBar';
export ResponsiveEmbed from './ResponsiveEmbed';
export Row from './Row';
@@ -6,8 +6,11 @@ import Popover from '../src/Popover';
describe('Popover', () => {
it('Should output a popover title and content', () => {
mount(
<Popover id="test-popover" title="Popover title">
<strong>Popover Content</strong>
<Popover id="test-popover">
<Popover.Title>Popover title</Popover.Title>
<Popover.Content>
<strong>Popover Content</strong>
</Popover.Content>
</Popover>,
).assertSingle(
'.popover[x-placement="right"][role="tooltip"].bs-popover-right strong',
@@ -1,6 +1,10 @@
const popover = (
<Popover id="popover-basic" title="Popover right">
And here's some <strong>amazing</strong> content. It's very engaging. right?
<Popover id="popover-basic">
<Popover.Title as="h3">Popover right</Popover.Title>
<Popover.Content>
And here's some <strong>amazing</strong> content. It's very engaging.
right?
</Popover.Content>
</Popover>
);

@@ -23,8 +23,11 @@ class Example extends React.Component {
container={this}
containerPadding={20}
>
<Popover id="popover-contained" title="Popover bottom">
<strong>Holy guacamole!</strong> Check this info.
<Popover id="popover-contained">
<Popover.Title as="h3">Popover bottom</Popover.Title>
<Popover.Content>
<strong>Holy guacamole!</strong> Check this info.
</Popover.Content>
</Popover>
</Overlay>
</ButtonToolbar>
@@ -5,11 +5,11 @@
key={placement}
placement={placement}
overlay={
<Popover
id={`popover-positioned-${placement}`}
title={`Popover ${placement}`}
>
<strong>Holy guacamole!</strong> Check this info.
<Popover id={`popover-positioned-${placement}`}>
<Popover.Title as="h3">{`Popover ${placement}`}</Popover.Title>
<Popover.Content>
<strong>Holy guacamole!</strong> Check this info.
</Popover.Content>
</Popover>
}
>
@@ -1,26 +1,19 @@
const popoverLeft = (
<Popover id="popover-positioned-scrolling-left" title="Popover left">
<strong>Holy guacamole!</strong> Check this info.
const popover = position => (
<Popover id={`popover-positioned-scrolling-${position}`}>
<Popover.Title as="h3">{`Popover ${position}`}</Popover.Title>
<Popover.Content>
<strong>Holy guacamole!</strong> Check this info.
</Popover.Content>
</Popover>
);

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

const popoverBottom = (
<Popover id="popover-positioned-scrolling-bottom" title="Popover bottom">
<strong>Holy guacamole!</strong> Check this info.
</Popover>
);
const popoverTop = <popover position="top" />;

const popoverRight = (
<Popover id="popover-positioned-scrolling-right" title="Popover right">
<strong>Holy guacamole!</strong> Check this info.
</Popover>
);
const popoverBottom = <popover position="bottom" />;

const popoverRight = <popover position="right" />;

class Positioner extends React.Component {
render() {
@@ -1,26 +1,19 @@
const popoverClick = (
<Popover id="popover-trigger-click" title="Popover bottom">
<strong>Holy guacamole!</strong> Check this info.
const popover = triggerBehavior => (
<Popover id={`popover-trigger-${triggerBehavior}`}>
<Popover.Title as="h3">Popover bottom</Popover.Title>
<Popover.Content>
<strong>Holy guacamole!</strong> Check this info.
</Popover.Content>
</Popover>
);

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

const popoverFocus = (
<Popover id="popover-trigger-focus" title="Popover bottom">
<strong>Holy guacamole!</strong> Check this info.
</Popover>
);
const popoverHoverFocus = <popover triggerBehavior="hover-focus" />;

const popoverClickRootClose = (
<Popover id="popover-trigger-click-root-close" title="Popover bottom">
<strong>Holy guacamole!</strong> Check this info.
</Popover>
);
const popoverFocus = <popover triggerBehavior="focus" />;

const popoverClickRootClose = <popover triggerBehavior="click-root-close" />;

render(
<ButtonToolbar>
@@ -212,6 +212,8 @@ export default withLayout(function TooltipSection({ data }) {
<ComponentApi metadata={data.OverlayTrigger} />
<ComponentApi metadata={data.Tooltip} />
<ComponentApi metadata={data.Popover} />
<ComponentApi metadata={data.PopoverContent} />
<ComponentApi metadata={data.PopoverTitle} />
</>
);
});
@@ -224,6 +226,12 @@ export const query = graphql`
Popover: componentMetadata(displayName: { eq: "Popover" }) {
...ComponentApi_metadata
}
PopoverContent: componentMetadata(displayName: { eq: "PopoverContent" }) {
...ComponentApi_metadata
}
PopoverTitle: componentMetadata(displayName: { eq: "PopoverTitle" }) {
...ComponentApi_metadata
}
Overlay: componentMetadata(displayName: { eq: "Overlay" }) {
...ComponentApi_metadata
}

0 comments on commit f608c42

Please sign in to comment.
You can’t perform that action at this time.