diff --git a/fixtures/dom/src/components/Header.js b/fixtures/dom/src/components/Header.js index 9671d2c1d9af7..e4b743ea92c1f 100644 --- a/fixtures/dom/src/components/Header.js +++ b/fixtures/dom/src/components/Header.js @@ -68,6 +68,7 @@ class Header extends React.Component { Pointer Events Mouse Events Selection Events + Suspense diff --git a/fixtures/dom/src/components/fixtures/suspense/index.js b/fixtures/dom/src/components/fixtures/suspense/index.js new file mode 100644 index 0000000000000..27e6ef5cde896 --- /dev/null +++ b/fixtures/dom/src/components/fixtures/suspense/index.js @@ -0,0 +1,297 @@ +import Fixture from '../../Fixture'; +import FixtureSet from '../../FixtureSet'; +import TestCase from '../../TestCase'; + +const React = window.React; +const ReactDOM = window.ReactDOM; + +const Suspense = React.unstable_Suspense; + +let cache = new Set(); + +function AsyncStep({text, ms}) { + if (!cache.has(text)) { + throw new Promise(resolve => + setTimeout(() => { + cache.add(text); + resolve(); + }, ms) + ); + } + return null; +} + +let suspendyTreeIdCounter = 0; +class SuspendyTreeChild extends React.Component { + id = suspendyTreeIdCounter++; + state = { + step: 1, + isHidden: false, + }; + increment = () => this.setState(s => ({step: s.step + 1})); + + componentDidMount() { + document.addEventListener('keydown', this.onKeydown); + } + + componentWillUnmount() { + document.removeEventListener('keydown', this.onKeydown); + } + + onKeydown = event => { + if (event.metaKey && event.key === 'Enter') { + this.increment(); + } + }; + + render() { + return ( + + (display: none)}> + + + {this.props.children} + + + Hide + + ); + } +} + +class SuspendyTree extends React.Component { + parentContainer = React.createRef(null); + container = React.createRef(null); + componentDidMount() { + this.setState({}); + document.addEventListener('keydown', this.onKeydown); + } + componentWillUnmount() { + document.removeEventListener('keydown', this.onKeydown); + } + onKeydown = event => { + if (event.metaKey && event.key === '/') { + this.removeAndRestore(); + } + }; + removeAndRestore = () => { + const parentContainer = this.parentContainer.current; + const container = this.container.current; + parentContainer.removeChild(container); + parentContainer.textContent = '(removed from DOM)'; + setTimeout(() => { + parentContainer.textContent = ''; + parentContainer.appendChild(container); + }, 500); + }; + render() { + return ( + + + + + + {this.container.current !== null + ? ReactDOM.createPortal( + + {this.props.children} + Remove + , + this.container.current + ) + : null} + + + ); + } +} + +class TextInputFixtures extends React.Component { + render() { + return ( + + + Clicking "Hide" will cause the fixtures to time-out for 1 second, then + restore. You can also use Command + Enter (or Control + Enter on + Windows, Linux), which is useful for preserving focus. + + + + Use your cursor to select the text below. + Click "Hide" or "Remove". + + + + Text selection is preserved when hiding, but not when removing. + + + + + Select this entire sentence (and only this sentence). + + + + + + + Use your cursor to select a range that includes both the text and + the "Go" button. + + Click "Hide" or "Remove". + + + + Text selection is preserved when hiding, but not when removing. + + + + + Select a range that includes both this sentence and the "Go" + button. + + + + + + + Use your cursor to select a range that includes both the text and + the "Go" button. + + + Intead of clicking "Go", which switches focus, press Command + + Enter (or Control + Enter on Windows, Linux). + + + + + The ideal behavior is that the focus would not be lost, but + currently it is (both when hiding and removing). + + + + + Focus me + + + + + + + Type something ("Hello") into the text input. + Click "Hide" or "Remove". + + + + Input is preserved when hiding, but not when removing. + + + + + + + + + + + + Click "Hide" or "Remove". + + + + The image should reappear without flickering. The text should not + reflow. + + + + + React + is cool + + + + + + + + The iframe shows a nested version of this fixtures app. Navigate + to the "Text inputs" page. + + Select one of the checkboxes. + Click "Hide" or "Remove". + + + + When removing, the iframe is reloaded. When hiding, the iframe + should still be on the "Text inputs" page. The checkbox should still + be checked. (Unfortunately, scroll position is lost.) + + + + + + + + + + + + Start playing the video, or seek to a specific position. + Click "Hide" or "Remove". + + + + The playback position should stay the same. When hiding, the video + plays in the background for the entire duration. When removing, the + video stops playing, but the position is not lost. + + + + + + + + + + + + + + + + Start playing the audio, or seek to a specific position. + Click "Hide" or "Remove". + + + + The playback position should stay the same. When hiding, the audio + plays in the background for the entire duration. When removing, the + audio stops playing, but the position is not lost. + + + + + + + + + + + ); + } +} + +export default TextInputFixtures;