• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

geosolutions-it / MapStore2 / 14246996716

03 Apr 2025 03:34PM UTC coverage: 76.922% (-0.05%) from 76.971%
14246996716

Pull #10925

github

web-flow
Merge 775cc2f92 into 646bb6e37
Pull Request #10925: #10923, #10924: Disabling GeoFence Rules and Add filter by IP for GeoFence rules

31068 of 48387 branches covered (64.21%)

61 of 109 new or added lines in 10 files covered. (55.96%)

147 existing lines in 34 files now uncovered.

38588 of 50165 relevant lines covered (76.92%)

35.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

45.28
/web/client/plugins/Annotations/containers/AnnotationsPanel.jsx
1
/*
2
 * Copyright 2023, GeoSolutions Sas.
3
 * All rights reserved.
4
 *
5
 * This source code is licensed under the BSD-style license found in the
6
 * LICENSE file in the root directory of this source tree.
7
*/
8

9

10
import React, { useEffect, useState } from 'react';
11
import { Nav, NavItem, Glyphicon, ButtonGroup, Alert, ControlLabel } from "react-bootstrap";
12
import { connect } from 'react-redux';
13
import { createSelector } from 'reselect';
14
import isFunction from 'lodash/isFunction';
15
import capitalize from 'lodash/capitalize';
16
import { ANNOTATIONS } from '../utils/AnnotationsUtils';
17
import { registerRowViewer } from '../../../utils/MapInfoUtils';
18
import MSButton from '../../../components/misc/Button';
19
import tooltip from '../../../components/misc/enhancers/tooltip';
20
import { updateNode } from '../../../actions/layers';
21
import {
22
    cancelCloseAnnotations,
23
    confirmCloseAnnotations,
24
    download,
25
    editAnnotation,
26
    removeAnnotation
27
} from '../actions/annotations';
28
import { getSelectedAnnotationLayer } from '../selectors/annotations';
29
import { createControlEnabledSelector } from '../../../selectors/controls';
30
import { mapLayoutValuesSelector } from '../../../selectors/maplayout';
31
import { setControlProperty } from '../../../actions/controls';
32
import Message from '../../../components/I18N/Message';
33
import AnnotationsFields from '../components/AnnotationsFields';
34
import { DEFAULT_TARGET_ID } from '../constants';
35
import ConfirmDialog from '../../../components/layout/ConfirmDialog';
36
import Portal from '../../../components/misc/Portal';
37
import { mapSelector } from '../../../selectors/map';
38
import VisibilityLimitsForm from '../../../components/TOC/fragments/settings/VisibilityLimitsForm';
39

40
const Button = tooltip(MSButton);
1✔
41

42
function AnnotationsInfoViewer({
43
    fields,
44
    onEdit = () => {},
×
45
    onRemove = () => {},
×
46
    onDownload = () => {},
×
47
    ...layer
48
}) {
49
    const [removeModal, setRemoveModal] = useState(false);
×
50
    return (
×
51
        <div>
52
            <div>
53
                <ButtonGroup>
54
                    <Button
55
                        className="square-button-md"
56
                        bsStyle="primary"
57
                        tooltipId="annotations.edit"
58
                        onClick={() => onEdit(layer.id)}
×
59
                    >
60
                        <Glyphicon glyph="pencil"/>
61
                    </Button>
62
                    <Button
63
                        className="square-button-md"
64
                        bsStyle="primary"
65
                        tooltipId="annotations.remove"
66
                        onClick={() => setRemoveModal(true)}
×
67
                    >
68
                        <Glyphicon glyph="trash"/>
69
                    </Button>
70
                    <Button
71
                        className="square-button-md"
72
                        bsStyle="primary"
73
                        tooltipId="annotations.downloadcurrenttooltip"
74
                        onClick={() => onDownload([layer])}
×
75
                    >
76
                        <Glyphicon glyph="download"/>
77
                    </Button>
78
                </ButtonGroup>
79
            </div>
80
            <AnnotationsFields
81
                preview
82
                properties={{
83
                    title: layer?.title,
84
                    description: layer?.description,
85
                    ...layer?.options
86
                }}
87
                fields={fields}
88
            />
89
            <Portal>
90
                <ConfirmDialog
91
                    show={removeModal}
UNCOV
92
                    onCancel={() => setRemoveModal(false)}
×
93
                    onConfirm={() => {
UNCOV
94
                        onRemove(layer.id);
×
95
                        setRemoveModal(false);
×
96
                    }}
97
                    variant="danger"
98
                    preventHide
99
                    titleId={"annotations.undoDeleteFeature"}
100
                    confirmId={`annotations.confirm`}
101
                    cancelId={`annotations.cancel`}>
102
                </ConfirmDialog>
103
            </Portal>
104
        </div>
105
    );
106
}
107

