Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EuiResizeObserver; Allow dynamic content height in EuiContextMenuPanel #1559

Merged
merged 13 commits into from
Feb 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
## [`master`](https://github.com/elastic/eui/tree/master)

No public interface changes since `7.1.0`.
- Added `EuiResizeObserver` to expose ResizeObserver API to React components; falls back to MutationObserver API in unsupported browsers ([#1559](https://github.com/elastic/eui/pull/1559))

**Bug fixes**

- Fixed content cut off in `EuiContextMenuPanel` when height changes dynamically ([#1559](https://github.com/elastic/eui/pull/1559))

## [`7.1.0`](https://github.com/elastic/eui/tree/v7.1.0)

Expand Down
4 changes: 4 additions & 0 deletions src-docs/src/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,9 @@ import { ProgressExample }
import { RangeControlExample }
from './views/range/range_example';

import { ResizeObserverExample }
from './views/resize_observer/resize_observer_example';

import { ResponsiveExample }
from './views/responsive/responsive_example';

Expand Down Expand Up @@ -437,6 +440,7 @@ const navigation = [{
MutationObserverExample,
OutsideClickDetectorExample,
PortalExample,
ResizeObserverExample,
ResponsiveExample,
ToggleExample,
WindowEventExample,
Expand Down
142 changes: 95 additions & 47 deletions src-docs/src/views/context_menu/context_menu_with_content.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ import {
EuiContextMenu,
EuiIcon,
EuiPopover,
EuiSpacer,
EuiText
} from '../../../../src/components';

import EuiTabsExample from '../tabs/tabbed_content';

function flattenPanelTree(tree, array = []) {
array.push(tree);

Expand All @@ -33,41 +36,45 @@ export default class extends Component {
isPopoverOpen: false,
};

const panelTree = {
id: 0,
title: 'View options',
items: [{
name: 'Show fullscreen',
icon: (
<EuiIcon
type="search"
size="m"
/>
),
onClick: () => { this.closePopover(); window.alert('Show fullscreen'); },
}, {
name: 'See more',
icon: 'plusInCircle',
panel: {
id: 1,
width: 400,
title: 'See more',
content: (
<EuiText style={{ padding: 24 }} textAlign="center">
<p><EuiIcon type="faceHappy" size="xxl" /></p>

<h3>Context panels can contain anything</h3>
<p>
You can stuff just about anything into these panels. Be mindful of size though.
This panel is set to 400px and the height will grow as space allows.
</p>
</EuiText>
)
},
}],
this.createPanelTree = (Content) => {
return flattenPanelTree({
id: 0,
title: 'View options',
items: [{
name: 'Show fullscreen',
icon: (
<EuiIcon
type="search"
size="m"
/>
),
onClick: () => { this.closePopover(); window.alert('Show fullscreen'); },
}, {
name: 'See more',
icon: 'plusInCircle',
panel: {
id: 1,
width: 400,
title: 'See more',
content: <Content />
},
}],
});
};

this.panels = flattenPanelTree(panelTree);
this.panels = this.createPanelTree(() => (
<EuiText style={{ padding: 24 }} textAlign="center">
<p><EuiIcon type="faceHappy" size="xxl" /></p>

<h3>Context panels can contain anything</h3>
<p>
You can stuff just about anything into these panels. Be mindful of size though.
This panel is set to 400px and the height will grow as space allows.
</p>
</EuiText>
));

this.dynamicPanels = this.createPanelTree(EuiTabsExample);
}

onButtonClick = () => {
Expand All @@ -76,12 +83,24 @@ export default class extends Component {
}));
};

onDynamicButtonClick = () => {
this.setState(prevState => ({
isDynamicPopoverOpen: !prevState.isDynamicPopoverOpen,
}));
};

closePopover = () => {
this.setState({
isPopoverOpen: false,
});
};

closeDynamicPopover = () => {
this.setState({
isDynamicPopoverOpen: false,
});
};

render() {
const button = (
<EuiButton
Expand All @@ -93,21 +112,50 @@ export default class extends Component {
</EuiButton>
);

return (
<EuiPopover
id="contextMenu"
button={button}
isOpen={this.state.isPopoverOpen}
closePopover={this.closePopover}
panelPaddingSize="none"
withTitle
anchorPosition="upLeft"
const dynamicButton = (
<EuiButton
iconType="arrowDown"
iconSide="right"
onClick={this.onDynamicButtonClick}
>
<EuiContextMenu
initialPanelId={0}
panels={this.panels}
/>
</EuiPopover>
Click me to load dynamic mixed content menu
</EuiButton>
);

return (
<React.Fragment>
<EuiPopover
id="contextMenu"
button={button}
isOpen={this.state.isPopoverOpen}
closePopover={this.closePopover}
panelPaddingSize="none"
withTitle
anchorPosition="upLeft"
>
<EuiContextMenu
initialPanelId={0}
panels={this.panels}
/>
</EuiPopover>

<EuiSpacer size="l" />

<EuiPopover
id="contextMenuDynamic"
button={dynamicButton}
isOpen={this.state.isDynamicPopoverOpen}
closePopover={this.closeDynamicPopover}
panelPaddingSize="none"
withTitle
anchorPosition="upLeft"
>
<EuiContextMenu
initialPanelId={0}
panels={this.dynamicPanels}
/>
</EuiPopover>
</React.Fragment>
);
}
}
84 changes: 84 additions & 0 deletions src-docs/src/views/resize_observer/resize_observer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import React, {
Component,
} from 'react';

import {
EuiButton,
EuiButtonEmpty,
EuiCode,
EuiIcon,
EuiResizeObserver,
EuiPanel,
EuiSpacer,
EuiText
} from '../../../../src/components';

export class ResizeObserverExample extends Component {
state = {
hasResizeObserver: typeof ResizeObserver !== 'undefined',
height: 0,
width: 0,
paddingSize: 's',
items: ['Item 1', 'Item 2', 'Item 3'],
};

togglePaddingSize = () => {
this.setState(({ paddingSize }) => ({
paddingSize: paddingSize === 's' ? 'l' : 's'
}));
}

addItem = () => {
this.setState(({ items }) => ({
items: [...items, `Item ${items.length + 1}`]
}));
}

onResize = ({ height, width }) => {
this.setState({
height,
width
});
}

render() {
return (
<div>
<EuiText>
{this.state.hasResizeObserver ? (
<p><EuiIcon type="checkInCircleFilled" color="secondary" /> Browser supports ResizeObserver API.</p>
) : (
<p>
<EuiIcon type="crossInACircleFilled" color="danger" /> Browser does not support ResizeObserver API. Using MutationObserver.
</p>
)}
<p><EuiCode>{`height: ${this.state.height}; width: ${this.state.width}`}</EuiCode></p>
</EuiText>

<EuiSpacer/>

<EuiButton fill={true} onClick={this.togglePaddingSize}>
Toggle container padding
</EuiButton>

<EuiSpacer/>

<EuiResizeObserver
onResize={this.onResize}
>
{resizeRef => (
<div className="eui-displayInlineBlock" ref={resizeRef}>
<EuiPanel className="eui-displayInlineBlock" paddingSize={this.state.paddingSize}>
<ul>
{this.state.items.map(item => <li key={item}>{item}</li>)}
</ul>
<EuiSpacer size="s"/>
<EuiButtonEmpty onClick={this.addItem}>add item</EuiButtonEmpty>
</EuiPanel>
</div>
)}
</EuiResizeObserver>
</div>
);
}
}
53 changes: 53 additions & 0 deletions src-docs/src/views/resize_observer/resize_observer_example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from 'react';

import { renderToHtml } from '../../services';

import {
GuideSectionTypes,
} from '../../components';

import {
EuiCode,
EuiLink,
EuiResizeObserver,
} from '../../../../src/components';

import { ResizeObserverExample as ResizeObserver } from './resize_observer';
const resizeObserverSource = require('!!raw-loader!./resize_observer');
const resizeObserverHtml = renderToHtml(ResizeObserver);

export const ResizeObserverExample = {
title: 'ResizeObserver',
sections: [{
title: 'ResizeObserver',
source: [{
type: GuideSectionTypes.JS,
code: resizeObserverSource,
}, {
type: GuideSectionTypes.HTML,
code: resizeObserverHtml,
}],
text: (
<React.Fragment>
<p>
<EuiCode>ResizeObserver</EuiCode> is a wrapper around the
<EuiLink href="https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver"> Resizer Observer API </EuiLink>
which allows watching for changes to the content rectangle of DOM elements.
Unlike <EuiCode>MutationObserver</EuiCode>, <EuiCode>ResizeObserver</EuiCode> does not take parameters,
but it does fire a more efficient and informative callback when resize events occur.
</p>
<p>
This is a render prop component, <EuiCode>ResizeObserver</EuiCode> will pass a <EuiCode>ref</EuiCode>
callback which you must put on the element you wish to observe.
</p>
<p>
Due to limited browser support (currently supported in Chrome and Opera), <EuiCode>EuiResizeObserver</EuiCode> will
fallback to using the <EuiCode>MutationObserver</EuiCode> API with a default
set of paramters that approximate the results of <EuiCode>MutationObserver</EuiCode>.
</p>
</React.Fragment>
),
components: { EuiResizeObserver },
demo: <ResizeObserver />,
}],
};
2 changes: 1 addition & 1 deletion src/components/accordion/accordion.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {

import {
EuiMutationObserver,
} from '../mutation_observer';
} from '../observer/mutation_observer';

const paddingSizeToClassNameMap = {
none: null,
Expand Down
Loading