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

geosolutions-it / MapStore2 / 12831531306

17 Jan 2025 03:01PM UTC coverage: 77.182% (+0.07%) from 77.115%
12831531306

Pull #10746

github

web-flow
Merge 501dbaeea into 4e4dabc03
Pull Request #10746: Fix #10739 Changing correctly resolutions limits when switching map CRS

30373 of 47156 branches covered (64.41%)

34 of 43 new or added lines in 2 files covered. (79.07%)

126 existing lines in 15 files now uncovered.

37769 of 48935 relevant lines covered (77.18%)

35.14 hits per line

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

81.82
/web/client/plugins/TOC/components/LayersTree.jsx
1
/*
2
 * Copyright 2022, 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
import React, { useState, useLayoutEffect, useRef } from 'react';
10
import DefaultLayerOrGroup from './DefaultLayerOrGroup';
11
import { getTitleAndTooltip, isSingleDefaultGroup } from '../utils/TOCUtils';
12
import { ROOT_GROUP_ID, DEFAULT_GROUP_ID, NodeTypes } from '../../../utils/LayersUtils';
13
import DefaultLayer from './DefaultLayer';
14
import DefaultGroup from './DefaultGroup';
15
import Message from '../../../components/I18N/Message';
16

17
const filterTitle = ({
1✔
18
    node,
19
    filterText,
20
    currentLocale
21
}) => {
22
    const { title: currentTitle } = getTitleAndTooltip({ node, currentLocale });
8✔
23
    return currentTitle.toLowerCase().includes(filterText.toLocaleLowerCase());
8✔
24
};
25

26
const loopFilter = ({ node: groupNode, filterText, currentLocale }) => {
1✔
27
    return !!groupNode?.nodes?.find((node) => {
6✔
28
        if (node?.nodes) {
6!
29
            return loopFilter({ node, filterText, currentLocale });
×
30
        }
31
        return filterTitle({
6✔
32
            node,
33
            filterText,
34
            currentLocale
35
        });
36
    });
37
};
38

39
const loopGroupCondition = (groupNode, condition) => {
1✔
40
    return !!groupNode?.nodes?.find((node) => {
152✔
41
        if (condition(node)) {
132!
42
            return true;
×
43
        }
44
        if (node?.nodes) {
132✔
45
            return !!loopGroupCondition(node, condition);
4✔
46
        }
47
        return !!condition(node);
128✔
48
    });
49
};
50
/**
51
 * LayersTree renders all nodes given a tree representation
52
 * @prop {object} tree nodes tree
53
 * @prop {object} contextMenu context menu payload
54
 * @prop {function} onSort return the sorted node information
55
 * @prop {function} onChange return the changes of a specific node
56
 * @prop {function} onContextMenu return the context menu event of a specific node
57
 * @prop {component} groupNodeComponent custom group node component
58
 * @prop {component} layerNodeComponent custom layer node component
59
 * @prop {array} selectedNodes list of selected node objects
60
 * @prop {function} onSelect return the current selected node on click event
61
 * @prop {string} filterText filter to apply to layers title
62
 * @prop {string} theme layers tree theme, one of undefined or `legend`
63
 * @prop {string} className additional class name for the layer tree
64
 * @prop {array} nodeItems list of node component to customize specific nodes, expected structure [ { name, Component, selector } ]
65
 * @prop {array} nodeContentItems list of node content component to customize specific content available after expanding the node, expected structure [ { name, Component } ]
66
 * @prop {array} nodeToolItems list of node tool component to customize specific tool available on a node, expected structure [ { name, Component } ]
67
 * @prop {object} singleDefaultGroup if true it hides the default group nodes
68
 * @prop {string} noFilteredResultsMsgId message id for no result on filter
69
 * @prop {object} config optional configuration available for the nodes
70
 * @prop {boolean} config.sortable activate the possibility to sort nodes
71
 */
