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

CBIIT / bento-c3dc-frontend / 21523815572

30 Jan 2026 05:00PM UTC coverage: 0.154% (-0.01%) from 0.167%
21523815572

Pull #478

github

web-flow
Merge 28c144a05 into c99082bc6
Pull Request #478: C3DC-1912 & C3DC-1935, Restricted Constructed URL + Interop URL feature

6 of 4070 branches covered (0.15%)

Branch coverage included in aggregate %.

0 of 489 new or added lines in 14 files covered. (0.0%)

24 existing lines in 6 files now uncovered.

10 of 6301 relevant lines covered (0.16%)

0.08 hits per line

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

0.0
/src/pages/inventory/filterQueryBar/QueryBarView.js
1
import React from 'react';
×
2
import { connect, useDispatch } from 'react-redux';
×
NEW
3
import {
×
4
  useLocation
5
}  from "react-router-dom";
6
import { clearAllFilters, clearFacetSection, clearSliderSection, toggleCheckBox } from '@bento-core/facet-filter';
×
7
import { resetAllData, resetUploadData, updateAutocompleteData } from '@bento-core/local-find';
×
8
// import { updateImportfrom } from '../../../components/Inventory/InventoryState';
9
import store from '../../../store';
×
10
import { QueryBarGenerator } from '@bento-core/query-bar';
×
NEW
11
import { generateQueryStr } from '@bento-core/util';
×
NEW
12
import {
×
13
  facetsConfig,
14
  queryParams,
15
  excludedParams,
16
  ageRelatedParams
17
} from '../../../bento/dashTemplate';
18
import { customStyles } from './QueryBarStyles';
×
19
import { Container, createTheme, ThemeProvider } from '@material-ui/core';
×
20
import theme from './QueryBarTheme';
×
21
import { generateUrl } from './QueryBarUtils';
×
NEW
22
import { useUrlManager } from '../../../hooks/useUrlManager';
×
23

24
/**
25
 * Generate the Explore Tab Query Bar
26
 *
27
 * @param {object} props
28
 * @param {object} props.data API search resultset
29
 * @param {object} props.statusReducer Facet Filter State
30
 * @param {object} props.localFind Local Find State
31
 * @param {object} props.unknownAgesState Unknown Ages State
32
 * @param {boolean} props.hasImportFrom Has Import From Data
33
 * @returns {JSX.Element}
34
 */
