@samitouri / QOS-React / commits / a55e98f738

[playground] ViewTransition on internals toggle & tab expansion (#34597)

<!-- Thanks for submitting a pull request! We appreciate you spending the time to work on these changes. Please provide enough information so that others can review your pull request. The three fields below are mandatory. Before submitting a pull request, please make sure the following is done: 1. Fork [the repository](https://github.com/facebook/react) and create your branch from `main`. 2. Run `yarn` in the repository root. 3. If you've fixed a bug or added code that should be tested, add tests! 4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch TestName` is helpful in development. 5. Run `yarn test --prod` to test in the production environment. It supports the same options as `yarn test`. 6. If you need a debugger, run `yarn test --debug --watch TestName`, open `chrome://inspect`, and press "Inspect". 7. Format your code with [prettier](https://github.com/prettier/prettier) (`yarn prettier`). 8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only check changed files. 9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`). 10. If you haven't already, complete the CLA. Learn more about contributing: https://reactjs.org/docs/how-to-contribute.html --> ## Summary <!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? --> Added `<ViewTransition>` for when the "Show Internals" button is toggled for a basic fade transition. Additionally added a transition for when tabs are expanded in the advanced view of the Compiler Playground to display a smoother show/hide animation. ## How did you test this change? <!-- Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes the user interface. How exactly did you verify that your PR solves the issue you wanted to solve? If you leave this empty, your PR will very likely be closed. --> https://github.com/user-attachments/assets/c706b337-289e-488d-8cd7-45ff1d27788d

Eugene Choi committed Sep 30, 2025 at 15:25 UTC a55e98f738f4a7ab7196a4c4210cbc6e82ec9b0c
5 files changed +89 -37
compiler/apps/playground/components/AccordionWindow.tsx
+61 -34
@@ -6,7 +6,14 @@
6 */
7
8 import {Resizable} from 're-resizable';
9 -import React, {useCallback} from 'react';
9 +import React, {
10 + useCallback,
11 + useId,
12 + unstable_ViewTransition as ViewTransition,
13 + unstable_addTransitionType as addTransitionType,
14 + startTransition,
15 +} from 'react';
16 +import {EXPAND_ACCORDION_TRANSITION} from '../lib/transitionTypes';
17
18 type TabsRecord = Map<string, React.ReactNode>;
19
@@ -50,17 +57,23 @@ function AccordionWindowItem({
57 setTabsOpen: (newTab: Set<string>) => void;
58 hasChanged: boolean;
59 }): React.ReactElement {
60 + const id = useId();
61 const isShow = tabsOpen.has(name);
62
55 - const toggleTabs = useCallback(() => {
56 - const nextState = new Set(tabsOpen);
57 - if (nextState.has(name)) {
58 - nextState.delete(name);
59 - } else {
60 - nextState.add(name);
61 - }
62 - setTabsOpen(nextState);
63 - }, [tabsOpen, name, setTabsOpen]);
63 + const transitionName = `accordion-window-item-${id}`;
64 +
65 + const toggleTabs = () => {
66 + startTransition(() => {
67 + addTransitionType(EXPAND_ACCORDION_TRANSITION);
68 + const nextState = new Set(tabsOpen);
69 + if (nextState.has(name)) {
70 + nextState.delete(name);
71 + } else {
72 + nextState.add(name);
73 + }
74 + setTabsOpen(nextState);
75 + });
76 + };
77
78 // Replace spaces with non-breaking spaces
79 const displayName = name.replace(/ /g, '\u00A0');
@@ -68,31 +81,45 @@ function AccordionWindowItem({
81 return (
82 <div key={name} className="flex flex-row">
83 {isShow ? (
71 - <Resizable className="border-r" minWidth={550} enable={{right: true}}>
72 - <h2
73 - title="Minimize tab"
74 - aria-label="Minimize tab"
75 - onClick={toggleTabs}
76 - className={`p-4 duration-150 ease-in border-b cursor-pointer border-grey-200 ${
77 - hasChanged ? 'font-bold' : 'font-light'
78 - } text-secondary hover:text-link`}>
79 - - {displayName}
80 - </h2>
81 - {tabs.get(name) ?? <div>No output for {name}</div>}
82 - </Resizable>
84 + <ViewTransition
85 + name={transitionName}
86 + update={{
87 + [EXPAND_ACCORDION_TRANSITION]: 'expand-accordion',
88 + default: 'none',
89 + }}>
90 + <Resizable className="border-r" minWidth={550} enable={{right: true}}>
91 + <h2
92 + title="Minimize tab"
93 + aria-label="Minimize tab"
94 + onClick={toggleTabs}
95 + className={`p-4 duration-150 ease-in border-b cursor-pointer border-grey-200 ${
96 + hasChanged ? 'font-bold' : 'font-light'
97 + } text-secondary hover:text-link`}>
98 + - {displayName}
99 + </h2>
100 + {tabs.get(name) ?? <div>No output for {name}</div>}
101 + </Resizable>
102 + </ViewTransition>
103 ) : (
84 - <div className="relative items-center h-full px-1 py-6 align-middle border-r border-grey-200">
85 - <button
86 - title={`Expand compiler tab: ${name}`}
87 - aria-label={`Expand compiler tab: ${name}`}
88 - style={{transform: 'rotate(90deg) translate(-50%)'}}
89 - onClick={toggleTabs}
90 - className={`flex-grow-0 w-5 transition-colors duration-150 ease-in ${
91 - hasChanged ? 'font-bold' : 'font-light'
92 - } text-secondary hover:text-link`}>
93 - {displayName}
94 - </button>
95 - </div>
104 + <ViewTransition
105 + name={transitionName}
106 + update={{
107 + [EXPAND_ACCORDION_TRANSITION]: 'expand-accordion',
108 + default: 'none',
109 + }}>
110 + <div className="relative items-center h-full px-1 py-6 align-middle border-r border-grey-200">
111 + <button
112 + title={`Expand compiler tab: ${name}`}
113 + aria-label={`Expand compiler tab: ${name}`}
114 + style={{transform: 'rotate(90deg) translate(-50%)'}}
115 + onClick={toggleTabs}
116 + className={`flex-grow-0 w-5 transition-colors duration-150 ease-in ${
117 + hasChanged ? 'font-bold' : 'font-light'
118 + } text-secondary hover:text-link`}>
119 + {displayName}
120 + </button>
121 + </div>
122 + </ViewTransition>
123 )}
124 </div>
125 );
compiler/apps/playground/components/Editor/Output.tsx
+6 -1
@@ -32,7 +32,10 @@ import AccordionWindow from '../AccordionWindow';
32 import TabbedWindow from '../TabbedWindow';
33 import {monacoOptions} from './monacoOptions';
34 import {BabelFileResult} from '@babel/core';
35 -import {CONFIG_PANEL_TRANSITION} from '../../lib/transitionTypes';
35 +import {
36 + CONFIG_PANEL_TRANSITION,
37 + TOGGLE_INTERNALS_TRANSITION,
38 +} from '../../lib/transitionTypes';
39 import {LRUCache} from 'lru-cache';
40
41 const MemoizedOutput = memo(Output);
@@ -291,6 +294,7 @@ function OutputContent({store, compilerOutput}: Props): JSX.Element {
294 <ViewTransition
295 update={{
296 [CONFIG_PANEL_TRANSITION]: 'container',
297 + [TOGGLE_INTERNALS_TRANSITION]: '',
298 default: 'none',
299 }}>
300 <TabbedWindow
@@ -306,6 +310,7 @@ function OutputContent({store, compilerOutput}: Props): JSX.Element {
310 <ViewTransition
311 update={{
312 [CONFIG_PANEL_TRANSITION]: 'accordion-container',
313 + [TOGGLE_INTERNALS_TRANSITION]: '',
314 default: 'none',
315 }}>
316 <AccordionWindow
compiler/apps/playground/components/Header.tsx
+12 -2
@@ -10,11 +10,16 @@ import {CheckIcon} from '@heroicons/react/solid';
10 import clsx from 'clsx';
11 import Link from 'next/link';
12 import {useSnackbar} from 'notistack';
13 -import {useState} from 'react';
13 +import {
14 + useState,
15 + startTransition,
16 + unstable_addTransitionType as addTransitionType,
17 +} from 'react';
18 import {defaultStore} from '../lib/defaultStore';
19 import {IconGitHub} from './Icons/IconGitHub';
20 import Logo from './Logo';
21 import {useStore, useStoreDispatch} from './StoreContext';
22 +import {TOGGLE_INTERNALS_TRANSITION} from '../lib/transitionTypes';
23
24 export default function Header(): JSX.Element {
25 const [showCheck, setShowCheck] = useState(false);
@@ -62,7 +67,12 @@ export default function Header(): JSX.Element {
67 <input
68 type="checkbox"
69 checked={store.showInternals}
65 - onChange={() => dispatchStore({type: 'toggleInternals'})}
70 + onChange={() =>
71 + startTransition(() => {
72 + addTransitionType(TOGGLE_INTERNALS_TRANSITION);
73 + dispatchStore({type: 'toggleInternals'});
74 + })
75 + }
76 className="absolute opacity-0 cursor-pointer h-full w-full m-0"
77 />
78 <span
compiler/apps/playground/lib/transitionTypes.ts
+2
@@ -7,3 +7,5 @@
7
8 export const CONFIG_PANEL_TRANSITION = 'config-panel';
9 export const TOGGLE_TAB_TRANSITION = 'toggle-tab';
10 +export const TOGGLE_INTERNALS_TRANSITION = 'toggle-internals';
11 +export const EXPAND_ACCORDION_TRANSITION = 'open-accordion';
compiler/apps/playground/styles/globals.css
+8
@@ -116,3 +116,11 @@
116 ::view-transition-group(.tab-text) {
117 z-index: 1;
118 }
119 +
120 +::view-transition-old(.expand-accordion),
121 +::view-transition-new(.expand-accordion) {
122 + width: auto;
123 +}
124 +::view-transition-group(.expand-accordion) {
125 + overflow: clip;
126 +}