Files
react/packages/react-devtools-shared/src/devtools/views/Settings/SettingsModal.js
Mofei Zhang b658948159 [DevTools] Add Filtering of Environment Names (#30850)
Stacked on #30842.

This adds a filter to be able to exclude Components from a certain
environment. Default to Client or Server.

The available options are computed into a dropdown based on the names
that are currently used on the page (or an option that were previously
used). In addition to the hardcoded "Client". Meaning that if you have
Server Components on the page you see "Server" or "Client" as possible
options but it can be anything if there are multiple RSC environments on
the page.

"Client" in this case means Function and Class Components in Fiber -
excluding built-ins.

If a Server Component has two environments (primary and secondary) then
both have to be filtered to exclude it.

We don't show the option at all if there are no Server Components used
in the page to avoid confusing existing users that are just using Client
Components and wouldn't know the difference between Server vs Client.

<img width="815" alt="Screenshot 2024-08-30 at 12 56 42 AM"
src="https://github.com/user-attachments/assets/e06b225a-e85d-4cdc-8707-d4630fede19e">

[ghstack-poisoned]
2024-10-11 16:21:29 -04:00

145 lines
3.7 KiB
JavaScript

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import * as React from 'react';
import {useCallback, useContext, useEffect, useMemo, useRef} from 'react';
import {SettingsModalContext} from './SettingsModalContext';
import Button from '../Button';
import ButtonIcon from '../ButtonIcon';
import TabBar from '../TabBar';
import {StoreContext} from '../context';
import {
useLocalStorage,
useModalDismissSignal,
useSubscription,
} from '../hooks';
import ComponentsSettings from './ComponentsSettings';
import DebuggingSettings from './DebuggingSettings';
import GeneralSettings from './GeneralSettings';
import ProfilerSettings from './ProfilerSettings';
import styles from './SettingsModal.css';
type TabID = 'general' | 'components' | 'profiler';
export default function SettingsModal(_: {}): React.Node {
const {isModalShowing, setIsModalShowing} = useContext(SettingsModalContext);
const store = useContext(StoreContext);
const {profilerStore} = store;
// Updating preferences while profiling is in progress could break things (e.g. filtering)
// Explicitly disallow it for now.
const isProfilingSubscription = useMemo(
() => ({
getCurrentValue: () => profilerStore.isProfiling,
subscribe: (callback: Function) => {
profilerStore.addListener('isProfiling', callback);
return () => profilerStore.removeListener('isProfiling', callback);
},
}),
[profilerStore],
);
const isProfiling = useSubscription<boolean>(isProfilingSubscription);
if (isProfiling && isModalShowing) {
setIsModalShowing(false);
}
if (!isModalShowing) {
return null;
}
return <SettingsModalImpl />;
}
function SettingsModalImpl(_: {}) {
const {setIsModalShowing, environmentNames} =
useContext(SettingsModalContext);
const dismissModal = useCallback(
() => setIsModalShowing(false),
[setIsModalShowing],
);
const [selectedTabID, selectTab] = useLocalStorage<TabID>(
'React::DevTools::selectedSettingsTabID',
'general',
);
const modalRef = useRef<HTMLDivElement | null>(null);
useModalDismissSignal(modalRef, dismissModal);
useEffect(() => {
if (modalRef.current !== null) {
modalRef.current.focus();
}
}, [modalRef]);
let view = null;
switch (selectedTabID) {
case 'components':
view = <ComponentsSettings environmentNames={environmentNames} />;
break;
// $FlowFixMe[incompatible-type] is this missing in TabID?
case 'debugging':
view = <DebuggingSettings />;
break;
case 'general':
view = <GeneralSettings />;
break;
case 'profiler':
view = <ProfilerSettings />;
break;
default:
break;
}
return (
<div className={styles.Background}>
<div className={styles.Modal} ref={modalRef}>
<div className={styles.Tabs}>
<TabBar
currentTab={selectedTabID}
id="Settings"
selectTab={selectTab}
tabs={tabs}
type="settings"
/>
<div className={styles.Spacer} />
<Button onClick={dismissModal} title="Close settings dialog">
<ButtonIcon type="close" />
</Button>
</div>
<div className={styles.Content}>{view}</div>
</div>
</div>
);
}
const tabs = [
{
id: 'general',
icon: 'settings',
label: 'General',
},
{
id: 'debugging',
icon: 'bug',
label: 'Debugging',
},
{
id: 'components',
icon: 'components',
label: 'Components',
},
{
id: 'profiler',
icon: 'profiler',
label: 'Profiler',
},
];