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

CBIIT / bento-c3dc-frontend / 25557662953

08 May 2026 01:15PM UTC coverage: 0.112% (-0.04%) from 0.153%
25557662953

push

github

web-flow
Merge pull request #504 from CBIIT/C3DC-2146

C3DC 2146

6 of 6162 branches covered (0.1%)

Branch coverage included in aggregate %.

0 of 2386 new or added lines in 69 files covered. (0.0%)

31 existing lines in 7 files now uncovered.

10 of 8158 relevant lines covered (0.12%)

0.06 hits per line

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

0.0
/src/pages/CohortAnalyzer/components/AddChartInlinePanel.js
NEW
1
import React, { useMemo } from 'react';
×
NEW
2
import { makeStyles } from '@material-ui/core';
×
NEW
3
import { ADD_CHART_DATA_TYPES, isAddChartDataTypeOnStrip } from '../config/cohortAnalyzerChartCatalog';
×
NEW
4
import { ChartTypeSelectIcon, CHART_TYPE_OPTIONS } from '../HistogramPanel/chart/HistogramChartTypeIcons';
×
5

NEW
6
const useStyles = makeStyles(() => ({
×
7
  root: {
8
    flex: 1,
9
    display: 'flex',
10
    flexDirection: 'column',
11
    alignItems: 'stretch',
12
    minHeight: 0,
13
    width: '100%',
14
  },
15
  headerRow: {
16
    display: 'flex',
17
    alignItems: 'center',
18
    justifyContent: 'space-between',
19
    gap: 8,
20
    margin: '0 0 8px',
21
  },
22
  header: {
23
    fontFamily: 'Poppins, sans-serif',
24
    fontSize: 14,
25
    fontWeight: 500,
26
    color: '#5C5C5C',
27
    margin: 0,
28
  },
29
  closeButton: {
30
    flexShrink: 0,
31
    margin: 0,
32
    padding: '2px 6px',
33
    border: 'none',
34
    borderRadius: 4,
35
    background: 'transparent',
36
    cursor: 'pointer',
37
    fontFamily: 'Poppins, sans-serif',
38
    fontSize: 18,
39
    lineHeight: 1,
40
    color: '#5C5C5C',
41
    '&:hover': {
42
      background: 'rgba(0, 0, 0, 0.06)',
43
    },
44
  },
45
  divider: {
46
    height: 1,
47
    background: '#ADADAD',
48
    margin: '0 0 10px',
49
  },
50
  list: {
51
    listStyle: 'none',
52
    margin: 0,
53
    padding: 0,
54
    flex: 1,
55
    minHeight: 0,
56
    overflowY: 'auto',
57
  },
58
  listItem: {
59
    display: 'flex',
60
    alignItems: 'center',
61
    gap: 10,
62
    padding: '8px 4px',
63
    fontFamily: 'Poppins, sans-serif',
64
    fontSize: 13,
65
    color: '#1C2B33',
66
    cursor: 'pointer',
67
    borderBottom: '1px solid #EFEFEF',
68
    '&:last-child': {
69
      borderBottom: 'none',
70
    },
71
  },
72
  /** Unavailable / not wired — entire row reads as disabled. */
73
  listItemDisabled: {
74
    cursor: 'not-allowed',
75
    color: '#9E9E9E',
76
    opacity: 0.5,
77
    filter: 'grayscale(1)',
78
    pointerEvents: 'none',
79
  },
80
  /** Addable in catalog but already on layout — only the primary label is muted; status text stays readable. */
81
  listItemAlreadyOnCard: {
82
    cursor: 'not-allowed',
83
    pointerEvents: 'none',
84
  },
85
  primaryBlock: {
86
    display: 'flex',
87
    alignItems: 'center',
88
    gap: 10,
89
    minWidth: 0,
90
    flex: 1,
91
  },
92
  primaryBlockMuted: {
93
    color: '#9E9E9E',
94
    opacity: 0.75,
95
  },
96
  radioOuterMuted: {
97
    borderColor: '#CFCFCF',
98
    filter: 'grayscale(0.6)',
99
  },
100
  statusHintLegible: {
101
    marginLeft: 'auto',
102
    fontSize: 11,
103
    lineHeight: 1.3,
104
    color: '#1C2B33',
105
    fontWeight: 500,
106
    flexShrink: 0,
107
  },
108
  statusHintDim: {
109
    marginLeft: 'auto',
110
    fontSize: 10,
111
    color: '#9E9E9E',
112
  },
113
  radioOuter: {
114
    width: 16,
115
    height: 16,
116
    borderRadius: '50%',
117
    border: '2px solid #ADADAD',
118
    flexShrink: 0,
119
    display: 'flex',
120
    alignItems: 'center',
121
    justifyContent: 'center',
122
  },
123
  radioOuterSelected: {
124
    borderColor: '#18677A',
125
  },
126
  radioInner: {
127
    width: 8,
128
    height: 8,
129
    borderRadius: '50%',
130
    background: '#18677A',
131
  },
132
  hint: {
133
    fontFamily: 'Poppins, sans-serif',
134
    fontSize: 11,
135
    color: '#5C5C5C',
136
    margin: '0 0 10px',
137
  },
138
  step2Body: {
139
    flex: 1,
140
    display: 'flex',
141
    flexDirection: 'column',
142
    minHeight: 0,
143
    marginTop: 4,
144
  },
145
  chartGrid: {
146
    display: 'grid',
147
    gridTemplateColumns: '1fr 1fr',
148
    gap: 10,
149
    flex: 1,
150
    alignContent: 'start',
151
  },
152
  chartCard: {
153
    borderRadius: 6,
154
    border: '2px solid #18677A',
155
    background: '#E2F1F5',
156
    padding: '10px 8px',
157
    cursor: 'pointer',
158
    textAlign: 'center',
159
    fontFamily: 'Poppins, sans-serif',
160
    fontSize: 11,
161
    color: '#1C2B33',
162
    transition: 'box-shadow 0.15s ease',
163
    '&:hover': {
164
      boxShadow: '0 4px 12px rgba(24, 103, 122, 0.2)',
165
    },
166
  },
167
  chartLabel: {
168
    marginTop: 6,
169
    lineHeight: 1.3,
170
  },
171
}));
172

