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

SAP / ui5-webcomponents-react / 10486318980

21 Aug 2024 08:28AM CUT coverage: 86.822% (+7.0%) from 79.858%
10486318980

Pull #6214

github

web-flow
Merge 1081ba16b into 7a4697321
Pull Request #6214: refactor(FilterBar): remove reference copying of filter/input elements

2769 of 3747 branches covered (73.9%)

116 of 119 new or added lines in 3 files covered. (97.48%)

22 existing lines in 3 files now uncovered.

4961 of 5714 relevant lines covered (86.82%)

72997.84 hits per line

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

95.36
/packages/main/src/components/FilterBar/FilterDialog.tsx
1
import BarDesign from '@ui5/webcomponents/dist/types/BarDesign.js';
2
import ButtonDesign from '@ui5/webcomponents/dist/types/ButtonDesign.js';
3
import TableSelectionMode from '@ui5/webcomponents/dist/types/TableSelectionMode.js';
4
import TitleLevel from '@ui5/webcomponents/dist/types/TitleLevel.js';
5
import group2Icon from '@ui5/webcomponents-icons/dist/group-2.js';
6
import listIcon from '@ui5/webcomponents-icons/dist/list.js';
7
import searchIcon from '@ui5/webcomponents-icons/dist/search.js';
8
import { enrichEventWithDetails, useI18nBundle, useStylesheet } from '@ui5/webcomponents-react-base';
9
import type { ReactElement, RefObject } from 'react';
10
import { Children, cloneElement, useEffect, useId, useReducer, useRef, useState } from 'react';
11
import { FlexBoxDirection, FlexBoxJustifyContent, MessageBoxAction, MessageBoxType } from '../../enums/index.js';
12
import {
13
  ACTIVE,
14
  ALL,
15
  BASIC,
16
  CANCEL,
17
  FIELDS_BY_ATTRIBUTE,
18
  FILTER,
19
  FILTER_DIALOG_RESET_WARNING,
20
  FILTERS,
21
  GROUP_VIEW,
22
  HIDE_VALUES,
23
  LIST_VIEW,
24
  MANDATORY,
25
  OK,
26
  RESET,
27
  SEARCH_FOR_FILTERS,
28
  SHOW_VALUES,
29
  VISIBLE,
30
  VISIBLE_AND_ACTIVE
31
} from '../../i18n/i18n-defaults.js';
32
import { addCustomCSSWithScoping } from '../../internal/addCustomCSSWithScoping.js';
33
import type { OnReorderParams } from '../../internal/FilterBarDialogContext.js';
34
import { FilterBarDialogContext } from '../../internal/FilterBarDialogContext.js';
35
import { stopPropagation } from '../../internal/stopPropagation.js';
36
import type {
37
  DialogDomRef,
38
  InputPropTypes,
39
  SegmentedButtonPropTypes,
40
  TableSelectionDomRef
41
} from '../../webComponents/index.js';
42
import {
43
  Bar,
44
  Button,
45
  Dialog,
46
  Icon,
47
  Input,
48
  Option,
49
  Panel,
50
  SegmentedButton,
51
  SegmentedButtonItem,
52
  Select,
53
  Table,
54
  TableHeaderCell,
55
  TableHeaderRow,
56
  TableSelection,
57
  Title
58
} from '../../webComponents/index.js';
59
import type { FilterGroupItemInternalProps } from '../FilterGroupItem/types.js';
60
import { FlexBox } from '../FlexBox/index.js';
61
import { MessageBox } from '../MessageBox/index.js';
62
import { classNames, styleData } from './FilterBarDialog.module.css.js';
63
import type { FilterBarPropTypes } from './types.js';
64

65
interface ForceRequiredObject {
66
  required: string[];
67
  target: TableSelectionDomRef;
68
  selected: Set<string>;
69
  prevSelected: Set<string>;
70
  selectedKeys: Set<string>;
71
}
72

