@weasel-js/labkit 1.7.3 → 1.8.1
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/README.md +24 -0
- package/dist/_dts/{CanvasStackContext-Cjy6I-Iv.d.ts → CanvasStackContext-B05ZM04l.d.ts} +3 -1
- package/dist/_dts/{Stage-D6t7o0pF.d.ts → Stage-CIJlbjrE.d.ts} +5 -3
- package/dist/_dts/{index-DCMA4Fy1.d.ts → index-8geQNQet.d.ts} +84 -12
- package/dist/_dts/{index-BTmaRy6a.d.ts → index-DRi3V5sN.d.ts} +6 -4
- package/dist/_dts/{types-BUBSIZuV.d.ts → types-DUfQ0isU.d.ts} +5 -1
- package/dist/_dts/{useTrialState-Bh8bzFgE.d.ts → useTrialState-Ar0AFpCe.d.ts} +94 -87
- package/dist/canvas/index.d.ts +5 -5
- package/dist/canvas/index.js +2 -2
- package/dist/chrome/index.d.ts +3 -3
- package/dist/chrome/index.js +7 -7
- package/dist/{chunk-HBGH344L.js → chunk-4RCVWX4M.js} +55 -18
- package/dist/chunk-4RCVWX4M.js.map +1 -0
- package/dist/{chunk-WIG6XHJ7.js → chunk-6IIKLXBZ.js} +13 -4
- package/dist/chunk-6IIKLXBZ.js.map +1 -0
- package/dist/{chunk-F4ACIK7Z.js → chunk-6Q6ACJ3H.js} +3 -3
- package/dist/{chunk-F4ACIK7Z.js.map → chunk-6Q6ACJ3H.js.map} +1 -1
- package/dist/{chunk-PMLZP7RT.js → chunk-7ALJOVGP.js} +5 -9
- package/dist/chunk-7ALJOVGP.js.map +1 -0
- package/dist/{chunk-XE5HJOPL.js → chunk-7FMZT2WL.js} +3 -3
- package/dist/{chunk-XE5HJOPL.js.map → chunk-7FMZT2WL.js.map} +1 -1
- package/dist/{chunk-XVUKDSYV.js → chunk-AVOILDN2.js} +2 -2
- package/dist/{chunk-XVUKDSYV.js.map → chunk-AVOILDN2.js.map} +1 -1
- package/dist/{chunk-2N7UBSS2.js → chunk-J5MDJEOZ.js} +3 -3
- package/dist/{chunk-2N7UBSS2.js.map → chunk-J5MDJEOZ.js.map} +1 -1
- package/dist/{chunk-LPGBC63T.js → chunk-KHB3KRRQ.js} +7 -7
- package/dist/chunk-KHB3KRRQ.js.map +1 -0
- package/dist/{chunk-IBVRMHW5.js → chunk-NHIO2O3O.js} +31 -30
- package/dist/chunk-NHIO2O3O.js.map +1 -0
- package/dist/{chunk-EESJUSYR.js → chunk-QMAY74TP.js} +56 -5
- package/dist/chunk-QMAY74TP.js.map +1 -0
- package/dist/{chunk-4X44DMRS.js → chunk-RN7T7OBO.js} +46 -24
- package/dist/chunk-RN7T7OBO.js.map +1 -0
- package/dist/{chunk-4TJ34FXA.js → chunk-RXO5UEGT.js} +5 -12
- package/dist/chunk-RXO5UEGT.js.map +1 -0
- package/dist/{chunk-E6NARGU7.js → chunk-SQX35X5V.js} +3 -3
- package/dist/{chunk-E6NARGU7.js.map → chunk-SQX35X5V.js.map} +1 -1
- package/dist/{chunk-YCFKW5RX.js → chunk-T5OEUNCB.js} +16 -2
- package/dist/chunk-T5OEUNCB.js.map +1 -0
- package/dist/{chunk-RL2LOLNI.js → chunk-UX66USTN.js} +2 -2
- package/dist/chunk-UX66USTN.js.map +1 -0
- package/dist/{chunk-IRMPZIYU.js → chunk-V4SMGEYY.js} +6732 -6507
- package/dist/chunk-V4SMGEYY.js.map +1 -0
- package/dist/{chunk-E23Q6A3Y.js → chunk-VCK5T6EP.js} +3 -3
- package/dist/{chunk-E23Q6A3Y.js.map → chunk-VCK5T6EP.js.map} +1 -1
- package/dist/{chunk-T6DLGEJB.js → chunk-XWR2P6MR.js} +23 -6
- package/dist/chunk-XWR2P6MR.js.map +1 -0
- package/dist/{chunk-XSJJTXNA.js → chunk-YZA73U4A.js} +19 -19
- package/dist/chunk-YZA73U4A.js.map +1 -0
- package/dist/config/index.d.ts +12 -5
- package/dist/config/index.js +2 -2
- package/dist/controls/index.d.ts +2 -2
- package/dist/controls/index.js +5 -5
- package/dist/dragdrop/index.d.ts +2 -2
- package/dist/dragdrop/index.js +2 -2
- package/dist/index.d.ts +158 -16
- package/dist/index.js +1036 -415
- package/dist/index.js.map +1 -1
- package/dist/layers/index.d.ts +2 -2
- package/dist/layers/index.js +3 -3
- package/dist/loupe/index.d.ts +176 -28
- package/dist/loupe/index.js +547 -141
- package/dist/loupe/index.js.map +1 -1
- package/dist/overview/index.d.ts +1 -1
- package/dist/overview/index.js +5 -5
- package/dist/passthrough/weasel-ui.js +3 -3
- package/dist/primitives/index.d.ts +4 -19
- package/dist/primitives/index.js +7 -7
- package/dist/state/index.d.ts +6 -8
- package/dist/state/index.js +5 -5
- package/dist/styles.css +181 -44
- package/dist/surface/index.d.ts +20 -1
- package/dist/surface/index.js +2 -2
- package/dist/undo/index.d.ts +19 -8
- package/dist/undo/index.js +1 -1
- package/package.json +11 -11
- package/src/annotations/AnnotationOverlay.abandon.test.tsx +4 -0
- package/src/annotations/AnnotationOverlay.tsx +10 -1
- package/src/annotations/Annotations.less +2 -0
- package/src/annotations/Annotations.overlay.test.tsx +29 -0
- package/src/canvas/CanvasStack.test.tsx +35 -0
- package/src/canvas/CanvasStack.tsx +12 -1
- package/src/canvas/useLayerScheduler.ts +14 -0
- package/src/chrome/builtins.test.ts +14 -0
- package/src/chrome/builtins.tsx +17 -2
- package/src/chrome/regions/StatusRegion.tsx +20 -14
- package/src/chrome/regions/regions.test.tsx +28 -0
- package/src/chrome/types.ts +8 -0
- package/src/clock/clock.browser.test.less +5 -0
- package/src/clock/clock.browser.test.tsx +77 -0
- package/src/clock/clock.integration.test.tsx +95 -0
- package/src/clock/clockRegistry.test.ts +33 -0
- package/src/clock/clockRegistry.ts +46 -0
- package/src/clock/hooks.ts +44 -0
- package/src/clock/index.ts +2 -0
- package/src/clock/trialClock.test.ts +265 -0
- package/src/clock/trialClock.ts +245 -0
- package/src/clock/useClockLoop.ts +45 -0
- package/src/config/builder.ts +11 -1
- package/src/config/resolve.test.ts +12 -0
- package/src/config/resolve.ts +5 -0
- package/src/config/types.ts +5 -1
- package/src/controls/ControlMatrix.test.tsx +10 -0
- package/src/controls/ControlMatrix.tsx +110 -58
- package/src/controls/ControlPanel.test.tsx +30 -0
- package/src/index.ts +3 -2
- package/src/instrument/types.ts +21 -1
- package/src/lab/Lab.chrome.test.tsx +8 -1
- package/src/lab/Lab.presentation.test.tsx +113 -0
- package/src/lab/Lab.surface.test.tsx +20 -9
- package/src/lab/Lab.tsx +132 -257
- package/src/lab/LabLightbox.browser.test.less +17 -0
- package/src/lab/LabLightbox.browser.test.tsx +216 -0
- package/src/lab/LabShell.less +10 -3
- package/src/lab/LabSurface.tsx +129 -0
- package/src/lab/Workspace.stories.tsx +18 -0
- package/src/lab/Workspace.surface.test.tsx +4 -0
- package/src/lab/Workspace.test.tsx +37 -1
- package/src/lab/Workspace.tsx +34 -6
- package/src/lab/index.ts +2 -0
- package/src/lab/nebula.ts +30 -0
- package/src/lab/openLab.test.ts +81 -0
- package/src/lab/openLab.ts +119 -0
- package/src/lab/presentation.browser.test.less +5 -0
- package/src/lab/presentation.browser.test.tsx +119 -0
- package/src/lab/presentation.less +64 -0
- package/src/lab/presentation.tsx +47 -0
- package/src/lab/useFocusPick.ts +43 -0
- package/src/lightbox/Lightbox.browser.test.less +14 -0
- package/src/lightbox/Lightbox.browser.test.tsx +113 -0
- package/src/lightbox/Lightbox.less +128 -0
- package/src/lightbox/Lightbox.test.tsx +195 -0
- package/src/lightbox/Lightbox.tsx +205 -0
- package/src/lightbox/LightboxContext.ts +38 -0
- package/src/lightbox/LightboxLayers.tsx +40 -0
- package/src/lightbox/index.ts +8 -0
- package/src/lightbox/modal.ts +94 -0
- package/src/loupe/AGENTS.md +40 -2
- package/src/loupe/CanvasLoupe.tsx +5 -4
- package/src/loupe/DomLoupe.tsx +7 -5
- package/src/loupe/Loupe.less +15 -0
- package/src/loupe/LoupeBubble.tsx +35 -13
- package/src/loupe/SourceLoupe.browser.test.tsx +204 -0
- package/src/loupe/SourceLoupe.tsx +60 -0
- package/src/loupe/TrialLoupe.browser.test.tsx +342 -0
- package/src/loupe/TrialLoupe.tsx +126 -28
- package/src/loupe/canvasLens.ts +20 -17
- package/src/loupe/index.ts +26 -2
- package/src/loupe/sourceLens.test.ts +65 -0
- package/src/loupe/sourceLens.ts +97 -0
- package/src/loupe/types.test.ts +6 -0
- package/src/loupe/types.ts +109 -7
- package/src/loupe/useLoupe.test.tsx +188 -3
- package/src/loupe/useLoupe.ts +106 -11
- package/src/primitives/index.ts +2 -2
- package/src/specimen/Specimen.tsx +0 -7
- package/src/state/helpers.test.ts +4 -11
- package/src/state/helpers.ts +2 -8
- package/src/state/index.ts +0 -2
- package/src/state/openLabStore.test.ts +10 -8
- package/src/state/openLabStore.ts +1 -1
- package/src/state/store.test.ts +2 -2
- package/src/state/store.ts +15 -16
- package/src/state/types.ts +9 -9
- package/src/styles.less +2 -1
- package/src/surface/AGENTS.md +13 -0
- package/src/surface/index.ts +1 -0
- package/src/surface/useSurfaceTile.test.tsx +4 -0
- package/src/surface/useSurfaceTile.ts +17 -1
- package/src/surface/useTiledSurface.test.tsx +65 -0
- package/src/surface/useTiledSurface.ts +68 -1
- package/src/theme/interstellar.theme.json +0 -14
- package/src/trial/Trial.stories.tsx +0 -1
- package/src/trial/Trial.test.tsx +40 -3
- package/src/trial/Trial.tsx +87 -43
- package/src/trial/TrialChrome.tsx +20 -2
- package/src/trial/trialOps.test.ts +1 -2
- package/src/trial/trialOps.ts +2 -3
- package/src/undo/stateOp.ts +21 -0
- package/src/undo/undoStack.ts +18 -6
- package/dist/chunk-4TJ34FXA.js.map +0 -1
- package/dist/chunk-4X44DMRS.js.map +0 -1
- package/dist/chunk-EESJUSYR.js.map +0 -1
- package/dist/chunk-HBGH344L.js.map +0 -1
- package/dist/chunk-IBVRMHW5.js.map +0 -1
- package/dist/chunk-IRMPZIYU.js.map +0 -1
- package/dist/chunk-LPGBC63T.js.map +0 -1
- package/dist/chunk-PMLZP7RT.js.map +0 -1
- package/dist/chunk-RL2LOLNI.js.map +0 -1
- package/dist/chunk-T6DLGEJB.js.map +0 -1
- package/dist/chunk-WIG6XHJ7.js.map +0 -1
- package/dist/chunk-XSJJTXNA.js.map +0 -1
- package/dist/chunk-YCFKW5RX.js.map +0 -1
- package/src/primitives/StatusBar.less +0 -34
- package/src/primitives/StatusBar.stories.tsx +0 -24
- package/src/primitives/StatusBar.test.tsx +0 -25
- package/src/primitives/StatusBar.tsx +0 -28
package/src/instrument/types.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { ReactNode } from 'react';
|
|
|
3
3
|
import type { AnnotationsCapability } from '../annotations/types';
|
|
4
4
|
import type { ViewportSize, WorldSpec } from '../canvas/worldSpec';
|
|
5
5
|
import type { TrialContribution } from '../chrome/types';
|
|
6
|
+
import type { ClockCapability, TrialClock } from '../clock/trialClock';
|
|
6
7
|
import type { ConfigPath, ConfigSchema } from '../config/types';
|
|
7
8
|
import type { ConfigField } from '../controls/types';
|
|
8
9
|
import type { JobCapability, JobHandle } from '../job/types';
|
|
@@ -41,6 +42,8 @@ export interface RenderContext<TS = unknown, TC = unknown> {
|
|
|
41
42
|
* reading it works over either view.
|
|
42
43
|
*/
|
|
43
44
|
pointer: PointerContextValue;
|
|
45
|
+
/** The trial's clock, present when the instrument declares `clock`. */
|
|
46
|
+
clock?: TrialClock;
|
|
44
47
|
};
|
|
45
48
|
emit: (event: string) => void;
|
|
46
49
|
/** Present only when the instrument declares a `job`. */
|
|
@@ -54,7 +57,21 @@ export interface RenderContext<TS = unknown, TC = unknown> {
|
|
|
54
57
|
* set `ctx.lineWidth = 1 / zoom` to keep a hairline hairline. */
|
|
55
58
|
export interface CanvasLayer<TS = unknown, TC = unknown> {
|
|
56
59
|
id: string;
|
|
57
|
-
|
|
60
|
+
/** Redraw every frame the trial's clock moves, not only when state, config
|
|
61
|
+
* or the view change. For a layer that reads `elapsed`, `pass` or `phase`. */
|
|
62
|
+
timed?: boolean;
|
|
63
|
+
draw: (
|
|
64
|
+
ctx: CanvasRenderingContext2D,
|
|
65
|
+
args: {
|
|
66
|
+
state: TS;
|
|
67
|
+
config: TC;
|
|
68
|
+
zoom: number;
|
|
69
|
+
/** The trial clock's, as `TrialClock` has them; 0 with no clock. */
|
|
70
|
+
elapsed: number;
|
|
71
|
+
pass: number;
|
|
72
|
+
phase: number;
|
|
73
|
+
},
|
|
74
|
+
) => void;
|
|
58
75
|
}
|
|
59
76
|
|
|
60
77
|
/** Declares that an instrument draws to a canvas: its layers, and where the
|
|
@@ -183,6 +200,9 @@ export interface Instrument<TS = unknown, TC = unknown, TItem = unknown> {
|
|
|
183
200
|
annotations?: AnnotationsCapability<TS, TC>;
|
|
184
201
|
/** Chrome this instrument contributes beyond what its capabilities imply. */
|
|
185
202
|
chrome?: TrialContribution[];
|
|
203
|
+
/** Gives the trial a clock: playback time that layers, renderers and chrome
|
|
204
|
+
* read and drive. */
|
|
205
|
+
clock?: ClockCapability;
|
|
186
206
|
/** Work too slow to do during a render. The runtime starts it, aborts it on
|
|
187
207
|
* unmount and on a `key` change, and renders progress into the trial. */
|
|
188
208
|
job?: JobCapability<TS, TC, TItem>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { render, screen } from '@testing-library/react';
|
|
1
|
+
import { fireEvent, render, screen } from '@testing-library/react';
|
|
2
2
|
import { renderSettled } from '@weasel-js/react/testing/renderSettled';
|
|
3
3
|
import { describe, expect, it, vi } from 'vitest';
|
|
4
4
|
import type { TrialContribution } from '../chrome/types';
|
|
@@ -32,6 +32,13 @@ describe('<Lab> chrome', () => {
|
|
|
32
32
|
expect(screen.getByRole('button', { name: 'Export' })).toBeInTheDocument();
|
|
33
33
|
});
|
|
34
34
|
|
|
35
|
+
it('puts the expand toggle in the trial title bar instead of the tile corner', async () => {
|
|
36
|
+
await renderSettled(<Lab title="T" instruments={[bare]} defaultInstrument="Bare" />);
|
|
37
|
+
expect(screen.queryByRole('button', { name: 'Expand' })).toBeNull();
|
|
38
|
+
fireEvent.click(screen.getByRole('button', { name: 'Expand trial' }));
|
|
39
|
+
expect(document.querySelector('.lk-lightbox--expanded .lk-trial')).not.toBeNull();
|
|
40
|
+
});
|
|
41
|
+
|
|
35
42
|
it('suppresses a built-in by id', async () => {
|
|
36
43
|
await renderSettled(
|
|
37
44
|
<Lab title="T" instruments={[bare]} defaultInstrument="Bare" suppress={['snapshot']} />,
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { act, fireEvent, render, waitFor } from '@testing-library/react';
|
|
2
|
+
import { renderSettled } from '@weasel-js/react/testing/renderSettled';
|
|
3
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
+
import type { Instrument } from '../instrument/types';
|
|
5
|
+
import { createMemoryAdapter } from '../state/adapters';
|
|
6
|
+
import { labPrefix } from '../state/labRecords';
|
|
7
|
+
import { Lab, type LabProps } from './Lab';
|
|
8
|
+
import { LabContext, type LabContextValue } from './LabContext';
|
|
9
|
+
import { type Presentation, usePresentation } from './presentation';
|
|
10
|
+
|
|
11
|
+
const stub: Instrument = {
|
|
12
|
+
name: 'Stub',
|
|
13
|
+
defaultConfig: () => ({ count: 0 }),
|
|
14
|
+
initialState: (config) => ({ value: (config as { count: number }).count }),
|
|
15
|
+
render: ({ state }) => <div data-testid="stub-content">{(state as { value: number }).value}</div>,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
let presentation: Presentation | null = null;
|
|
19
|
+
let lab: LabContextValue | null = null;
|
|
20
|
+
function Capture() {
|
|
21
|
+
presentation = usePresentation();
|
|
22
|
+
return (
|
|
23
|
+
<LabContext.Consumer>
|
|
24
|
+
{(value) => {
|
|
25
|
+
lab = value;
|
|
26
|
+
return null;
|
|
27
|
+
}}
|
|
28
|
+
</LabContext.Consumer>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function mountLab(props: Partial<LabProps> = {}) {
|
|
33
|
+
return renderSettled(
|
|
34
|
+
<Lab {...({ instruments: [stub], defaultInstrument: 'Stub', ...props } as LabProps)}>
|
|
35
|
+
<Capture />
|
|
36
|
+
</Lab>,
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const root = (container: HTMLElement) => container.querySelector('.lk-lab') as HTMLElement;
|
|
41
|
+
const presented = (container: HTMLElement) =>
|
|
42
|
+
container.querySelectorAll<HTMLElement>('.lk-trial[data-lk-presented]');
|
|
43
|
+
|
|
44
|
+
afterEach(() => {
|
|
45
|
+
presentation = null;
|
|
46
|
+
lab = null;
|
|
47
|
+
history.replaceState(null, '', '/');
|
|
48
|
+
vi.restoreAllMocks();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('<Lab present>', () => {
|
|
52
|
+
it('presents one trial opened on the seed', async () => {
|
|
53
|
+
const { container, getByTestId } = await mountLab({
|
|
54
|
+
present: true,
|
|
55
|
+
seed: { config: { count: 4 } },
|
|
56
|
+
});
|
|
57
|
+
expect(root(container).classList.contains('lk-lab--present')).toBe(true);
|
|
58
|
+
expect(presented(container)).toHaveLength(1);
|
|
59
|
+
expect(presented(container)[0]?.querySelector('.lk-trial__stage')).not.toBeNull();
|
|
60
|
+
expect(getByTestId('stub-content').textContent).toBe('4');
|
|
61
|
+
expect(presentation?.active).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('presents from `?present` in the URL', async () => {
|
|
65
|
+
history.replaceState(null, '', '/?present');
|
|
66
|
+
const { container } = await mountLab();
|
|
67
|
+
expect(presented(container)).toHaveLength(1);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('keeps a stored lab presenting under its own key', async () => {
|
|
71
|
+
const storage = createMemoryAdapter();
|
|
72
|
+
const { unmount } = await mountLab({ present: true, storageKey: 'lab', storage });
|
|
73
|
+
await waitFor(async () =>
|
|
74
|
+
expect((await storage.list(labPrefix('lab:present'))).length).toBeGreaterThan(0),
|
|
75
|
+
);
|
|
76
|
+
unmount();
|
|
77
|
+
expect(await storage.list(labPrefix('lab'))).toEqual([]);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('does not leave on Escape, having nowhere to go back to', async () => {
|
|
81
|
+
const { container } = await mountLab({ present: true });
|
|
82
|
+
fireEvent.keyDown(document, { key: 'Escape' });
|
|
83
|
+
expect(presented(container)).toHaveLength(1);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('warns that a seed is ignored when the lab does not start presenting', async () => {
|
|
87
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
88
|
+
await mountLab({ seed: { config: { count: 4 } } });
|
|
89
|
+
expect(warn).toHaveBeenCalledWith(expect.stringContaining('`seed`'));
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe('usePresentation', () => {
|
|
94
|
+
it('presents the focused trial and returns on Escape', async () => {
|
|
95
|
+
const { container } = await mountLab();
|
|
96
|
+
await act(async () => lab?.addTrial('Stub', { config: { count: 2 } }));
|
|
97
|
+
await act(async () => lab?.focusTrial(lab.trials[1]?.id as string));
|
|
98
|
+
expect(presented(container)).toHaveLength(0);
|
|
99
|
+
|
|
100
|
+
await act(async () => presentation?.enter());
|
|
101
|
+
expect(presented(container)).toHaveLength(1);
|
|
102
|
+
expect(presented(container)[0]?.dataset.trialId).toBe(lab?.trials[1]?.id);
|
|
103
|
+
|
|
104
|
+
await act(async () => fireEvent.keyDown(document, { key: 'Escape' }));
|
|
105
|
+
expect(presented(container)).toHaveLength(0);
|
|
106
|
+
expect(root(container).classList.contains('lk-lab--present')).toBe(false);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('throws outside <Lab>', () => {
|
|
110
|
+
vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
111
|
+
expect(() => render(<Capture />)).toThrow(/inside <Lab>/);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
* `SurfaceContext` in production — so `useSurfaceOptional()` always answered
|
|
4
4
|
* null and a tile registered from inside a trial reached nothing.
|
|
5
5
|
*
|
|
6
|
-
* The surface
|
|
7
|
-
*
|
|
8
|
-
* selector, so an extra element between them silently drops that
|
|
6
|
+
* The surface's layers sit in `.lk-lab__body` beside the workspace rather than
|
|
7
|
+
* wrapped around it: `.lk-lab__body > .lk-workspace { min-width: 0 }` is a
|
|
8
|
+
* direct-child selector, so an extra element between them silently drops that
|
|
9
|
+
* rule.
|
|
9
10
|
*/
|
|
10
11
|
import { act } from '@testing-library/react';
|
|
11
12
|
import { renderSettled } from '@weasel-js/react/testing/renderSettled';
|
|
@@ -84,19 +85,29 @@ describe('<Lab> surface provider', () => {
|
|
|
84
85
|
expect(typeof (seen as unknown as SurfaceHandle).registerTile).toBe('function');
|
|
85
86
|
});
|
|
86
87
|
|
|
87
|
-
it('stacks an under and an over buffer
|
|
88
|
+
it('stacks an under and an over buffer, each in its own layer of the lab body', async () => {
|
|
88
89
|
const { container } = await renderSettled(
|
|
89
90
|
<Lab instruments={[probeInstrument]} defaultInstrument="Probe" />,
|
|
90
91
|
);
|
|
91
|
-
const
|
|
92
|
-
expect(canvases).toHaveLength(2);
|
|
92
|
+
const layers = container.querySelectorAll('.lk-lab__body > .lk-lab__layer');
|
|
93
93
|
// Source order, because the z-index that separates them is in a stylesheet
|
|
94
94
|
// jsdom does not resolve — this is a proxy for the stacking, not proof of
|
|
95
95
|
// it. The real check is a browser.
|
|
96
|
-
expect([...
|
|
97
|
-
'lk-
|
|
98
|
-
'lk-
|
|
96
|
+
expect([...layers].map((l) => l.className)).toEqual([
|
|
97
|
+
'lk-lab__layer lk-lab__layer--under',
|
|
98
|
+
'lk-lab__layer lk-lab__layer--over',
|
|
99
99
|
]);
|
|
100
|
+
for (const layer of layers)
|
|
101
|
+
expect(layer.querySelector('canvas.lk-lab__surface')).not.toBeNull();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('measures tile rects against the over layer', async () => {
|
|
105
|
+
const { container } = await renderSettled(
|
|
106
|
+
<Lab instruments={[probeInstrument]} defaultInstrument="Probe" />,
|
|
107
|
+
);
|
|
108
|
+
expect((seen as SurfaceHandle).getContainer()).toBe(
|
|
109
|
+
container.querySelector('.lk-lab__layer--over'),
|
|
110
|
+
);
|
|
100
111
|
});
|
|
101
112
|
|
|
102
113
|
it('hands a tenant whichever of the two it asks for', async () => {
|