chore: simplify settings management in UI mode (#32440)

This commit is contained in:
Dmitry Gozman 2024-09-04 01:05:07 -07:00 committed by GitHub
parent b3d767fa14
commit d7393f998e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 31 additions and 52 deletions

View file

@ -15,14 +15,19 @@
*/ */
import * as React from 'react'; import * as React from 'react';
import type { Setting } from '@web/uiUtils';
import './settingsView.css'; import './settingsView.css';
export type Setting<T> = {
value: T,
set: (value: T) => void,
title: string
};
export const SettingsView: React.FunctionComponent<{ export const SettingsView: React.FunctionComponent<{
settings: Setting<boolean>[], settings: Setting<boolean>[],
}> = ({ settings }) => { }> = ({ settings }) => {
return <div className='vbox settings-view'> return <div className='vbox settings-view'>
{settings.map(([value, set, title]) => { {settings.map(({ value, set, title }) => {
return <div key={title} className='setting'> return <div key={title} className='setting'>
<label> <label>
<input type='checkbox' checked={value} onClick={() => set(!value)}/> <input type='checkbox' checked={value} onClick={() => set(!value)}/>

View file

@ -100,36 +100,13 @@ export const UIModeView: React.FC<{}> = ({
const [testingOptionsVisible, setTestingOptionsVisible] = React.useState(false); const [testingOptionsVisible, setTestingOptionsVisible] = React.useState(false);
const [revealSource, setRevealSource] = React.useState(false); const [revealSource, setRevealSource] = React.useState(false);
const onRevealSource = React.useCallback(() => setRevealSource(true), [setRevealSource]); const onRevealSource = React.useCallback(() => setRevealSource(true), [setRevealSource]);
const showTestingOptions = false; const showTestingOptions = false;
const [singleWorker, setSingleWorker] = React.useState(queryParams.workers === '1');
const [runWorkers, setRunWorkers] = React.useState(queryParams.workers); const [showBrowser, setShowBrowser] = React.useState(queryParams.headed);
const singleWorkerSetting = React.useMemo(() => { const [updateSnapshots, setUpdateSnapshots] = React.useState(queryParams.updateSnapshots === 'all');
return [ const [showRouteActions, setShowRouteActions] = useSetting('show-route-actions', true);
runWorkers === '1', const [darkMode, setDarkMode] = useDarkModeSetting();
(value: boolean) => {
// When started with `--workers=1`, the setting allows to undo that.
// Otherwise, fallback to the cli `--workers=X` argument.
setRunWorkers(value ? '1' : (queryParams.workers === '1' ? undefined : queryParams.workers));
},
'Single worker',
] as const;
}, [runWorkers, setRunWorkers]);
const [runHeaded, setRunHeaded] = React.useState(queryParams.headed);
const showBrowserSetting = React.useMemo(() => [runHeaded, setRunHeaded, 'Show browser'] as const, [runHeaded, setRunHeaded]);
const [runUpdateSnapshots, setRunUpdateSnapshots] = React.useState(queryParams.updateSnapshots);
const updateSnapshotsSetting = React.useMemo(() => {
return [
runUpdateSnapshots === 'all',
(value: boolean) => setRunUpdateSnapshots(value ? 'all' : 'missing'),
'Update snapshots',
] as const;
}, [runUpdateSnapshots, setRunUpdateSnapshots]);
const [, , showRouteActionsSetting] = useSetting('show-route-actions', true, 'Show route actions');
const darkModeSetting = useDarkModeSetting();
const inputRef = React.useRef<HTMLInputElement>(null); const inputRef = React.useRef<HTMLInputElement>(null);
@ -309,11 +286,13 @@ export const UIModeView: React.FC<{}> = ({
grepInvert: queryParams.grepInvert, grepInvert: queryParams.grepInvert,
testIds: [...testIds], testIds: [...testIds],
projects: [...projectFilters].filter(([_, v]) => v).map(([p]) => p), projects: [...projectFilters].filter(([_, v]) => v).map(([p]) => p),
workers: runWorkers, // When started with `--workers=1`, the setting allows to undo that.
// Otherwise, fallback to the cli `--workers=X` argument.
workers: singleWorker ? '1' : (queryParams.workers === '1' ? undefined : queryParams.workers),
timeout: queryParams.timeout, timeout: queryParams.timeout,
headed: runHeaded, headed: showBrowser,
outputDir: queryParams.outputDir, outputDir: queryParams.outputDir,
updateSnapshots: runUpdateSnapshots, updateSnapshots: updateSnapshots ? 'all' : queryParams.updateSnapshots,
reporters: queryParams.reporters, reporters: queryParams.reporters,
trace: 'on', trace: 'on',
}); });
@ -325,7 +304,7 @@ export const UIModeView: React.FC<{}> = ({
setTestModel({ ...testModel }); setTestModel({ ...testModel });
setRunningState(oldState => oldState ? ({ ...oldState, completed: true }) : undefined); setRunningState(oldState => oldState ? ({ ...oldState, completed: true }) : undefined);
}); });
}, [projectFilters, isRunningTest, testModel, testServerConnection, runWorkers, runHeaded, runUpdateSnapshots]); }, [projectFilters, isRunningTest, testModel, testServerConnection, singleWorker, showBrowser, updateSnapshots]);
React.useEffect(() => { React.useEffect(() => {
if (!testServerConnection || !teleSuiteUpdater) if (!testServerConnection || !teleSuiteUpdater)
@ -522,9 +501,9 @@ export const UIModeView: React.FC<{}> = ({
<div className='section-title'>Testing Options</div> <div className='section-title'>Testing Options</div>
</Toolbar> </Toolbar>
{testingOptionsVisible && <SettingsView settings={[ {testingOptionsVisible && <SettingsView settings={[
singleWorkerSetting, { value: singleWorker, set: setSingleWorker, title: 'Single worker' },
showBrowserSetting, { value: showBrowser, set: setShowBrowser, title: 'Show browser' },
updateSnapshotsSetting, { value: updateSnapshots, set: setUpdateSnapshots, title: 'Update snapshots' },
]} />} ]} />}
</>} </>}
<Toolbar noShadow={true} noMinHeight={true} className='settings-toolbar' onClick={() => setSettingsVisible(!settingsVisible)}> <Toolbar noShadow={true} noMinHeight={true} className='settings-toolbar' onClick={() => setSettingsVisible(!settingsVisible)}>
@ -536,8 +515,8 @@ export const UIModeView: React.FC<{}> = ({
<div className='section-title'>Settings</div> <div className='section-title'>Settings</div>
</Toolbar> </Toolbar>
{settingsVisible && <SettingsView settings={[ {settingsVisible && <SettingsView settings={[
darkModeSetting, { value: darkMode, set: setDarkMode, title: 'Dark mode' },
showRouteActionsSetting, { value: showRouteActions, set: setShowRouteActions, title: 'Show route actions' },
]} />} ]} />}
</div> </div>
} }

View file

@ -72,7 +72,7 @@ export const Workbench: React.FunctionComponent<{
const activeAction = model ? highlightedAction || selectedAction : undefined; const activeAction = model ? highlightedAction || selectedAction : undefined;
const [selectedTime, setSelectedTime] = React.useState<Boundaries | undefined>(); const [selectedTime, setSelectedTime] = React.useState<Boundaries | undefined>();
const [sidebarLocation, setSidebarLocation] = useSetting<'bottom' | 'right'>('propertiesSidebarLocation', 'bottom'); const [sidebarLocation, setSidebarLocation] = useSetting<'bottom' | 'right'>('propertiesSidebarLocation', 'bottom');
const [showRouteActions, , showRouteActionsSetting] = useSetting('show-route-actions', true, 'Show route actions'); const [showRouteActions, setShowRouteActions] = useSetting('show-route-actions', true);
const filteredActions = React.useMemo(() => { const filteredActions = React.useMemo(() => {
return (model?.actions || []).filter(action => showRouteActions || !isRouteAction(action)); return (model?.actions || []).filter(action => showRouteActions || !isRouteAction(action));
@ -299,7 +299,7 @@ export const Workbench: React.FunctionComponent<{
const settingsTab: TabbedPaneTabModel = { const settingsTab: TabbedPaneTabModel = {
id: 'settings', id: 'settings',
title: 'Settings', title: 'Settings',
component: <SettingsView settings={[showRouteActionsSetting]}/>, component: <SettingsView settings={[{ value: showRouteActions, set: setShowRouteActions, title: 'Show route actions' }]}/>,
}; };
return <div className='vbox workbench' {...(inert ? { inert: 'true' } : {})}> return <div className='vbox workbench' {...(inert ? { inert: 'true' } : {})}>

View file

@ -15,7 +15,7 @@
*/ */
import React from 'react'; import React from 'react';
import { type Setting, settings } from './uiUtils'; import { settings } from './uiUtils';
declare global { declare global {
interface Document { interface Document {
@ -68,12 +68,12 @@ export function currentTheme(): Theme {
return document.body.classList.contains('dark-mode') ? 'dark-mode' : 'light-mode'; return document.body.classList.contains('dark-mode') ? 'dark-mode' : 'light-mode';
} }
export function useDarkModeSetting() { export function useDarkModeSetting(): [boolean, (value: boolean) => void] {
const [theme, setTheme] = React.useState(currentTheme() === 'dark-mode'); const [theme, setTheme] = React.useState(currentTheme() === 'dark-mode');
return [theme, (value: boolean) => { return [theme, (value: boolean) => {
const current = currentTheme() === 'dark-mode'; const current = currentTheme() === 'dark-mode';
if (current !== value) if (current !== value)
toggleTheme(); toggleTheme();
setTheme(value); setTheme(value);
}, 'Dark mode'] as Setting<boolean>; }];
} }

View file

@ -139,10 +139,7 @@ export function copy(text: string) {
textArea.remove(); textArea.remove();
} }
export type Setting<T> = readonly [T, (value: T) => void, string]; export function useSetting<S>(name: string | undefined, defaultValue: S, title?: string): [S, React.Dispatch<React.SetStateAction<S>>] {
export function useSetting<S>(name: string | undefined, defaultValue: S, title?: string): [S, React.Dispatch<React.SetStateAction<S>>, Setting<S>] {
if (name) if (name)
defaultValue = settings.getObject(name, defaultValue); defaultValue = settings.getObject(name, defaultValue);
const [value, setValue] = React.useState<S>(defaultValue); const [value, setValue] = React.useState<S>(defaultValue);
@ -160,9 +157,7 @@ export function useSetting<S>(name: string | undefined, defaultValue: S, title?:
return () => settings.onChangeEmitter.removeEventListener(name, onStoreChange); return () => settings.onChangeEmitter.removeEventListener(name, onStoreChange);
} }
}, [defaultValue, name]); }, [defaultValue, name]);
return [value, setValueWrapper];
const setting = [value, setValueWrapper, title || name || ''] as Setting<S>;
return [value, setValueWrapper, setting];
} }
declare global { declare global {