73
addCustomCSSWithScoping(
376✔
74
  'ui5-table',
75
  `
76
:host([data-component-name="FilterBarDialogTable"][data-with-value="false"]) #table,
77
:host([data-component-name="FilterBarDialogPanelTable"][data-with-value="false"]) #table {
78
   grid-template-columns: var(--_ui5wcr-CheckBoxWidthHeight) minmax(3rem, auto) minmax(3rem, 25%) !important;
79
}
80
:host([data-component-name="FilterBarDialogTable"][data-is-grouped]) #nodata-row {
81
  display: none;
82
}
83
`
84
);
85

86
addCustomCSSWithScoping(
376✔
87
  'ui5-table-header-row',
88
  `
89
:host([data-component-name="FilterBarDialogGroupTableHeaderRow"]) :first-child {
90
  visibility: hidden;
91
}
92
`
93
);
94

95
type ActiveFilterAttributes = 'all' | 'visible' | 'active' | 'visibleAndActive' | 'mandatory';
96
const getActiveFilters = (
376✔
97
  activeFilterAttribute: ActiveFilterAttributes,
98
  filter: ReactElement<FilterGroupItemInternalProps>
99
) => {
100
  switch (activeFilterAttribute) {
120!
101
    case 'all':
102
      return true;
120✔
103
    case 'visible':
104
      return filter.props?.hiddenInFilterBar !== true;
×
105
    case 'active':
106
      return filter.props?.active;
×
107
    case 'visibleAndActive':
108
      return filter.props?.hiddenInFilterBar !== true && filter.props?.active;
×
109
    case 'mandatory':
110
      return filter.props?.required;
×
111
    default:
112
      return true;
×
113
  }
114
};
115

116
interface FilterDialogPropTypes {
117
  open: boolean;
118
  handleDialogClose: FilterBarPropTypes['onFiltersDialogClose'];
119
  children: ReactElement<FilterGroupItemInternalProps>[];
120
  showRestoreButton: boolean;
121
  handleRestoreFilters: FilterBarPropTypes['onRestore'];
122
  handleDialogSave: (e, selectionChangePayload, orderedChildren) => void;
123
  onFiltersDialogSelectionChange?: FilterBarPropTypes['onFiltersDialogSelectionChange'];
124
  handleDialogSearch?: FilterBarPropTypes['onFiltersDialogSearch'];
125
  handleDialogCancel?: FilterBarPropTypes['onFiltersDialogCancel'];
126
  onAfterFiltersDialogOpen: FilterBarPropTypes['onAfterFiltersDialogOpen'];
127
  dialogRef: RefObject<DialogDomRef>;
128
  enableReordering?: FilterBarPropTypes['enableReordering'];
129
  isPhone?: boolean;
130
  onReorder?: FilterBarPropTypes['onReorder'];
131
}
132

