Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions modules/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@
"react-dom": ">=16.3.0"
},
"devDependencies": {
"@types/react": "^18.2.0",
"@types/react": "^19.2.0",
"@types/react-dom": "^19.2.3",
"react": "^18.2.0",
"react": "^19.2.0",
"react-dom": "^19.2.5"
},
"gitHead": "13ace64fc2cee08c133afc882fc307253489a4e4"
Expand Down
2 changes: 1 addition & 1 deletion modules/react/src/utils/evaluate-children.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export function isComponent(child: React.ReactNode): child is React.ReactElement
}

function isReactMap(child: React.ReactElement): boolean {
return child.props?.mapStyle;
return (child.props as any)?.mapStyle;
}

function needsDeckGLViewProps(child: React.ReactElement): boolean {
Expand Down
8 changes: 6 additions & 2 deletions modules/react/src/utils/extract-jsx-layers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ function wrapInView(node: React.ReactNode | DeckGLRenderCallback): React.ReactNo
}
if (isComponent(node)) {
if (node.type === React.Fragment) {
return wrapInView(node.props.children);
return wrapInView((node.props as any).children);
}
if (inheritsFrom(node.type, View)) {
return node;
Expand Down Expand Up @@ -92,7 +92,11 @@ export default function extractJSXLayers<ViewsT extends ViewOrViews>({
}

// empty id => default view
if (inheritsFrom(ElementType, View) && ElementType !== View && reactElement.props.id) {
if (
inheritsFrom(ElementType, View) &&
ElementType !== View &&
(reactElement.props as any).id
) {
// @ts-ignore Cannot instantiate an abstract class (View)
const view = new ElementType(reactElement.props);
jsxViews[view.id] = view;
Expand Down
4 changes: 2 additions & 2 deletions modules/react/src/utils/position-children-under-views.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ export default function positionChildrenUnderViews<ViewsT extends ViewOrViews>({
let viewChildren = child;

if (isComponent(child) && inheritsFrom(child.type, View)) {
viewId = child.props.id || defaultViewId;
viewChildren = child.props.children;
viewId = (child.props as any).id || defaultViewId;
viewChildren = (child.props as any).children;
}

const viewport = viewManager.getViewport(viewId) as Viewport;
Expand Down
2 changes: 1 addition & 1 deletion modules/widgets/src/stats-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import {Widget, type WidgetPlacement, type WidgetProps} from '@deck.gl/core';
import {luma} from '@luma.gl/core';
import {render} from 'preact';
import {render, type JSX} from 'preact';
import {useEffect, useState} from 'preact/hooks';
import type {Stats, Stat} from '@probe.gl/stats';
import {IconButton} from './lib/components/icon-button';
Expand Down
10 changes: 1 addition & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,7 @@
"vite-bundle-analyzer": "^1.3.6"
},
"resolutions": {
"vite": "^7.3.1",
"@luma.gl/core": "^9.3.2",
"@luma.gl/effects": "^9.3.2",
"@luma.gl/engine": "^9.3.2",
"@luma.gl/gltf": "^9.3.2",
"@luma.gl/shadertools": "^9.3.2",
"@luma.gl/test-utils": "^9.3.2",
"@luma.gl/webgl": "^9.3.2",
"@luma.gl/webgpu": "^9.3.2"
"vite": "^7.3.1"
},
"pre-commit": [
"test-fast",
Expand Down
166 changes: 75 additions & 91 deletions test/modules/react/deckgl.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@

/* eslint-disable no-unused-vars */
import {test, expect} from 'vitest';
import {createElement, createRef} from 'react';
import {createElement, createRef, act} from 'react';
import {createRoot} from 'react-dom/client';
import {act} from 'react-dom/test-utils';

import {DeckGL, Layer, Widget} from 'deck.gl';
import {Layer, Widget} from '@deck.gl/core';
import DeckGL, {type DeckGLRef} from '@deck.gl/react';
import {type WidgetProps, type WidgetPlacement} from '@deck.gl/core';

import {device, gl} from '@deck.gl/test-utils/vitest';

// Required by React 19
// @ts-expect-error undefined global flag
self.IS_REACT_ACT_ENVIRONMENT = true;

const TEST_VIEW_STATE = {
latitude: 37.7515,
longitude: -122.4269,
Expand All @@ -26,12 +30,12 @@ const TEST_VIEW_STATE = {
const getDeckProps = () => (globalThis.__JSDOM__ ? {gl} : {device});

test('DeckGL#mount/unmount', async () => {
const ref = createRef();
const ref = createRef<DeckGLRef>();
const container = document.createElement('div');
document.body.append(container);
const root = createRoot(container);

const loadPromise = new Promise<void>((resolve, reject) => {
const loadPromise = new Promise<void>(resolve => {
act(() => {
root.render(
createElement(DeckGL, {
Expand All @@ -40,69 +44,61 @@ test('DeckGL#mount/unmount', async () => {
width: 100,
height: 100,
...getDeckProps(),
onLoad: () => {
try {
const {deck} = ref.current;
expect(deck, 'DeckGL is initialized').toBeTruthy();
const viewport = deck.getViewports()[0];
expect(viewport && viewport.longitude, 'View state is set').toBe(
TEST_VIEW_STATE.longitude
);

act(() => {
root.render(null);
});

expect(deck.animationLoop, 'Deck is finalized').toBeFalsy();

container.remove();
resolve();
} catch (error) {
reject(error);
}
}
onLoad: () => resolve()
})
);
});
});
await loadPromise;

expect(ref.current, 'DeckGL overlay is rendered.').toBeTruthy();
await loadPromise;

const {deck} = ref.current!;
expect(deck).toBeTruthy();
expect(deck, 'DeckGL is initialized').toBeTruthy();
const viewport = deck!.getViewports()[0];
expect(viewport && viewport.longitude, 'View state is set').toBe(TEST_VIEW_STATE.longitude);

act(() => {
root.render(null);
});

expect(deck!.animationLoop, 'Deck is finalized').toBeFalsy();

container.remove();
});

test('DeckGL#render', async () => {
const container = document.createElement('div');
document.body.append(container);
const root = createRoot(container);

await new Promise<void>((resolve, reject) => {
const renderPromise = new Promise<void>(resolve => {
act(() => {
root.render(
createElement(
DeckGL,
{
viewState: TEST_VIEW_STATE,
initialViewState: TEST_VIEW_STATE,
width: 100,
height: 100,
...getDeckProps(),
onAfterRender: () => {
try {
const child = container.querySelector('.child');
expect(child, 'Child is rendered').toBeTruthy();

root.render(null);
container.remove();
resolve();
} catch (error) {
reject(error);
}
}
onAfterRender: () => resolve()
},
[createElement('div', {key: 0, className: 'child'}, 'Child')]
createElement('div', {className: 'child'}, 'Child')
)
);
});
});
await renderPromise;

const child = container.querySelector('.child');
expect(child, 'Child is rendered').toBeTruthy();

act(() => {
root.render(null);
});
container.remove();
});

class TestLayer extends Layer {
Expand All @@ -117,22 +113,18 @@ class TestWidget extends Widget<WidgetProps> {
placement: WidgetPlacement = 'top-left';
className = 'deck-test-widget';

constructor(props: WidgetProps = {}) {
super(props, Widget.defaultProps);
}

onRenderHTML(rootElement: HTMLElement): void {}
}

const WIDGETS = [new TestWidget({id: 'A'})];

test('DeckGL#props omitted are reset', async () => {
const ref = createRef();
const ref = createRef<DeckGLRef>();
const container = document.createElement('div');
document.body.append(container);
const root = createRoot(container);

const renderPromise = new Promise<void>((resolve, reject) => {
let renderPromise = new Promise<void>(resolve => {
// Initialize widgets and layers on first render.
act(() => {
root.render(
Expand All @@ -144,54 +136,46 @@ test('DeckGL#props omitted are reset', async () => {
...getDeckProps(),
layers: LAYERS,
widgets: WIDGETS,
onLoad: () => {
try {
const {deck} = ref.current;
expect(deck, 'DeckGL is initialized').toBeTruthy();
const {widgets, layers} = deck.props;
expect(widgets && Array.isArray(widgets) && widgets.length, 'Widgets is set').toBe(1);
expect(layers && Array.isArray(layers) && layers.length, 'Layers is set').toBe(1);

act(() => {
// Render deck a second time without setting widget or layer props.
root.render(
createElement(DeckGL, {
ref,
...getDeckProps(),
onAfterRender: () => {
try {
const {deck} = ref.current;
const {widgets, layers} = deck.props;
expect(
widgets && Array.isArray(widgets) && widgets.length,
'Widgets is reset to an empty array'
).toBe(0);
expect(
layers && Array.isArray(layers) && layers.length,
'Layers is reset to an empty array'
).toBe(0);

root.render(null);
container.remove();
resolve();
} catch (error) {
reject(error);
}
}
})
);
});
} catch (error) {
reject(error);
}
}
onLoad: () => resolve()
})
);
});
});
await renderPromise;

act(() => {
expect(ref.current, 'DeckGL overlay is rendered.').toBeTruthy();
const {deck} = ref.current!;
expect(deck, 'DeckGL is initialized').toBeTruthy();
let {widgets, layers} = deck!.props;
expect(widgets && Array.isArray(widgets) && widgets.length, 'Widgets is set').toBe(1);
expect(layers && Array.isArray(layers) && layers.length, 'Layers is set').toBe(1);

renderPromise = new Promise<void>(resolve => {
act(() => {
// Render deck a second time without setting widget or layer props.
root.render(
createElement(DeckGL, {
ref,
...getDeckProps(),
onAfterRender: () => resolve()
})
);
});
});
await renderPromise;

widgets = deck!.props.widgets;
layers = deck!.props.layers;
expect(
widgets && Array.isArray(widgets) && widgets.length,
'Widgets is reset to an empty array'
).toBe(0);
expect(
layers && Array.isArray(layers) && layers.length,
'Layers is reset to an empty array'
).toBe(0);

act(() => {
root.render(null);
});
container.remove();
});
Loading
Loading