import Fixture from '../../Fixture'; import FixtureSet from '../../FixtureSet'; import TestCase from '../../TestCase'; const React = window.React; const ReactDOM = window.ReactDOM; const Suspense = React.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}
); } } 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} , this.container.current ) : null}
); } } class TextInputFixtures extends React.Component { render() { return (

Clicking "Hide" will hide the fixture context using{' '} display: none for 0.5 seconds, then restore. This is the built-in behavior for timed-out children. Each fixture tests whether the state of the DOM is preserved. Clicking "Remove" will remove the fixture content from the DOM for 0.5 seconds, then restore. This is{' '} not how timed-out children are hidden, but is included for comparison purposes.

As a shortcut, you can use Command + Enter (or Control + Enter on Windows, Linux) to "Hide" all the fixtures, or Command + / to "Remove" them.
  • 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.
  • Instead 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).
  • 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.)