github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/web/src/raf.tsx (about)

     1  import React, { useContext } from "react"
     2  
     3  // requestAnimationFrame (RAF) is a general browser primitive
     4  // for scheduling things that are only necessary for rendering.
     5  //
     6  // The advantage of using a RAF callback is:
     7  // - They're paused on background tabs when the browser isn't rendering.
     8  // - They allow you to "yield" the CPU so that if you have some long-running
     9  //   rendering task, it doesn't make animation jittery.
    10  //
    11  // RafContext is a way for us to use RAF callbcks in tests.
    12  
    13  export type RafContext = {
    14    requestAnimationFrame(callback: () => void): number
    15    cancelAnimationFrame(id: number): void
    16  }
    17  
    18  const rafContext = React.createContext<RafContext>({
    19    // By default, use the normal window schedulers.
    20    requestAnimationFrame: (callback: () => void) =>
    21      window.requestAnimationFrame(callback),
    22    cancelAnimationFrame: (id: number) => window.cancelAnimationFrame(id),
    23  })
    24  
    25  export function useRaf(): RafContext {
    26    return useContext(rafContext)
    27  }
    28  
    29  // Inject a RAF provider that runs all callbacks synchronously.
    30  export function SyncRafProvider(props: React.PropsWithChildren<{}>) {
    31    let context = {
    32      requestAnimationFrame: (callback: () => void) => {
    33        callback()
    34        return 0
    35      },
    36      cancelAnimationFrame: () => {},
    37    }
    38    return (
    39      <rafContext.Provider value={context}>{props.children}</rafContext.Provider>
    40    )
    41  }
    42  
    43  export let RafProvider = rafContext.Provider
    44  
    45  export type TestRafContext = {
    46    callbacks: { [key: string]: () => void }
    47    invoke: jest.Mock<void, [id: number]>
    48    requestAnimationFrame: jest.Mock<number, [callback: () => void]>
    49    cancelAnimationFrame: jest.Mock<void, [id: number]>
    50  }
    51  
    52  // Returns a scheduler that pauses callbacks
    53  // until they're invoked manually by ID.
    54  export function newFakeRaf(): TestRafContext {
    55    let callbacks: any = {}
    56    let callbackCount = 0
    57    return {
    58      callbacks: callbacks,
    59      invoke: jest.fn((id: number) => {
    60        callbacks[id].call()
    61        delete callbacks[id]
    62      }),
    63      requestAnimationFrame: jest.fn((callback: () => void) => {
    64        let id = ++callbackCount
    65        callbacks[id] = callback
    66        return id
    67      }),
    68      cancelAnimationFrame: jest.fn((id: number) => {
    69        delete callbacks[id]
    70      }),
    71    }
    72  }