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

geosolutions-it / MapStore2 / 18371528919

09 Oct 2025 09:16AM UTC coverage: 76.738% (-0.05%) from 76.789%
18371528919

Pull #11572

github

web-flow
Merge 62e9c9670 into 2686c544e
Pull Request #11572: Feat: #11527 Add the tabbed view for the dashboard

31855 of 49574 branches covered (64.26%)

94 of 155 new or added lines in 10 files covered. (60.65%)

3 existing lines in 2 files now uncovered.

39633 of 51647 relevant lines covered (76.74%)

37.71 hits per line

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

90.91
/web/client/selectors/widgets.js
1
/*
2
 * Copyright 2019, 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 { find, get, castArray, flatten } from 'lodash';
10

11
import { mapSelector } from './map';
12
import { getSelectedLayer } from './layers';
13
import { pathnameSelector } from './router';
14
import { DEFAULT_TARGET, DEPENDENCY_SELECTOR_KEY, WIDGETS_REGEX } from '../actions/widgets';
15
import { getWidgetsGroups, getWidgetDependency, getSelectedWidgetData, extractTraceData, canTableWidgetBeDependency } from '../utils/WidgetsUtils';
16
import { dashboardServicesSelector, isDashboardAvailable, isDashboardEditing } from './dashboard';
17
import { createSelector, createStructuredSelector } from 'reselect';
18
import { createShallowSelector } from '../utils/ReselectUtils';
19
import { getAttributesNames } from "../utils/FeatureGridUtils";
20

21

22
export const getEditorSettings = state => get(state, "widgets.builder.settings");
4✔
23
export const getDependenciesMap = s => get(s, "widgets.dependencies") || {};
17!
24
export const getDependenciesKeys = s => Object.keys(getDependenciesMap(s)).map(k => getDependenciesMap(s)[k]);
14✔
25
export const getEditingWidget = state => get(state, "widgets.builder.editor");
29✔
26
export const getSelectedChartId = state => get(getEditingWidget(state), 'selectedChartId');
1✔
27
export const getEditingWidgetLayer = state => {
1✔
28
    const { layer, charts, selectedChartId, selectedTraceId } = getEditingWidget(state) || {};
13✔
29
    return charts ? extractTraceData({ selectedChartId, selectedTraceId, charts })?.layer : layer;
13✔
30
};
31
export const getWidgetLayer = createSelector(
1✔
32
    getEditingWidgetLayer,
33
    getSelectedLayer,
34
    state => isDashboardAvailable(state) && isDashboardEditing(state),
8✔
35
    (layer, selectedLayer, dashboardEditing) => layer || !dashboardEditing && selectedLayer
8✔
36
);
37
export const getChartWidgetLayers = (state) => getEditingWidget(state)?.charts?.map(c => c.layer) || [];
2!
38

39
export const getFloatingWidgets = state => get(state, `widgets.containers[${DEFAULT_TARGET}].widgets`);
85✔
40
export const getCollapsedState = state => get(state, `widgets.containers[${DEFAULT_TARGET}].collapsed`);
53✔
41
export const getMaximizedState = state => get(state, `widgets.containers[${DEFAULT_TARGET}].maximized`);
24✔
42
export const getVisibleFloatingWidgets = createSelector(
1✔
43
    getFloatingWidgets,
44
    getCollapsedState,
45
    getMaximizedState,
46
    (widgets, collapsed, maximized) => {
47
        if (widgets) {
17✔
48
            if (maximized?.widget) {
16✔
49
                return widgets.filter(({ id } = {}) => id === maximized.widget.id);
6!
50
            } else if (collapsed) {
14✔
51
                return widgets.filter(({ id } = {}) => !collapsed[id]);
12!
52
            }
53
        }
54
        return widgets;
11✔
55
    }
56
);
57
export const getWidgetAttributeFilter = (id, attributeName) => createSelector(
2✔
58
    getVisibleFloatingWidgets,
59
    (widgets) => {
60
        const widget = find(widgets, {id});
2✔
61
        return widget && widget.quickFilters && widget.options && find(getAttributesNames(widget.options?.propertyName), f => f === attributeName) && widget.quickFilters[attributeName] || {};
2!
62
    });
63

64
export const getCollapsedIds = createSelector(
1✔
65
    getCollapsedState,
66
    (collapsed = {}) => Object.keys(collapsed)
2!
67
);
68
export const getMapWidgets = state => (getFloatingWidgets(state) || []).filter(({ widgetType } = {}) => widgetType === "map");
15!
69
export const getTableWidgets = state => (getFloatingWidgets(state) || []).filter(({ widgetType } = {}) => widgetType === "table");
16!
70

71
/**
72
 * Find in the state the available dependencies to connect
73
 *
74
 * Note: table widgets are excluded from selection when viewer is present,
75
 * because there were conflict between map and other widgets
76
 * (the map contains other widgets)
77
 */
78
export const availableDependenciesSelector = createSelector(
1✔
79
    getMapWidgets,
80
    getTableWidgets,
81
    mapSelector,
82
    pathnameSelector,
83
    (ws = [], tableWidgets = [], map = [], pathname) => ({
3!
84
        availableDependencies:
85
            flatten(ws
86
                .map(({id, maps = []}) => maps.map(({mapId} = {})=> `widgets[${id}].maps[${mapId}].map`)))
3!
87
                .concat(castArray(map).map(() => "map"))
3✔
88
                .concat(castArray(tableWidgets).filter(() => pathname.indexOf("viewer") === -1).map(({id}) => `widgets[${id}]`))
1✔
89
    })
90
);
91
/**
92
 * this selector adds some more filtering on tables when a widget is in edit mode
93
 * and the table widgets does not share the same dataset (layername)
94
 */
