Skip to content
Open
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
11 changes: 0 additions & 11 deletions web/client/actions/__tests__/featuregrid-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,6 @@ import {
OPEN_ADVANCED_SEARCH,
initPlugin,
INIT_PLUGIN,
sizeChange,
SIZE_CHANGE,
START_SYNC_WMS,
startSyncWMS,
storeAdvancedSearchFilter,
Expand Down Expand Up @@ -310,15 +308,6 @@ describe('Test correctness of featurgrid actions', () => {
expect(retval.type).toBe(UPDATE_FILTER);
expect(retval.update).toBe(update);
});
it('Test sizeChange', () => {
const size = 0.5;
const dockProps = {maxDockSize: 0.7, minDockSize: 0.1};
const retval = sizeChange(size, dockProps);
expect(retval).toExist();
expect(retval.type).toBe(SIZE_CHANGE);
expect(retval.size).toBe(size);
expect(retval.dockProps).toEqual(dockProps);
});
it('Test storeAdvancedSearchFilter', () => {
const filterObj = {name: "A"};
const retval = storeAdvancedSearchFilter(filterObj);
Expand Down
15 changes: 0 additions & 15 deletions web/client/actions/featuregrid.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ export const SET_LAYER = 'FEATUREGRID:SET_LAYER';
export const UPDATE_FILTER = 'QUERY:UPDATE_FILTER';
export const CHANGE_PAGE = 'FEATUREGRID:CHANGE_PAGE';
export const GEOMETRY_CHANGED = 'FEATUREGRID:GEOMETRY_CHANGED';
export const DOCK_SIZE_FEATURES = 'DOCK_SIZE_FEATURES';
export const TOGGLE_TOOL = 'FEATUREGRID:TOGGLE_TOOL';
export const CUSTOMIZE_ATTRIBUTE = 'FEATUREGRID:CUSTOMIZE_ATTRIBUTE';
export const CLOSE_FEATURE_GRID_CONFIRM = 'ASK_CLOSE_FEATURE_GRID_CONFIRM';
Expand All @@ -47,7 +46,6 @@ export const DEACTIVATE_GEOMETRY_FILTER = 'FEATUREGRID:DEACTIVATE_GEOMETRY_FILTE
export const OPEN_ADVANCED_SEARCH = 'FEATUREGRID:ADVANCED_SEARCH';
export const ZOOM_ALL = 'FEATUREGRID:ZOOM_ALL';
export const INIT_PLUGIN = 'FEATUREGRID:INIT_PLUGIN';
export const SIZE_CHANGE = 'FEATUREGRID:SIZE_CHANGE';
export const TOGGLE_SHOW_AGAIN_FLAG = 'FEATUREGRID:TOGGLE_SHOW_AGAIN_FLAG';
export const UPDATE_EDITORS_OPTIONS = 'FEATUREGRID:UPDATE_EDITORS_OPTIONS';
export const LAUNCH_UPDATE_FILTER_FUNC = 'FEATUREGRID:LAUNCH_UPDATE_FILTER_FUNC';
Expand Down Expand Up @@ -194,12 +192,6 @@ export function setFeatures(features) {
};
}

export function dockSizeFeatures(dockSize) {
return {
type: DOCK_SIZE_FEATURES,
dockSize: dockSize
};
}
export function sort(sortBy, sortOrder) {
return {
type: SORT_BY,
Expand Down Expand Up @@ -346,13 +338,6 @@ export function startSyncWMS() {
type: START_SYNC_WMS
};
}
export function sizeChange(size, dockProps) {
return {
type: SIZE_CHANGE,
size,
dockProps
};
}
export const moreFeatures = (pages) => {
return {
type: LOAD_MORE_FEATURES,
Expand Down
20 changes: 4 additions & 16 deletions web/client/components/data/featuregrid/FeatureEditorFallback.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,15 @@
* LICENSE file in the root directory of this source tree.
*/

import React, { useMemo } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { mapLayoutValuesSelector } from '../../../selectors/maplayout';
import React from 'react';
import Loader from '../../misc/Loader';

const FeatureEditorFallback = ({ size, dockStyle }) => {
const containerStyle = useMemo(() => {
return { height: `${size * 100}%`, ...dockStyle };
}, [size, dockStyle]);
const FeatureEditorFallback = () => {
return (
<div className="feature-editor-fallback-container" style={containerStyle}>
<div className="feature-editor-fallback-container">
<Loader size={100} style={{ margin: '0 auto' }} />
</div>
);
};

export default connect(createSelector(
state => state?.featuregrid?.dockSize,
state => mapLayoutValuesSelector(state, { transform: true }),
(size, dockStyle) => ({
size,
dockStyle
})))(FeatureEditorFallback);
export default FeatureEditorFallback;
7 changes: 2 additions & 5 deletions web/client/components/data/featuregrid/toolbars/Toolbar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ const standardButtons = {
visible={selectedCount <= 1 && mode === "VIEW"}
onClick={events.settings}
glyph="features-grid-set"/>),
syncGridFilterToMap: ({disabled, isSyncActive = false, showSyncOnMapButton = true, events = {}, syncPopover = { dockSize: "32.2%" }, showPopoverSync, hideSyncPopover}) => (<TButton
syncGridFilterToMap: ({disabled, isSyncActive = false, showSyncOnMapButton = true, events = {}, syncPopover = {}, showPopoverSync, hideSyncPopover}) => (<TButton
id="grid-map-filter"
keyProp="grid-map-filter"
tooltipId="featuregrid.toolbar.syncOnMap"
Expand Down Expand Up @@ -164,10 +164,7 @@ const standardButtons = {
}} className="close">
<Glyphicon className="pull-right" glyph="1-close"/>
</button>
</div>,
style: {
bottom: syncPopover.dockSize
}
</div>
}}
} />),
syncTimeParameter: ({timeSync, showTimeSyncButton = false, events = {}}) => (<TButton
Expand Down
45 changes: 45 additions & 0 deletions web/client/components/layout/MapViewerLayout.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from "react";
import FlexBox, { FlexFill } from "./FlexBox";
import useResizeObserver from "./hooks/useResizeObserver";

const MapViewerLayout = ({
id,
header,
footer,
background,
leftColumn,
rightColumn,
columns,
className,
top,
bottom,
children,
bodyClassName,
onResize
}) => {
const contentResizeRef = useResizeObserver({
onResize,
watch: ['bottom']
});
return (
<FlexBox id={id} className={className} column classNames={['_fill', '_absolute']}>
{header}
<FlexFill flexBox column className={bodyClassName} classNames={['_relative', 'ms-map-viewer-layout-body']}>
<div className="_fill _absolute">{background}</div>
<div className="_relative">{top}</div>
<FlexFill flexBox classNames={['_relative', 'ms-map-viewer-layout-main-content']}>
<div className="_relative ms-map-viewer-layout-left-column">{leftColumn}</div>
<FlexFill ref={contentResizeRef} classNames={['_relative', 'ms-map-viewer-layout-content']}>
{children}
</FlexFill>
<div className="_relative ms-map-viewer-layout-right-column">{rightColumn}</div>
<div className="ms-map-viewer-layout-columns">{columns}</div>
</FlexFill>
<div className="_relative">{bottom}</div>
</FlexFill>
{footer}
</FlexBox>
);
};

export default MapViewerLayout;
110 changes: 110 additions & 0 deletions web/client/components/layout/__tests__/MapViewerLayout-test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* Copyright 2026, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
import React from 'react';

import expect from 'expect';
import ReactDOM from 'react-dom';
import MapViewerLayout from '../MapViewerLayout';

describe("Test MapViewerLayout Component", () => {
beforeEach((done) => {
document.body.innerHTML = '<div id="container"></div>';
setTimeout(done);
});
afterEach((done) => {
ReactDOM.unmountComponentAtNode(document.getElementById("container"));
document.body.innerHTML = '';
setTimeout(done);
});

it('renders with basic props', () => {
ReactDOM.render(
<MapViewerLayout id="MAPVIEWER" className="MAP_CLASS">
<div className="content"></div>
</MapViewerLayout>,
document.getElementById("container")
);

expect(document.getElementById('MAPVIEWER')).toExist();
expect(document.getElementsByClassName('MAP_CLASS')[0]).toExist();
expect(document.getElementsByClassName('ms-map-viewer-layout-body')[0]).toExist();
expect(document.getElementsByClassName('ms-map-viewer-layout-content')[0]).toExist();
expect(document.getElementsByClassName('content')[0]).toExist();
});

it('renders header and footer', () => {
ReactDOM.render(
<MapViewerLayout
header={<div className="header"></div>}
footer={<div className="footer"></div>}
>
<div className="content"></div>
</MapViewerLayout>,
document.getElementById("container")
);

expect(document.getElementsByClassName('header')[0]).toExist();
expect(document.getElementsByClassName('footer')[0]).toExist();
expect(document.getElementsByClassName('content')[0]).toExist();
});

it('renders background, top and bottom containers', () => {
ReactDOM.render(
<MapViewerLayout
background={<div className="background"></div>}
top={<div className="top"></div>}
bottom={<div className="bottom"></div>}
>
<div className="content"></div>
</MapViewerLayout>,
document.getElementById("container")
);

// background is inside a _fill _absolute container
expect(document.getElementsByClassName('background')[0]).toExist();
// top and bottom containers
expect(document.getElementsByClassName('top')[0]).toExist();
expect(document.getElementsByClassName('bottom')[0]).toExist();
});

it('renders left and right columns', () => {
ReactDOM.render(
<MapViewerLayout
leftColumn={<div className="left-column"></div>}
rightColumn={<div className="right-column"></div>}
>
<div className="content"></div>
</MapViewerLayout>,
document.getElementById("container")
);

expect(document.getElementsByClassName('ms-map-viewer-layout-left-column')[0]).toExist();
expect(document.getElementsByClassName('left-column')[0]).toExist();
expect(document.getElementsByClassName('ms-map-viewer-layout-right-column')[0]).toExist();
expect(document.getElementsByClassName('right-column')[0]).toExist();
});

it('renders additional columns container', () => {
ReactDOM.render(
<MapViewerLayout
columns={[
<div key="col-1" className="extra-col-1"></div>,
<div key="col-2" className="extra-col-2"></div>
]}
>
<div className="content"></div>
</MapViewerLayout>,
document.getElementById("container")
);

expect(document.getElementsByClassName('ms-map-viewer-layout-columns')[0]).toExist();
expect(document.getElementsByClassName('extra-col-1')[0]).toExist();
expect(document.getElementsByClassName('extra-col-2')[0]).toExist();
});
});

Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright 2026, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

import expect from 'expect';
import React from 'react';
import ReactDOM from 'react-dom';
import { act } from 'react-dom/test-utils';
import useResizeObserver from '../../hooks/useResizeObserver';

describe('useResizeObserver', () => {
beforeEach((done) => {
document.body.innerHTML = '<div id="container"></div>';
setTimeout(done);
});

afterEach((done) => {
ReactDOM.unmountComponentAtNode(document.getElementById("container"));
document.body.innerHTML = '';
setTimeout(done);
});

it('should observe element and call onResize when size changes', (done) => {
const onResizeSpy = expect.createSpy();

const Component = () => {
const elementRef = useResizeObserver({
onResize: onResizeSpy
});

return <div ref={elementRef} style={{ width: '100px', height: '100px' }}>Test</div>;
};

act(() => {
ReactDOM.render(<Component />, document.getElementById('container'));
});

// Wait for ResizeObserver to trigger and debounce
setTimeout(() => {
expect(onResizeSpy).toHaveBeenCalled();
done();
}, 200);
});

it('should debounce resize callbacks', (done) => {
const onResizeSpy = expect.createSpy();

const Component = () => {
const elementRef = useResizeObserver({
onResize: onResizeSpy,
debounceTime: 200
});

return <div ref={elementRef} style={{ width: '100px', height: '100px' }}>Test</div>;
};

act(() => {
ReactDOM.render(<Component />, document.getElementById('container'));
});

// Wait for debounce
setTimeout(() => {
expect(onResizeSpy).toHaveBeenCalled();
done();
}, 250);
});

it('should disconnect observer on unmount', () => {
const Component = () => {
const elementRef = useResizeObserver({
onResize: () => {}
});

return <div ref={elementRef}>Test</div>;
};

act(() => {
ReactDOM.render(<Component />, document.getElementById('container'));
});

act(() => {
ReactDOM.unmountComponentAtNode(document.getElementById('container'));
});

// Test passes if unmount completes without errors
expect(true).toBe(true);
});
});

Loading