173
/**
174
 * In-strip add chart: step 1 pick data (click advances); step 2 pick chart (click adds).
175
 */
NEW
176
const AddChartInlinePanel = ({
×
177
  step,
178
  setStep,
179
  selectedCatalogId,
180
  setSelectedCatalogId,
181
  onCompleteWithChartType,
182
  onClose,
183
  existingStripKeys = [],
×
184
  selectedDatasets = [],
×
185
}) => {
NEW
186
  const classes = useStyles();
×
187

NEW
188
  const selectedEntry = useMemo(
×
NEW
189
    () => ADD_CHART_DATA_TYPES.find((e) => e.id === selectedCatalogId),
×
190
    [selectedCatalogId],
191
  );
192

NEW
193
  const sortedAddChartDataTypes = useMemo(() => {
×
NEW
194
    const indexed = ADD_CHART_DATA_TYPES.map((entry, index) => ({ entry, index }));
×
NEW
195
    indexed.sort((a, b) => {
×
NEW
196
      const aOn = isAddChartDataTypeOnStrip(a.entry, existingStripKeys, selectedDatasets);
×
NEW
197
      const bOn = isAddChartDataTypeOnStrip(b.entry, existingStripKeys, selectedDatasets);
×
NEW
198
      const aAddable = a.entry.available && a.entry.datasetKey && !aOn;
×
NEW
199
      const bAddable = b.entry.available && b.entry.datasetKey && !bOn;
×
NEW
200
      if (aAddable && !bAddable) return -1;
×
NEW
201
      if (!aAddable && bAddable) return 1;
×
NEW
202
      return a.index - b.index;
×
203
    });
NEW
204
    return indexed.map(({ entry }) => entry);
×
205
  }, [existingStripKeys, selectedDatasets]);
206

NEW
207
  const handlePickDataType = (entry) => {
×
NEW
208
    if (!entry.available || !entry.datasetKey) return;
×
NEW
209
    if (isAddChartDataTypeOnStrip(entry, existingStripKeys, selectedDatasets)) return;
×
NEW
210
    if (entry.skipChartTypeStep) {
×
NEW
211
      onCompleteWithChartType(null, entry.id);
×
NEW
212
      return;
×
213
    }
NEW
214
    setSelectedCatalogId(entry.id);
×
NEW
215
    setStep(2);
×
216
  };
217

NEW
218
  return (
×
219
    <div className={classes.root}>
220
      <div className={classes.headerRow}>
221
        <p className={classes.header}>
222
          {step === 1 ? 'Choose one:' : 'Choose chart type:'}
×
223
        </p>
224
        {typeof onClose === 'function' ? (
225
          <button
×
226
            type="button"
227
            className={classes.closeButton}
228
            onClick={onClose}
229
            aria-label="Close"
230
          >
231
            <svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
232
              <rect x="0.177734" y="0.926758" width="1.06064" height="11.6671" transform="rotate(-45 0.177734 0.926758)" fill="#5C5C5C" stroke="#5C5C5C" stroke-width="0.25" />
233
              <rect x="8.42773" y="0.176758" width="1.06064" height="11.6671" transform="rotate(45 8.42773 0.176758)" fill="#5C5C5C" stroke="#5C5C5C" stroke-width="0.25" />
234
            </svg>
235

236
          </button>
237
        ) : null}
238
      </div>
239
      <div className={classes.divider} />
240

241
      {step === 1 && (
×
242
        <ul className={classes.list} role="listbox" aria-label="Chart data types">
243
          {sortedAddChartDataTypes.map((entry) => {
NEW
244
            const onStrip = isAddChartDataTypeOnStrip(entry, existingStripKeys, selectedDatasets);
×
NEW
245
            const isFullyDisabled = !entry.available || !entry.datasetKey;
×
NEW
246
            const disabled = isFullyDisabled || onStrip;
×
NEW
247
            const isAlreadyOnCard = onStrip && !isFullyDisabled;
×
NEW
248
            const selected = selectedCatalogId === entry.id;
×
NEW
249
            const disabledHint = (() => {
×
NEW
250
              if (!disabled) return null;
×
NEW
251
              if (
×
252
                entry.datasetKey === 'survivalAnalysis'
×
253
                && Array.isArray(selectedDatasets)
254
                && selectedDatasets.includes('survivalAnalysis')
255
              ) {
NEW
256
                return 'Already showing';
×
257
              }
NEW
258
              if (onStrip) return 'Already on chart';
×
NEW
259
              return 'Coming soon';
×
260
            })();
NEW
261
            return (
×
262
              <li
263
                key={entry.id}
264
                role="option"
265
                aria-selected={selected}
266
                aria-disabled={disabled}
267
                className={[
268
                  classes.listItem,
269
                  isFullyDisabled ? classes.listItemDisabled : '',
×
270
                  isAlreadyOnCard ? classes.listItemAlreadyOnCard : '',
×
271
                ].filter(Boolean).join(' ')}
NEW
272
                onClick={() => !disabled && handlePickDataType(entry)}
×
273
                onKeyDown={(ev) => {
NEW
274
                  if (disabled) return;
×
NEW
275
                  if (ev.key === 'Enter' || ev.key === ' ') {
×
NEW
276
                    ev.preventDefault();
×
NEW
277
                    handlePickDataType(entry);
×
278
                  }
279
                }}
280
                tabIndex={disabled ? -1 : 0}
×
281
              >
282
                <span
283
                  className={[
284
                    classes.primaryBlock,
285
                    isAlreadyOnCard ? classes.primaryBlockMuted : '',
×
286
                  ].filter(Boolean).join(' ')}
287
                >
288
                  <span
289
                    className={[
290
                      classes.radioOuter,
291
                      selected ? classes.radioOuterSelected : '',
×
292
                      isAlreadyOnCard ? classes.radioOuterMuted : '',
×
293
                    ].filter(Boolean).join(' ')}
294
                    aria-hidden
295
                  >
296
                    {selected ? <span className={classes.radioInner} /> : null}
×
297
                  </span>
298
                  <span>{entry.label}</span>
299
                </span>
300
                {disabledHint ? (
301
                  <span
×
302
                    className={isAlreadyOnCard ? classes.statusHintLegible : classes.statusHintDim}
×
303
                  >
304
                    {disabledHint}
305
                  </span>
306
                ) : null}
307
              </li>
308
            );
309
          })}
310
        </ul>
311
      )}
312

313
      {step === 2 && (
×
314
        <div className={classes.step2Body}>
315
          <p className={classes.hint}>{selectedEntry ? selectedEntry.label : null}</p>
×
316
          <div className={classes.chartGrid} role="listbox" aria-label="Chart visualizations">
317
            {CHART_TYPE_OPTIONS.map(({ type, label }) => (
NEW
318
              <div
×
319
                key={type}
320
                role="option"
321
                aria-selected={false}
322
                tabIndex={0}
323
                className={classes.chartCard}
324
                onClick={() => {
NEW
325
                  if (selectedEntry && selectedEntry.datasetKey) {
×
NEW
326
                    onCompleteWithChartType(type);
×
327
                  }
328
                }}
329
                onKeyDown={(ev) => {
NEW
330
                  if (ev.key === 'Enter' || ev.key === ' ') {
×
NEW
331
                    ev.preventDefault();
×
NEW
332
                    if (selectedEntry && selectedEntry.datasetKey) {
×
NEW
333
                      onCompleteWithChartType(type);
×
334
                    }
335
                  }
336
                }}
337
              >
338
                <ChartTypeSelectIcon type={type} size={40} />
339
                <div className={classes.chartLabel}>{label}</div>
340
              </div>
341
            ))}
342
          </div>
343
        </div>
344
      )}
345
    </div>
346
  );
NEW
347
};
×
348

349
export default AddChartInlinePanel;
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