95
export const availableDependenciesForEditingWidgetSelector = createSelector(
1✔
96
    getMapWidgets,
97
    getTableWidgets,
98
    mapSelector,
99
    pathnameSelector,
100
    getEditingWidget,
101
    (ws = [], tableWidgets = [], map = {}, pathname, editingWidget) => {
×
102
        return {
3✔
103
            availableDependencies:
104
                flatten(ws
105
                    .map(({id, maps = []}) => maps.map(({mapId} = {})=> `widgets[${id}].maps[${mapId}].map`)))
2!
106
                    .concat(castArray(map).map(() => map ? "map" : null))
3!
107
                    .filter(w => w)
5✔
108
                    .concat(
109
                        castArray(tableWidgets)
110
                            .filter(() => pathname.indexOf("viewer") === -1)
6✔
111
                            .filter((w) => canTableWidgetBeDependency(editingWidget, w))
6✔
112
                            .filter((w) => editingWidget && editingWidget.id !== w.id)
5✔
113
                            .map(({id}) => `widgets[${id}]`)
5✔
114
                    )
115
        };
116
    }
117
);
118
/**
119
 * returns if the dependency selector state
120
 * @param {object} state the state
121
 */
122
export const getDependencySelectorConfig = state => get(getEditorSettings(state), `${DEPENDENCY_SELECTOR_KEY}`);
2✔
123
/**
124
 * Determines if the dependencySelector is active
125
 * @param {object} state the state
126
 */
127
export const isWidgetSelectionActive = state => get(getDependencySelectorConfig(state), 'active');
1✔
128

129
export const getWidgetsDependenciesGroups = createSelector(
1✔
130
    getFloatingWidgets,
131
    widgets => getWidgetsGroups(widgets)
×
132
);
133
export const getFloatingWidgetsLayout = state => get(state, `widgets.containers[${DEFAULT_TARGET}].layouts`);
34✔
134
export const getFloatingWidgetsCurrentLayout = state => get(state, `widgets.containers[${DEFAULT_TARGET}].layout`);
1✔
135

136
export const getDashboardWidgets = state => get(state, `widgets.containers[${DEFAULT_TARGET}].widgets`);
10✔
137

138
export const isTrayEnabled = state => get(state, "widgets.tray");
2✔
139

140
// let's use the same container for the moment
141
export const dashboardHasWidgets = state => (getDashboardWidgets(state) || []).length > 0;
1!
142
export const getDashboardWidgetsLayout = state => get(state, `widgets.containers[${DEFAULT_TARGET}].layouts`);
10✔
143
export const returnToFeatureGridSelector = (state) => get(state, "widgets.builder.editor.returnToFeatureGrid", false);
2✔
144
export const getEditingWidgetFilter = state => {
1✔
145
    const editingWidget = getSelectedWidgetData(getEditingWidget(state));
6✔
146
    return get(editingWidget, "filter");
6✔
147
};
148
export const dashBoardDependenciesSelector = () => ({}); // TODO dashboard dependencies
1✔
149
/**
150
 * transforms dependencies in the form `{ k1: "path1", k1, "path2" }` into
151
 * a map like `{k1: v1, k2: v2}` where `v1 = get("path1", state)`.
152
 * Dependencies paths map comes from getDependenciesMap.
153
 * map.... is a special path that brings to the map of mapstore.
154
 */
155
export const dependenciesSelector = createShallowSelector(
1✔
156
    getDependenciesMap,
157
    getDependenciesKeys,
158
    // produces the array of values of the keys in getDependenciesKeys
159
    state => getDependenciesKeys(state).map(k =>
1✔
160
        k.indexOf("map.") === 0
7✔
161
            ? get(mapSelector(state), k.slice(4))
162
            : k.match(WIDGETS_REGEX)
6✔
163
                ? getWidgetDependency(k, getFloatingWidgets(state), getMapWidgets(state))
164
                : get(state, k) ),
165
    // iterate the dependencies keys to set the dependencies values in a map
166
    (map, keys, values) => keys.reduce((acc, k, i) => ({
7✔
167
        ...acc,
168
        [Object.keys(map)[i]]: values[i]
169
    }), {})
170
);
171
export const widgetsConfig = createStructuredSelector({
1✔
172
    widgets: getFloatingWidgets,
173
    layouts: getFloatingWidgetsLayout,
174
    catalogs: dashboardServicesSelector
175
});
176

177

178
/**
179
 * Get editor change key for updating filter object
180
 * @param {object} state the state
181
 */
182
export const getWidgetFilterKey = (state) => {
1✔
183
    const { selectedChartId, charts = [],  selectedTraceId } = getEditingWidget(state) || {};
4!
184
    if (!selectedChartId) {
4✔
185
        return 'filter';
3✔
186
    }
187
    const selectedTrace = extractTraceData({ selectedChartId, selectedTraceId, charts });
1✔
188
    // Set chart key if editor widget type is chart
189
    return `charts[${selectedChartId}].traces[${selectedTrace?.id}].filter`;
1✔
190
};
191

192
export const getTblWidgetZoomLoader = state => {
1✔
193
    let tableWidgets = (getFloatingWidgets(state) || []).filter(({ widgetType } = {}) => widgetType === "table");
×
194
    return tableWidgets?.find(t=>t.dependencies?.zoomLoader) ? true : false;
×
195
};
196

197
export const getSelectedLayoutId = state => {
1✔
NEW
198
    const selectedLayoutId = get(state, `widgets.containers[${DEFAULT_TARGET}].selectedLayoutId`);
×
NEW
199
    if (selectedLayoutId) return selectedLayoutId;
×
200

NEW
201
    const layouts = get(state, `widgets.containers[${DEFAULT_TARGET}].layouts`);
×
NEW
202
    return layouts?.[0]?.id;
×
203
};
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