Skip to content

Commit

Permalink
Merge 83ca8f1 into 738584e
Browse files Browse the repository at this point in the history
  • Loading branch information
parostatkiem-zz committed Nov 20, 2019
2 parents 738584e + 83ca8f1 commit ef6a3c9
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 13 deletions.
41 changes: 35 additions & 6 deletions src/Dropdown/Dropdown.Component.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import React from 'react';
import { Button, Dropdown, Menu, Popover } from '../';
import { ComponentPage, Example } from '../_playground';


export const DropdownComponent = () => {

return (
<ComponentPage
description={`The **Dropdown** component lets the user select one of the different options.
Expand Down Expand Up @@ -45,7 +47,7 @@ export const DropdownComponent = () => {
}
control={
<Button className='fd-dropdown__control' compact>
Select
Select
</Button>
}
id='jhqD0556'
Expand All @@ -71,7 +73,7 @@ export const DropdownComponent = () => {
}
control={
<Button className='fd-dropdown__control' glyph='filter'>
Select
Select
</Button>
}
id='jhqD0557'
Expand All @@ -93,7 +95,7 @@ export const DropdownComponent = () => {
control={
<Button className='fd-dropdown__control' compact
glyph='filter'>
Select
Select
</Button>
}
id='jhqD0558'
Expand All @@ -118,7 +120,7 @@ export const DropdownComponent = () => {
}
control={
<Button className='fd-dropdown__control'>
Select
Select
</Button>
}
id='jhqD0559'
Expand All @@ -139,14 +141,41 @@ export const DropdownComponent = () => {
}
control={
<Button className='fd-dropdown__control' compact>
Select
Select
</Button>
}
id='jhqD0560'
noArrow />
</Dropdown>
</Example>

<Example
centered
title='Popover width limited to Dropdown'>
<Dropdown>
<Popover
body={
<Menu>
<Menu.List>
<Menu.Item url='#'>Option 1</Menu.Item>
<Menu.Item url='#'>Option 2</Menu.Item>
<Menu.Item url='#'>Option 3</Menu.Item>
<Menu.Item url='#'>Option 4</Menu.Item>
</Menu.List>
</Menu>
}
control={
<Button className='fd-dropdown__control'
glyph='navigation-down-arrow'>
Open the dropdown
</Button>
}
id='jhqD0561'
noArrow
widthSizingType='matchTarget' />
</Dropdown>
</Example>

<Example
centered
title='Disabled State'>
Expand All @@ -165,7 +194,7 @@ export const DropdownComponent = () => {
control={
<Button className='fd-dropdown__control' disabled
glyph='filter'>
Select
Select
</Button>
}
disabled
Expand Down
27 changes: 27 additions & 0 deletions src/Popover/Popover.Component.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import 'fundamental-styles/dist/layout.css'; //needed for layout container class for placement example
import path from 'path';
import { POPPER_SIZING_TYPES } from '../utils/constants';
import React from 'react';
import { Button, Icon, Identifier, Image, Menu, Modal, Popover } from '../';
import { ComponentPage, Example } from '../_playground';


const bodyContent = (
<Menu>
<Menu.List>
Expand All @@ -15,6 +17,17 @@ const bodyContent = (
</Menu>
);

const longBodyContent = (
<Menu>
<Menu.List>
<Menu.Item url='#'>Lorem ipsum dolor sit amet</Menu.Item>
<Menu.Item url='#'>consectetur adipiscing elit</Menu.Item>
<Menu.Item url='#'>sed do eiusmod tempor</Menu.Item>
<Menu.Item url='#'>incididunt ut labore et dolore</Menu.Item>
</Menu.List>
</Menu>
);

export class PopoverComponent extends React.Component {
constructor(props) {
super(props);
Expand Down Expand Up @@ -178,6 +191,20 @@ export class PopoverComponent extends React.Component {
placement='bottom' />
</Example>

<Example
centered
title='Width Sizing Type'>
{POPPER_SIZING_TYPES.map(type =>
(<Popover
body={longBodyContent}
control={<Button>widthizingType: <strong>'{type}'</strong></Button>}
disableEdgeDetection
placement='bottom'
widthSizingType={type} />)
)}

</Example>

<Example
centered
description={`When an overlay (\`body\`) is visible, the reference (\`control\`)
Expand Down
20 changes: 15 additions & 5 deletions src/Popover/Popover.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import chain from 'chain-function';
import classnames from 'classnames';
import keycode from 'keycode';
import Popper from '../utils/_Popper';
import { POPPER_PLACEMENTS } from '../utils/constants';
import PropTypes from 'prop-types';
import withStyles from '../utils/WithStyles/WithStyles';
import { POPPER_PLACEMENTS, POPPER_SIZING_TYPES, POPPER_SIZING_TYPES_DESCRIPTION } from '../utils/constants';
import React, { Component } from 'react';

class Popover extends Component {
Expand All @@ -14,6 +14,7 @@ class Popover extends Component {
this.state = {
isExpanded: false
};
this.placementTargetRef = React.createRef();
}

isButton = (node) => {
Expand Down Expand Up @@ -71,6 +72,7 @@ class Popover extends Component {
className,
placement,
popperProps,
widthSizingType,
...rest
} = this.props;

Expand All @@ -93,7 +95,10 @@ class Popover extends Component {
};
}

const referenceComponent = React.cloneElement(control, controlProps);
const referenceComponent = React.cloneElement(control, {
...controlProps,
ref: this.placementTargetRef
});

const popoverClasses = classnames('fd-popover', className);

Expand All @@ -105,12 +110,14 @@ class Popover extends Component {
noArrow={noArrow}
onClickOutside={chain(this.handleOutsideClick, onClickOutside)}
onEscapeKey={chain(this.handleOutsideClick, onEscapeKey)}
placementTargetRef={this.placementTargetRef}
popperPlacement={placement}
popperProps={popperProps}
referenceClassName='fd-popover__control'
referenceComponent={referenceComponent}
show={this.state.isExpanded && !disabled}
usePortal>
usePortal
widthSizingType={widthSizingType}>
{body}
</Popper>
</div>
Expand All @@ -132,13 +139,15 @@ Popover.propTypes = {
noArrow: PropTypes.bool,
placement: PropTypes.oneOf(POPPER_PLACEMENTS),
popperProps: PropTypes.object,
widthSizingType: PropTypes.oneOf(POPPER_SIZING_TYPES),
onClickOutside: PropTypes.func,
onEscapeKey: PropTypes.func
};

Popover.defaultProps = {
onClickOutside: () => {},
onEscapeKey: () => {}
widthSizingType: 'none',
onClickOutside: () => { },
onEscapeKey: () => { }
};

Popover.propDescriptions = {
Expand All @@ -149,6 +158,7 @@ Popover.propDescriptions = {
noArrow: 'Set to **true** to render a popover without an arrow.',
placement: 'Initial position of the `body` (overlay) related to the `control`.',
popperProps: 'Additional props to be spread to the overlay element, supported by <a href="https://popper.js.org" target="_blank">popper.js</a>.',
widthSizingType: POPPER_SIZING_TYPES_DESCRIPTION,
onClickOutside: 'Callback for consumer clicking outside of popover body.',
onEscapeKey: 'Callback when escape key is pressed when popover body is visible.'
};
Expand Down
47 changes: 47 additions & 0 deletions src/Popover/Popover.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,53 @@ describe('<Popover />', () => {
expect(wrapper.state('isExpanded')).toBeFalsy();
});



describe('widthSizingType', () => {
const targetBoundaries = { left: 123, right: 456 };
beforeAll(()=>{
Object.defineProperty(window.HTMLElement.prototype, 'getBoundingClientRectTest', {
// because of getBoundingClientRectTest cannot be redefined
value: () => targetBoundaries,
writable: true
});
});

test('matchTarget', () => {
const popoverWithParent = mount(popOver).setProps({ widthSizingType: 'matchTarget' });
popoverWithParent.find('div.fd-popover__control .sap-icon--cart').simulate('click');
const popoverStyle = popoverWithParent.find('.fd-popover__popper').prop('style');

expect(popoverStyle).toHaveProperty('left', targetBoundaries.left);
expect(popoverStyle).toHaveProperty('width', targetBoundaries.right - targetBoundaries.left);
});

test('minTarget', () => {
const popoverWithParent = mount(popOver).setProps({ widthSizingType: 'minTarget' });
popoverWithParent.find('div.fd-popover__control .sap-icon--cart').simulate('click');
const popoverStyle = popoverWithParent.find('.fd-popover__popper').prop('style');

expect(popoverStyle).toHaveProperty('left');
const { left } = popoverStyle;
expect(popoverStyle).toHaveProperty('minWidth', targetBoundaries.right - left);
});

test('maxTarget', () => {
const popoverWithParent = mount(popOver).setProps({ widthSizingType: 'maxTarget' });
popoverWithParent.find('div.fd-popover__control .sap-icon--cart').simulate('click');
const popoverStyle = popoverWithParent.find('.fd-popover__popper').prop('style');

expect(popoverStyle).toHaveProperty('left');
const { left } = popoverStyle;
expect(popoverStyle).toHaveProperty('maxWidth', targetBoundaries.right - left);
});

afterAll(()=>{
delete window.HTMLElement.prototype.getBoundingClientRectTest;
});
});


describe('Prop spreading', () => {
test('should allow props to be spread to the Popover component', () => {
const element = mount(
Expand Down
23 changes: 21 additions & 2 deletions src/utils/_Popper.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import classnames from 'classnames';
import Foco from 'react-foco';
import keycode from 'keycode';
import { modalManager } from './modalManager';
import { POPPER_PLACEMENTS } from './constants';
import PropTypes from 'prop-types';
import React from 'react';
import ReactDOM from 'react-dom';
import { Manager, Popper as ReactPopper, Reference } from 'react-popper';
import { POPPER_PLACEMENTS, POPPER_SIZING_TYPES } from './constants';

class Popper extends React.Component {
constructor(props) {
Expand Down Expand Up @@ -65,6 +65,7 @@ class Popper extends React.Component {
disableEdgeDetection,
noArrow,
onClickOutside,
placementTargetRef,
popperClassName,
popperModifiers,
popperPlacement,
Expand All @@ -73,7 +74,8 @@ class Popper extends React.Component {
referenceComponent,
referenceProps,
show,
usePortal
usePortal,
widthSizingType
} = this.props;

const flipModifier = disableEdgeDetection ? { flip: { enabled: false } } : {};
Expand All @@ -95,6 +97,21 @@ class Popper extends React.Component {
return null;
}

const currentPlacementTarget = placementTargetRef && placementTargetRef.current;
if (widthSizingType !== 'none' && currentPlacementTarget) {
const { right: targetRight, left: targetLeft } = typeof currentPlacementTarget.getBoundingClientRectTest === 'function' ?
currentPlacementTarget.getBoundingClientRectTest() : // for test purpose. getBoundingClientRect cannot be redefined.
currentPlacementTarget.getBoundingClientRect();

const { left: popperLeft } = style;

let funMap = new Map();
funMap.set('matchTarget', { width: targetRight - targetLeft, left: targetLeft });
funMap.set('minTarget', { minWidth: targetRight - popperLeft });
funMap.set('maxTarget', { maxWidth: targetRight - popperLeft });
style = { ...style, ...funMap.get(widthSizingType) };
}

return (
<div
{...popperProps}
Expand Down Expand Up @@ -150,6 +167,7 @@ Popper.propTypes = {
referenceComponent: PropTypes.element.isRequired,
disableEdgeDetection: PropTypes.bool,
noArrow: PropTypes.bool,
placementTargetRef: PropTypes.shape({ current: PropTypes.any }),
popperClassName: PropTypes.string,
popperModifiers: PropTypes.object,
popperPlacement: PropTypes.oneOf(POPPER_PLACEMENTS),
Expand All @@ -158,6 +176,7 @@ Popper.propTypes = {
referenceProps: PropTypes.object,
show: PropTypes.bool,
usePortal: PropTypes.bool,
widthSizingType: PropTypes.oneOf(POPPER_SIZING_TYPES),
onClickOutside: PropTypes.func,
onEscapeKey: PropTypes.func,
onKeyDown: PropTypes.func
Expand Down
8 changes: 8 additions & 0 deletions src/utils/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,11 @@ export const POPPER_PLACEMENTS = [
'top',
'top-end'
];

export const POPPER_SIZING_TYPES = ['none', 'matchTarget', 'minTarget', 'maxTarget'];

export const POPPER_SIZING_TYPES_DESCRIPTION = `<ul>
<li>"matchTarget" - left and right edges align with the target</li>
<li>"minTarget" - right edge aligns with target unless Popover content is bigger</li>
<li>"maxTarget" - right edge aligns with target unless Popover content is smaller</li>
</ul>`;

0 comments on commit ef6a3c9

Please sign in to comment.