@tanstack/react-query-devtools 4.15.0 → 4.16.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/build/lib/Explorer.d.ts +6 -1
- package/build/lib/Explorer.esm.js +102 -3
- package/build/lib/Explorer.esm.js.map +1 -1
- package/build/lib/Explorer.js +105 -2
- package/build/lib/Explorer.js.map +1 -1
- package/build/lib/Explorer.mjs +102 -3
- package/build/lib/Explorer.mjs.map +1 -1
- package/build/lib/devtools.esm.js +2 -1
- package/build/lib/devtools.esm.js.map +1 -1
- package/build/lib/devtools.js +2 -1
- package/build/lib/devtools.js.map +1 -1
- package/build/lib/devtools.mjs +2 -1
- package/build/lib/devtools.mjs.map +1 -1
- package/build/lib/index.prod.esm.js +102 -3
- package/build/lib/index.prod.esm.js.map +1 -1
- package/build/lib/index.prod.js +102 -3
- package/build/lib/index.prod.js.map +1 -1
- package/build/lib/index.prod.mjs +102 -3
- package/build/lib/index.prod.mjs.map +1 -1
- package/build/umd/index.development.js +104 -5
- package/build/umd/index.development.js.map +1 -1
- package/package.json +4 -4
- package/src/Explorer.tsx +127 -0
- package/src/__tests__/Explorer.test.tsx +66 -2
- package/src/devtools.tsx +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/react-query-devtools",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.16.0",
|
|
4
4
|
"description": "Developer tools to interact with and visualize the TanStack/react-query cache",
|
|
5
5
|
"author": "tannerlinsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -46,17 +46,17 @@
|
|
|
46
46
|
"react-dom": "^18.2.0",
|
|
47
47
|
"react-dom-17": "npm:react-dom@^17.0.2",
|
|
48
48
|
"react-error-boundary": "^3.1.4",
|
|
49
|
-
"@tanstack/react-query": "4.15.
|
|
49
|
+
"@tanstack/react-query": "4.15.1"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@tanstack/match-sorter-utils": "
|
|
52
|
+
"@tanstack/match-sorter-utils": "8.1.1",
|
|
53
53
|
"superjson": "^1.10.0",
|
|
54
54
|
"use-sync-external-store": "^1.2.0"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
|
58
58
|
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
|
59
|
-
"@tanstack/react-query": "4.15.
|
|
59
|
+
"@tanstack/react-query": "4.15.1"
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|
|
62
62
|
"clean": "rm -rf ./build",
|
package/src/Explorer.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
2
|
|
|
3
3
|
import { displayValue, styled } from './utils'
|
|
4
|
+
import superjson from 'superjson'
|
|
4
5
|
|
|
5
6
|
export const Entry = styled('div', {
|
|
6
7
|
fontFamily: 'Menlo, monospace',
|
|
@@ -29,6 +30,55 @@ export const ExpandButton = styled('button', {
|
|
|
29
30
|
padding: 0,
|
|
30
31
|
})
|
|
31
32
|
|
|
33
|
+
type CopyState = 'NoCopy' | 'SuccessCopy' | 'ErrorCopy'
|
|
34
|
+
|
|
35
|
+
export const CopyButton = ({ value }: { value: unknown }) => {
|
|
36
|
+
const [copyState, setCopyState] = React.useState<CopyState>('NoCopy')
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<button
|
|
40
|
+
onClick={
|
|
41
|
+
copyState === 'NoCopy'
|
|
42
|
+
? () => {
|
|
43
|
+
navigator.clipboard.writeText(superjson.stringify(value)).then(
|
|
44
|
+
() => {
|
|
45
|
+
setCopyState('SuccessCopy')
|
|
46
|
+
setTimeout(() => {
|
|
47
|
+
setCopyState('NoCopy')
|
|
48
|
+
}, 1500)
|
|
49
|
+
},
|
|
50
|
+
(err) => {
|
|
51
|
+
console.error('Failed to copy: ', err)
|
|
52
|
+
setCopyState('ErrorCopy')
|
|
53
|
+
setTimeout(() => {
|
|
54
|
+
setCopyState('NoCopy')
|
|
55
|
+
}, 1500)
|
|
56
|
+
},
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
: undefined
|
|
60
|
+
}
|
|
61
|
+
style={{
|
|
62
|
+
cursor: 'pointer',
|
|
63
|
+
color: 'inherit',
|
|
64
|
+
font: 'inherit',
|
|
65
|
+
outline: 'inherit',
|
|
66
|
+
background: 'transparent',
|
|
67
|
+
border: 'none',
|
|
68
|
+
padding: 0,
|
|
69
|
+
}}
|
|
70
|
+
>
|
|
71
|
+
{copyState === 'NoCopy' ? (
|
|
72
|
+
<Copier />
|
|
73
|
+
) : copyState === 'SuccessCopy' ? (
|
|
74
|
+
<CopiedCopier />
|
|
75
|
+
) : (
|
|
76
|
+
<ErrorCopier />
|
|
77
|
+
)}
|
|
78
|
+
</button>
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
|
|
32
82
|
export const Value = styled('span', (_props, theme) => ({
|
|
33
83
|
color: theme.danger,
|
|
34
84
|
}))
|
|
@@ -62,6 +112,76 @@ export const Expander = ({ expanded, style = {} }: ExpanderProps) => (
|
|
|
62
112
|
</span>
|
|
63
113
|
)
|
|
64
114
|
|
|
115
|
+
const Copier = () => (
|
|
116
|
+
<span
|
|
117
|
+
aria-label="Copy object to clipboard"
|
|
118
|
+
title="Copy object to clipboard"
|
|
119
|
+
style={{
|
|
120
|
+
paddingLeft: '1em',
|
|
121
|
+
}}
|
|
122
|
+
>
|
|
123
|
+
<svg height="12" viewBox="0 0 16 12" width="10">
|
|
124
|
+
<path
|
|
125
|
+
fill="currentColor"
|
|
126
|
+
d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"
|
|
127
|
+
></path>
|
|
128
|
+
<path
|
|
129
|
+
fill="currentColor"
|
|
130
|
+
d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"
|
|
131
|
+
></path>
|
|
132
|
+
</svg>
|
|
133
|
+
</span>
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
const ErrorCopier = () => (
|
|
137
|
+
<span
|
|
138
|
+
aria-label="Failed copying to clipboard"
|
|
139
|
+
title="Failed copying to clipboard"
|
|
140
|
+
style={{
|
|
141
|
+
paddingLeft: '1em',
|
|
142
|
+
display: 'flex',
|
|
143
|
+
alignItems: 'center',
|
|
144
|
+
}}
|
|
145
|
+
>
|
|
146
|
+
<svg height="12" viewBox="0 0 16 12" width="10" display="block">
|
|
147
|
+
<path
|
|
148
|
+
fill="red"
|
|
149
|
+
d="M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.75.75 0 111.06 1.06L9.06 8l3.22 3.22a.75.75 0 11-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 01-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 010-1.06z"
|
|
150
|
+
></path>
|
|
151
|
+
</svg>
|
|
152
|
+
<span
|
|
153
|
+
style={{
|
|
154
|
+
color: 'red',
|
|
155
|
+
fontSize: '12px',
|
|
156
|
+
paddingLeft: '4px',
|
|
157
|
+
position: 'relative',
|
|
158
|
+
top: '2px',
|
|
159
|
+
}}
|
|
160
|
+
>
|
|
161
|
+
See console
|
|
162
|
+
</span>
|
|
163
|
+
</span>
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
const CopiedCopier = () => (
|
|
167
|
+
<span
|
|
168
|
+
aria-label="Object copied to clipboard"
|
|
169
|
+
title="Object copied to clipboard"
|
|
170
|
+
style={{
|
|
171
|
+
paddingLeft: '1em',
|
|
172
|
+
display: 'inline-block',
|
|
173
|
+
verticalAlign: 'middle',
|
|
174
|
+
}}
|
|
175
|
+
>
|
|
176
|
+
<svg height="16" viewBox="0 0 16 16" width="16" display="block">
|
|
177
|
+
<path
|
|
178
|
+
fill="green"
|
|
179
|
+
d="M13.78 4.22a.75.75 0 010 1.06l-7.25 7.25a.75.75 0 01-1.06 0L2.22 9.28a.75.75 0 011.06-1.06L6 10.94l6.72-6.72a.75.75 0 011.06 0z"
|
|
180
|
+
></path>
|
|
181
|
+
</svg>
|
|
182
|
+
</span>
|
|
183
|
+
)
|
|
184
|
+
|
|
65
185
|
type Entry = {
|
|
66
186
|
label: string
|
|
67
187
|
}
|
|
@@ -74,6 +194,7 @@ type RendererProps = {
|
|
|
74
194
|
subEntryPages: Entry[][]
|
|
75
195
|
type: string
|
|
76
196
|
expanded: boolean
|
|
197
|
+
copyable: boolean
|
|
77
198
|
toggleExpanded: () => void
|
|
78
199
|
pageSize: number
|
|
79
200
|
}
|
|
@@ -108,6 +229,7 @@ export const DefaultRenderer: Renderer = ({
|
|
|
108
229
|
subEntryPages = [],
|
|
109
230
|
type,
|
|
110
231
|
expanded = false,
|
|
232
|
+
copyable = false,
|
|
111
233
|
toggleExpanded,
|
|
112
234
|
pageSize,
|
|
113
235
|
}) => {
|
|
@@ -124,6 +246,7 @@ export const DefaultRenderer: Renderer = ({
|
|
|
124
246
|
{subEntries.length} {subEntries.length > 1 ? `items` : `item`}
|
|
125
247
|
</Info>
|
|
126
248
|
</ExpandButton>
|
|
249
|
+
{copyable ? <CopyButton value={value} /> : null}
|
|
127
250
|
{expanded ? (
|
|
128
251
|
subEntryPages.length === 1 ? (
|
|
129
252
|
<SubEntries>{subEntries.map(handleEntry)}</SubEntries>
|
|
@@ -166,6 +289,7 @@ export const DefaultRenderer: Renderer = ({
|
|
|
166
289
|
type ExplorerProps = Partial<RendererProps> & {
|
|
167
290
|
renderer?: Renderer
|
|
168
291
|
defaultExpanded?: true | Record<string, boolean>
|
|
292
|
+
copyable?: boolean
|
|
169
293
|
}
|
|
170
294
|
|
|
171
295
|
type Property = {
|
|
@@ -183,6 +307,7 @@ export default function Explorer({
|
|
|
183
307
|
defaultExpanded,
|
|
184
308
|
renderer = DefaultRenderer,
|
|
185
309
|
pageSize = 100,
|
|
310
|
+
copyable = false,
|
|
186
311
|
...rest
|
|
187
312
|
}: ExplorerProps) {
|
|
188
313
|
const [expanded, setExpanded] = React.useState(Boolean(defaultExpanded))
|
|
@@ -241,6 +366,7 @@ export default function Explorer({
|
|
|
241
366
|
key={entry.label}
|
|
242
367
|
value={value}
|
|
243
368
|
renderer={renderer}
|
|
369
|
+
copyable={copyable}
|
|
244
370
|
{...rest}
|
|
245
371
|
{...entry}
|
|
246
372
|
/>
|
|
@@ -250,6 +376,7 @@ export default function Explorer({
|
|
|
250
376
|
subEntryPages,
|
|
251
377
|
value,
|
|
252
378
|
expanded,
|
|
379
|
+
copyable,
|
|
253
380
|
toggleExpanded,
|
|
254
381
|
pageSize,
|
|
255
382
|
...rest,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { fireEvent, render, screen } from '@testing-library/react'
|
|
1
|
+
import { fireEvent, render, screen, act } from '@testing-library/react'
|
|
2
2
|
import * as React from 'react'
|
|
3
3
|
|
|
4
|
-
import { chunkArray, DefaultRenderer } from '../Explorer'
|
|
4
|
+
import { chunkArray, CopyButton, DefaultRenderer } from '../Explorer'
|
|
5
5
|
import { displayValue } from '../utils'
|
|
6
6
|
|
|
7
7
|
describe('Explorer', () => {
|
|
@@ -38,6 +38,7 @@ describe('Explorer', () => {
|
|
|
38
38
|
toggleExpanded={toggleExpanded}
|
|
39
39
|
pageSize={10}
|
|
40
40
|
expanded={false}
|
|
41
|
+
copyable={false}
|
|
41
42
|
subEntryPages={[[{ label: 'A lovely label' }]]}
|
|
42
43
|
handleEntry={() => <></>}
|
|
43
44
|
value={undefined}
|
|
@@ -54,6 +55,69 @@ describe('Explorer', () => {
|
|
|
54
55
|
|
|
55
56
|
expect(toggleExpanded).toHaveBeenCalledTimes(1)
|
|
56
57
|
})
|
|
58
|
+
|
|
59
|
+
it('when the copy button is clicked, update the clipboard value', async () => {
|
|
60
|
+
// Mock clipboard
|
|
61
|
+
let clipBoardContent = null
|
|
62
|
+
const value = 'someValue'
|
|
63
|
+
Object.defineProperty(navigator, 'clipboard', {
|
|
64
|
+
value: {
|
|
65
|
+
writeText: async () => {
|
|
66
|
+
return new Promise(() => (clipBoardContent = value))
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
configurable: true,
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
act(() => {
|
|
73
|
+
render(<CopyButton value={value} />)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
// After rendering the clipboard content should be null
|
|
77
|
+
expect(clipBoardContent).toBe(null)
|
|
78
|
+
|
|
79
|
+
const copyButton = screen.getByRole('button')
|
|
80
|
+
|
|
81
|
+
await screen.findByLabelText('Copy object to clipboard')
|
|
82
|
+
|
|
83
|
+
// After clicking the content should be added to the clipboard
|
|
84
|
+
await act(async () => {
|
|
85
|
+
fireEvent.click(copyButton)
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
expect(clipBoardContent).toBe(value)
|
|
89
|
+
screen.findByLabelText('Object copied to clipboard')
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it('when the copy button is clicked but there is an error, show error state', async () => {
|
|
93
|
+
// Mock clipboard with error state
|
|
94
|
+
Object.defineProperty(navigator, 'clipboard', {
|
|
95
|
+
value: {
|
|
96
|
+
writeText: async () => {
|
|
97
|
+
return new Promise(() => {
|
|
98
|
+
throw Error
|
|
99
|
+
})
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
configurable: true,
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
act(() => {
|
|
106
|
+
render(<CopyButton value={'someValue'} />)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
const copyButton = screen.getByRole('button')
|
|
110
|
+
|
|
111
|
+
await screen.findByLabelText('Copy object to clipboard')
|
|
112
|
+
|
|
113
|
+
// After clicking the content should NOT be added to the clipboard
|
|
114
|
+
await act(async () => {
|
|
115
|
+
fireEvent.click(copyButton)
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
// Check that it has failed
|
|
119
|
+
await screen.findByLabelText('Failed copying to clipboard')
|
|
120
|
+
})
|
|
57
121
|
})
|
|
58
122
|
|
|
59
123
|
describe('displayValue', () => {
|