72
const LayersTree = ({
1✔
73
    tree,
74
    filterText,
75
    onSort = () => {},
9✔
76
    onChange = () => {},
8✔
77
    groupNodeComponent = DefaultGroup,
72✔
78
    layerNodeComponent = DefaultLayer,
72✔
79
    onContextMenu = () => {},
8✔
80
    onSelect = () => {},
8✔
81
    contextMenu,
82
    selectedNodes = [],
9✔
83
    rootGroupId = ROOT_GROUP_ID,
50✔
84
    defaultGroupId = DEFAULT_GROUP_ID,
73✔
85
    nodeTypes = NodeTypes,
9✔
86
    noFilteredResultsMsgId = 'toc.noFilteredResults',
73✔
87
    config = {},
2✔
88
    className,
89
    nodeItems,
90
    nodeToolItems,
91
    nodeContentItems,
92
    singleDefaultGroup = isSingleDefaultGroup(tree),
50✔
93
    theme
94
}) => {
95

96
    const containerNode = useRef();
73✔
97
    const root = singleDefaultGroup ? tree[0].nodes : tree;
73✔
98
    const rootParentId = singleDefaultGroup ? defaultGroupId : rootGroupId;
73✔
99
    const [sortId, setSortId] = useState(null);
73✔
100
    const parseSortId = (id) => {
73✔
UNCOV
101
        const parts = (id || '').split('.');
×
UNCOV
102
        return parts[parts.length - 1];
×
103
    };
104

105
    const [isContainerEmpty, setIsContainerEmpty] = useState(false);
73✔
106
    useLayoutEffect(() => {
73✔
107
        setIsContainerEmpty(containerNode?.current?.children?.length === 0);
60✔
108
    }, [filterText]);
109

110
    const getGroup = () => {
73✔
111
        const Group = groupNodeComponent;
92✔
112
        return (<Group />);
92✔
113
    };
114

115
    const getLayer = () => {
73✔
116
        const Layer = layerNodeComponent;
92✔
117
        return (<Layer />);
92✔
118
    };
119

120
    const getNodeStyle = () => {
73✔
121
        return {};
113✔
122
    };
123

124
    const getNodeClassName = (currentNode) => {
73✔
125
        const selected = selectedNodes.find((selectedNode) => currentNode.id === selectedNode.id);
113✔
126
        const contextMenuHighlight = contextMenu?.id === currentNode.id;
113✔
127
        const inactive = currentNode.visibilityWarningMessageId
113✔
128
            || currentNode.visibility === false
129
            || currentNode.inactive === true;
130
        return [
113✔
131
            ...(contextMenuHighlight ? ['focused'] : []),
113!
132
            ...(selected ? ['selected'] : []),
113✔
133
            ...(currentNode.error ? ['error'] : []),
113!
134
            ...(inactive ? ['inactive'] : [])
113✔
135
        ].join(' ');
136
    };
137

138
    return (
73✔
139
        <div
140
            className={`ms-layers-tree${className ? ` ${className}` : ''}${theme ? ` ${theme}-tree` : ''}${!config.showFullTitle && !filterText ? ' single-line-title' : ''}`}
344!
141
            onPointerLeave={() => {
UNCOV
142
                if (sortId) {
×
UNCOV
143
                    setSortId(null);
×
144
                }
145
            }}
146
        >
147
            <ul
148
                ref={containerNode}
149
                data-root-parent-id={rootParentId}
150
                onContextMenu={(event) => {
UNCOV
151
                    event.preventDefault();
×
152
                }}
153
            >
154
                {(root || []).map((node, index) => {
75✔
155
                    return (
92✔
156
                        <DefaultLayerOrGroup
157
                            containerNode={containerNode.current}
158
                            key={node.id}
159
                            index={index}
160
                            node={node}
161
                            groupElement={getGroup()}
162
                            layerElement={getLayer()}
163
                            nodeTypes={nodeTypes}
164
                            replaceNodeOptions={(currentNode, nodeType) => {
165
                                return {
234✔
166
                                    ...currentNode,
167
                                    ...(sortId && parseSortId(sortId) === parseSortId(currentNode.id) && { dragging: true }),
234!
168
                                    ...(filterText && { sortable: false }),
250✔
169
                                    ...(nodeType === nodeTypes.GROUP && filterText && { expanded: true }),
320✔
170
                                    ...(nodeType === nodeTypes.GROUP && currentNode?.id === defaultGroupId && { sortable: false }),
308!
171
                                    ...(nodeType === nodeTypes.GROUP && loopGroupCondition(currentNode, childNode => childNode.loadingError === 'Error') && { error: true }),
130!
172
                                    ...(nodeType === nodeTypes.GROUP && loopGroupCondition(currentNode, childNode => childNode.loading ) && { loading: true })
130!
173
                                };
174
                            }}
175
                            getNodeStyle={getNodeStyle}
176
                            getNodeClassName={getNodeClassName}
177
                            filterText={filterText}
178
                            config={config}
179
                            theme={theme}
180
                            filter={(currentNode, nodeType) => {
181
                                if (nodeType === nodeTypes.GROUP && filterText) {
117✔
182
                                    return loopFilter({ node: currentNode, filterText });
6✔
183
                                }
184
                                if (nodeType === nodeTypes.LAYER && filterText) {
111✔
185
                                    return filterTitle({
2✔
186
                                        node: currentNode,
187
                                        filterText,
188
                                        currentLocale: config.currentLocale
189
                                    });
190
                                }
191
                                return true;
109✔
192
                            }}
193
                            sort={{
194
                                beginDrag: (id) => {
UNCOV
195
                                    setSortId(id);
×
196
                                },
197
                                hover: (id, groupId, newIndex) => {
UNCOV
198
                                    onSort(id, groupId, newIndex);
×
199
                                },
200
                                drop: () => {
UNCOV
201
                                    setSortId(null);
×
202
                                }
203
                            }}
204
                            onChange={onChange}
205
                            onContextMenu={onContextMenu}
206
                            onSelect={onSelect}
207
                            nodeItems={nodeItems}
208
                            nodeToolItems={nodeToolItems}
209
                            nodeContentItems={nodeContentItems}
210
                        />
211
                    );
212
                })}
213
            </ul>
214
            {isContainerEmpty && filterText ? <Message msgId={noFilteredResultsMsgId} /> : null}
155✔
215
        </div>
216
    );
217
};
218

219
export default LayersTree;
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

© 2025 Coveralls, Inc