Add feature toggle to the settings tab, make Search Panel responsive

This commit is contained in:
Zsofia Vagi 2023-06-17 22:29:36 +02:00 committed by Zsofia Vagi
parent c19c29af64
commit 606daf7fa0
9 changed files with 139 additions and 110 deletions

View File

@ -1,31 +1,33 @@
import { LiftedAction, LiftedState } from '@redux-devtools/core';
import { SchemeName, ThemeName } from '@redux-devtools/ui'; import { SchemeName, ThemeName } from '@redux-devtools/ui';
import { AuthStates, States } from 'socketcluster-client/lib/clientsocket'; import { Action } from 'redux';
import { REHYDRATE } from 'redux-persist'; import { REHYDRATE } from 'redux-persist';
import { AuthStates, States } from 'socketcluster-client/lib/clientsocket';
import { import {
CHANGE_SECTION, CHANGE_SECTION,
CHANGE_STATE_TREE_SETTINGS,
CHANGE_THEME, CHANGE_THEME,
SELECT_INSTANCE, CLEAR_NOTIFICATION,
SELECT_MONITOR, ERROR,
UPDATE_MONITOR_STATE, EXPORT,
GET_REPORT_ERROR,
GET_REPORT_REQUEST,
GET_REPORT_SUCCESS,
LIFTED_ACTION, LIFTED_ACTION,
MONITOR_ACTION, MONITOR_ACTION,
EXPORT, REMOVE_INSTANCE,
TOGGLE_SYNC, SELECT_INSTANCE,
TOGGLE_SLIDER, SELECT_MONITOR,
SET_PERSIST,
SET_STATE,
SHOW_NOTIFICATION,
TOGGLE_DISPATCHER, TOGGLE_DISPATCHER,
TOGGLE_PERSIST, TOGGLE_PERSIST,
GET_REPORT_REQUEST, TOGGLE_SLIDER,
SHOW_NOTIFICATION, TOGGLE_SYNC,
CLEAR_NOTIFICATION, UPDATE_MONITOR_STATE,
UPDATE_STATE,
UPDATE_REPORTS, UPDATE_REPORTS,
REMOVE_INSTANCE, UPDATE_STATE,
SET_STATE,
GET_REPORT_ERROR,
GET_REPORT_SUCCESS,
ERROR,
SET_PERSIST,
CHANGE_STATE_TREE_SETTINGS,
} from '../constants/actionTypes'; } from '../constants/actionTypes';
import { import {
AUTH_ERROR, AUTH_ERROR,
@ -43,12 +45,9 @@ import {
SUBSCRIBE_SUCCESS, SUBSCRIBE_SUCCESS,
UNSUBSCRIBE, UNSUBSCRIBE,
} from '../constants/socketActionTypes'; } from '../constants/socketActionTypes';
import { Action } from 'redux';
import { Features, State } from '../reducers/instances'; import { Features, State } from '../reducers/instances';
import { MonitorStateMonitorState } from '../reducers/monitor'; import { MonitorStateMonitorState } from '../reducers/monitor';
import { LiftedAction } from '@redux-devtools/core';
import { Data } from '../reducers/reports'; import { Data } from '../reducers/reports';
import { LiftedState } from '@redux-devtools/core';
let monitorReducer: ( let monitorReducer: (
monitorProps: unknown, monitorProps: unknown,
@ -86,6 +85,7 @@ export function changeTheme(data: ChangeThemeData): ChangeThemeAction {
interface ChangeStateTreeSettingsFormData { interface ChangeStateTreeSettingsFormData {
readonly sortAlphabetically: boolean; readonly sortAlphabetically: boolean;
readonly disableCollection: boolean; readonly disableCollection: boolean;
readonly enableSearchPanel: boolean;
} }
interface ChangeStateTreeSettingsData { interface ChangeStateTreeSettingsData {
@ -96,6 +96,7 @@ export interface ChangeStateTreeSettingsAction {
readonly type: typeof CHANGE_STATE_TREE_SETTINGS; readonly type: typeof CHANGE_STATE_TREE_SETTINGS;
readonly sortAlphabetically: boolean; readonly sortAlphabetically: boolean;
readonly disableCollection: boolean; readonly disableCollection: boolean;
readonly enableSearchPanel: boolean;
} }
export function changeStateTreeSettings( export function changeStateTreeSettings(

View File

@ -1,6 +1,6 @@
import React, { Component } from 'react';
import { connect, ResolveThunks } from 'react-redux';
import { Container, Form } from '@redux-devtools/ui'; import { Container, Form } from '@redux-devtools/ui';
import React, { Component } from 'react';
import { ResolveThunks, connect } from 'react-redux';
import { changeStateTreeSettings } from '../../actions'; import { changeStateTreeSettings } from '../../actions';
import { StoreState } from '../../reducers'; import { StoreState } from '../../reducers';
@ -14,6 +14,7 @@ export class StateTree extends Component<Props> {
const formData = { const formData = {
sortAlphabetically: stateTree.sortAlphabetically, sortAlphabetically: stateTree.sortAlphabetically,
disableCollection: stateTree.disableCollection, disableCollection: stateTree.disableCollection,
enableSearchPanel: stateTree.enableSearchPanel,
}; };
return ( return (
@ -30,6 +31,10 @@ export class StateTree extends Component<Props> {
title: 'Disable collapsing of nodes', title: 'Disable collapsing of nodes',
type: 'boolean', type: 'boolean',
}, },
enableSearchPanel: {
title: 'Show search panel in State tab',
type: 'boolean',
},
}, },
}} }}
formData={formData} formData={formData}

View File

@ -1,13 +1,13 @@
import React, { Component } from 'react';
import { withTheme } from 'styled-components';
import { LiftedAction, LiftedState } from '@redux-devtools/core'; import { LiftedAction, LiftedState } from '@redux-devtools/core';
import { ThemeFromProvider } from '@redux-devtools/ui';
import React, { Component } from 'react';
import { Action } from 'redux'; import { Action } from 'redux';
import getMonitor from '../utils/getMonitor'; import { withTheme } from 'styled-components';
import { InitMonitorAction } from '../actions'; import { InitMonitorAction } from '../actions';
import { Features, State } from '../reducers/instances'; import { Features, State } from '../reducers/instances';
import { MonitorStateMonitorState } from '../reducers/monitor'; import { MonitorStateMonitorState } from '../reducers/monitor';
import { ThemeFromProvider } from '@redux-devtools/ui';
import { StateTreeSettings } from '../reducers/stateTreeSettings'; import { StateTreeSettings } from '../reducers/stateTreeSettings';
import getMonitor from '../utils/getMonitor';
interface Props { interface Props {
monitor: string; monitor: string;
@ -118,6 +118,7 @@ class DevTools extends Component<Props> {
disableStateTreeCollection={ disableStateTreeCollection={
this.props.stateTreeSettings.disableCollection this.props.stateTreeSettings.disableCollection
} }
enableSearchPanel={this.props.stateTreeSettings.enableSearchPanel}
/> />
</div> </div>
); );

View File

@ -1,15 +1,17 @@
import { CHANGE_STATE_TREE_SETTINGS } from '../constants/actionTypes';
import { StoreAction } from '../actions'; import { StoreAction } from '../actions';
import { CHANGE_STATE_TREE_SETTINGS } from '../constants/actionTypes';
export interface StateTreeSettings { export interface StateTreeSettings {
readonly sortAlphabetically: boolean; readonly sortAlphabetically: boolean;
readonly disableCollection: boolean; readonly disableCollection: boolean;
readonly enableSearchPanel: boolean;
} }
export function stateTreeSettings( export function stateTreeSettings(
state: StateTreeSettings = { state: StateTreeSettings = {
sortAlphabetically: false, sortAlphabetically: false,
disableCollection: false, disableCollection: false,
enableSearchPanel: false,
}, },
action: StoreAction, action: StoreAction,
) { ) {
@ -17,6 +19,7 @@ export function stateTreeSettings(
return { return {
sortAlphabetically: action.sortAlphabetically, sortAlphabetically: action.sortAlphabetically,
disableCollection: action.disableCollection, disableCollection: action.disableCollection,
enableSearchPanel: action.enableSearchPanel,
}; };
} }
return state; return state;

View File

@ -1,15 +1,15 @@
import React, { Component } from 'react';
import { Base16Theme } from 'redux-devtools-themes';
import { Action } from 'redux';
import type { StylingFunction } from 'react-base16-styling';
import type { LabelRenderer } from 'react-json-tree';
import { PerformAction } from '@redux-devtools/core'; import { PerformAction } from '@redux-devtools/core';
import { Delta } from 'jsondiffpatch'; import { Delta } from 'jsondiffpatch';
import { DEFAULT_STATE, DevtoolsInspectorState } from './redux'; import React, { Component } from 'react';
import type { StylingFunction } from 'react-base16-styling';
import type { LabelRenderer } from 'react-json-tree';
import { Action } from 'redux';
import { Base16Theme } from 'redux-devtools-themes';
import ActionPreviewHeader from './ActionPreviewHeader'; import ActionPreviewHeader from './ActionPreviewHeader';
import { DEFAULT_STATE, DevtoolsInspectorState } from './redux';
import ActionTab from './tabs/ActionTab';
import DiffTab from './tabs/DiffTab'; import DiffTab from './tabs/DiffTab';
import StateTab from './tabs/StateTab'; import StateTab from './tabs/StateTab';
import ActionTab from './tabs/ActionTab';
export interface TabComponentProps<S, A extends Action<unknown>> { export interface TabComponentProps<S, A extends Action<unknown>> {
labelRenderer: LabelRenderer; labelRenderer: LabelRenderer;
@ -23,6 +23,7 @@ export interface TabComponentProps<S, A extends Action<unknown>> {
isWideLayout: boolean; isWideLayout: boolean;
sortStateTreeAlphabetically: boolean; sortStateTreeAlphabetically: boolean;
disableStateTreeCollection: boolean; disableStateTreeCollection: boolean;
enableSearchPanel: boolean;
dataTypeKey: string | symbol | undefined; dataTypeKey: string | symbol | undefined;
delta: Delta | null | undefined | false; delta: Delta | null | undefined | false;
action: A; action: A;
@ -74,6 +75,7 @@ interface Props<S, A extends Action<unknown>> {
onSelectTab: (tabName: string) => void; onSelectTab: (tabName: string) => void;
sortStateTreeAlphabetically: boolean; sortStateTreeAlphabetically: boolean;
disableStateTreeCollection: boolean; disableStateTreeCollection: boolean;
enableSearchPanel: boolean;
} }
class ActionPreview<S, A extends Action<unknown>> extends Component< class ActionPreview<S, A extends Action<unknown>> extends Component<
@ -107,6 +109,7 @@ class ActionPreview<S, A extends Action<unknown>> extends Component<
updateMonitorState, updateMonitorState,
sortStateTreeAlphabetically, sortStateTreeAlphabetically,
disableStateTreeCollection, disableStateTreeCollection,
enableSearchPanel,
} = this.props; } = this.props;
const renderedTabs: Tab<S, A>[] = const renderedTabs: Tab<S, A>[] =
@ -141,6 +144,7 @@ class ActionPreview<S, A extends Action<unknown>> extends Component<
isWideLayout, isWideLayout,
sortStateTreeAlphabetically, sortStateTreeAlphabetically,
disableStateTreeCollection, disableStateTreeCollection,
enableSearchPanel,
dataTypeKey, dataTypeKey,
delta, delta,
action, action,

View File

@ -1,25 +1,20 @@
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { Base16Theme } from 'redux-devtools-themes';
import {
getBase16Theme,
invertTheme,
StylingFunction,
} from 'react-base16-styling';
import { import {
ActionCreators, ActionCreators,
LiftedAction, LiftedAction,
LiftedState, LiftedState,
} from '@redux-devtools/core'; } from '@redux-devtools/core';
import { Action, Dispatch } from 'redux';
import { Delta, DiffContext } from 'jsondiffpatch'; import { Delta, DiffContext } from 'jsondiffpatch';
import PropTypes from 'prop-types';
import React, { PureComponent } from 'react';
import { import {
createStylingFromTheme, StylingFunction,
base16Themes, getBase16Theme,
} from './utils/createStylingFromTheme'; invertTheme,
} from 'react-base16-styling';
import { Action, Dispatch } from 'redux';
import { Base16Theme } from 'redux-devtools-themes';
import ActionList from './ActionList'; import ActionList from './ActionList';
import ActionPreview, { Tab } from './ActionPreview'; import ActionPreview, { Tab } from './ActionPreview';
import getInspectedState from './utils/getInspectedState';
import createDiffPatcher from './createDiffPatcher'; import createDiffPatcher from './createDiffPatcher';
import { import {
DevtoolsInspectorAction, DevtoolsInspectorAction,
@ -27,6 +22,11 @@ import {
reducer, reducer,
updateMonitorState, updateMonitorState,
} from './redux'; } from './redux';
import {
base16Themes,
createStylingFromTheme,
} from './utils/createStylingFromTheme';
import getInspectedState from './utils/getInspectedState';
const { const {
// eslint-disable-next-line @typescript-eslint/unbound-method // eslint-disable-next-line @typescript-eslint/unbound-method
@ -153,6 +153,7 @@ export interface ExternalProps<S, A extends Action<unknown>> {
invertTheme: boolean; invertTheme: boolean;
sortStateTreeAlphabetically: boolean; sortStateTreeAlphabetically: boolean;
disableStateTreeCollection: boolean; disableStateTreeCollection: boolean;
enableSearchPanel: boolean;
dataTypeKey?: string | symbol; dataTypeKey?: string | symbol;
tabs: Tab<S, A>[] | ((tabs: Tab<S, A>[]) => Tab<S, A>[]); tabs: Tab<S, A>[] | ((tabs: Tab<S, A>[]) => Tab<S, A>[]);
} }
@ -181,6 +182,7 @@ export interface DevtoolsInspectorProps<S, A extends Action<unknown>>
hideActionButtons?: boolean; hideActionButtons?: boolean;
sortStateTreeAlphabetically: boolean; sortStateTreeAlphabetically: boolean;
disableStateTreeCollection: boolean; disableStateTreeCollection: boolean;
enableSearchPanel: boolean;
invertTheme: boolean; invertTheme: boolean;
dataTypeKey?: string | symbol; dataTypeKey?: string | symbol;
tabs: Tab<S, A>[] | ((tabs: Tab<S, A>[]) => Tab<S, A>[]); tabs: Tab<S, A>[] | ((tabs: Tab<S, A>[]) => Tab<S, A>[]);
@ -226,6 +228,7 @@ class DevtoolsInspector<S, A extends Action<unknown>> extends PureComponent<
invertTheme: PropTypes.bool, invertTheme: PropTypes.bool,
sortStateTreeAlphabetically: PropTypes.bool, sortStateTreeAlphabetically: PropTypes.bool,
disableStateTreeCollection: PropTypes.bool, disableStateTreeCollection: PropTypes.bool,
enableSearchPanel: PropTypes.bool,
skippedActionIds: PropTypes.array, skippedActionIds: PropTypes.array,
dataTypeKey: PropTypes.any, dataTypeKey: PropTypes.any,
tabs: PropTypes.oneOfType([PropTypes.array, PropTypes.func]), tabs: PropTypes.oneOfType([PropTypes.array, PropTypes.func]),
@ -313,6 +316,7 @@ class DevtoolsInspector<S, A extends Action<unknown>> extends PureComponent<
hideActionButtons, hideActionButtons,
sortStateTreeAlphabetically, sortStateTreeAlphabetically,
disableStateTreeCollection, disableStateTreeCollection,
enableSearchPanel,
} = this.props; } = this.props;
const { selectedActionId, startActionId, searchValue, tabName } = const { selectedActionId, startActionId, searchValue, tabName } =
monitorState; monitorState;
@ -374,6 +378,7 @@ class DevtoolsInspector<S, A extends Action<unknown>> extends PureComponent<
dataTypeKey, dataTypeKey,
sortStateTreeAlphabetically, sortStateTreeAlphabetically,
disableStateTreeCollection, disableStateTreeCollection,
enableSearchPanel,
}} }}
monitorState={this.props.monitorState} monitorState={this.props.monitorState}
updateMonitorState={this.updateMonitorState} updateMonitorState={this.updateMonitorState}

View File

@ -114,41 +114,43 @@ function SearchPanel({
{searchStatus === 'pending' && 'Searching...'} {searchStatus === 'pending' && 'Searching...'}
{searchStatus === 'done' && ( {searchStatus === 'done' && (
<> <>
<JumpSearchResultButton <div {...styling('jumpResultContainer')}>
buttonDirection={BUTTON_DIRECTION.LEFT} <JumpSearchResultButton
buttonDisabled={!results || results.length < 2} buttonDirection={BUTTON_DIRECTION.LEFT}
styling={styling} buttonDisabled={!results || results.length < 2}
jumpToNewResult={() => { styling={styling}
if (!results) { jumpToNewResult={() => {
return; if (!results) {
} return;
const newIndex = }
resultIndex - 1 < 0 ? results.length - 1 : resultIndex - 1; const newIndex =
setResultIndex(newIndex); resultIndex - 1 < 0 ? results.length - 1 : resultIndex - 1;
onSubmit({ setResultIndex(newIndex);
searchResult: results[newIndex] || [], onSubmit({
searchInProgress: true, searchResult: results[newIndex] || [],
}); searchInProgress: true,
}} });
/> }}
<JumpSearchResultButton />
buttonDirection={BUTTON_DIRECTION.RIGHT} <JumpSearchResultButton
buttonDisabled={!results || results.length < 2} buttonDirection={BUTTON_DIRECTION.RIGHT}
styling={styling} buttonDisabled={!results || results.length < 2}
jumpToNewResult={() => { styling={styling}
if (!results) { jumpToNewResult={() => {
return; if (!results) {
} return;
const newIndex = (resultIndex + 1) % results.length || 0; }
setResultIndex(newIndex); const newIndex = (resultIndex + 1) % results.length || 0;
onSubmit({ setResultIndex(newIndex);
searchResult: results[newIndex] || [], onSubmit({
searchInProgress: true, searchResult: results[newIndex] || [],
}); searchInProgress: true,
}} });
/> }}
{results && />
`${results.length ? resultIndex + 1 : 0}/${results.length}`} {results &&
`${results.length ? resultIndex + 1 : 0}/${results.length}`}
</div>
<button {...styling('searchButton')} onClick={() => reset()}> <button {...styling('searchButton')} onClick={() => reset()}>
Reset Reset
</button> </button>

View File

@ -1,11 +1,16 @@
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { useEffect, useState } from 'react';
import { JSONTree } from 'react-json-tree'; import { JSONTree } from 'react-json-tree';
import { Action } from 'redux'; import { Action } from 'redux';
import getItemString from './getItemString';
import getJsonTreeTheme from './getJsonTreeTheme';
import { TabComponentProps } from '../ActionPreview'; import { TabComponentProps } from '../ActionPreview';
import SearchPanel from '../searchPanel/SearchPanel'; import SearchPanel from '../searchPanel/SearchPanel';
import getItemString from './getItemString';
import getJsonTreeTheme from './getJsonTreeTheme';
interface SearchState {
searchResult: string[];
searchInProgress: boolean;
}
const StateTab: React.FunctionComponent< const StateTab: React.FunctionComponent<
TabComponentProps<any, Action<unknown>> TabComponentProps<any, Action<unknown>>
@ -19,11 +24,8 @@ const StateTab: React.FunctionComponent<
isWideLayout, isWideLayout,
sortStateTreeAlphabetically, sortStateTreeAlphabetically,
disableStateTreeCollection, disableStateTreeCollection,
enableSearchPanel,
}) => { }) => {
interface SearchState {
searchResult: string[];
searchInProgress: boolean;
}
const [searchState, setSearchState] = useState<SearchState>({ const [searchState, setSearchState] = useState<SearchState>({
searchResult: [], searchResult: [],
searchInProgress: false, searchInProgress: false,
@ -32,7 +34,11 @@ const StateTab: React.FunctionComponent<
const displayedResult = React.useRef<HTMLDivElement>(null); const displayedResult = React.useRef<HTMLDivElement>(null);
useEffect(() => { useEffect(() => {
if (searchState.searchInProgress && displayedResult.current) { if (
enableSearchPanel &&
searchState.searchInProgress &&
displayedResult.current
) {
displayedResult.current.scrollIntoView({ displayedResult.current.scrollIntoView({
behavior: 'smooth', behavior: 'smooth',
block: 'center', block: 'center',
@ -40,21 +46,23 @@ const StateTab: React.FunctionComponent<
}); });
setSearchState({ ...searchState, searchInProgress: false }); setSearchState({ ...searchState, searchInProgress: false });
} }
}, [searchState, setSearchState]); }, [searchState, setSearchState, enableSearchPanel]);
return ( return (
<> <>
<SearchPanel {enableSearchPanel && (
onSubmit={setSearchState} <SearchPanel
onReset={() => onSubmit={setSearchState}
setSearchState({ onReset={() =>
searchResult: [], setSearchState({
searchInProgress: false, searchResult: [],
}) searchInProgress: false,
} })
styling={styling} }
state={nextState} styling={styling}
/> state={nextState}
/>
)}
<JSONTree <JSONTree
labelRenderer={(keyPath, nodeType, expanded, expandable) => { labelRenderer={(keyPath, nodeType, expanded, expandable) => {
return isMatch(searchState.searchResult, [...keyPath].reverse()) ? ( return isMatch(searchState.searchResult, [...keyPath].reverse()) ? (

View File

@ -271,11 +271,11 @@ const getSheetFromColorMap = (map: ColorMap) => ({
searchPanel: { searchPanel: {
display: 'flex', display: 'flex',
'flex-wrap': 'wrap',
position: 'sticky', position: 'sticky',
top: 0, top: 0,
width: '100%', width: '100%',
'z-index': 1, 'z-index': 1,
height: '2em',
gap: '1em', gap: '1em',
padding: '5px 10px', padding: '5px 10px',
'align-items': 'center', 'align-items': 'center',
@ -286,12 +286,6 @@ const getSheetFromColorMap = (map: ColorMap) => ({
'border-bottom-color': map.HEADER_BORDER_COLOR, 'border-bottom-color': map.HEADER_BORDER_COLOR,
}, },
searchForm: {
display: 'flex',
gap: '1em',
'align-items': 'center',
},
searchPanelParameterSelection: { searchPanelParameterSelection: {
display: 'inline-grid', display: 'inline-grid',
color: map.SEARCH_BUTTON_COLOR, color: map.SEARCH_BUTTON_COLOR,
@ -336,6 +330,12 @@ const getSheetFromColorMap = (map: ColorMap) => ({
color: map.TEXT_COLOR, color: map.TEXT_COLOR,
}, },
jumpResultContainer: {
display: 'flex',
gap: '1em',
'align-items': 'center',
},
jumpResultButton: { jumpResultButton: {
'background-color': map.SEARCH_BUTTON_COLOR, 'background-color': map.SEARCH_BUTTON_COLOR,
'border-color': 'transparent', 'border-color': 'transparent',