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

geosolutions-it / MapStore2 / 19930140317

04 Dec 2025 01:11PM UTC coverage: 76.657% (-0.02%) from 76.674%
19930140317

push

github

web-flow
Fix the dashboard save, legend dependency and maximize issue (#11715)

---------

Co-authored-by: allyoucanmap <stefano.bovio@geosolutionsgroup.com>

32321 of 50296 branches covered (64.26%)

22 of 41 new or added lines in 5 files covered. (53.66%)

1 existing line in 1 file now uncovered.

40218 of 52465 relevant lines covered (76.66%)

37.97 hits per line

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

42.86
/web/client/components/dashboard/WidgetViewWrapper.jsx
1
import React from 'react';
2
import WidgetsView from '../widgets/view/WidgetsView';
3
import ViewSwitcher from './ViewSwitcher';
4
import uuidv1 from 'uuid/v1';
5
import { getNextAvailableName } from '../../utils/WidgetsUtils';
6
import ConfigureView from './ConfigureView';
7
import FlexBox from '../layout/FlexBox';
8

9
const WidgetViewWrapper = props => {
1✔
10
    const {
11
        layouts = [],
×
12
        onLayoutViewReplace,
13
        selectedLayoutId,
14
        onLayoutViewSelected,
15
        viewConfigurationActive,
16
        setViewConfigurationActive,
17
        widgets = [],
×
18
        onWidgetsReplace,
19
        canEdit
20
    } = props;
4✔
21

22
    const getSelectedLayout = () => {
4✔
23
        if (Array.isArray(layouts)) {
4!
24
            return layouts.find(l => l?.id === selectedLayoutId) || {};
4✔
25
        }
26
        // fallback for old object format
27
        return layouts;
×
28
    };
29

30
    const handleSelectLayout = (id) => {
4✔
31
        onLayoutViewSelected(id);
×
32
    };
33

34
    // strip out "properties" before passing
35
    const selectedLayout = getSelectedLayout();
4✔
36
    const { id, name, color, order, ...layoutForWidgets } = selectedLayout;
4✔
37

38
    const filteredProps = {...props};
4✔
39
    if (props.widgets) {
4!
40
        filteredProps.widgets = props.widgets.filter(widget => widget.layoutId === selectedLayoutId);
4✔
41
    }
42

43
    const handleAddLayout = () => {
4✔
44
        const newLayout = {
×
45
            id: uuidv1(),
46
            name: getNextAvailableName(layouts),
47
            color: null,
48
            md: [],
49
            xxs: []
50
        };
51
        const finalLayout = [...layouts, newLayout];
×
52
        onLayoutViewReplace?.(finalLayout);
×
53
        onLayoutViewSelected(newLayout.id);
×
NEW
54
        setViewConfigurationActive(true);
×
55
    };
56

57
    const handleRemoveLayout = (layoutId) => {
4✔
58
        const updatedLayouts = layouts.filter(layout => layout.id !== layoutId);
×
59
        onLayoutViewReplace(updatedLayouts);
×
60
        onLayoutViewSelected(updatedLayouts?.[updatedLayouts.length - 1]?.id);
×
61

62
        const updatedWidgets = widgets.filter(w => w.layoutId !== layoutId);
×
63
        onWidgetsReplace(updatedWidgets);
×
64
    };
65

66
    const handleMoveLayout = (layoutId, direction) => {
4✔
67
        const index = layouts.findIndex(layout => layout.id === layoutId);
×
68
        if (index === -1) return; // Layout not found
×
69

70
        // Clone the array to avoid mutating state directly
71
        const updatedLayouts = [...layouts];
×
72

73
        if (direction === "left" && index > 0) {
×
74
            // Swap with the previous layout
75
            [updatedLayouts[index - 1], updatedLayouts[index]] = [updatedLayouts[index], updatedLayouts[index - 1]];
×
76
        } else if (direction === "right" && index < updatedLayouts.length - 1) {
×
77
            // Swap with the next layout
78
            [updatedLayouts[index], updatedLayouts[index + 1]] = [updatedLayouts[index + 1], updatedLayouts[index]];
×
79
        }
80
        onLayoutViewReplace(updatedLayouts);
×
81
    };
82

83
    const handleToggle = () => setViewConfigurationActive(false);
4✔
84

85
    const handleSave = (data) => {
4✔
86
        const updatedLayouts = layouts.map(layout => layout.id === id
×
87
            ? { ...layout, name: data.name, color: data.color }
88
            : layout
89
        );
90
        onLayoutViewReplace(updatedLayouts);
×
NEW
91
        setViewConfigurationActive(false);
×
92
    };
93

94
    const layoutViews = Array.isArray(layouts) ? layouts : [layouts];
4!
95

96
    return (
4✔
97
        <FlexBox column classNames={["_relative", "_fill"]}>
98
            <FlexBox.Fill classNames={["_relative", "_overflow-auto"]}>
99
                <WidgetsView
100
                    {...filteredProps}
101
                    layouts={layoutForWidgets} // only selected layout without properties
102
                />
103
            </FlexBox.Fill>
104
            {(canEdit || layoutViews.length > 1) && (
8!
105
                <ViewSwitcher
106
                    layouts={layoutViews}
107
                    selectedLayoutId={selectedLayoutId}
108
                    onSelect={handleSelectLayout}
109
                    onAdd={handleAddLayout}
110
                    onRemove={handleRemoveLayout}
111
                    onMove={handleMoveLayout}
NEW
112
                    onConfigure={() => setViewConfigurationActive(true)}
×
113
                    canEdit={canEdit}
114
                />
115
            )}
116
            <ConfigureView
117
                active={viewConfigurationActive}
118
                onToggle={handleToggle}
119
                onSave={handleSave}
120
                name={name}
121
                color={color}
122
            />
123
        </FlexBox>
124
    );
125
};
126

127
export default WidgetViewWrapper;
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