35
const QueryBarView = ({ data, statusReducer, localFind, unknownAgesState, hasImportFrom }) => {
×
36
  const dispatch = useDispatch();
×
37
  const query = new URLSearchParams(useLocation().search);
×
NEW
38
  const updateUrl = useUrlManager('/explore');
×
39

40
  const sectionOrder = facetsConfig.map((v) => v.datafield);
×
41

42
  // Helper function to sync participant IDs from Redux state to URL
NEW
43
  const syncParticipantIdsToUrl = (paramValue) => {
×
44
    /* eslint-disable no-param-reassign */
45
    // Sync autocomplete participant IDs
NEW
46
    if (localFind && localFind.autocomplete && localFind.autocomplete.length > 0) {
×
NEW
47
      paramValue.p_id = localFind.autocomplete.map((data) => data.title).join('|');
×
48
    } else {
NEW
49
      paramValue.p_id = '';
×
50
    }
51

52
    // Sync uploaded participant IDs
NEW
53
    if (localFind && localFind.upload && localFind.upload.length > 0) {
×
NEW
54
      paramValue.u = localFind.upload.map((data) => data.participant_id).join('|');
×
55

56
      // Sync upload metadata
NEW
57
      if (localFind.uploadMetadata && localFind.uploadMetadata.fileContent) {
×
NEW
58
        const fc = localFind.uploadMetadata.fileContent
×
59
          .split(/[,\n]/g)
NEW
60
          .map((e) => e.trim().replace(/\r/g, '').toUpperCase())
×
NEW
61
          .filter((e) => e && e.length > 1);
×
NEW
62
        paramValue.u_fc = fc.join('|');
×
63
      } else {
NEW
64
        paramValue.u_fc = '';
×
65
      }
66

NEW
67
      if (localFind.uploadMetadata
×
68
        && localFind.uploadMetadata.unmatched
69
        && localFind.uploadMetadata.unmatched.length > 0) {
NEW
70
        paramValue.u_um = localFind.uploadMetadata.unmatched.join('|');
×
71
      } else {
NEW
72
        paramValue.u_um = '';
×
73
      }
74
    } else {
NEW
75
      paramValue.u = '';
×
NEW
76
      paramValue.u_fc = '';
×
NEW
77
      paramValue.u_um = '';
×
78
    }
79
    /* eslint-enable no-param-reassign */
80
  };
81

82
  // Create mapped filter state from regular facets
83
  const mappedFilterState = Object.keys(statusReducer || {}).map((facet) => {
×
84
    const config = facetsConfig.find((config) => config.datafield === facet);
×
85
    if (!config) {
×
86
      console.warn(`No configuration found for facet: ${facet}`);
×
87
      return null;
×
88
    }
89
    return {
×
90
      ...config,
91
      items: statusReducer[facet],
92
      data: data[config.apiForFiltering],
93
    };
94
  }).filter(Boolean);
95

96
  // Add unknownAges parameters to existing entries or create new ones
97
  // Check both Redux state and URL parameters for unknownAges
98
  ageRelatedParams.forEach(param => {
×
99
    let unknownAges = 'include'; // default value
×
100

101
    // First check Redux state
102
    if (unknownAgesState && unknownAgesState[param]) {
×
103
      unknownAges = unknownAgesState[param];
×
104
    }
105
    // If not in Redux state, check URL parameters for page load
106
    else {
107
      const unknownAgesParam = `${param}_unknownAges`;
×
108
      const urlUnknownAges = query.get(unknownAgesParam);
×
109
      if (urlUnknownAges) {
×
110
        unknownAges = urlUnknownAges;
×
111
      }
112
    }
113

114
    if (unknownAges && unknownAges !== 'include') {
×
115
      // Check if there's already an entry for this parameter (with range)
116
      const existingEntryIndex = mappedFilterState.findIndex(entry => entry.datafield === param);
×
117

118
      if (existingEntryIndex !== -1) {
×
119
        // Add unknownAges to existing entry
120
        mappedFilterState[existingEntryIndex].unknownAges = unknownAges;
×
121
      } else {
122
        // Create a new entry only if there's no range selected
123
        const config = facetsConfig.find((config) => config.datafield === param);
×
124
        if (config) {
×
125
          const unknownAgesItem = {
×
126
            ...config,
127
            datafield: `${param}_unknownAges`,
128
            label: config.label, // Use original label, let SliderFilter handle unknownAges formatting
129
            items: [unknownAges],
130
            data: data[config.apiForFiltering],
131
            isUnknownAges: true,
132
            parentDatafield: param,
133
            unknownAges: unknownAges,
134
          };
135
          mappedFilterState.push(unknownAgesItem);
×
136
        }
137
      }
138
    }
139
  });
140

141
  mappedFilterState.sort((a, b) => sectionOrder.indexOf(a.datafield) - sectionOrder.indexOf(b.datafield));
×
142

143
  const { QueryBar } = QueryBarGenerator({
×
144
    config: {
145
      maxItems: 2,
146
      displayAllActiveFilters: true,
147
      count: 'count',
148
      caseIDLabel: 'Participant IDs',
149
      rootPath: `${window.location.href}/`,
150
      viewQueryURL: true,
151
      queryUrlCharacterLimit: 70,
152
    },
153
    functions: {
154
      clearAll: () => {
UNCOV
155
        const paramValue = queryParams
×
UNCOV
156
          .filter((param) => !excludedParams.includes(param))
×
157
          .reduce((acc, param) => {
UNCOV
158
            acc[param] = '';
×
UNCOV
159
            return acc;
×
160
          }, {});
NEW
161
        const queryStr = generateQueryStr(query, queryParams, paramValue);
×
NEW
162
        window.history.replaceState(null, '', `/explore${queryStr}`);
×
163
        dispatch(resetAllData());
×
164
        dispatch(clearAllFilters());
×
165
      },
166
      clearImportFrom: () => {
167
        /*
168
        const paramValue = {
169
          'import_from': '',
170
        };
171
        // const queryStr = generateQueryStr(query, queryParams, paramValue);
172
        // navigate(`/explore${queryStr}`, { replace: true });
173
        // dispatch(updateImportfrom(null, []));
174
        */
175
      },
176
      clearUpload: () => {
UNCOV
177
        const paramValue = {
×
178
          'u': '',
179
          'u_fc': '',
180
          'u_um': '',
181
        };
NEW
182
        updateUrl(paramValue);
×
UNCOV
183
        dispatch(resetUploadData());
×
184
      },
185
      clearAutocomplete: () => {
UNCOV
186
        const paramValue = {
×
187
          'p_id': ''
188
        };
NEW
189
        updateUrl(paramValue);
×
UNCOV
190
        dispatch(updateAutocompleteData([]));
×
191
      },
192
      deleteAutocompleteItem: (item) => {
193
        const { autocomplete } = localFind;
×
194
        const newdata = [...autocomplete];
×
195
        // Handle both object (new) and string (legacy) parameter
196
        // For objects, use strict matching (title + type + synonym) from 1.9.0
NEW
197
        const index = typeof item === 'object'
×
NEW
198
          ? newdata.findIndex((v) => v.title === item.title && v.type === item.type && v.synonym === item.synonym)
×
NEW
199
          : newdata.findIndex((v) => v.title === item);
×
200

201
        if (index > -1) {
×
202
          newdata.splice(index, 1);
×
UNCOV
203
          const paramValue = {
×
UNCOV
204
            'p_id': newdata.map((dt) => dt.title).join('|')
×
205
          };
NEW
206
          updateUrl(paramValue);
×
UNCOV
207
          dispatch(updateAutocompleteData(newdata));
×
208
        }
209
      },
210
      resetFacetSection: (section) => {
211
        // Only update URL if updateURL flag is explicitly set to true in facet config
NEW
212
        if (section.updateURL === true) {
×
NEW
213
          const field = section.datafield;
×
NEW
214
          const paramValue = {};
×
NEW
215
          paramValue[field] = '';
×
216

217
          // Sync participant IDs from Redux state
NEW
218
          syncParticipantIdsToUrl(paramValue);
×
219

NEW
220
          updateUrl(paramValue);
×
221
        }
UNCOV
222
        dispatch(clearFacetSection(section));
×
223
      },
224
      resetFacetSlider: (section) => {
225
        const field = section.datafield;
×
NEW
226
        const paramValue = {};
×
227

228
        // Check if this is an unknownAges entry
229
        if (section.isUnknownAges) {
×
230
          // For unknownAges entries, clear the unknownAges parameter
231
          const unknownAgesField = `${section.parentDatafield}_unknownAges`;
×
232
          paramValue[unknownAgesField] = '';
×
233

234
          // Get the parent config to check updateURL flag
NEW
235
          const parentConfig = facetsConfig.find((c) => c.datafield === section.parentDatafield);
×
NEW
236
          if (parentConfig && parentConfig.updateURL === true) {
×
237
            // Sync participant IDs from Redux state
NEW
238
            syncParticipantIdsToUrl(paramValue);
×
239

NEW
240
            updateUrl(paramValue);
×
241
          }
242

243
          // Reset the unknownAges parameter in Redux state
244
          store.dispatch({
×
245
            type: 'UNKNOWN_AGES_CHANGED',
246
            payload: {
247
              datafield: section.parentDatafield,
248
              unknownAges: 'include',
249
            },
250
          });
251
        } else {
252
          // For regular slider entries, clear the slider range
253
          paramValue[field] = '';
×
254

255
          // Also clear the corresponding unknownAges parameter if it exists
256
          const unknownAgesField = `${field}_unknownAges`;
×
257
          if (queryParams.includes(unknownAgesField)) {
×
258
            paramValue[unknownAgesField] = '';
×
259
          }
260

261
          // Only update URL if updateURL flag is explicitly set to true in facet config
NEW
262
          if (section.updateURL === true) {
×
263
            // Sync participant IDs from Redux state
NEW
264
            syncParticipantIdsToUrl(paramValue);
×
265

NEW
266
            updateUrl(paramValue);
×
267
          }
UNCOV
268
          dispatch(clearSliderSection(section));
×
269
        }
270
      },
271
      resetUnknownAges: (section) => {
272
        const field = section.parentDatafield || section.datafield.replace('_unknownAges', '');
×
273
        const unknownAgesField = `${field}_unknownAges`;
×
NEW
274
        const paramValue = {};
×
275
        paramValue[unknownAgesField] = '';
×
276

277
        // Get the parent config to check updateURL flag
NEW
278
        const parentConfig = facetsConfig.find((c) => c.datafield === field);
×
NEW
279
        if (parentConfig && parentConfig.updateURL === true) {
×
280
          // Sync participant IDs from Redux state
NEW
281
          syncParticipantIdsToUrl(paramValue);
×
282

NEW
283
          updateUrl(paramValue);
×
284
        }
285

286
        // Reset the corresponding unknownAges parameter in Redux state
287
        store.dispatch({
×
288
          type: 'UNKNOWN_AGES_CHANGED',
289
          payload: {
290
            datafield: field,
291
            unknownAges: 'include',
292
          },
293
        });
294
      },
295
      resetFacetCheckbox: (section, checkbox) => {
296
        const field = section.datafield;
×
297
        const items = section.items;
×
298
        const idx = items.indexOf(checkbox);
×
299
        if (idx > -1) {
×
300
          items.splice(idx, 1);
×
301
        }
302

303
        // Only update URL if updateURL flag is explicitly set to true in facet config
NEW
304
        if (section.updateURL === true) {
×
NEW
305
          const paramValue = {};
×
NEW
306
          paramValue[field] = items.length > 0 ? items.join('|') : '';
×
307

308
          // Sync participant IDs from Redux state
NEW
309
          syncParticipantIdsToUrl(paramValue);
×
310

NEW
311
          updateUrl(paramValue);
×
312
        }
313

UNCOV
314
        dispatch(toggleCheckBox({
×
315
          datafield: section.datafield,
316
          isChecked: false,
317
          name: checkbox
318
        }));
319
      },
320
      generateUrl,
321
    },
322
    customStyles,
323
  });
324

325
  return (
×
326
    <ThemeProvider theme={createTheme(theme)}>
327
      <Container
328
        maxWidth="xl"
329
        className="c3dc_query_bar"
330
      >
331
        <QueryBar
332
          hasImportFrom={hasImportFrom}
333
          statusReducer={mappedFilterState}
334
          localFind={localFind}
335
        />
336
      </Container>
337
    </ThemeProvider>
338
  );
339
};
340

341
const mapStateToProps = (state) => ({
×
342
  hasImportFrom: state.inventoryReducer && state.inventoryReducer.importFromData && state.inventoryReducer.importFromData.length > 0,
×
343
  statusReducer: state.statusReducer.filterState,
344
  localFind: state.localFind,
345
  unknownAgesState: state.statusReducer.unknownAgesState,
346
});
×
347

348
export default connect(mapStateToProps, null)(QueryBarView);
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