Skip to content

Commit

Permalink
useInnerText; Fix EuiListGroupItem title attrs (#2100)
Browse files Browse the repository at this point in the history
* dont set title attr if tooltip will be shown

* useInnerText and EuiInnerText

* use useInnerText for EuiListGroupItem; alter truncation rendering

* docs

* docs for innerText

* shared snippet

* useInnerText filterButton

* docs updates

* data-text

* more docs

* fallbacks: value and textContent

* bump enzyme; tests

* use mutationObserver and useState; docs

* observe after update

* clean up observer

* add act util to MutationObserver test polyfill; uncomment test

* CL
  • Loading branch information
thompsongl authored Jul 15, 2019
1 parent 4b06a38 commit abf4d52
Show file tree
Hide file tree
Showing 18 changed files with 601 additions and 28 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
## [`master`](https://github.com/elastic/eui/tree/master)

- Centered the square of the `popout` glyph in the artboard ([#2120](https://github.com/elastic/eui/pull/2120))
- Added `useInnerText` and `EuiInnerText` component utilities for retrieving text content of elements ([#2100](https://github.com/elastic/eui/pull/2100))

**Bug fixes**

- Fixed `EuiComboBox`'s options list from staying open when scrolled in a container by auto-closing the list on scroll ([#2106](https://github.com/elastic/eui/pull/2106))
- Fixed content provided to `EuiListGroupItem` and `EuiFilterButton` `title` attribute to prevent unreadable popover ([#2100](https://github.com/elastic/eui/pull/2100))

## [`12.3.1`](https://github.com/elastic/eui/tree/v12.3.1)

Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@
"css-loader": "^0.28.7",
"cssnano": "^4.0.5",
"dts-generator": "^2.1.0",
"enzyme": "^3.9.0",
"enzyme-adapter-react-16": "^1.9.1",
"enzyme": "^3.10.0",
"enzyme-adapter-react-16": "^1.14.0",
"enzyme-to-json": "^3.3.0",
"eslint": "^5.16.0",
"eslint-config-prettier": "^4.2.0",
Expand Down
8 changes: 6 additions & 2 deletions scripts/jest/polyfills/mutation_observer.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ SOFTWARE.
* Repository: https://github.com/megawac/MutationObserver.js
*/
import { EventEmitter } from 'events';
import { act } from 'react-dom/test-utils';

var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
Expand Down Expand Up @@ -279,8 +280,11 @@ var MutationObserver = /** @class */ (function () {
observer._timeout = null;
var mutations = observer.takeRecords();
if (mutations.length) { // fire away
// calling the listener with context is not spec but currently consistent with FF and WebKit
observer._listener(mutations, observer);
// `act` to support hooks-based callbacks
act(
// calling the listener with context is not spec but currently consistent with FF and WebKit
() => observer._listener(mutations, observer)
);
}
};
MutationObserver.prototype.searchSubtree = function (mutations, $target, $oldstate, config) {
Expand Down
3 changes: 3 additions & 0 deletions src-docs/src/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ import { IconExample } from './views/icon/icon_example';

import { ImageExample } from './views/image/image_example';

import { InnerTextExample } from './views/inner_text/inner_text_example';

import { KeyPadMenuExample } from './views/key_pad_menu/key_pad_menu_example';

import { LinkExample } from './views/link/link_example';
Expand Down Expand Up @@ -371,6 +373,7 @@ const navigation = [
ErrorBoundaryExample,
FocusTrapExample,
HighlightExample,
InnerTextExample,
I18nExample,
IsColorDarkExample,
MutationObserverExample,
Expand Down
123 changes: 123 additions & 0 deletions src-docs/src/views/inner_text/inner_text.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import React, { useEffect, useState } from 'react';

import { EuiInnerText } from '../../../../src/components/inner_text';

import {
EuiBadge,
EuiCode,
EuiFlexGroup,
EuiHighlight,
EuiFlexItem,
EuiHorizontalRule,
EuiPanel,
EuiText,
} from '../../../../src/components';

export default () => {
const first = 'First';
const second = 'Second';
const [thing, setThing] = useState(first);
const [[thing2, type], setThingAndType] = useState([first, 'span']);
useEffect(() => {
setTimeout(() => {
const newThing = thing === second ? first : second;
const newType = type === 'div' ? 'span' : 'div';
setThing(newThing);
setThingAndType([newThing, newType]);
}, 5000);
}, [thing]);

return (
<EuiText size="s">
<h5>Example:</h5>
<EuiInnerText>
{(ref, innerText) => (
<React.Fragment>
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiPanel paddingSize="s" grow={false}>
<span ref={ref} title={innerText}>
Simple string content
</span>
</EuiPanel>
</EuiFlexItem>
</EuiFlexGroup>
<h5 className="eui-displayInlineBlock">Output:</h5>{' '}
<EuiCode>{innerText}</EuiCode>
</React.Fragment>
)}
</EuiInnerText>

<EuiHorizontalRule margin="xl" />

<h5>Example with complex children:</h5>
<EuiInnerText>
{(ref, innerText) => (
<React.Fragment>
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiPanel paddingSize="s" grow={false}>
<span ref={ref} title={innerText}>
<EuiHighlight search="content">
EuiHighlight content
</EuiHighlight>{' '}
<EuiBadge>with EuiBadge</EuiBadge>
</span>
</EuiPanel>
</EuiFlexItem>
</EuiFlexGroup>
<h5 className="eui-displayInlineBlock">Output:</h5>{' '}
<EuiCode>{innerText}</EuiCode>
</React.Fragment>
)}
</EuiInnerText>

<EuiHorizontalRule margin="xl" />

<h5>Example with updating content:</h5>
<EuiInnerText>
{(ref, innerText) => (
<React.Fragment>
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiPanel paddingSize="s" grow={false}>
<span ref={ref} title={innerText}>
{thing}
</span>
</EuiPanel>
</EuiFlexItem>
</EuiFlexGroup>
<h5 className="eui-displayInlineBlock">Output:</h5>{' '}
<EuiCode>{innerText}</EuiCode>
</React.Fragment>
)}
</EuiInnerText>

<EuiHorizontalRule margin="xl" />

<h5>Example with updating element:</h5>
<EuiInnerText>
{(ref, innerText) => (
<React.Fragment>
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiPanel paddingSize="s" grow={false}>
{React.createElement(
type,
{
ref,
title: innerText,
},
thing2
)}
</EuiPanel>
</EuiFlexItem>
</EuiFlexGroup>
<h5 className="eui-displayInlineBlock">Output:</h5>{' '}
<EuiCode>{innerText}</EuiCode>
</React.Fragment>
)}
</EuiInnerText>
</EuiText>
);
};
71 changes: 71 additions & 0 deletions src-docs/src/views/inner_text/inner_text_example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React from 'react';

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

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

import { EuiCode, EuiSpacer, EuiText } from '../../../../src/components';

import InnerText from './inner_text';
const innerTextSource = require('!!raw-loader!./inner_text');
const innerTextHtml = renderToHtml(InnerText);
const useInnerTextSnippet = `const [ref, innerText] = useInnerText();
<span ref={ref} title={innerText}>
Content
</span>`;
const euiInnerTextSnippet = `<EuiInnerText>
{(ref, innerText) => (
<span ref={ref} title={innerText}>
Content
</span>
)}
</EuiInnerText>`;

export const InnerTextExample = {
title: 'Inner Text',
intro: (
<React.Fragment>
<EuiText>
<p>
For instances where accessing the text content of a component that may
be wrapped or interspersed with other components, two utilities are
available:
</p>
<ul>
<li>
<EuiCode>useInnerText</EuiCode> - A custom React hook, usable in
function components
</li>
<li>
<EuiCode>EuiInnerText</EuiCode> - A higher order{' '}
<EuiCode>useInnerText</EuiCode> component for use in class
components
</li>
</ul>
<p>
Both utilities make available a <EuiCode>ref</EuiCode> reference to
add to the target DOM element, and the resulting{' '}
<EuiCode>innerText</EuiCode> value to use as needed.
</p>
</EuiText>
<EuiSpacer />
</React.Fragment>
),
sections: [
{
title: 'Rendered',
source: [
{
type: GuideSectionTypes.JS,
code: innerTextSource,
},
{
type: GuideSectionTypes.HTML,
code: innerTextHtml,
},
],
demo: <InnerText />,
snippet: [useInnerTextSnippet, euiInnerTextSnippet],
},
],
};
2 changes: 1 addition & 1 deletion src-docs/src/views/list_group/list_group.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export default class extends Component {

<EuiSpacer size="l" />

<EuiListGroup flush={flushWidth} bordered={showBorder} showToolTips>
<EuiListGroup flush={flushWidth} bordered={showBorder}>
<EuiListGroupItem label="First item" />

<EuiListGroupItem label="Second item" />
Expand Down
26 changes: 26 additions & 0 deletions src-docs/src/views/list_group/list_group_example.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ import ListGroupLinkActions from './list_group_link_actions';
const listGroupLinkActionsSource = require('!!raw-loader!./list_group_link_actions');
const listGroupLinkActionsHtml = renderToHtml(ListGroupLinkActions);

import ListGroupExtra from './list_group_extra';
const listGroupExtraSource = require('!!raw-loader!./list_group_extra');
const listGroupExtraHtml = renderToHtml(ListGroupExtra);

export const ListGroupExample = {
title: 'List Group',
sections: [
Expand Down Expand Up @@ -95,5 +99,27 @@ export const ListGroupExample = {
),
demo: <ListGroupLinkActions />,
},
{
title: 'Text wrapping and tooltips',
source: [
{
type: GuideSectionTypes.JS,
code: listGroupExtraSource,
},
{
type: GuideSectionTypes.HTML,
code: listGroupExtraHtml,
},
],
text: (
<p>
Optional props <EuiCode>showToolTip</EuiCode> and{' '}
<EuiCode>wrapLines</EuiCode> can be used to augment the display of
list items. Use these when lists are inside small containers where it
is likely that the content will be truncated.
</p>
),
demo: <ListGroupExtra />,
},
],
};
25 changes: 25 additions & 0 deletions src-docs/src/views/list_group/list_group_extra.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';

import { EuiListGroup, EuiListGroupItem } from '../../../../src/components';

export default () => (
<EuiListGroup showToolTips>
<EuiListGroupItem label="First item" />

<EuiListGroupItem label="Second item" />

<EuiListGroupItem
label={
<span>
Third very, very long item that <strong>will surely</strong> force
truncation
</span>
}
/>

<EuiListGroupItem
wrapText
label="Fourth very, very long item with wrapping enabled that will not force truncation"
/>
</EuiListGroup>
);
4 changes: 4 additions & 0 deletions src/components/delay_hide/delay_hide.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ describe('when EuiDelayHide is visible initially', () => {
const wrapper = getWrapper();
wrapper.setProps({ hide: true });
jest.advanceTimersByTime(1100);
wrapper.setProps({});
expect(wrapper.html()).toEqual(null);
});

Expand Down Expand Up @@ -95,6 +96,7 @@ describe('when EuiDelayHide is hidden initially', () => {
expect(wrapper.html()).toEqual('<div>Hello World</div>');

jest.advanceTimersByTime(200);
wrapper.setProps({});
expect(wrapper.html()).toEqual(null);
});
});
Expand Down Expand Up @@ -127,6 +129,7 @@ describe('when EuiDelayHide is visible initially and has a minimumDuration of 20
test('it should be hidden after 2100ms', () => {
const wrapper = getWrapper();
jest.advanceTimersByTime(2100);
wrapper.setProps({});
expect(wrapper.html()).toEqual(null);
});
});
Expand All @@ -148,6 +151,7 @@ describe('when EuiDelayHide has been visible and become hidden', () => {
expect(wrapper.html()).toEqual('<div>Hello World</div>');

jest.advanceTimersByTime(1100);
wrapper.setProps({});

expect(wrapper.html()).toEqual(null);
});
Expand Down
8 changes: 6 additions & 2 deletions src/components/filter_group/filter_button.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { COLORS, ICON_SIDES, EuiButtonEmpty } from '../button/button_empty';

import { IconPropType } from '../icon';

import { useInnerText } from '../inner_text';

export const EuiFilterButton = ({
children,
className,
Expand Down Expand Up @@ -53,12 +55,14 @@ export const EuiFilterButton = ({
dataText = children;
}

const [ref, innerText] = useInnerText();
const buttonContents = (
<Fragment>
<span
ref={ref}
className="euiFilterButton__textShift"
data-text={dataText}
title={dataText}>
data-text={dataText || innerText}
title={dataText || innerText}>
{children}
</span>

Expand Down
2 changes: 2 additions & 0 deletions src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ export { ICON_TYPES, EuiIcon } from './icon';

export { EuiImage } from './image';

export { useInnerText, EuiInnerText } from './inner_text';

export { EuiI18n, EuiI18nNumber } from './i18n';

export {
Expand Down
Loading

0 comments on commit abf4d52

Please sign in to comment.