Skip to content
This repository was archived by the owner on Nov 25, 2021. It is now read-only.

Commit be45d5b

Browse files
committed
feat: pass through hover data to HoverOverlay component
1 parent 3f92379 commit be45d5b

File tree

4 files changed

+42
-29
lines changed

4 files changed

+42
-29
lines changed

src/HoverOverlay.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,19 @@ export type ActionComponent<A> = React.ComponentType<A & React.HTMLAttributes<HT
1818

1919
/**
2020
* @template C Extra context for the hovered token.
21+
* @template D The type of the hover content data.
2122
* @template A The type of an action.
2223
*/
23-
export interface HoverOverlayProps<C extends object, A> {
24+
export interface HoverOverlayProps<C extends object, D, A> {
2425
/** What to show as contents */
25-
hoverOrError?: typeof LOADING | HoverAttachment | null | ErrorLike // TODO disallow null and undefined
26+
hoverOrError?: typeof LOADING | (HoverAttachment & D) | null | ErrorLike // TODO disallow null and undefined
2627

2728
/** The position of the tooltip (assigned to `style`) */
2829
overlayPosition?: { left: number; top: number }
2930

3031
/** A ref callback to get the root overlay element. Use this to calculate the position. */
3132
hoverRef?: React.Ref<HTMLDivElement>
3233

33-
/** The content of the hover overlay. */
34-
children?: React.ReactNode | React.ReactNode[]
35-
3634
/**
3735
* The hovered token (position and word).
3836
* Used for the Find References buttons and for error messages
@@ -56,12 +54,17 @@ export interface HoverOverlayProps<C extends object, A> {
5654

5755
const transformMouseEvent = (handler: (event: MouseEvent) => void) => (event: React.MouseEvent<HTMLElement>) =>
5856
handler(toNativeEvent(event))
57+
5958
/**
6059
* @template C Extra context for the hovered token.
60+
* @template D The type of the hover content data.
6161
* @template A The type of an action.
6262
*/
63-
export const HoverOverlay: <C extends object, A>(
64-
props: HoverOverlayProps<C, A> & {
63+
export const HoverOverlay: <C extends object, D, A>(
64+
props: HoverOverlayProps<C, D, A> & {
65+
/** The content of the hover overlay. */
66+
children?: React.ReactNode | React.ReactNode[]
67+
6568
/** The component used to render actions. */
6669
actionComponent: ActionComponent<A>
6770
}

src/hoverifier.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ describe('Hoverifier', () => {
390390
const outputDiagram = `${LOADER_DELAY}ms a ${TOOLTIP_DISPLAY_DELAY - 1}ms b`
391391

392392
const outputValues: {
393-
[key: string]: Pick<HoverOverlayProps<{}, string>, 'hoverOrError' | 'actionsOrError'>
393+
[key: string]: Pick<HoverOverlayProps<{}, {}, string>, 'hoverOrError' | 'actionsOrError'>
394394
} = {
395395
a: { hoverOrError: LOADING, actionsOrError: LOADING }, // actions is undefined when it is loading
396396
b: { hoverOrError: createHoverAttachment(hover), actionsOrError: actions },

src/hoverifier.ts

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,10 @@ export { HoveredToken }
4848

4949
/**
5050
* @template C Extra context for the hovered token.
51+
* @template D The type of the hover content data.
5152
* @template A The type of an action.
5253
*/
53-
export interface HoverifierOptions<C extends object, A> {
54+
export interface HoverifierOptions<C extends object, D, A> {
5455
/**
5556
* Emit the HoverOverlay element on this after it was rerendered when its content changed and it needs to be repositioned.
5657
*/
@@ -78,7 +79,10 @@ export interface HoverifierOptions<C extends object, A> {
7879

7980
hoverOverlayElements: Subscribable<HTMLElement | null>
8081

81-
fetchHover: HoverFetcher<C>
82+
/**
83+
* Called to fetch the data to display in the hover.
84+
*/
85+
fetchHover: HoverFetcher<C, D>
8286

8387
/**
8488
* Called to fetch the actions to display in the hover.
@@ -93,17 +97,18 @@ export interface HoverifierOptions<C extends object, A> {
9397
* There can be multiple code views in the DOM, which will only show a single HoverOverlay if the same Hoverifier was used.
9498
*
9599
* @template C Extra context for the hovered token.
100+
* @template D The type of the hover content data.
96101
* @template A The type of an action.
97102
*/
98-
export interface Hoverifier<C extends object, A> {
103+
export interface Hoverifier<C extends object, D, A> {
99104
/**
100105
* The current Hover state. You can use this to read the initial state synchronously.
101106
*/
102-
hoverState: Readonly<HoverState<C, A>>
107+
hoverState: Readonly<HoverState<C, D, A>>
103108
/**
104109
* This Observable is to notify that the state that is used to render the HoverOverlay needs to be updated.
105110
*/
106-
hoverStateUpdates: Observable<Readonly<HoverState<C, A>>>
111+
hoverStateUpdates: Observable<Readonly<HoverState<C, D, A>>>
107112

108113
/**
109114
* Hoverifies a code view.
@@ -187,13 +192,14 @@ export interface HoverifyOptions<C extends object> extends EventOptions<C> {
187192
* Output that contains the information needed to render the HoverOverlay.
188193
*
189194
* @template C Extra context for the hovered token.
195+
* * @template D The type of the hover content data.
190196
* @template A The type of an action.
191197
*/
192-
export interface HoverState<C extends object, A> {
198+
export interface HoverState<C extends object, D, A> {
193199
/**
194200
* The props to pass to `HoverOverlay`, or `undefined` if it should not be rendered.
195201
*/
196-
hoverOverlayProps?: Pick<HoverOverlayProps<C, A>, Exclude<keyof HoverOverlayProps<C, A>, 'actionComponent'>>
202+
hoverOverlayProps?: Pick<HoverOverlayProps<C, D, A>, Exclude<keyof HoverOverlayProps<C, D, A>, 'actionComponent'>>
197203

198204
/**
199205
* The highlighted range, which is the range in the hover result or else the range of the hovered token.
@@ -210,10 +216,11 @@ export interface HoverState<C extends object, A> {
210216

211217
/**
212218
* @template C Extra context for the hovered token.
219+
* @template D The type of the hover content data.
213220
* @template A The type of an action.
214221
*/
215-
interface InternalHoverifierState<C extends object, A> {
216-
hoverOrError?: typeof LOADING | HoverAttachment | null | ErrorLike
222+
interface InternalHoverifierState<C extends object, D, A> {
223+
hoverOrError?: typeof LOADING | (HoverAttachment & D) | null | ErrorLike
217224

218225
hoverOverlayIsFixed: boolean
219226

@@ -246,18 +253,19 @@ interface InternalHoverifierState<C extends object, A> {
246253
/**
247254
* Returns true if the HoverOverlay component should be rendered according to the given state.
248255
*/
249-
const shouldRenderOverlay = (state: InternalHoverifierState<{}, {}>): boolean =>
256+
const shouldRenderOverlay = (state: InternalHoverifierState<{}, {}, {}>): boolean =>
250257
!(!state.hoverOverlayIsFixed && state.mouseIsMoving) && !!state.hoverOrError && !isErrorLike(state.hoverOrError)
251258

252259
/**
253260
* Maps internal HoverifierState to the publicly exposed HoverState
254261
*
255262
* @template C Extra context for the hovered token.
263+
* @template D The type of the hover content data.
256264
* @template A The type of an action.
257265
*/
258-
const internalToExternalState = <C extends object, A>(
259-
internalState: InternalHoverifierState<C, A>
260-
): HoverState<C, A> => ({
266+
const internalToExternalState = <C extends object, D, A>(
267+
internalState: InternalHoverifierState<C, D, A>
268+
): HoverState<C, D, A> => ({
261269
selectedPosition: internalState.selectedPosition,
262270
highlightedRange: shouldRenderOverlay(internalState) ? internalState.highlightedRange : undefined,
263271
hoverOverlayProps: shouldRenderOverlay(internalState)
@@ -282,10 +290,11 @@ export const MOUSEOVER_DELAY = 50
282290

283291
/**
284292
* @template C Extra context for the hovered token.
293+
* @template D The type of the hover content data.
285294
*/
286-
export type HoverFetcher<C extends object> = (
295+
export type HoverFetcher<C extends object, D> = (
287296
position: HoveredToken & C
288-
) => SubscribableOrPromise<HoverAttachment | null>
297+
) => SubscribableOrPromise<(HoverAttachment & D) | null>
289298

290299
/**
291300
* @template C Extra context for the hovered token.
@@ -303,18 +312,19 @@ export type ContextResolver<C extends object> = (hoveredToken: HoveredToken) =>
303312

304313
/**
305314
* @template C Extra context for the hovered token.
315+
* @template D The type of the hover content data.
306316
* @template A The type of an action.
307317
*/
308-
export function createHoverifier<C extends object, A>({
318+
export function createHoverifier<C extends object, D, A>({
309319
goToDefinitionClicks,
310320
closeButtonClicks,
311321
hoverOverlayRerenders,
312322
fetchHover,
313323
fetchActions,
314-
}: HoverifierOptions<C, A>): Hoverifier<C, A> {
324+
}: HoverifierOptions<C, D, A>): Hoverifier<C, D, A> {
315325
// Internal state that is not exposed to the caller
316326
// Shared between all hoverified code views
317-
const container = createObservableStateContainer<InternalHoverifierState<C, A>>({
327+
const container = createObservableStateContainer<InternalHoverifierState<C, D, A>>({
318328
hoverOverlayIsFixed: false,
319329
hoveredToken: undefined,
320330
hoverOrError: undefined,
@@ -549,7 +559,7 @@ export function createHoverifier<C extends object, A>({
549559
target: HTMLElement
550560
adjustPosition?: PositionAdjuster<C>
551561
codeView: HTMLElement
552-
hoverOrError?: typeof LOADING | HoverAttachment | ErrorLike | null
562+
hoverOrError?: typeof LOADING | (HoverAttachment & D) | ErrorLike | null
553563
position?: HoveredToken & C
554564
part?: DiffPart
555565
}>
@@ -780,7 +790,7 @@ export function createHoverifier<C extends object, A>({
780790
)
781791

782792
return {
783-
get hoverState(): Readonly<HoverState<C, A>> {
793+
get hoverState(): Readonly<HoverState<C, D, A>> {
784794
return internalToExternalState(container.values)
785795
},
786796
hoverStateUpdates: container.updates.pipe(

src/testutils/lsp.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export const createHoverAttachment = (hover: Partial<HoverAttachment> = {}): Hov
2222
* @param hover optional values to be passed to createHoverAttachment
2323
* @param delayTime optionally delay the hover fetch
2424
*/
25-
export function createStubHoverFetcher(hover: Partial<HoverAttachment> = {}, delayTime?: number): HoverFetcher<{}> {
25+
export function createStubHoverFetcher(hover: Partial<HoverAttachment> = {}, delayTime?: number): HoverFetcher<{}, {}> {
2626
return () => of(createHoverAttachment(hover)).pipe(delay(delayTime || 0))
2727
}
2828

0 commit comments

Comments
 (0)