polen 0.10.0-next.12 → 0.10.0-next.13
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/graphql-document/components/CopyButton.d.ts +19 -0
- package/build/lib/graphql-document/components/CopyButton.d.ts.map +1 -0
- package/build/lib/graphql-document/components/CopyButton.js +43 -0
- package/build/lib/graphql-document/components/CopyButton.js.map +1 -0
- package/build/lib/graphql-document/components/GraphQLDocument.d.ts +0 -4
- package/build/lib/graphql-document/components/GraphQLDocument.d.ts.map +1 -1
- package/build/lib/graphql-document/components/GraphQLDocument.js +31 -74
- package/build/lib/graphql-document/components/GraphQLDocument.js.map +1 -1
- package/build/lib/graphql-document/components/GraphQLIdentifierPopover.d.ts +33 -0
- package/build/lib/graphql-document/components/GraphQLIdentifierPopover.d.ts.map +1 -0
- package/build/lib/graphql-document/components/GraphQLIdentifierPopover.js +48 -0
- package/build/lib/graphql-document/components/GraphQLIdentifierPopover.js.map +1 -0
- package/build/lib/graphql-document/components/IdentifierLink.d.ts +15 -13
- package/build/lib/graphql-document/components/IdentifierLink.d.ts.map +1 -1
- package/build/lib/graphql-document/components/IdentifierLink.js +51 -117
- package/build/lib/graphql-document/components/IdentifierLink.js.map +1 -1
- package/build/lib/graphql-document/components/graphql-document-styles.d.ts +5 -0
- package/build/lib/graphql-document/components/graphql-document-styles.d.ts.map +1 -0
- package/build/lib/graphql-document/components/graphql-document-styles.js +167 -0
- package/build/lib/graphql-document/components/graphql-document-styles.js.map +1 -0
- package/build/lib/graphql-document/components/index.d.ts +2 -1
- package/build/lib/graphql-document/components/index.d.ts.map +1 -1
- package/build/lib/graphql-document/components/index.js +2 -1
- package/build/lib/graphql-document/components/index.js.map +1 -1
- package/build/lib/graphql-document/hooks/use-tooltip-state.d.ts +43 -0
- package/build/lib/graphql-document/hooks/use-tooltip-state.d.ts.map +1 -0
- package/build/lib/graphql-document/hooks/use-tooltip-state.js +132 -0
- package/build/lib/graphql-document/hooks/use-tooltip-state.js.map +1 -0
- package/package.json +2 -1
- package/src/lib/graphql-document/components/CopyButton.tsx +76 -0
- package/src/lib/graphql-document/components/GraphQLDocument.tsx +52 -86
- package/src/lib/graphql-document/components/GraphQLIdentifierPopover.tsx +197 -0
- package/src/lib/graphql-document/components/IdentifierLink.tsx +105 -166
- package/src/lib/graphql-document/components/graphql-document-styles.ts +167 -0
- package/src/lib/graphql-document/components/index.ts +2 -1
- package/src/lib/graphql-document/hooks/use-tooltip-state.test.ts +76 -0
- package/src/lib/graphql-document/hooks/use-tooltip-state.ts +191 -0
- package/build/lib/graphql-document/components/HoverTooltip.d.ts +0 -35
- package/build/lib/graphql-document/components/HoverTooltip.d.ts.map +0 -1
- package/build/lib/graphql-document/components/HoverTooltip.js +0 -132
- package/build/lib/graphql-document/components/HoverTooltip.js.map +0 -1
- package/src/lib/graphql-document/components/HoverTooltip.tsx +0 -282
@@ -1,13 +1,13 @@
|
|
1
|
+
/**
|
2
|
+
* Interactive overlay for GraphQL identifiers
|
3
|
+
*/
|
4
|
+
|
1
5
|
import type { React } from '#dep/react/index'
|
2
|
-
import { useState } from 'react'
|
3
6
|
import type { DOMPosition } from '../positioning-simple.ts'
|
4
7
|
import type { SchemaResolution } from '../schema-integration.ts'
|
5
8
|
import type { Identifier } from '../types.ts'
|
6
|
-
import {
|
9
|
+
import { GraphQLIdentifierPopover } from './GraphQLIdentifierPopover.tsx'
|
7
10
|
|
8
|
-
/**
|
9
|
-
* Props for the IdentifierLink component
|
10
|
-
*/
|
11
11
|
export interface IdentifierLinkProps {
|
12
12
|
/** The GraphQL identifier */
|
13
13
|
identifier: Identifier
|
@@ -20,16 +20,24 @@ export interface IdentifierLinkProps {
|
|
20
20
|
/** Whether to show debug visuals */
|
21
21
|
debug?: boolean
|
22
22
|
/** Whether this tooltip is open */
|
23
|
-
isOpen
|
24
|
-
/**
|
25
|
-
|
23
|
+
isOpen: boolean
|
24
|
+
/** Whether this tooltip is pinned */
|
25
|
+
isPinned: boolean
|
26
|
+
/** Handle hover start */
|
27
|
+
onHoverStart: () => void
|
28
|
+
/** Handle hover end */
|
29
|
+
onHoverEnd: () => void
|
30
|
+
/** Toggle pin state */
|
31
|
+
onTogglePin: () => void
|
32
|
+
/** Handle tooltip hover */
|
33
|
+
onTooltipHover: () => void
|
26
34
|
}
|
27
35
|
|
28
36
|
/**
|
29
37
|
* Interactive overlay for a GraphQL identifier
|
30
38
|
*
|
31
39
|
* Renders an invisible clickable area over the identifier text
|
32
|
-
* with hover
|
40
|
+
* with hover popovers and navigation to schema reference pages.
|
33
41
|
*/
|
34
42
|
export const IdentifierLink: React.FC<IdentifierLinkProps> = ({
|
35
43
|
identifier,
|
@@ -37,17 +45,13 @@ export const IdentifierLink: React.FC<IdentifierLinkProps> = ({
|
|
37
45
|
position,
|
38
46
|
onNavigate,
|
39
47
|
debug = false,
|
40
|
-
isOpen
|
41
|
-
|
48
|
+
isOpen,
|
49
|
+
isPinned,
|
50
|
+
onHoverStart,
|
51
|
+
onHoverEnd,
|
52
|
+
onTogglePin,
|
53
|
+
onTooltipHover,
|
42
54
|
}) => {
|
43
|
-
const [isHovered, setIsHovered] = useState(false)
|
44
|
-
|
45
|
-
// Use external state if provided, otherwise manage locally
|
46
|
-
const showTooltip = isOpen
|
47
|
-
const setShowTooltip = (show: boolean) => {
|
48
|
-
onToggle?.(show)
|
49
|
-
}
|
50
|
-
|
51
55
|
// Determine visual state
|
52
56
|
const isClickable = resolution.exists
|
53
57
|
const hasError = !resolution.exists && (identifier.kind === 'Type' || identifier.kind === 'Field')
|
@@ -60,162 +64,97 @@ export const IdentifierLink: React.FC<IdentifierLinkProps> = ({
|
|
60
64
|
isClickable && 'graphql-clickable',
|
61
65
|
hasError && 'graphql-error',
|
62
66
|
isDeprecated && 'graphql-deprecated',
|
63
|
-
|
64
|
-
|
67
|
+
isOpen && 'graphql-hovered',
|
68
|
+
isOpen && 'graphql-tooltip-open',
|
65
69
|
debug && 'graphql-debug',
|
66
70
|
].filter(Boolean).join(' ')
|
67
71
|
|
68
72
|
const handleClick = (e: React.MouseEvent) => {
|
69
73
|
e.preventDefault()
|
70
74
|
e.stopPropagation()
|
71
|
-
|
72
|
-
// Toggle tooltip on click
|
73
|
-
setShowTooltip(!showTooltip)
|
75
|
+
onTogglePin()
|
74
76
|
}
|
75
77
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
78
|
+
// Create trigger element
|
79
|
+
const triggerElement = isClickable
|
80
|
+
? (
|
81
|
+
<a
|
82
|
+
href={resolution.referenceUrl}
|
83
|
+
className={classNames + ' graphql-identifier-link'}
|
84
|
+
style={{
|
85
|
+
position: 'absolute',
|
86
|
+
top: position.top,
|
87
|
+
left: position.left,
|
88
|
+
width: position.width,
|
89
|
+
height: position.height,
|
90
|
+
cursor: 'pointer',
|
91
|
+
zIndex: 10,
|
92
|
+
pointerEvents: 'auto',
|
93
|
+
display: 'block',
|
94
|
+
textDecoration: 'none',
|
95
|
+
// Debug mode visual
|
96
|
+
...(debug && {
|
97
|
+
backgroundColor: hasError ? 'rgba(239, 68, 68, 0.1)' : 'rgba(59, 130, 246, 0.1)',
|
98
|
+
border: `1px solid ${hasError ? 'rgba(239, 68, 68, 0.3)' : 'rgba(59, 130, 246, 0.3)'}`,
|
99
|
+
}),
|
100
|
+
}}
|
101
|
+
onClick={handleClick}
|
102
|
+
onMouseEnter={onHoverStart}
|
103
|
+
onMouseLeave={onHoverEnd}
|
104
|
+
aria-label={`${identifier.kind} ${identifier.name} - Click to view documentation`}
|
105
|
+
data-graphql-identifier={identifier.name}
|
106
|
+
data-graphql-kind={identifier.kind}
|
107
|
+
/>
|
108
|
+
)
|
109
|
+
: (
|
110
|
+
<div
|
111
|
+
className={classNames}
|
112
|
+
style={{
|
113
|
+
position: 'absolute',
|
114
|
+
top: position.top,
|
115
|
+
left: position.left,
|
116
|
+
width: position.width,
|
117
|
+
height: position.height,
|
118
|
+
cursor: 'pointer', // Make it clickable even for errors
|
119
|
+
zIndex: 10,
|
120
|
+
pointerEvents: 'auto',
|
121
|
+
// Debug mode visual
|
122
|
+
...(debug && {
|
123
|
+
backgroundColor: 'rgba(239, 68, 68, 0.1)',
|
124
|
+
border: `1px solid rgba(239, 68, 68, 0.3)`,
|
125
|
+
}),
|
126
|
+
}}
|
127
|
+
onClick={handleClick}
|
128
|
+
onMouseEnter={onHoverStart}
|
129
|
+
onMouseLeave={onHoverEnd}
|
130
|
+
role='button'
|
131
|
+
aria-label={`${identifier.kind} ${identifier.name} - Click to view information`}
|
132
|
+
data-graphql-identifier={identifier.name}
|
133
|
+
data-graphql-kind={identifier.kind}
|
134
|
+
/>
|
135
|
+
)
|
83
136
|
|
84
137
|
return (
|
85
|
-
|
86
|
-
{
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
}),
|
107
|
-
}}
|
108
|
-
onClick={handleClick}
|
109
|
-
onMouseEnter={() => setIsHovered(true)}
|
110
|
-
onMouseLeave={() => setIsHovered(false)}
|
111
|
-
aria-label={`${identifier.kind} ${identifier.name} - Click to view documentation`}
|
112
|
-
data-graphql-identifier={identifier.name}
|
113
|
-
data-graphql-kind={identifier.kind}
|
114
|
-
/>
|
115
|
-
)
|
116
|
-
: (
|
117
|
-
<div
|
118
|
-
className={classNames}
|
119
|
-
style={{
|
120
|
-
position: 'absolute',
|
121
|
-
top: position.top,
|
122
|
-
left: position.left,
|
123
|
-
width: position.width,
|
124
|
-
height: position.height,
|
125
|
-
cursor: 'pointer', // Make it clickable even for errors
|
126
|
-
zIndex: 10,
|
127
|
-
pointerEvents: 'auto',
|
128
|
-
// Debug mode visual
|
129
|
-
...(debug && {
|
130
|
-
backgroundColor: 'rgba(239, 68, 68, 0.1)',
|
131
|
-
border: `1px solid rgba(239, 68, 68, 0.3)`,
|
132
|
-
}),
|
133
|
-
}}
|
134
|
-
onClick={handleClick} // Add click handler for errors too
|
135
|
-
onMouseEnter={() => setIsHovered(true)}
|
136
|
-
onMouseLeave={() => setIsHovered(false)}
|
137
|
-
role='button'
|
138
|
-
aria-label={`${identifier.kind} ${identifier.name} - Click to view information`}
|
139
|
-
data-graphql-identifier={identifier.name}
|
140
|
-
data-graphql-kind={identifier.kind}
|
141
|
-
/>
|
142
|
-
)}
|
143
|
-
|
144
|
-
{/* Tooltip - show on click, not hover */}
|
145
|
-
{showTooltip && (
|
146
|
-
<HoverTooltip
|
147
|
-
identifier={identifier}
|
148
|
-
documentation={resolution.documentation || {
|
149
|
-
description: hasError
|
150
|
-
? `${identifier.kind} "${identifier.name}" not found in schema. This ${identifier.kind.toLowerCase()} does not exist in the current GraphQL schema.`
|
151
|
-
: `${identifier.kind}: ${identifier.name}`,
|
152
|
-
typeInfo: identifier.kind,
|
153
|
-
}}
|
154
|
-
position={position}
|
155
|
-
hasError={hasError}
|
156
|
-
referenceUrl={resolution.referenceUrl}
|
157
|
-
onClose={() => setShowTooltip(false)}
|
158
|
-
onNavigate={isClickable ? () => onNavigate(resolution.referenceUrl) : undefined}
|
159
|
-
/>
|
160
|
-
)}
|
161
|
-
</>
|
138
|
+
<GraphQLIdentifierPopover
|
139
|
+
identifier={identifier}
|
140
|
+
documentation={resolution.documentation || {
|
141
|
+
description: hasError
|
142
|
+
? `${identifier.kind} "${identifier.name}" not found in schema. This ${identifier.kind.toLowerCase()} does not exist in the current GraphQL schema.`
|
143
|
+
: `${identifier.kind}: ${identifier.name}`,
|
144
|
+
typeInfo: identifier.kind,
|
145
|
+
}}
|
146
|
+
hasError={hasError}
|
147
|
+
referenceUrl={resolution.referenceUrl}
|
148
|
+
open={isOpen}
|
149
|
+
isPinned={isPinned}
|
150
|
+
onOpenChange={(open) => {
|
151
|
+
if (!open && isPinned) {
|
152
|
+
onTogglePin() // Unpin when closing
|
153
|
+
}
|
154
|
+
}}
|
155
|
+
onNavigate={isClickable ? onNavigate : undefined}
|
156
|
+
>
|
157
|
+
{triggerElement}
|
158
|
+
</GraphQLIdentifierPopover>
|
162
159
|
)
|
163
160
|
}
|
164
|
-
|
165
|
-
/**
|
166
|
-
* Default styles for identifier overlays
|
167
|
-
*
|
168
|
-
* These can be included in the document or overridden by custom styles.
|
169
|
-
*/
|
170
|
-
export const identifierLinkStyles = `
|
171
|
-
.graphql-identifier-overlay {
|
172
|
-
/* Base styles for all overlays */
|
173
|
-
transition: background-color 0.2s ease;
|
174
|
-
}
|
175
|
-
|
176
|
-
.graphql-identifier-overlay.graphql-clickable:hover {
|
177
|
-
/* Subtle highlight on hover for clickable identifiers */
|
178
|
-
background-color: rgba(59, 130, 246, 0.05);
|
179
|
-
}
|
180
|
-
|
181
|
-
.graphql-identifier-overlay.graphql-error {
|
182
|
-
/* Error indicator */
|
183
|
-
border-bottom: 2px wavy red;
|
184
|
-
}
|
185
|
-
|
186
|
-
.graphql-identifier-overlay.graphql-deprecated {
|
187
|
-
/* Deprecated indicator */
|
188
|
-
text-decoration: line-through;
|
189
|
-
opacity: 0.7;
|
190
|
-
}
|
191
|
-
|
192
|
-
.graphql-identifier-overlay.graphql-debug {
|
193
|
-
/* Debug mode makes overlays visible */
|
194
|
-
background-color: rgba(59, 130, 246, 0.1) !important;
|
195
|
-
border: 1px solid rgba(59, 130, 246, 0.3) !important;
|
196
|
-
}
|
197
|
-
|
198
|
-
/* Kind-specific styles */
|
199
|
-
.graphql-identifier-overlay.graphql-type {
|
200
|
-
/* Type identifiers */
|
201
|
-
}
|
202
|
-
|
203
|
-
.graphql-identifier-overlay.graphql-field {
|
204
|
-
/* Field identifiers */
|
205
|
-
}
|
206
|
-
|
207
|
-
.graphql-identifier-overlay.graphql-argument {
|
208
|
-
/* Argument identifiers */
|
209
|
-
font-style: italic;
|
210
|
-
}
|
211
|
-
|
212
|
-
.graphql-identifier-overlay.graphql-variable {
|
213
|
-
/* Variable identifiers */
|
214
|
-
color: var(--purple-11);
|
215
|
-
}
|
216
|
-
|
217
|
-
.graphql-identifier-overlay.graphql-directive {
|
218
|
-
/* Directive identifiers */
|
219
|
-
color: var(--amber-11);
|
220
|
-
}
|
221
|
-
`
|
@@ -0,0 +1,167 @@
|
|
1
|
+
/**
|
2
|
+
* Minimal styles for GraphQL Document interactive code blocks
|
3
|
+
*/
|
4
|
+
|
5
|
+
export const graphqlDocumentStyles = `
|
6
|
+
/* Container styles */
|
7
|
+
.graphql-document {
|
8
|
+
position: relative;
|
9
|
+
}
|
10
|
+
|
11
|
+
.graphql-interaction-layer {
|
12
|
+
position: absolute;
|
13
|
+
top: 0;
|
14
|
+
left: 0;
|
15
|
+
right: 0;
|
16
|
+
bottom: 0;
|
17
|
+
pointer-events: none;
|
18
|
+
}
|
19
|
+
|
20
|
+
.graphql-interaction-layer > * {
|
21
|
+
pointer-events: auto;
|
22
|
+
}
|
23
|
+
|
24
|
+
/* Identifier overlay styles */
|
25
|
+
.graphql-identifier-overlay {
|
26
|
+
transition: background-color 0.2s ease;
|
27
|
+
}
|
28
|
+
|
29
|
+
/* Clickable identifiers get visual feedback */
|
30
|
+
.graphql-identifier-overlay.graphql-clickable {
|
31
|
+
/* Subtle underline effect using box-shadow to not affect layout */
|
32
|
+
box-shadow: 0 1px 0 0 rgba(var(--accent-9), 0.3);
|
33
|
+
transition: box-shadow 0.2s ease, background-color 0.2s ease;
|
34
|
+
}
|
35
|
+
|
36
|
+
.graphql-identifier-overlay.graphql-clickable:hover {
|
37
|
+
background-color: rgba(var(--accent-3), 0.5);
|
38
|
+
box-shadow: 0 1px 0 0 rgba(var(--accent-9), 0.6);
|
39
|
+
}
|
40
|
+
|
41
|
+
/* Active/open state */
|
42
|
+
.graphql-identifier-overlay.graphql-tooltip-open {
|
43
|
+
background-color: rgba(var(--accent-3), 0.5);
|
44
|
+
box-shadow: 0 1px 0 0 var(--accent-9);
|
45
|
+
}
|
46
|
+
|
47
|
+
/* Error state */
|
48
|
+
.graphql-identifier-overlay.graphql-error {
|
49
|
+
box-shadow: 0 1.5px 0 0 var(--red-9);
|
50
|
+
}
|
51
|
+
|
52
|
+
.graphql-identifier-overlay.graphql-error:hover {
|
53
|
+
background-color: rgba(var(--red-3), 0.5);
|
54
|
+
}
|
55
|
+
|
56
|
+
/* Deprecated state */
|
57
|
+
.graphql-identifier-overlay.graphql-deprecated {
|
58
|
+
text-decoration: line-through;
|
59
|
+
opacity: 0.7;
|
60
|
+
}
|
61
|
+
|
62
|
+
/* Debug mode */
|
63
|
+
.graphql-identifier-overlay.graphql-debug {
|
64
|
+
background-color: rgba(59, 130, 246, 0.1) !important;
|
65
|
+
border: 1px solid rgba(59, 130, 246, 0.3) !important;
|
66
|
+
}
|
67
|
+
|
68
|
+
/* Kind-specific colors */
|
69
|
+
.graphql-identifier-overlay.graphql-type.graphql-clickable {
|
70
|
+
box-shadow: 0 1px 0 0 rgba(var(--blue-9), 0.3);
|
71
|
+
}
|
72
|
+
|
73
|
+
.graphql-identifier-overlay.graphql-field.graphql-clickable {
|
74
|
+
box-shadow: 0 1px 0 0 rgba(var(--green-9), 0.3);
|
75
|
+
}
|
76
|
+
|
77
|
+
.graphql-identifier-overlay.graphql-argument.graphql-clickable {
|
78
|
+
box-shadow: 0 1px 0 0 rgba(var(--orange-9), 0.3);
|
79
|
+
}
|
80
|
+
|
81
|
+
.graphql-identifier-overlay.graphql-variable {
|
82
|
+
box-shadow: 0 1px 0 0 rgba(var(--purple-9), 0.3);
|
83
|
+
}
|
84
|
+
|
85
|
+
.graphql-identifier-overlay.graphql-directive.graphql-clickable {
|
86
|
+
box-shadow: 0 1px 0 0 rgba(var(--amber-9), 0.3);
|
87
|
+
}
|
88
|
+
|
89
|
+
/* Popover animation */
|
90
|
+
.graphql-identifier-popover {
|
91
|
+
animation: graphql-popover-show 150ms ease-out;
|
92
|
+
}
|
93
|
+
|
94
|
+
@keyframes graphql-popover-show {
|
95
|
+
from {
|
96
|
+
opacity: 0;
|
97
|
+
transform: translateY(2px);
|
98
|
+
}
|
99
|
+
to {
|
100
|
+
opacity: 1;
|
101
|
+
transform: translateY(0);
|
102
|
+
}
|
103
|
+
}
|
104
|
+
|
105
|
+
/* Validation errors */
|
106
|
+
.graphql-validation-errors {
|
107
|
+
margin-top: 1rem;
|
108
|
+
padding: 0.5rem;
|
109
|
+
background-color: var(--red-2);
|
110
|
+
border: 1px solid var(--red-6);
|
111
|
+
border-radius: 4px;
|
112
|
+
}
|
113
|
+
|
114
|
+
.graphql-error {
|
115
|
+
color: var(--red-11);
|
116
|
+
font-size: 0.875rem;
|
117
|
+
margin: 0.25rem 0;
|
118
|
+
}
|
119
|
+
|
120
|
+
/* Loading state */
|
121
|
+
.graphql-document.graphql-loading {
|
122
|
+
opacity: 0.6;
|
123
|
+
pointer-events: none;
|
124
|
+
}
|
125
|
+
|
126
|
+
.graphql-document.graphql-loading::after {
|
127
|
+
content: '';
|
128
|
+
position: absolute;
|
129
|
+
top: 50%;
|
130
|
+
left: 50%;
|
131
|
+
width: 20px;
|
132
|
+
height: 20px;
|
133
|
+
margin: -10px 0 0 -10px;
|
134
|
+
border: 2px solid var(--gray-6);
|
135
|
+
border-top-color: var(--accent-9);
|
136
|
+
border-radius: 50%;
|
137
|
+
animation: graphql-spinner 0.8s linear infinite;
|
138
|
+
}
|
139
|
+
|
140
|
+
@keyframes graphql-spinner {
|
141
|
+
to {
|
142
|
+
transform: rotate(360deg);
|
143
|
+
}
|
144
|
+
}
|
145
|
+
|
146
|
+
/* Copy button */
|
147
|
+
.graphql-document-copy {
|
148
|
+
position: absolute;
|
149
|
+
top: 1rem;
|
150
|
+
right: 1rem;
|
151
|
+
opacity: 0;
|
152
|
+
transition: opacity 0.2s ease;
|
153
|
+
z-index: 10;
|
154
|
+
}
|
155
|
+
|
156
|
+
.graphql-document:hover .graphql-document-copy {
|
157
|
+
opacity: 0.8;
|
158
|
+
}
|
159
|
+
|
160
|
+
.graphql-document-copy:hover {
|
161
|
+
opacity: 1 !important;
|
162
|
+
}
|
163
|
+
|
164
|
+
.graphql-document-copy[data-copied="true"] {
|
165
|
+
opacity: 1 !important;
|
166
|
+
color: var(--green-9);
|
167
|
+
}`
|
@@ -0,0 +1,76 @@
|
|
1
|
+
/**
|
2
|
+
* Unit tests for tooltip state management hook
|
3
|
+
*
|
4
|
+
* @vitest-environment jsdom
|
5
|
+
*/
|
6
|
+
|
7
|
+
import { act, renderHook } from '@testing-library/react'
|
8
|
+
import { describe, expect, it, vi } from 'vitest'
|
9
|
+
import { useTooltipState } from './use-tooltip-state.ts'
|
10
|
+
|
11
|
+
describe('useTooltipState', () => {
|
12
|
+
it('shows tooltip after hover delay', () => {
|
13
|
+
vi.useFakeTimers()
|
14
|
+
const { result } = renderHook(() => useTooltipState({ showDelay: 300 }))
|
15
|
+
|
16
|
+
act(() => {
|
17
|
+
result.current.onHoverStart('field-1')
|
18
|
+
})
|
19
|
+
expect(result.current.isOpen('field-1')).toBe(false)
|
20
|
+
|
21
|
+
act(() => {
|
22
|
+
vi.advanceTimersByTime(300)
|
23
|
+
})
|
24
|
+
expect(result.current.isOpen('field-1')).toBe(true)
|
25
|
+
vi.useRealTimers()
|
26
|
+
})
|
27
|
+
|
28
|
+
it('hides tooltip after hover end delay', () => {
|
29
|
+
vi.useFakeTimers()
|
30
|
+
const { result } = renderHook(() => useTooltipState())
|
31
|
+
|
32
|
+
// Show tooltip
|
33
|
+
act(() => {
|
34
|
+
result.current.onHoverStart('field-1')
|
35
|
+
vi.advanceTimersByTime(300)
|
36
|
+
})
|
37
|
+
|
38
|
+
// Trigger hide
|
39
|
+
act(() => {
|
40
|
+
result.current.onHoverEnd('field-1')
|
41
|
+
})
|
42
|
+
expect(result.current.isOpen('field-1')).toBe(true) // Still open during delay
|
43
|
+
|
44
|
+
act(() => {
|
45
|
+
vi.advanceTimersByTime(200)
|
46
|
+
})
|
47
|
+
expect(result.current.isOpen('field-1')).toBe(false)
|
48
|
+
vi.useRealTimers()
|
49
|
+
})
|
50
|
+
|
51
|
+
it('pins and unpins tooltip on toggle', () => {
|
52
|
+
const { result } = renderHook(() => useTooltipState())
|
53
|
+
|
54
|
+
act(() => {
|
55
|
+
result.current.onTogglePin('field-1')
|
56
|
+
})
|
57
|
+
expect(result.current.isPinned('field-1')).toBe(true)
|
58
|
+
|
59
|
+
act(() => {
|
60
|
+
result.current.onTogglePin('field-1')
|
61
|
+
})
|
62
|
+
expect(result.current.isPinned('field-1')).toBe(false)
|
63
|
+
})
|
64
|
+
|
65
|
+
it('allows multiple pins when enabled', () => {
|
66
|
+
const { result } = renderHook(() => useTooltipState())
|
67
|
+
|
68
|
+
act(() => {
|
69
|
+
result.current.onTogglePin('field-1')
|
70
|
+
result.current.onTogglePin('field-2')
|
71
|
+
})
|
72
|
+
|
73
|
+
expect(result.current.isPinned('field-1')).toBe(true)
|
74
|
+
expect(result.current.isPinned('field-2')).toBe(true)
|
75
|
+
})
|
76
|
+
})
|