133
export const FilterDialog = (props: FilterDialogPropTypes) => {
376✔
134
  const {
135
    open,
136
    handleDialogClose,
137
    children,
138
    showRestoreButton,
139
    dialogRef,
140
    enableReordering,
141
    isPhone,
142
    handleRestoreFilters,
143
    handleDialogSave,
144
    onFiltersDialogSelectionChange,
145
    handleDialogSearch,
146
    handleDialogCancel,
147
    onAfterFiltersDialogOpen,
148
    onReorder
149
  } = props;
1,690✔
150
  useStylesheet(styleData, 'FilterBarDialog');
1,690✔
151
  const uniqueId = useId();
1,690✔
152
  const [searchString, setSearchString] = useState('');
1,690✔
153
  const [selectedFilters, setSelectedFilters] = useState<undefined | string[]>(undefined);
1,690✔
154
  const [requiredFilters, setRequiredKeys] = useState({});
1,690✔
155
  const [showValues, toggleValues] = useReducer((prev) => !prev, false);
1,690✔
156
  const [messageBoxOpen, setMessageBoxOpen] = useState(false);
1,690✔
157
  const [forceRequired, setForceRequired] = useState<ForceRequiredObject>();
1,690✔
158
  const [showBtnsOnHover, setShowBtnsOnHover] = useState(true);
1,690✔
159
  const [isListView, setIsListView] = useState(true);
1,690✔
160
  const [filteredAttribute, setFilteredAttribute] = useState<ActiveFilterAttributes>('all');
1,690✔
161
  const [currentReorderedItem, setCurrentReorderedItem] = useState<OnReorderParams | Record<string, never>>({});
1,690✔
162
  const [orderedChildren, setOrderedChildren] = useState([]);
1,690✔
163
  const [updatedIndex, setUpdatedIndex] = useState(undefined);
1,690✔
164

165
  const currentReorderedItemOrderId = currentReorderedItem?.filterKey;
1,690✔
166
  const selected = (selectedFilters ?? []).join(' ');
1,690✔
167

168
  const dialogSearchRef = useRef(null);
1,690✔
169
  const tableRef = useRef(null);
1,690✔
170
  const okBtnRef = useRef(null);
1,690✔
171
  const prevIsListView = useRef(true);
1,690✔
172
  const selectionChangePayloadRef = useRef({ selectedFilterKeys: selectedFilters ?? [] });
1,690✔
173
  const initialSelected = useRef<string[] | undefined>(undefined);
1,690✔
174
  const prevRowKey = useRef(undefined);
1,690✔
175

176
  const i18nBundle = useI18nBundle('@ui5/webcomponents-react');
1,690✔
177
  const basicText = i18nBundle.getText(BASIC);
1,690✔
178
  const cancelText = i18nBundle.getText(CANCEL);
1,690✔
179
  const okText = i18nBundle.getText(OK);
1,690✔
180
  const searchForFiltersText = i18nBundle.getText(SEARCH_FOR_FILTERS);
1,690✔
181
  const filtersTitle = i18nBundle.getText(FILTERS);
1,690✔
182
  const resetText = i18nBundle.getText(RESET);
1,690✔
183
  const allText = i18nBundle.getText(ALL);
1,690✔
184
  const activeText = i18nBundle.getText(ACTIVE);
1,690✔
185
  const visibleText = i18nBundle.getText(VISIBLE);
1,690✔
186
  const visibleAndActiveText = i18nBundle.getText(VISIBLE_AND_ACTIVE);
1,690✔
187
  const mandatoryText = i18nBundle.getText(MANDATORY);
1,690✔
188
  const listViewText = i18nBundle.getText(LIST_VIEW);
1,690✔
189
  const groupViewText = i18nBundle.getText(GROUP_VIEW);
1,690✔
190
  const showValuesText = i18nBundle.getText(SHOW_VALUES);
1,690✔
191
  const hideValuesText = i18nBundle.getText(HIDE_VALUES);
1,690✔
192
  const filterText = i18nBundle.getText(FILTER);
1,690✔
193
  const fieldsByAttributeText = i18nBundle.getText(FIELDS_BY_ATTRIBUTE);
1,690✔
194

195
  const wasReordered = useRef(false);
1,690✔
196
  const handleReorder = (e: OnReorderParams) => {
1,690✔
197
    wasReordered.current = true;
50✔
198
    setCurrentReorderedItem(e);
50✔
199
  };
200

201
  const handleFocusFallback = () => {
1,690✔
202
    const rowKey = currentReorderedItem?.target?.rowKey;
65✔
203
    if (rowKey && tableRef.current && rowKey !== prevRowKey.current) {
65✔
204
      // we have to retrigger the internal item navigation logic after reordering,
205
      // otherwise keyboard nav and general focus handling is not working properly
206
      setTimeout(() => {
50✔
207
        const itemNav = tableRef.current._tableNavigation;
50✔
208
        itemNav._gridWalker.setGrid(itemNav._getNavigationItemsOfGrid());
50✔
209
        tableRef.current.querySelector(`[row-key="${rowKey}"]`).focus();
50✔
210
      }, 0);
211
      prevRowKey.current = rowKey;
50✔
212
    }
213
  };
214

215
  const visibleChildren = () =>
1,690✔
216
    children.filter((item) => {
299✔
217
      return !!item?.props && !item?.props?.hidden;
937✔
218
    });
219

220
  useEffect(() => {
1,690✔
221
    if (children.length) {
280✔
222
      setOrderedChildren(visibleChildren());
280✔
223
    }
224
  }, [children]);
225

226
  const renderChildren = () => {
1,690✔
227
    const searchStringLower = searchString.toLowerCase();
1,690✔
228
    const filteredChildren =
229
      searchStringLower.length > 0 || filteredAttribute !== 'all'
1,690✔
230
        ? orderedChildren.filter(
231
            (item) =>
321✔
232
              (searchStringLower === '' || item.props.label?.toLowerCase().includes(searchStringLower)) &&
233
              getActiveFilters(filteredAttribute, item)
234
          )
235
        : orderedChildren;
236

237
    return filteredChildren.map((child, index) => {
1,690✔
238
      return cloneElement<FilterGroupItemInternalProps>(child, {
5,093✔
239
        'data-index': index
240
      });
241
    });
242
  };
243

244
  const handleSearch = (e: Parameters<InputPropTypes['onInput']>[0]) => {
1,690✔
245
    if (typeof handleDialogSearch === 'function') {
138✔
246
      handleDialogSearch(enrichEventWithDetails(e, { value: e.target.value, element: e.target }));
99✔
247
    }
248
    setSearchString(e.target.value);
138✔
249
  };
250
  const handleSave = (e) => {
1,690✔
251
    const orderedChildrenIds = enableReordering ? orderedChildren.map((child) => child.props.filterKey) : [];
33✔
252
    handleDialogSave(e, selectionChangePayloadRef.current, orderedChildrenIds);
33✔
253
  };
254

255
  const handleClose = (e) => {
1,690✔
256
    stopPropagation(e);
14✔
257
    if (e.target !== e.currentTarget) {
14!
NEW
258
      return;
×
259
    }
260
    if (typeof handleDialogCancel === 'function') {
14✔
261
      handleDialogCancel(true);
14✔
262
    }
263
    handleDialogClose('escPressed');
14✔
264
  };
265

266
  const handleCancel = () => {
1,690✔
267
    if (typeof handleDialogCancel === 'function') {
14✔
268
      handleDialogCancel(false);
14✔
269
    }
270
    handleDialogClose('cancelButtonPressed');
14✔
271
  };
272

273
  const handleRestore = () => {
1,690✔
274
    setMessageBoxOpen(true);
47✔
275
  };
276
  const handleViewChange: SegmentedButtonPropTypes['onSelectionChange'] = (e) => {
1,690✔
277
    const selectedItem = e.detail.selectedItems.at(0);
20✔
278
    prevIsListView.current = isListView;
20✔
279
    setIsListView(selectedItem.dataset.id === 'list');
20✔
280
  };
281

282
  const handleMessageBoxClose = (action) => {
1,690✔
283
    if (action === 'OK') {
47✔
284
      const initialChildren = visibleChildren();
19✔
285
      const payload = {
19✔
286
        source: 'dialog' as const,
287
        selectedFilterKeys: initialSelected.current,
288
        previousSelectedFilterKeys: selectedFilters,
289
        reorderedFilterKeys: enableReordering ? initialChildren.map((child) => `${child.props.filterKey}`) : null
30✔
290
      };
291
      setSelectedFilters(initialSelected.current);
19✔
292
      setOrderedChildren(initialChildren);
19✔
293
      handleRestoreFilters(payload);
19✔
294
    }
295
    setMessageBoxOpen(false);
47✔
296
    setTimeout(() => {
47✔
297
      okBtnRef.current.focus();
47✔
298
    }, 50);
299
  };
300

301
  useEffect(() => {
1,690✔
302
    if (orderedChildren.length && wasReordered.current) {
461✔
303
      if (typeof onReorder === 'function') {
50✔
304
        onReorder({ reorderedFilterKeys: orderedChildren.map((item) => `${item.props.filterKey}`) });
300✔
305
      }
306
      wasReordered.current = false;
50✔
307
    }
308
  }, [orderedChildren, onReorder]);
309

310
  useEffect(() => {
1,690✔
311
    if (currentReorderedItem?.index != null) {
162✔
312
      setOrderedChildren((prev: any[]) => {
50✔
313
        const { index, direction } = currentReorderedItem;
50✔
314
        switch (direction) {
50✔
315
          case 'up':
316
            if (index > 0) {
10✔
317
              setUpdatedIndex(index - 1);
10✔
318
              const temp = prev[index];
10✔
319
              prev[index] = prev[index - 1];
10✔
320
              prev[index - 1] = temp;
10✔
321
            }
322
            break;
10✔
323
          case 'down':
324
            if (index < prev.length - 1) {
10✔
325
              setUpdatedIndex(index + 1);
10✔
326
              const temp = prev[index];
10✔
327
              prev[index] = prev[index + 1];
10✔
328
              prev[index + 1] = temp;
10✔
329
            }
330
            break;
10✔
331
          case 'top':
332
            if (index > 0) {
10✔
333
              setUpdatedIndex(0);
10✔
334
              const item = prev.splice(index, 1)[0];
10✔
335
              prev.unshift(item);
10✔
336
            }
337
            break;
10✔
338
          case 'bottom':
339
            if (index < prev.length - 1) {
20✔
340
              setUpdatedIndex(prev.length - 1);
20✔
341
              const item = prev.splice(index, 1)[0];
20✔
342
              prev.push(item);
20✔
343
            }
344
            break;
20✔
345
        }
346
        return [...prev];
50✔
347
      });
348
      void currentReorderedItem.target.focus();
50✔
349
    }
350
  }, [currentReorderedItem]);
351

352
  useEffect(() => {
1,690✔
353
    if (updatedIndex != null) {
157✔
354
      prevRowKey.current = undefined;
45✔
355
    }
356
  }, [updatedIndex]);
357

358
  const handleAttributeFilterChange = (e) => {
1,690✔
359
    setFilteredAttribute(e.detail.selectedOption.dataset.id);
×
360
  };
361

362
  const fireOnFiltersDialogSelectionChange = (_selected, selectedKeys, prevSelected, prevent = false) => {
1,690!
363
    setSelectedFilters(Array.from(_selected));
224✔
364
    if (!prevent && typeof onFiltersDialogSelectionChange === 'function') {
224✔
365
      const payload = {
168✔
366
        toggledFilterKeys: selectedKeys,
367
        selected: selectedKeys.size === 1 ? _selected.has(selectedKeys.values().next().value) : undefined,
168✔
368
        selectedFilterKeys: _selected,
369
        previousSelectedFilterKeys: prevSelected
370
      };
371
      onFiltersDialogSelectionChange(payload);
168✔
372
    }
373
  };
374

375
  useEffect(() => {
1,690✔
376
    if (selectedFilters?.length) {
484✔
377
      selectionChangePayloadRef.current = { selectedFilterKeys: selectedFilters };
372✔
378
    }
379
  }, [selectedFilters]);
380

381
  const handleCheckBoxChange = (e) => {
1,690✔
382
    if (e.target.hasAttribute('ui5-table-selection')) {
224✔
383
      const _selected: Set<string> = new Set(e.target.selected.length ? e.target.selected.split(' ') : []);
224✔
384
      const prevSelected: Set<string> = new Set(selectedFilters ?? []);
224!
385
      const alwaysSelected = Object.keys(requiredFilters).filter((key) => requiredFilters[key]);
672✔
386
      const selectedKeys: Set<string> = _selected.symmetricDifference(prevSelected);
224✔
387

388
      // reset required filters to `true`
389
      if (alwaysSelected.length) {
224✔
390
        setForceRequired({
224✔
391
          required: alwaysSelected,
392
          target: e.target,
393
          selected: _selected,
394
          prevSelected,
395
          selectedKeys
396
        });
397
        return;
224✔
398
      }
399

NEW
400
      fireOnFiltersDialogSelectionChange(_selected, selectedKeys, prevSelected);
×
401
    }
402
  };
403

404
  useEffect(() => {
1,690✔
405
    if (forceRequired && forceRequired.target) {
560✔
406
      const { prevSelected, selectedKeys, selected: _selected, required, target } = forceRequired;
224✔
407
      required.forEach((requiredString) => {
224✔
408
        _selected.add(requiredString);
224✔
409
      });
410

411
      setTimeout(() => {
224✔
412
        target.selected = Array.from(_selected).join(' ');
224✔
413
        const prevent = selectedKeys.size === 1 && required.includes(selectedKeys.values().next().value);
224✔
414
        fireOnFiltersDialogSelectionChange(_selected, selectedKeys, prevSelected, prevent);
224✔
415
      });
416

417
      setForceRequired(undefined);
224✔
418
    }
419
  }, [forceRequired]);
420

421
  const renderGroups = () => {
1,690✔
422
    const groups = {};
74✔
423
    Children.forEach(renderChildren(), (child) => {
74✔
424
      const childGroups = child.props.groupName ?? 'default';
132✔
425
      if (groups[childGroups]) {
132✔
426
        groups[childGroups].push(child);
22✔
427
      } else {
428
        groups[childGroups] = [child];
110✔
429
      }
430
    });
431

432
    // filter groups
433
    return Object.keys(groups)
74✔
434
      .sort((x, y) => (x === 'default' ? -1 : y === 'role' ? 1 : 0))
81!
435
      .map((item, index) => {
436
        return (
110✔
437
          <Panel
438
            headerText={item === 'default' ? basicText : item}
110✔
439
            className={classNames.groupPanel}
440
            key={`${item === 'default' ? basicText : item}${index}`}
110✔
441
          >
442
            <Table
443
              className={classNames.tableInGroup}
444
              data-component-name="FilterBarDialogPanelTable"
445
              data-with-value={showValues}
446
              features={
447
                <TableSelection
448
                  mode={TableSelectionMode.Multiple}
449
                  selected={selected}
450
                  onChange={handleCheckBoxChange}
451
                />
452
              }
453
              headerRow={
454
                <TableHeaderRow className={classNames.groupedTableHeader}>
455
                  <TableHeaderCell>{filterText}</TableHeaderCell>
456
                  {!showValues && <TableHeaderCell className={classNames.tHactive}>{activeText}</TableHeaderCell>}
220✔
457
                </TableHeaderRow>
458
              }
459
            >
460
              {groups[item]}
461
            </Table>
462
          </Panel>
463
        );
464
      });
465
  };
466

467
  useEffect(() => {
1,690✔
468
    if (initialSelected.current === undefined && selected.length) {
448✔
469
      initialSelected.current = selectedFilters;
112✔
470
    }
471
  }, [selected]);
472

473
  return (
1,690✔
474
    <FilterBarDialogContext.Provider
475
      value={{
476
        isFilterInDialog: true,
477
        enableReordering,
478
        onReorder: handleReorder,
479
        isListView,
480
        withValues: showValues,
481
        handleFocusFallback,
482
        showBtnsOnHover,
483
        setShowBtnsOnHover,
484
        currentReorderedItemOrderId,
485
        setSelectedKeys: setSelectedFilters,
486
        setRequiredKeys,
487
        prevIsListView
488
      }}
489
    >
490
      <Dialog
491
        open={open}
492
        ref={dialogRef}
493
        data-component-name="FilterBarDialog"
494
        data-is-phone={isPhone}
495
        onClose={handleClose}
496
        onOpen={onAfterFiltersDialogOpen}
497
        resizable
498
        draggable
499
        className={classNames.dialogComponent}
500
        preventFocusRestore
501
        initialFocus={`${uniqueId}-fb-dialog-search`}
502
        header={
503
          <Bar
504
            design={BarDesign.Header}
505
            startContent={
506
              <Title level={TitleLevel.H4} title={filtersTitle}>
507
                {filtersTitle}
508
              </Title>
509
            }
510
            endContent={
511
              showRestoreButton && (
3,033✔
512
                <Button design={ButtonDesign.Transparent} onClick={handleRestore}>
513
                  {resetText}
514
                </Button>
515
              )
516
            }
517
          />
518
        }
519
        footer={
520
          <Bar
521
            design={BarDesign.Footer}
522
            endContent={
523
              <FlexBox justifyContent={FlexBoxJustifyContent.End} className={classNames.footer}>
524
                <Button
525
                  ref={okBtnRef}
526
                  onClick={handleSave}
527
                  data-component-name="FilterBarDialogSaveBtn"
528
                  design={ButtonDesign.Emphasized}
529
                >
530
                  {okText}
531
                </Button>
532
                <Button
533
                  design={ButtonDesign.Transparent}
534
                  onClick={handleCancel}
535
                  data-component-name="FilterBarDialogCancelBtn"
536
                >
537
                  {cancelText}
538
                </Button>
539
              </FlexBox>
540
            }
541
          />
542
        }
543
      >
544
        <FlexBox direction={FlexBoxDirection.Column} className={classNames.subheaderContainer}>
545
          <FlexBox direction={FlexBoxDirection.Row}>
546
            <Select
547
              onChange={handleAttributeFilterChange}
548
              title={fieldsByAttributeText}
549
              accessibleName={fieldsByAttributeText}
550
              onClose={(e) => {
NEW
551
                e.stopPropagation();
×
552
              }}
553
            >
554
              <Option selected={filteredAttribute === 'all'} data-id="all">
555
                {allText}
556
              </Option>
557
              <Option selected={filteredAttribute === 'visible'} data-id="visible">
558
                {visibleText}
559
              </Option>
560
              <Option selected={filteredAttribute === 'active'} data-id="active">
561
                {activeText}
562
              </Option>
563
              <Option selected={filteredAttribute === 'visibleAndActive'} data-id="visibleAndActive">
564
                {visibleAndActiveText}
565
              </Option>
566
              <Option selected={filteredAttribute === 'mandatory'} data-id="mandatory">
567
                {mandatoryText}
568
              </Option>
569
            </Select>
570
            <span className={classNames.spacer} />
571
            <Button
572
              design={ButtonDesign.Transparent}
573
              onClick={toggleValues}
574
              aria-live="polite"
575
              className={classNames.showValuesBtn}
576
            >
577
              {showValues ? hideValuesText : showValuesText}
1,690✔
578
            </Button>
579
            <SegmentedButton onSelectionChange={handleViewChange}>
580
              <SegmentedButtonItem icon={listIcon} data-id="list" selected={isListView} accessibleName={listViewText} />
581
              <SegmentedButtonItem
582
                icon={group2Icon}
583
                data-id="group"
584
                selected={!isListView}
585
                accessibleName={groupViewText}
586
              />
587
            </SegmentedButton>
588
          </FlexBox>
589
          <FlexBox className={classNames.searchInputContainer}>
590
            <Input
591
              id={`${uniqueId}-fb-dialog-search`}
592
              noTypeahead
593
              placeholder={searchForFiltersText}
594
              onInput={handleSearch}
595
              showClearIcon
596
              icon={<Icon name={searchIcon} />}
597
              ref={dialogSearchRef}
598
              className={classNames.searchInput}
599
              data-component-name="FilterBarDialogSearchInput"
600
            />
601
          </FlexBox>
602
        </FlexBox>
603
        <Table
604
          ref={tableRef}
605
          className={!isListView && classNames.inactiveTable}
1,764✔
606
          data-component-name="FilterBarDialogTable"
607
          data-is-grouped={!isListView}
608
          data-with-value={`${showValues}`}
609
          nodata={!isListView ? <span /> : undefined}
1,690✔
610
          tabIndex={!isListView ? -1 : undefined}
1,690✔
611
          features={
612
            <>
613
              <TableSelection mode={TableSelectionMode.Multiple} onChange={handleCheckBoxChange} selected={selected} />
614
            </>
615
          }
616
          headerRow={
617
            <TableHeaderRow
618
              data-component-name={!isListView ? 'FilterBarDialogGroupTableHeaderRow' : 'FilterBarDialogTableHeaderRow'}
1,690✔
619
            >
620
              <TableHeaderCell>{filterText}</TableHeaderCell>
621
              {!showValues && <TableHeaderCell className={classNames.tHactive}>{activeText}</TableHeaderCell>}
2,386✔
622
            </TableHeaderRow>
623
          }
624
        >
625
          {isListView && renderChildren()}
3,306✔
626
        </Table>
627
        {!isListView && renderGroups()}
1,764✔
628
      </Dialog>
629
      {showRestoreButton && messageBoxOpen && (
3,080✔
630
        <MessageBox
631
          open
632
          type={MessageBoxType.Warning}
633
          actions={[MessageBoxAction.OK, MessageBoxAction.Cancel]}
634
          onClose={handleMessageBoxClose}
635
          data-component-name="FilterBarDialogResetMessageBox"
636
        >
637
          {i18nBundle.getText(FILTER_DIALOG_RESET_WARNING)}
638
        </MessageBox>
639
      )}
640
    </FilterBarDialogContext.Provider>
641
  );
642
};
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