@samitouri / QOS-React-2 / commits / 74fa1667a7

[DevTools] Move "Back to full tree view" into Activity list item (#35164)

Sebastian "Sebbie" Silbermann committed Nov 26, 2025 at 10:42 UTC 74fa1667a7355fd55591d82b0e1e992909b1dca0
1 file changed +47 -38
packages/react-devtools-shared/src/devtools/views/SuspenseTab/ActivityList.js
+47 -38
@@ -30,8 +30,6 @@ import {
30 } from '../Components/TreeContext';
31 import {useHighlightHostInstance} from '../hooks';
32 import {StoreContext} from '../context';
33 -import ButtonIcon from '../ButtonIcon';
34 -import Button from '../Button';
33
34 export function useChangeActivitySliceAction(): (
35 id: Element['id'] | null,
@@ -118,6 +116,8 @@ export default function ActivityList({
116 useTransition();
117 const changeActivitySliceAction = useChangeActivitySliceAction();
118
119 + const includeAllOption = activityID !== null;
120 +
121 function handleKeyDown(event: SyntheticKeyboardEvent) {
122 switch (event.key) {
123 case 'Escape':
@@ -128,15 +128,16 @@ export default function ActivityList({
128 break;
129 case 'Enter':
130 case ' ':
131 - if (inspectedElementID !== null) {
132 - startActivitySliceSelection(() => {
133 - changeActivitySliceAction(inspectedElementID);
134 - });
135 - }
131 + startActivitySliceSelection(() => {
132 + changeActivitySliceAction(inspectedElementID);
133 + });
134 event.preventDefault();
135 break;
136 case 'Home':
139 - treeDispatch({type: 'SELECT_ELEMENT_BY_ID', payload: activities[0].id});
137 + treeDispatch({
138 + type: 'SELECT_ELEMENT_BY_ID',
139 + payload: includeAllOption ? null : activities[0].id,
140 + });
141 event.preventDefault();
142 break;
143 case 'End':
@@ -150,15 +151,21 @@ export default function ActivityList({
151 const currentIndex = activities.findIndex(
152 activity => activity.id === selectedActivityID,
153 );
153 - if (currentIndex !== undefined) {
154 - const nextIndex =
155 - (currentIndex + activities.length - 1) % activities.length;
156 -
157 - treeDispatch({
158 - type: 'SELECT_ELEMENT_BY_ID',
159 - payload: activities[nextIndex].id,
160 - });
154 + let nextIndex: number;
155 + if (currentIndex === -1) {
156 + // Currently selecting "All", wrap around to last Activity.
157 + nextIndex = activities.length - 1;
158 + } else {
159 + nextIndex = currentIndex - 1;
160 + if (!includeAllOption) {
161 + nextIndex = (nextIndex + activities.length) % activities.length;
162 + }
163 }
164 +
165 + treeDispatch({
166 + type: 'SELECT_ELEMENT_BY_ID',
167 + payload: nextIndex === -1 ? null : activities[nextIndex].id,
168 + });
169 event.preventDefault();
170 break;
171 }
@@ -166,14 +173,17 @@ export default function ActivityList({
173 const currentIndex = activities.findIndex(
174 activity => activity.id === selectedActivityID,
175 );
169 - if (currentIndex !== undefined) {
170 - const nextIndex = (currentIndex + 1) % activities.length;
171 -
172 - treeDispatch({
173 - type: 'SELECT_ELEMENT_BY_ID',
174 - payload: activities[nextIndex].id,
175 - });
176 + let nextIndex: number;
177 + if (includeAllOption && currentIndex === activities.length - 1) {
178 + // Currently selecting last Activity, wrap around to "All".
179 + nextIndex = -1;
180 + } else {
181 + nextIndex = (currentIndex + 1) % activities.length;
182 }
183 + treeDispatch({
184 + type: 'SELECT_ELEMENT_BY_ID',
185 + payload: nextIndex === -1 ? null : activities[nextIndex].id,
186 + });
187 event.preventDefault();
188 break;
189 }
@@ -182,7 +192,7 @@ export default function ActivityList({
192 }
193 }
194
185 - function handleClick(id: Element['id'], event: SyntheticMouseEvent) {
195 + function handleClick(id: Element['id'] | null, event: SyntheticMouseEvent) {
196 event.preventDefault();
197 treeDispatch({type: 'SELECT_ELEMENT_BY_ID', payload: id});
198 }
@@ -195,25 +205,24 @@ export default function ActivityList({
205
206 return (
207 <div className={styles.ActivityListContaier}>
198 - <div className={styles.ActivityListHeader}>
199 - {activityID !== null && (
200 - // TODO: Obsolete once filtered Activities are included in this list.
201 - <Button
202 - onClick={startActivitySliceSelection.bind(
203 - null,
204 - changeActivitySliceAction.bind(null, null),
205 - )}
206 - title="Back to full tree view">
207 - <ButtonIcon type="previous" />
208 - </Button>
209 - )}
210 - </div>
208 + <div className={styles.ActivityListHeader} />
209 <ol
210 role="listbox"
211 className={styles.ActivityListList}
212 data-pending-activity-slice-selection={isPendingActivitySliceSelection}
213 tabIndex={0}
214 onKeyDown={handleKeyDown}>
215 + {includeAllOption && (
216 + // TODO: Obsolete once filtered Activities are included in this list.
217 + <li
218 + role="option"
219 + aria-selected={null === selectedActivityID ? 'true' : 'false'}
220 + className={styles.ActivityListItem}
221 + onClick={handleClick.bind(null, null)}
222 + onDoubleClick={handleDoubleClick}>
223 + All
224 + </li>
225 + )}
226 {activities.map(({id, depth}) => {
227 const activity = store.getElementByID(id);
228 if (activity === null) {
@@ -244,7 +253,7 @@ export default function ActivityList({
253 false,
254 )}
255 onPointerLeave={clearHighlightHostInstance}>
247 - {'\u00A0'.repeat(depth) + name}
256 + {'\u00A0'.repeat(depth + (includeAllOption ? 1 : 0)) + name}
257 </li>
258 );
259 })}