@tanstack/devtools 0.6.24 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk/{XF4JFOLU.js → G64KXXVZ.js} +34 -11
- package/dist/dev.js +3 -3
- package/dist/devtools/{UUNAZSBD.js → MYTOQ6G4.js} +79 -51
- package/dist/devtools/{OBIHU6L6.js → RHZRAMXS.js} +72 -49
- package/dist/index.d.ts +15 -5
- package/dist/index.js +3 -3
- package/dist/server.js +2 -2
- package/package.json +2 -2
- package/src/components/trigger.tsx +22 -10
- package/src/context/devtools-context.test.ts +268 -1
- package/src/context/devtools-context.tsx +29 -9
- package/src/context/devtools-store.ts +12 -6
- package/src/context/use-devtools-context.ts +2 -1
- package/src/devtools.tsx +27 -8
- package/src/tabs/settings-tab.tsx +1 -7
- package/src/utils/constants.ts +4 -0
- package/src/utils/get-default-active-plugins.test.ts +194 -0
- package/src/utils/get-default-active-plugins.ts +36 -0
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it } from 'vitest'
|
|
2
2
|
import { TANSTACK_DEVTOOLS_STATE } from '../utils/storage'
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
getExistingStateFromStorage,
|
|
5
|
+
getStateFromLocalStorage,
|
|
6
|
+
} from './devtools-context'
|
|
7
|
+
import type { TanStackDevtoolsPlugin } from './devtools-context'
|
|
4
8
|
|
|
5
9
|
describe('getStateFromLocalStorage', () => {
|
|
6
10
|
beforeEach(() => {
|
|
@@ -56,4 +60,267 @@ describe('getStateFromLocalStorage', () => {
|
|
|
56
60
|
const state = getStateFromLocalStorage(undefined)
|
|
57
61
|
expect(state).toEqual(undefined)
|
|
58
62
|
})
|
|
63
|
+
|
|
64
|
+
it('should return undefined when no localStorage state exists (allowing defaultOpen to be applied)', () => {
|
|
65
|
+
// No existing state in localStorage - this allows defaultOpen logic to trigger
|
|
66
|
+
const plugins: Array<TanStackDevtoolsPlugin> = [
|
|
67
|
+
{
|
|
68
|
+
id: 'plugin1',
|
|
69
|
+
render: () => {},
|
|
70
|
+
name: 'Plugin 1',
|
|
71
|
+
defaultOpen: true,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
id: 'plugin2',
|
|
75
|
+
render: () => {},
|
|
76
|
+
name: 'Plugin 2',
|
|
77
|
+
defaultOpen: false,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: 'plugin3',
|
|
81
|
+
render: () => {},
|
|
82
|
+
name: 'Plugin 3',
|
|
83
|
+
defaultOpen: true,
|
|
84
|
+
},
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
// When undefined is returned, getExistingStateFromStorage will fill activePlugins with defaultOpen plugins
|
|
88
|
+
const state = getStateFromLocalStorage(plugins)
|
|
89
|
+
expect(state).toEqual(undefined)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it('should preserve existing activePlugins from localStorage (defaultOpen should not override)', () => {
|
|
93
|
+
const mockState = {
|
|
94
|
+
activePlugins: ['plugin2'],
|
|
95
|
+
settings: {
|
|
96
|
+
theme: 'dark',
|
|
97
|
+
},
|
|
98
|
+
}
|
|
99
|
+
localStorage.setItem(TANSTACK_DEVTOOLS_STATE, JSON.stringify(mockState))
|
|
100
|
+
|
|
101
|
+
const plugins: Array<TanStackDevtoolsPlugin> = [
|
|
102
|
+
{
|
|
103
|
+
id: 'plugin1',
|
|
104
|
+
render: () => {},
|
|
105
|
+
name: 'Plugin 1',
|
|
106
|
+
defaultOpen: true,
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: 'plugin2',
|
|
110
|
+
render: () => {},
|
|
111
|
+
name: 'Plugin 2',
|
|
112
|
+
defaultOpen: false,
|
|
113
|
+
},
|
|
114
|
+
]
|
|
115
|
+
|
|
116
|
+
const state = getStateFromLocalStorage(plugins)
|
|
117
|
+
// Should keep existing activePlugins - defaultOpen logic won't override in getExistingStateFromStorage
|
|
118
|
+
expect(state?.activePlugins).toEqual(['plugin2'])
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('should automatically activate a single plugin when no active plugins exist', () => {
|
|
122
|
+
// No existing state in localStorage
|
|
123
|
+
const plugins: Array<TanStackDevtoolsPlugin> = [
|
|
124
|
+
{
|
|
125
|
+
id: 'only-plugin',
|
|
126
|
+
render: () => {},
|
|
127
|
+
name: 'Only Plugin',
|
|
128
|
+
},
|
|
129
|
+
]
|
|
130
|
+
|
|
131
|
+
const state = getStateFromLocalStorage(plugins)
|
|
132
|
+
// Should return undefined - the single plugin activation happens in getExistingStateFromStorage
|
|
133
|
+
expect(state).toEqual(undefined)
|
|
134
|
+
})
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
describe('getExistingStateFromStorage - integration tests', () => {
|
|
138
|
+
beforeEach(() => {
|
|
139
|
+
localStorage.clear()
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('should automatically activate a single plugin when no localStorage state exists', () => {
|
|
143
|
+
const plugins: Array<TanStackDevtoolsPlugin> = [
|
|
144
|
+
{
|
|
145
|
+
id: 'only-plugin',
|
|
146
|
+
render: () => {},
|
|
147
|
+
name: 'Only Plugin',
|
|
148
|
+
},
|
|
149
|
+
]
|
|
150
|
+
|
|
151
|
+
const state = getExistingStateFromStorage(undefined, plugins)
|
|
152
|
+
expect(state.state.activePlugins).toEqual(['only-plugin'])
|
|
153
|
+
expect(state.plugins).toHaveLength(1)
|
|
154
|
+
expect(state.plugins![0]?.id).toBe('only-plugin')
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
it('should activate plugins with defaultOpen: true when no localStorage state exists', () => {
|
|
158
|
+
const plugins: Array<TanStackDevtoolsPlugin> = [
|
|
159
|
+
{
|
|
160
|
+
id: 'plugin1',
|
|
161
|
+
render: () => {},
|
|
162
|
+
name: 'Plugin 1',
|
|
163
|
+
defaultOpen: true,
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
id: 'plugin2',
|
|
167
|
+
render: () => {},
|
|
168
|
+
name: 'Plugin 2',
|
|
169
|
+
defaultOpen: false,
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
id: 'plugin3',
|
|
173
|
+
render: () => {},
|
|
174
|
+
name: 'Plugin 3',
|
|
175
|
+
defaultOpen: true,
|
|
176
|
+
},
|
|
177
|
+
]
|
|
178
|
+
|
|
179
|
+
const state = getExistingStateFromStorage(undefined, plugins)
|
|
180
|
+
expect(state.state.activePlugins).toEqual(['plugin1', 'plugin3'])
|
|
181
|
+
expect(state.plugins).toHaveLength(3)
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
it('should limit defaultOpen plugins to MAX_ACTIVE_PLUGINS (3) when 5 have defaultOpen: true', () => {
|
|
185
|
+
const plugins: Array<TanStackDevtoolsPlugin> = [
|
|
186
|
+
{
|
|
187
|
+
id: 'plugin1',
|
|
188
|
+
render: () => {},
|
|
189
|
+
name: 'Plugin 1',
|
|
190
|
+
defaultOpen: true,
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
id: 'plugin2',
|
|
194
|
+
render: () => {},
|
|
195
|
+
name: 'Plugin 2',
|
|
196
|
+
defaultOpen: true,
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
id: 'plugin3',
|
|
200
|
+
render: () => {},
|
|
201
|
+
name: 'Plugin 3',
|
|
202
|
+
defaultOpen: true,
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
id: 'plugin4',
|
|
206
|
+
render: () => {},
|
|
207
|
+
name: 'Plugin 4',
|
|
208
|
+
defaultOpen: true,
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
id: 'plugin5',
|
|
212
|
+
render: () => {},
|
|
213
|
+
name: 'Plugin 5',
|
|
214
|
+
defaultOpen: true,
|
|
215
|
+
},
|
|
216
|
+
]
|
|
217
|
+
|
|
218
|
+
const state = getExistingStateFromStorage(undefined, plugins)
|
|
219
|
+
// Should only activate first 3 plugins
|
|
220
|
+
expect(state.state.activePlugins).toEqual(['plugin1', 'plugin2', 'plugin3'])
|
|
221
|
+
expect(state.state.activePlugins).toHaveLength(3)
|
|
222
|
+
expect(state.state.activePlugins).not.toContain('plugin4')
|
|
223
|
+
expect(state.state.activePlugins).not.toContain('plugin5')
|
|
224
|
+
// All 5 plugins should still be in the plugins array
|
|
225
|
+
expect(state.plugins).toHaveLength(5)
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
it('should preserve existing activePlugins from localStorage even when plugins have defaultOpen', () => {
|
|
229
|
+
const mockState = {
|
|
230
|
+
activePlugins: ['plugin2', 'plugin4'],
|
|
231
|
+
settings: {
|
|
232
|
+
theme: 'dark',
|
|
233
|
+
},
|
|
234
|
+
}
|
|
235
|
+
localStorage.setItem(TANSTACK_DEVTOOLS_STATE, JSON.stringify(mockState))
|
|
236
|
+
|
|
237
|
+
const plugins: Array<TanStackDevtoolsPlugin> = [
|
|
238
|
+
{
|
|
239
|
+
id: 'plugin1',
|
|
240
|
+
render: () => {},
|
|
241
|
+
name: 'Plugin 1',
|
|
242
|
+
defaultOpen: true,
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
id: 'plugin2',
|
|
246
|
+
render: () => {},
|
|
247
|
+
name: 'Plugin 2',
|
|
248
|
+
defaultOpen: false,
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
id: 'plugin3',
|
|
252
|
+
render: () => {},
|
|
253
|
+
name: 'Plugin 3',
|
|
254
|
+
defaultOpen: true,
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
id: 'plugin4',
|
|
258
|
+
render: () => {},
|
|
259
|
+
name: 'Plugin 4',
|
|
260
|
+
defaultOpen: false,
|
|
261
|
+
},
|
|
262
|
+
]
|
|
263
|
+
|
|
264
|
+
const state = getExistingStateFromStorage(undefined, plugins)
|
|
265
|
+
// Should preserve the localStorage state, not use defaultOpen
|
|
266
|
+
expect(state.state.activePlugins).toEqual(['plugin2', 'plugin4'])
|
|
267
|
+
expect(state.plugins).toHaveLength(4)
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
it('should return empty activePlugins when no defaultOpen and multiple plugins', () => {
|
|
271
|
+
const plugins: Array<TanStackDevtoolsPlugin> = [
|
|
272
|
+
{
|
|
273
|
+
id: 'plugin1',
|
|
274
|
+
render: () => {},
|
|
275
|
+
name: 'Plugin 1',
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
id: 'plugin2',
|
|
279
|
+
render: () => {},
|
|
280
|
+
name: 'Plugin 2',
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
id: 'plugin3',
|
|
284
|
+
render: () => {},
|
|
285
|
+
name: 'Plugin 3',
|
|
286
|
+
},
|
|
287
|
+
]
|
|
288
|
+
|
|
289
|
+
const state = getExistingStateFromStorage(undefined, plugins)
|
|
290
|
+
expect(state.state.activePlugins).toEqual([])
|
|
291
|
+
expect(state.plugins).toHaveLength(3)
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
it('should handle single plugin with defaultOpen: false by activating it anyway', () => {
|
|
295
|
+
const plugins: Array<TanStackDevtoolsPlugin> = [
|
|
296
|
+
{
|
|
297
|
+
id: 'only-plugin',
|
|
298
|
+
render: () => {},
|
|
299
|
+
name: 'Only Plugin',
|
|
300
|
+
defaultOpen: false,
|
|
301
|
+
},
|
|
302
|
+
]
|
|
303
|
+
|
|
304
|
+
const state = getExistingStateFromStorage(undefined, plugins)
|
|
305
|
+
// Single plugin should be activated regardless of defaultOpen flag
|
|
306
|
+
expect(state.state.activePlugins).toEqual(['only-plugin'])
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
it('should merge config settings into the returned state', () => {
|
|
310
|
+
const plugins: Array<TanStackDevtoolsPlugin> = [
|
|
311
|
+
{
|
|
312
|
+
id: 'plugin1',
|
|
313
|
+
render: () => {},
|
|
314
|
+
name: 'Plugin 1',
|
|
315
|
+
},
|
|
316
|
+
]
|
|
317
|
+
|
|
318
|
+
const config = {
|
|
319
|
+
theme: 'light' as const,
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const state = getExistingStateFromStorage(config as any, plugins)
|
|
323
|
+
expect(state.settings.theme).toBe('light')
|
|
324
|
+
expect(state.state.activePlugins).toEqual(['plugin1'])
|
|
325
|
+
})
|
|
59
326
|
})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createContext, createEffect } from 'solid-js'
|
|
2
2
|
import { createStore } from 'solid-js/store'
|
|
3
|
+
import { getDefaultActivePlugins } from '../utils/get-default-active-plugins'
|
|
3
4
|
import { tryParseJson } from '../utils/sanitize'
|
|
4
5
|
import {
|
|
5
6
|
TANSTACK_DEVTOOLS_SETTINGS,
|
|
@@ -49,6 +50,12 @@ export interface TanStackDevtoolsPlugin {
|
|
|
49
50
|
* If not provided, it will be generated based on the name.
|
|
50
51
|
*/
|
|
51
52
|
id?: string
|
|
53
|
+
/**
|
|
54
|
+
* Whether the plugin should be open by default when there are no active plugins.
|
|
55
|
+
* If true, this plugin will be added to activePlugins on initial load when activePlugins is empty.
|
|
56
|
+
* @default false
|
|
57
|
+
*/
|
|
58
|
+
defaultOpen?: boolean
|
|
52
59
|
/**
|
|
53
60
|
* Render the plugin UI by using the provided element. This function will be called
|
|
54
61
|
* when the plugin tab is clicked and expected to be mounted.
|
|
@@ -127,26 +134,39 @@ export function getStateFromLocalStorage(
|
|
|
127
134
|
return existingState
|
|
128
135
|
}
|
|
129
136
|
|
|
130
|
-
const getExistingStateFromStorage = (
|
|
137
|
+
export const getExistingStateFromStorage = (
|
|
131
138
|
config?: TanStackDevtoolsConfig,
|
|
132
139
|
plugins?: Array<TanStackDevtoolsPlugin>,
|
|
133
140
|
) => {
|
|
134
141
|
const existingState = getStateFromLocalStorage(plugins)
|
|
135
142
|
const settings = getSettings()
|
|
136
143
|
|
|
144
|
+
const pluginsWithIds =
|
|
145
|
+
plugins?.map((plugin, i) => {
|
|
146
|
+
const id = generatePluginId(plugin, i)
|
|
147
|
+
return {
|
|
148
|
+
...plugin,
|
|
149
|
+
id,
|
|
150
|
+
}
|
|
151
|
+
}) || []
|
|
152
|
+
|
|
153
|
+
// If no active plugins exist, add plugins with defaultOpen: true
|
|
154
|
+
// Or if there's only 1 plugin, activate it automatically
|
|
155
|
+
let activePlugins = existingState?.activePlugins || []
|
|
156
|
+
|
|
157
|
+
const shouldFillWithDefaultOpenPlugins =
|
|
158
|
+
activePlugins.length === 0 && pluginsWithIds.length > 0
|
|
159
|
+
if (shouldFillWithDefaultOpenPlugins) {
|
|
160
|
+
activePlugins = getDefaultActivePlugins(pluginsWithIds)
|
|
161
|
+
}
|
|
162
|
+
|
|
137
163
|
const state: DevtoolsStore = {
|
|
138
164
|
...initialState,
|
|
139
|
-
plugins:
|
|
140
|
-
plugins?.map((plugin, i) => {
|
|
141
|
-
const id = generatePluginId(plugin, i)
|
|
142
|
-
return {
|
|
143
|
-
...plugin,
|
|
144
|
-
id,
|
|
145
|
-
}
|
|
146
|
-
}) || [],
|
|
165
|
+
plugins: pluginsWithIds,
|
|
147
166
|
state: {
|
|
148
167
|
...initialState.state,
|
|
149
168
|
...existingState,
|
|
169
|
+
activePlugins,
|
|
150
170
|
},
|
|
151
171
|
settings: {
|
|
152
172
|
...initialState.settings,
|
|
@@ -18,6 +18,10 @@ type TriggerPosition =
|
|
|
18
18
|
| 'middle-left'
|
|
19
19
|
| 'middle-right'
|
|
20
20
|
|
|
21
|
+
type TriggerProps = {
|
|
22
|
+
theme: 'light' | 'dark'
|
|
23
|
+
}
|
|
24
|
+
|
|
21
25
|
export type DevtoolsStore = {
|
|
22
26
|
settings: {
|
|
23
27
|
/**
|
|
@@ -61,15 +65,17 @@ export type DevtoolsStore = {
|
|
|
61
65
|
* @default "dark"
|
|
62
66
|
*/
|
|
63
67
|
theme: 'light' | 'dark'
|
|
64
|
-
|
|
65
|
-
* The image used for the dev tools trigger
|
|
66
|
-
* @default TanStackLogo
|
|
67
|
-
*/
|
|
68
|
-
triggerImage: string
|
|
68
|
+
|
|
69
69
|
/**
|
|
70
70
|
* Whether the trigger should be completely hidden or not (you can still open with the hotkey)
|
|
71
71
|
*/
|
|
72
72
|
triggerHidden?: boolean
|
|
73
|
+
/**
|
|
74
|
+
* An optional custom function to render the dev tools trigger component.
|
|
75
|
+
* If provided, it replaces the default trigger button.
|
|
76
|
+
* @default undefined
|
|
77
|
+
*/
|
|
78
|
+
customTrigger?: (el: HTMLElement, props: TriggerProps) => void
|
|
73
79
|
}
|
|
74
80
|
state: {
|
|
75
81
|
activeTab: TabName
|
|
@@ -95,8 +101,8 @@ export const initialState: DevtoolsStore = {
|
|
|
95
101
|
window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
96
102
|
? 'dark'
|
|
97
103
|
: 'light',
|
|
98
|
-
triggerImage: '',
|
|
99
104
|
triggerHidden: false,
|
|
105
|
+
customTrigger: undefined,
|
|
100
106
|
},
|
|
101
107
|
state: {
|
|
102
108
|
activeTab: 'plugins',
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createEffect, createMemo, useContext } from 'solid-js'
|
|
2
|
+
import { MAX_ACTIVE_PLUGINS } from '../utils/constants.js'
|
|
2
3
|
import { DevtoolsContext } from './devtools-context.jsx'
|
|
3
4
|
import { useDrawContext } from './draw-context.jsx'
|
|
4
5
|
|
|
@@ -50,7 +51,7 @@ export const usePlugins = () => {
|
|
|
50
51
|
const updatedPlugins = isActive
|
|
51
52
|
? prev.state.activePlugins.filter((id) => id !== pluginId)
|
|
52
53
|
: [...prev.state.activePlugins, pluginId]
|
|
53
|
-
if (updatedPlugins.length >
|
|
54
|
+
if (updatedPlugins.length > MAX_ACTIVE_PLUGINS) return prev
|
|
54
55
|
return {
|
|
55
56
|
...prev,
|
|
56
57
|
state: {
|
package/src/devtools.tsx
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { Show, createEffect, createSignal } from 'solid-js'
|
|
1
|
+
import { Show, createEffect, createSignal, onCleanup } from 'solid-js'
|
|
2
2
|
import { createShortcut } from '@solid-primitives/keyboard'
|
|
3
3
|
import { Portal } from 'solid-js/web'
|
|
4
4
|
import { ThemeContextProvider } from '@tanstack/devtools-ui'
|
|
5
|
+
import { devtoolsEventClient } from '@tanstack/devtools-client'
|
|
5
6
|
import {
|
|
6
7
|
useDevtoolsSettings,
|
|
7
8
|
useHeight,
|
|
@@ -31,14 +32,36 @@ export default function DevTools() {
|
|
|
31
32
|
const pip = usePiPWindow()
|
|
32
33
|
let panelRef: HTMLDivElement | undefined = undefined
|
|
33
34
|
const [isResizing, setIsResizing] = createSignal(false)
|
|
35
|
+
|
|
34
36
|
const toggleOpen = () => {
|
|
35
37
|
if (pip().pipWindow) {
|
|
36
38
|
return
|
|
37
39
|
}
|
|
38
40
|
const open = isOpen()
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
const newState = !open
|
|
42
|
+
setIsOpen(newState)
|
|
43
|
+
setPersistOpen(newState)
|
|
44
|
+
|
|
45
|
+
// Emit event when user toggles devtools
|
|
46
|
+
devtoolsEventClient.emit('trigger-toggled', { isOpen: newState })
|
|
41
47
|
}
|
|
48
|
+
|
|
49
|
+
// Listen for trigger-toggled events to control devtools
|
|
50
|
+
createEffect(() => {
|
|
51
|
+
const unsubscribe = devtoolsEventClient.on('trigger-toggled', (event) => {
|
|
52
|
+
if (pip().pipWindow) {
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
const payload = event.payload as unknown as { isOpen: boolean }
|
|
56
|
+
const shouldBeOpen = payload.isOpen
|
|
57
|
+
if (shouldBeOpen !== isOpen()) {
|
|
58
|
+
setIsOpen(shouldBeOpen)
|
|
59
|
+
setPersistOpen(shouldBeOpen)
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
onCleanup(unsubscribe)
|
|
64
|
+
})
|
|
42
65
|
// Used to resize the panel
|
|
43
66
|
const handleDragStart = (
|
|
44
67
|
panelElement: HTMLDivElement | undefined,
|
|
@@ -175,11 +198,7 @@ export default function DevTools() {
|
|
|
175
198
|
: true
|
|
176
199
|
}
|
|
177
200
|
>
|
|
178
|
-
<Trigger
|
|
179
|
-
isOpen={isOpen}
|
|
180
|
-
setIsOpen={toggleOpen}
|
|
181
|
-
image={settings().triggerImage}
|
|
182
|
-
/>
|
|
201
|
+
<Trigger isOpen={isOpen} setIsOpen={toggleOpen} />
|
|
183
202
|
<MainPanel isResizing={isResizing} isOpen={isOpen}>
|
|
184
203
|
<ContentPanel
|
|
185
204
|
ref={(ref) => (panelRef = ref)}
|
|
@@ -79,13 +79,7 @@ export const SettingsTab = () => {
|
|
|
79
79
|
}
|
|
80
80
|
checked={settings().triggerHidden}
|
|
81
81
|
/>
|
|
82
|
-
|
|
83
|
-
label="Trigger Image"
|
|
84
|
-
description="Specify the URL of the image to use for the trigger"
|
|
85
|
-
value={settings().triggerImage}
|
|
86
|
-
placeholder="Default TanStack Logo"
|
|
87
|
-
onChange={(value) => setSettings({ triggerImage: value })}
|
|
88
|
-
/>
|
|
82
|
+
|
|
89
83
|
<Select
|
|
90
84
|
label="Theme"
|
|
91
85
|
description="Choose the theme for the devtools panel"
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { getDefaultActivePlugins } from './get-default-active-plugins'
|
|
3
|
+
import type { PluginWithId } from './get-default-active-plugins'
|
|
4
|
+
|
|
5
|
+
describe('getDefaultActivePlugins', () => {
|
|
6
|
+
it('should return empty array when no plugins provided', () => {
|
|
7
|
+
const result = getDefaultActivePlugins([])
|
|
8
|
+
expect(result).toEqual([])
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
it('should automatically activate a single plugin', () => {
|
|
12
|
+
const plugins: Array<PluginWithId> = [
|
|
13
|
+
{
|
|
14
|
+
id: 'only-plugin',
|
|
15
|
+
},
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
const result = getDefaultActivePlugins(plugins)
|
|
19
|
+
expect(result).toEqual(['only-plugin'])
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('should automatically activate a single plugin even if defaultOpen is false', () => {
|
|
23
|
+
const plugins: Array<PluginWithId> = [
|
|
24
|
+
{
|
|
25
|
+
id: 'only-plugin',
|
|
26
|
+
defaultOpen: false,
|
|
27
|
+
},
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
const result = getDefaultActivePlugins(plugins)
|
|
31
|
+
expect(result).toEqual(['only-plugin'])
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('should return empty array when multiple plugins without defaultOpen', () => {
|
|
35
|
+
const plugins: Array<PluginWithId> = [
|
|
36
|
+
{
|
|
37
|
+
id: 'plugin1',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: 'plugin2',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'plugin3',
|
|
44
|
+
},
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
const result = getDefaultActivePlugins(plugins)
|
|
48
|
+
expect(result).toEqual([])
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('should activate plugins with defaultOpen: true', () => {
|
|
52
|
+
const plugins: Array<PluginWithId> = [
|
|
53
|
+
{
|
|
54
|
+
id: 'plugin1',
|
|
55
|
+
defaultOpen: true,
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: 'plugin2',
|
|
59
|
+
defaultOpen: false,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: 'plugin3',
|
|
63
|
+
defaultOpen: true,
|
|
64
|
+
},
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
const result = getDefaultActivePlugins(plugins)
|
|
68
|
+
expect(result).toEqual(['plugin1', 'plugin3'])
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('should limit defaultOpen plugins to MAX_ACTIVE_PLUGINS (3)', () => {
|
|
72
|
+
const plugins: Array<PluginWithId> = [
|
|
73
|
+
{
|
|
74
|
+
id: 'plugin1',
|
|
75
|
+
defaultOpen: true,
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: 'plugin2',
|
|
79
|
+
defaultOpen: true,
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
id: 'plugin3',
|
|
83
|
+
defaultOpen: true,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
id: 'plugin4',
|
|
87
|
+
defaultOpen: true,
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: 'plugin5',
|
|
91
|
+
defaultOpen: true,
|
|
92
|
+
},
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
const result = getDefaultActivePlugins(plugins)
|
|
96
|
+
// Should only return first 3
|
|
97
|
+
expect(result).toEqual(['plugin1', 'plugin2', 'plugin3'])
|
|
98
|
+
expect(result.length).toBe(3)
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it('should activate exactly MAX_ACTIVE_PLUGINS when that many have defaultOpen', () => {
|
|
102
|
+
const plugins: Array<PluginWithId> = [
|
|
103
|
+
{
|
|
104
|
+
id: 'plugin1',
|
|
105
|
+
defaultOpen: true,
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
id: 'plugin2',
|
|
109
|
+
defaultOpen: true,
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
id: 'plugin3',
|
|
113
|
+
defaultOpen: true,
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
id: 'plugin4',
|
|
117
|
+
defaultOpen: false,
|
|
118
|
+
},
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
const result = getDefaultActivePlugins(plugins)
|
|
122
|
+
expect(result).toEqual(['plugin1', 'plugin2', 'plugin3'])
|
|
123
|
+
expect(result.length).toBe(3)
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it('should handle mix of defaultOpen true/false/undefined', () => {
|
|
127
|
+
const plugins: Array<PluginWithId> = [
|
|
128
|
+
{
|
|
129
|
+
id: 'plugin1',
|
|
130
|
+
defaultOpen: true,
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
id: 'plugin2',
|
|
134
|
+
// undefined defaultOpen
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
id: 'plugin3',
|
|
138
|
+
defaultOpen: false,
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
id: 'plugin4',
|
|
142
|
+
defaultOpen: true,
|
|
143
|
+
},
|
|
144
|
+
]
|
|
145
|
+
|
|
146
|
+
const result = getDefaultActivePlugins(plugins)
|
|
147
|
+
// Only plugin1 and plugin4 have defaultOpen: true
|
|
148
|
+
expect(result).toEqual(['plugin1', 'plugin4'])
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it('should return single plugin even if it has defaultOpen: true', () => {
|
|
152
|
+
const plugins: Array<PluginWithId> = [
|
|
153
|
+
{
|
|
154
|
+
id: 'only-plugin',
|
|
155
|
+
defaultOpen: true,
|
|
156
|
+
},
|
|
157
|
+
]
|
|
158
|
+
|
|
159
|
+
const result = getDefaultActivePlugins(plugins)
|
|
160
|
+
expect(result).toEqual(['only-plugin'])
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
it('should stop at MAX_ACTIVE_PLUGINS limit when 5 plugins have defaultOpen: true', () => {
|
|
164
|
+
const plugins: Array<PluginWithId> = [
|
|
165
|
+
{
|
|
166
|
+
id: 'plugin1',
|
|
167
|
+
defaultOpen: true,
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
id: 'plugin2',
|
|
171
|
+
defaultOpen: true,
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
id: 'plugin3',
|
|
175
|
+
defaultOpen: true,
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
id: 'plugin4',
|
|
179
|
+
defaultOpen: true,
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
id: 'plugin5',
|
|
183
|
+
defaultOpen: true,
|
|
184
|
+
},
|
|
185
|
+
]
|
|
186
|
+
|
|
187
|
+
const result = getDefaultActivePlugins(plugins)
|
|
188
|
+
// Should only activate the first 3, plugin4 and plugin5 should be ignored
|
|
189
|
+
expect(result).toEqual(['plugin1', 'plugin2', 'plugin3'])
|
|
190
|
+
expect(result.length).toBe(3)
|
|
191
|
+
expect(result).not.toContain('plugin4')
|
|
192
|
+
expect(result).not.toContain('plugin5')
|
|
193
|
+
})
|
|
194
|
+
})
|