diff --git a/CHANGELOG.md b/CHANGELOG.md index 95f58d8f7df..f3d9416368f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/package.json b/package.json index e3574da5d92..412a9734b2c 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/scripts/jest/polyfills/mutation_observer.js b/scripts/jest/polyfills/mutation_observer.js index 577117aa50d..38e2f2c4726 100644 --- a/scripts/jest/polyfills/mutation_observer.js +++ b/scripts/jest/polyfills/mutation_observer.js @@ -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 || @@ -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) { diff --git a/src-docs/src/routes.js b/src-docs/src/routes.js index 81080050344..1cb118d3e5e 100644 --- a/src-docs/src/routes.js +++ b/src-docs/src/routes.js @@ -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'; @@ -371,6 +373,7 @@ const navigation = [ ErrorBoundaryExample, FocusTrapExample, HighlightExample, + InnerTextExample, I18nExample, IsColorDarkExample, MutationObserverExample, diff --git a/src-docs/src/views/inner_text/inner_text.js b/src-docs/src/views/inner_text/inner_text.js new file mode 100644 index 00000000000..2859dd52657 --- /dev/null +++ b/src-docs/src/views/inner_text/inner_text.js @@ -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 ( + +
Example:
+ + {(ref, innerText) => ( + + + + + + Simple string content + + + + +
Output:
{' '} + {innerText} +
+ )} +
+ + + +
Example with complex children:
+ + {(ref, innerText) => ( + + + + + + + EuiHighlight content + {' '} + with EuiBadge + + + + +
Output:
{' '} + {innerText} +
+ )} +
+ + + +
Example with updating content:
+ + {(ref, innerText) => ( + + + + + + {thing} + + + + +
Output:
{' '} + {innerText} +
+ )} +
+ + + +
Example with updating element:
+ + {(ref, innerText) => ( + + + + + {React.createElement( + type, + { + ref, + title: innerText, + }, + thing2 + )} + + + +
Output:
{' '} + {innerText} +
+ )} +
+
+ ); +}; diff --git a/src-docs/src/views/inner_text/inner_text_example.js b/src-docs/src/views/inner_text/inner_text_example.js new file mode 100644 index 00000000000..c1ed48d6cf3 --- /dev/null +++ b/src-docs/src/views/inner_text/inner_text_example.js @@ -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(); + + Content +`; +const euiInnerTextSnippet = ` + {(ref, innerText) => ( + + Content + + )} +`; + +export const InnerTextExample = { + title: 'Inner Text', + intro: ( + + +

+ For instances where accessing the text content of a component that may + be wrapped or interspersed with other components, two utilities are + available: +

+ +

+ Both utilities make available a ref reference to + add to the target DOM element, and the resulting{' '} + innerText value to use as needed. +

+
+ +
+ ), + sections: [ + { + title: 'Rendered', + source: [ + { + type: GuideSectionTypes.JS, + code: innerTextSource, + }, + { + type: GuideSectionTypes.HTML, + code: innerTextHtml, + }, + ], + demo: , + snippet: [useInnerTextSnippet, euiInnerTextSnippet], + }, + ], +}; diff --git a/src-docs/src/views/list_group/list_group.js b/src-docs/src/views/list_group/list_group.js index 8c0c771e9d3..e0fb8363877 100644 --- a/src-docs/src/views/list_group/list_group.js +++ b/src-docs/src/views/list_group/list_group.js @@ -60,7 +60,7 @@ export default class extends Component { - + diff --git a/src-docs/src/views/list_group/list_group_example.js b/src-docs/src/views/list_group/list_group_example.js index ab4930eeeb7..99758b562e6 100644 --- a/src-docs/src/views/list_group/list_group_example.js +++ b/src-docs/src/views/list_group/list_group_example.js @@ -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: [ @@ -95,5 +99,27 @@ export const ListGroupExample = { ), demo: , }, + { + title: 'Text wrapping and tooltips', + source: [ + { + type: GuideSectionTypes.JS, + code: listGroupExtraSource, + }, + { + type: GuideSectionTypes.HTML, + code: listGroupExtraHtml, + }, + ], + text: ( +

+ Optional props showToolTip and{' '} + wrapLines 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. +

+ ), + demo: , + }, ], }; diff --git a/src-docs/src/views/list_group/list_group_extra.js b/src-docs/src/views/list_group/list_group_extra.js new file mode 100644 index 00000000000..e8a134aeb4f --- /dev/null +++ b/src-docs/src/views/list_group/list_group_extra.js @@ -0,0 +1,25 @@ +import React from 'react'; + +import { EuiListGroup, EuiListGroupItem } from '../../../../src/components'; + +export default () => ( + + + + + + + Third very, very long item that will surely force + truncation + + } + /> + + + +); diff --git a/src/components/delay_hide/delay_hide.test.tsx b/src/components/delay_hide/delay_hide.test.tsx index d2e90a469da..7b02b1165c6 100644 --- a/src/components/delay_hide/delay_hide.test.tsx +++ b/src/components/delay_hide/delay_hide.test.tsx @@ -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); }); @@ -95,6 +96,7 @@ describe('when EuiDelayHide is hidden initially', () => { expect(wrapper.html()).toEqual('
Hello World
'); jest.advanceTimersByTime(200); + wrapper.setProps({}); expect(wrapper.html()).toEqual(null); }); }); @@ -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); }); }); @@ -148,6 +151,7 @@ describe('when EuiDelayHide has been visible and become hidden', () => { expect(wrapper.html()).toEqual('
Hello World
'); jest.advanceTimersByTime(1100); + wrapper.setProps({}); expect(wrapper.html()).toEqual(null); }); diff --git a/src/components/filter_group/filter_button.js b/src/components/filter_group/filter_button.js index c1499f79ec7..3b4108093f1 100644 --- a/src/components/filter_group/filter_button.js +++ b/src/components/filter_group/filter_button.js @@ -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, @@ -53,12 +55,14 @@ export const EuiFilterButton = ({ dataText = children; } + const [ref, innerText] = useInnerText(); const buttonContents = ( + data-text={dataText || innerText} + title={dataText || innerText}> {children} diff --git a/src/components/index.js b/src/components/index.js index f6008bc1596..2e93265dd77 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -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 { diff --git a/src/components/inner_text/__snapshots__/inner_text.test.tsx.snap b/src/components/inner_text/__snapshots__/inner_text.test.tsx.snap new file mode 100644 index 00000000000..c2ba7ae935b --- /dev/null +++ b/src/components/inner_text/__snapshots__/inner_text.test.tsx.snap @@ -0,0 +1,7 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`EuiInnerText is rendered 1`] = ` + + Test + +`; diff --git a/src/components/inner_text/index.ts b/src/components/inner_text/index.ts new file mode 100644 index 00000000000..f5b8b525632 --- /dev/null +++ b/src/components/inner_text/index.ts @@ -0,0 +1 @@ +export { useInnerText, EuiInnerText } from './inner_text'; diff --git a/src/components/inner_text/inner_text.test.tsx b/src/components/inner_text/inner_text.test.tsx new file mode 100644 index 00000000000..c681b4f5e2f --- /dev/null +++ b/src/components/inner_text/inner_text.test.tsx @@ -0,0 +1,188 @@ +import React, { useState, useEffect } from 'react'; +import { act } from 'react-dom/test-utils'; +import { render, mount } from 'enzyme'; +import { findTestSubject, requiredProps, sleep } from '../../test'; + +import { useInnerText, EuiInnerText } from './inner_text'; +import { EuiBadge } from '../badge'; + +describe('useInnerText', () => { + test('provides a callback `ref`', () => { + let ref; + const App = () => { + [ref] = useInnerText(); + return ; + }; + mount(); + + expect(ref).toBeInstanceOf(Function); + }); + + test('provides the result of `innerText`', () => { + const text = 'Test'; + let ref; + let innerText; + const App = () => { + [ref, innerText] = useInnerText(); + return {text}; + }; + mount(); + + expect(innerText).toEqual(text); + }); + + test('accepts a fallback value', () => { + const text = 'Test'; + const fallback = 'Fallback'; + let innerText; + const App = () => { + [, innerText] = useInnerText(fallback); + return {text}; + }; + mount(); + + expect(innerText).toEqual(fallback); + }); + + test('handles updated elements', async () => { + const timeout = 500; + const first = 'First'; + const second = 'Second'; + let innerText; + let ref; + const App = () => { + const [[thing, type], setThing] = useState([first, 'span']); + useEffect(() => { + setTimeout(() => { + act(() => setThing([second, 'div'])); + }, timeout); + }, [setThing]); + [ref, innerText] = useInnerText(); + return ( +
+ {React.createElement( + type, + { + ref, + title: innerText, + }, + thing + )} +
+ ); + }; + mount(); + + expect(innerText).toEqual(first); + + await sleep(timeout + 10); + + expect(innerText).toEqual(second); + }); + + test('handles updated content', async () => { + const timeout = 500; + const first = 'First'; + const second = 'Second'; + let innerText; + let ref; + const App = () => { + const [thing, setThing] = useState(first); + useEffect(() => { + setTimeout(() => { + act(() => setThing(second)); + }, timeout); + }, [setThing]); + [ref, innerText] = useInnerText(); + return ( +
+ + {thing} + +
+ ); + }; + mount(); + + expect(innerText).toEqual(first); + + // MutationObserver polyfill institues a 30ms mutation timeout period + const mutationObserverPolyfillPeriod = 30; + await sleep(timeout + mutationObserverPolyfillPeriod + 10); + + expect(innerText).toEqual(second); + }); +}); + +describe('EuiInnerText', () => { + test('is rendered', () => { + const component = render( + + {(ref, innerText) => ( + + Test + + )} + + ); + + expect(component).toMatchSnapshot(); + }); + + test('uses innerText', () => { + const text = 'Test'; + const component = mount( + + {(ref, innerText) => ( + + {text} + + )} + + ); + + const span = findTestSubject(component, 'span'); + expect(span.props().title).toBe(text); + }); + + test('accepts fallback prop', () => { + const text = 'Test'; + const fallback = 'Fallback'; + const component = mount( + + {({}, innerText) => ( + + {text} + + )} + + ); + + const span = findTestSubject(component, 'span'); + expect(span.props().title).toBe(fallback); + }); + + test('works with wrapper and interspersed DOM elements', () => { + const component = mount( + + {(ref, innerText) => ( + +
+ I{' '} + + can{' '} + + still read + this + + +
+
+ )} +
+ ); + + const span = findTestSubject(component, 'span'); + expect(span.props().title).toBe('I can still read this'); + }); +}); diff --git a/src/components/inner_text/inner_text.tsx b/src/components/inner_text/inner_text.tsx new file mode 100644 index 00000000000..8624b51d6cc --- /dev/null +++ b/src/components/inner_text/inner_text.tsx @@ -0,0 +1,51 @@ +import { FunctionComponent, ReactElement, useEffect, useState } from 'react'; + +type RefT = HTMLElement | Element | undefined | null; + +export function useInnerText( + innerTextFallback?: string +): [((node: RefT) => void), string | undefined] { + const [ref, setRef] = useState(null); + const [innerText, setInnerText] = useState(innerTextFallback); + const updateInnerText = (node: typeof ref) => { + if (!node) return; + setInnerText( + // Check for `innerText` implementation rather than a simple OR check + // because in real cases the result of `innerText` could correctly be `null` + // while the result of `textContent` could correctly be non-`null` due to + // differing reliance on browser layout calculations. + // We prefer the result of `innerText`, if available. + 'innerText' in node ? node.innerText : node.textContent || undefined + ); + }; + const observer = new MutationObserver(mutationsList => { + if (mutationsList.length) updateInnerText(ref); + }); + useEffect(() => { + if (ref) { + observer.disconnect(); + updateInnerText(ref); + observer.observe(ref, { + characterData: true, + subtree: true, + }); + } + return () => { + observer.disconnect(); + }; + }, [ref]); + + return [setRef, innerText]; +} + +export interface EuiInnerTextProps { + children: (ref?: (node: RefT) => void, innerText?: string) => ReactElement; + fallback?: string; +} +export const EuiInnerText: FunctionComponent = ({ + children, + fallback, +}) => { + const [ref, innerText] = useInnerText(fallback); + return children(ref, innerText); +}; diff --git a/src/components/list_group/list_group_item.js b/src/components/list_group/list_group_item.js index 679d986c8de..c24f1e74217 100644 --- a/src/components/list_group/list_group_item.js +++ b/src/components/list_group/list_group_item.js @@ -5,6 +5,7 @@ import classNames from 'classnames'; import { EuiButtonIcon } from '../button'; import { IconPropType, EuiIcon } from '../icon'; import { EuiToolTip } from '../tool_tip'; +import { useInnerText } from '../inner_text'; const sizeToClassNameMap = { xs: 'euiListGroupItem--xSmall', @@ -79,12 +80,18 @@ export const EuiListGroupItem = ({ } // Only add the label as the title attribute if it's possibly truncated - const labelContent = ( + // Also ensure the value of the title attribute is a string + const [ref, innerText] = useInnerText(); + const shouldRenderTitle = !wrapText && !showToolTip; + const labelContent = shouldRenderTitle ? ( + title={typeof label === 'string' ? label : innerText}> {label} + ) : ( + {label} ); // Handle the variety of interaction behavior diff --git a/yarn.lock b/yarn.lock index dbda2004dce..73a0e3ab6e4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1468,6 +1468,22 @@ aggregate-error@^1.0.0: clean-stack "^1.0.0" indent-string "^3.0.0" +airbnb-prop-types@^2.13.2: + version "2.13.2" + resolved "https://registry.yarnpkg.com/airbnb-prop-types/-/airbnb-prop-types-2.13.2.tgz#43147a5062dd2a4a5600e748a47b64004cc5f7fc" + integrity sha512-2FN6DlHr6JCSxPPi25EnqGaXC4OC3/B3k1lCd6MMYrZ51/Gf/1qDfaR+JElzWa+Tl7cY2aYOlsYJGFeQyVHIeQ== + dependencies: + array.prototype.find "^2.0.4" + function.prototype.name "^1.1.0" + has "^1.0.3" + is-regex "^1.0.4" + object-is "^1.0.1" + object.assign "^4.1.0" + object.entries "^1.1.0" + prop-types "^15.7.2" + prop-types-exact "^1.2.0" + react-is "^16.8.6" + ajv-errors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.0.tgz#ecf021fa108fd17dfb5e6b383f2dd233e31ffc59" @@ -1780,6 +1796,14 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= +array.prototype.find@^2.0.4: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.1.0.tgz#630f2eaf70a39e608ac3573e45cf8ccd0ede9ad7" + integrity sha512-Wn41+K1yuO5p7wRZDl7890c3xvv5UBrfVXTVIe28rSQb6LS0fZMDrQB6PAcxQFRFy6vJTLDc3A2+3CjQdzVKRg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.13.0" + array.prototype.flat@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.1.tgz#812db8f02cad24d3fab65dd67eabe3b8903494a4" @@ -4686,28 +4710,30 @@ entities@^1.1.1, entities@~1.1.1: resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" integrity sha1-blwtClYhtdra7O+AuQ7ftc13cvA= -enzyme-adapter-react-16@^1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.9.1.tgz#6d49a3a31c3a0fccf527610f31b837e0f307128a" - integrity sha512-Egzogv1y77DUxdnq/CyHxLHaNxmSSKDDSDNNB/EiAXCZVFXdFibaNy2uUuRQ1n24T2m6KH/1Rw16XDRq+1yVEg== +enzyme-adapter-react-16@^1.14.0: + version "1.14.0" + resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.14.0.tgz#204722b769172bcf096cb250d33e6795c1f1858f" + integrity sha512-7PcOF7pb4hJUvjY7oAuPGpq3BmlCig3kxXGi2kFx0YzJHppqX1K8IIV9skT1IirxXlu8W7bneKi+oQ10QRnhcA== dependencies: - enzyme-adapter-utils "^1.10.0" - function.prototype.name "^1.1.0" + enzyme-adapter-utils "^1.12.0" + has "^1.0.3" object.assign "^4.1.0" object.values "^1.1.0" - prop-types "^15.6.2" - react-is "^16.7.0" + prop-types "^15.7.2" + react-is "^16.8.6" react-test-renderer "^16.0.0-0" + semver "^5.7.0" -enzyme-adapter-utils@^1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.10.0.tgz#5836169f68b9e8733cb5b69cad5da2a49e34f550" - integrity sha512-VnIXJDYVTzKGbdW+lgK8MQmYHJquTQZiGzu/AseCZ7eHtOMAj4Rtvk8ZRopodkfPves0EXaHkXBDkVhPa3t0jA== +enzyme-adapter-utils@^1.12.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.12.0.tgz#96e3730d76b872f593e54ce1c51fa3a451422d93" + integrity sha512-wkZvE0VxcFx/8ZsBw0iAbk3gR1d9hK447ebnSYBf95+r32ezBq+XDSAvRErkc4LZosgH8J7et7H7/7CtUuQfBA== dependencies: + airbnb-prop-types "^2.13.2" function.prototype.name "^1.1.0" object.assign "^4.1.0" object.fromentries "^2.0.0" - prop-types "^15.6.2" + prop-types "^15.7.2" semver "^5.6.0" enzyme-to-json@^3.3.0: @@ -4717,10 +4743,10 @@ enzyme-to-json@^3.3.0: dependencies: lodash "^4.17.4" -enzyme@^3.9.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.9.0.tgz#2b491f06ca966eb56b6510068c7894a7e0be3909" - integrity sha512-JqxI2BRFHbmiP7/UFqvsjxTirWoM1HfeaJrmVSZ9a1EADKkZgdPcAuISPMpoUiHlac9J4dYt81MC5BBIrbJGMg== +enzyme@^3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.10.0.tgz#7218e347c4a7746e133f8e964aada4a3523452f6" + integrity sha512-p2yy9Y7t/PFbPoTvrWde7JIYB2ZyGC+NgTNbVEGvZ5/EyoYSr9aG/2rSbVvyNvMHEhw9/dmGUJHWtfQIEiX9pg== dependencies: array.prototype.flat "^1.2.1" cheerio "^1.0.0-rc.2" @@ -4773,7 +4799,7 @@ error@^7.0.2: string-template "~0.2.1" xtend "~4.0.0" -es-abstract@^1.10.0, es-abstract@^1.11.0, es-abstract@^1.12.0, es-abstract@^1.5.0: +es-abstract@^1.10.0, es-abstract@^1.11.0, es-abstract@^1.12.0, es-abstract@^1.13.0, es-abstract@^1.5.0: version "1.13.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9" integrity sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg== @@ -10095,6 +10121,16 @@ object.entries@^1.0.4: function-bind "^1.1.0" has "^1.0.1" +object.entries@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.0.tgz#2024fc6d6ba246aee38bdb0ffd5cfbcf371b7519" + integrity sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.12.0" + function-bind "^1.1.1" + has "^1.0.3" + object.fromentries@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.0.tgz#49a543d92151f8277b3ac9600f1e930b189d30ab" @@ -11554,6 +11590,15 @@ prompts@^2.0.1: kleur "^3.0.2" sisteransi "^1.0.0" +prop-types-exact@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/prop-types-exact/-/prop-types-exact-1.2.0.tgz#825d6be46094663848237e3925a98c6e944e9869" + integrity sha512-K+Tk3Kd9V0odiXFP9fwDHUYRyvK3Nun3GVyPapSIs5OBkITAm15W0CPFD/YKTkMUAbc0b9CUwRQp2ybiBIq+eA== + dependencies: + has "^1.0.3" + object.assign "^4.1.0" + reflect.ownkeys "^0.2.0" + prop-types@^15.5.10, prop-types@^15.5.6, prop-types@^15.6.0: version "15.6.0" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856" @@ -11917,6 +11962,11 @@ react-is@^16.7.0, react-is@^16.8.2: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.2.tgz#09891d324cad1cb0c1f2d91f70a71a4bee34df0f" integrity sha512-D+NxhSR2HUCjYky1q1DwpNUD44cDpUXzSmmFyC3ug1bClcU/iDNy0YNn1iwme28fn+NFhpA13IndOd42CrFb+Q== +react-is@^16.8.6: + version "16.8.6" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16" + integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA== + react-is@~16.3.0: version "16.3.2" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.3.2.tgz#f4d3d0e2f5fbb6ac46450641eb2e25bf05d36b22" @@ -12299,6 +12349,11 @@ redux@^4.0.1: loose-envify "^1.4.0" symbol-observable "^1.2.0" +reflect.ownkeys@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz#749aceec7f3fdf8b63f927a04809e90c5c0b3460" + integrity sha1-dJrO7H8/34tj+SegSAnpDFwLNGA= + regenerate-unicode-properties@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-7.0.0.tgz#107405afcc4a190ec5ed450ecaa00ed0cafa7a4c" @@ -13146,7 +13201,7 @@ semver@^4.0.3: resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" integrity sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto= -semver@^5.5.1: +semver@^5.5.1, semver@^5.7.0: version "5.7.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b" integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==