108
const ConnectedAnnotationsInfoViewer = connect(
1✔
UNCOV
109
    () => ({}),
×
110
    {
111
        onRemove: removeAnnotation,
112
        onDownload: download,
113
        onEdit: editAnnotation
114
    }
115
)(AnnotationsInfoViewer);
116

117
function AnnotationsPanel({
118
    targetId = DEFAULT_TARGET_ID,
1✔
119
    selected,
120
    style,
121
    enabled,
122
    onReady = () => {},
×
123
    onClose = () => {},
×
124
    onChange = () => {},
×
125
    onDownload = () => {},
×
126
    fields = [
1✔
127
        {
128
            name: 'title',
129
            type: 'text',
130
            validator: (val) => val,
3✔
131
            validateError: 'annotations.mandatory',
132
            showLabel: true,
133
            editable: true
134
        },
135
        {
136
            name: 'description',
137
            type: 'html',
138
            showLabel: true,
139
            editable: true
140
        }
141
    ],
142
    closeId,
143
    onCancel = () => {},
×
144
    activeClickEventListener,
145
    projection,
146
    resolutions,
147
    zoom
148
}) {
149
    const properties = {
1✔
150
        title: selected?.title,
151
        description: selected?.description,
152
        ...selected?.options
153
    };
154
    const [tab, setTab] = useState('properties');
1✔
155
    const [closeModal, setCloseModal] = useState(false);
1✔
156
    function validateFields() {
157
        return !fields.find((filed) => (isFunction(filed?.validator) ? !filed.validator(properties[filed.name]) : false));
2!
158
    }
159
    function validateFeatures() {
160
        return !selected?.features?.length
1!
161
            ? false
162
            : !selected.invalidFeatures;
163
    }
164
    useEffect(() => {
1✔
165
        registerRowViewer(ANNOTATIONS, (props) => <ConnectedAnnotationsInfoViewer  {...props} fields={fields} />);
1✔
166
        return () => {
1✔
167
            registerRowViewer(ANNOTATIONS, undefined);
1✔
168
        };
169
    }, []);
170

171
    useEffect(() => {
1✔
172
        setCloseModal(false);
1✔
173
        onReady(enabled);
1✔
174
        return () => {
1✔
175
            onReady(!enabled);
1✔
176
        };
177
    }, [enabled]);
178

179
    function handleClosePanel(event) {
UNCOV
180
        event.stopPropagation();
×
181
        if (validateFields() && validateFeatures()) {
×
182
            return onClose(selected);
×
183
        }
UNCOV
184
        return setCloseModal(true);
×
185
    }
186

187
    function handleCancelClose() {
UNCOV
188
        onCancel();
×
189
        setCloseModal(false);
×
190
    }
191
    function handleConfirmClose() {
UNCOV
192
        onClose(selected);
×
193
        setCloseModal(false);
×
194
    }
195

196
    useEffect(() => {
1✔
197
        if (closeId) {
1!
UNCOV
198
            handleClosePanel();
×
199
        }
200
    }, [closeId]);
201

202
    if (!selected) {
1!
UNCOV
203
        return null;
×
204
    }
205

206
    return (
1✔
207
        <div
208
            className="ms-annotations-panel"
209
            style={style}
210
        >
211
            <div className="ms-annotations-panel-header">
212
                <Button
UNCOV
213
                    onClick={(event) => handleClosePanel(event)}
×
214
                    className="square-button no-border"
215
                >
216
                    <Glyphicon glyph="1-close"/>
217
                </Button>
218
                <div className="ms-annotations-title">
219
                    <Message msgId="annotations.title" />
220
                </div>
221
                <div className="square-button text-primary">
222
                    <Glyphicon glyph="comment"/>
223
                </div>
224
            </div>
225
            <Nav bsStyle="tabs" activeKey={tab} className="ms-annotations-panel-nav">
226
                <NavItem
227
                    key="properties"
228
                    eventKey="properties"
UNCOV
229
                    onClick={() => setTab('properties')}>
×
230
                    <Message msgId="annotations.properties"/>
231
                    {' '}
232
                    <Glyphicon glyph={validateFields() ? 'ok-sign text-success' : 'exclamation-mark text-danger'}/>
1!
233
                </NavItem>
234
                <NavItem
235
                    key="geometries"
236
                    eventKey="geometries"
UNCOV
237
                    onClick={() => setTab('geometries')}>
×
238
                    <Message msgId="annotations.geometries"/>
239
                    {' '}
240
                    <Glyphicon glyph={validateFeatures() ? 'ok-sign text-success' : 'exclamation-mark text-danger'}/>
1!
241
                </NavItem>
242
                <NavItem
243
                    key="settings"
244
                    eventKey="settings"
UNCOV
245
                    onClick={() => setTab('settings')}>
×
246
                    <Message msgId="settings"/>
247
                </NavItem>
248
                <Button
249
                    className="square-button-md"
250
                    bsStyle="primary"
251
                    tooltipId="annotations.downloadcurrenttooltip"
252
                    disabled={!(validateFields() && validateFeatures())}
1!
UNCOV
253
                    onClick={() => onDownload([selected])}
×
254
                >
255
                    <Glyphicon glyph="download"/>
256
                </Button>
257
            </Nav>
258
            <div className="ms-annotations-panel-body">
259
                <div className="ms-annotations-panel-content" style={tab === 'properties' ? {  } : { display: 'none' }}>
1!
260
                    <AnnotationsFields
261
                        fields={fields}
262
                        properties={properties}
263
                        onChange={(newOptions) => {
UNCOV
264
                            const { title, description, ...options } = newOptions;
×
265
                            onChange(selected.id, 'layers', {
×
266
                                ...(title !== undefined && { title }),
×
267
                                ...(description !== undefined && { description }),
×
268
                                options: {
269
                                    ...selected.options,
270
                                    ...options
271
                                }
272
                            });
273
                        }}
274
                    />
275
                </div>
276
                <div id={targetId} style={tab === 'geometries' ? {} : { display: 'none' }} >
1!
277
                </div>
278
                <div className="ms-annotations-panel-content" style={tab === 'settings' ? {  } : { display: 'none' }}>
1!
279
                    <VisibilityLimitsForm
280
                        title={<ControlLabel><Message msgId="layerProperties.visibilityLimits.title"/></ControlLabel>}
281
                        layer={selected}
UNCOV
282
                        onChange={(options) => onChange(selected.id, 'layers', options)}
×
283
                        projection={projection}
284
                        resolutions={resolutions}
285
                        zoom={zoom}
286
                    />
287
                </div>
288
            </div>
289
            {activeClickEventListener && <Alert bsStyle="warning">
1!
290
                <Message msgId="annotations.deactivatedMapInteraction" msgParams={{ pluginName: capitalize(activeClickEventListener) }}/>
291
            </Alert>}
292
            <Portal>
293
                <ConfirmDialog
294
                    show={closeModal}
UNCOV
295
                    onCancel={() => handleCancelClose()}
×
UNCOV
296
                    onConfirm={() => handleConfirmClose()}
×
297
                    variant="danger"
298
                    preventHide
299
                    titleId={"annotations.undo"}
300
                    confirmId={`annotations.confirm`}
301
                    cancelId={`annotations.cancel`}>
302
                </ConfirmDialog>
303
            </Portal>
304
        </div>
305
    );
306
}
307

308
const ConnectedAnnotationsPanel = connect(
1✔
309
    createSelector([
310
        createControlEnabledSelector(ANNOTATIONS),
311
        state => mapLayoutValuesSelector(state, { height: true }),
2✔
312
        getSelectedAnnotationLayer,
313
        mapSelector
314
    ],
315
    (enabled, style, selected, map) => ({
1✔
316
        enabled,
317
        style,
318
        selected: enabled ? selected : null,
1!
319
        activeClickEventListener: map?.eventListeners?.click?.[0],
320
        projection: map?.projection,
321
        zoom: map?.zoom,
322
        resolutions: map?.resolutions
323
    })),
324
    {
325
        onClose: confirmCloseAnnotations,
326
        onCancel: cancelCloseAnnotations,
327
        onReady: setControlProperty.bind(null, ANNOTATIONS, 'ready'),
328
        onChange: updateNode,
329
        onDownload: download
330
    }
331
)(AnnotationsPanel);
332

333
export default ConnectedAnnotationsPanel;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc