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

SAP / ui5-webcomponents-react / 13678295434

05 Mar 2025 02:29PM CUT coverage: 87.646% (+0.2%) from 87.425%
13678295434

Pull #7040

github

web-flow
Merge 08b46ea92 into 5a406c31d
Pull Request #7040: fix(FilterBar): disable "down" reorder btns on last row

3061 of 4026 branches covered (76.03%)

7 of 7 new or added lines in 2 files covered. (100.0%)

2 existing lines in 1 file now uncovered.

5193 of 5925 relevant lines covered (87.65%)

90108.01 hits per line

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

97.93
/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 { addCustomCSSWithScoping } from '@ui5/webcomponents-react-base/dist/utils/addCustomCSSWithScoping.js';
10
import type { ReactElement, RefObject } from 'react';
11
import { Children, cloneElement, useEffect, useId, useReducer, useRef, useState } from 'react';
12
import { FlexBoxDirection, FlexBoxJustifyContent, MessageBoxAction, MessageBoxType } from '../../enums/index.js';
13
import {
14
  ACTIVE,
15
  ALL,
16
  BASIC,
17
  CANCEL,
18
  FIELDS_BY_ATTRIBUTE,
19
  FILTER,
20
  FILTER_DIALOG_RESET_WARNING,
21
  FILTERS,
22
  GROUP_VIEW,
23
  HIDE_VALUES,
24
  LIST_VIEW,
25
  MANDATORY,
26
  OK,
27
  RESET,
28
  SEARCH_FOR_FILTERS,
29
  SHOW_VALUES,
30
  VISIBLE,
31
  VISIBLE_AND_ACTIVE
32
} from '../../i18n/i18n-defaults.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(
671✔
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(
671✔
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 = (
671✔
97
  activeFilterAttribute: ActiveFilterAttributes,
98
  filter: ReactElement<FilterGroupItemInternalProps>
99
) => {
100
  switch (activeFilterAttribute) {
240!
101
    case 'all':
102
      return true;
240✔
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) => {
671✔
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;
3,941✔
150
  useStylesheet(styleData, 'FilterBarDialog');
3,941✔
151
  const uniqueId = useId();
3,941✔
152
  const [searchString, setSearchString] = useState('');
3,941✔
153
  const [selectedFilters, setSelectedFilters] = useState<undefined | string[]>(undefined);
3,941✔
154
  const [requiredFilters, setRequiredKeys] = useState({});
3,941✔
155
  const [showValues, toggleValues] = useReducer((prev) => !prev, false);
3,941✔
156
  const [messageBoxOpen, setMessageBoxOpen] = useState(false);
3,941✔
157
  const [forceRequired, setForceRequired] = useState<ForceRequiredObject>();
3,941✔
158
  const [showBtnsOnHover, setShowBtnsOnHover] = useState(true);
3,941✔
159
  const [isListView, setIsListView] = useState(true);
3,941✔
160
  const [filteredAttribute, setFilteredAttribute] = useState<ActiveFilterAttributes>('all');
3,941✔
161
  const [currentReorderedItem, setCurrentReorderedItem] = useState<OnReorderParams | Record<string, never>>({});
3,941✔
162
  const [orderedChildren, setOrderedChildren] = useState([]);
3,941✔
163
  const [updatedIndex, setUpdatedIndex] = useState(undefined);
3,941✔
164

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

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

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

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

201
  const handleFocusFallback = () => {
3,941✔
202
    const rowKey = currentReorderedItem?.target?.rowKey;
130✔
203
    if (rowKey && tableRef.current && rowKey !== prevRowKey.current) {
130✔
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(() => {
100✔
207
        const itemNav = tableRef.current._tableNavigation;
100✔
208
        itemNav._gridWalker.setGrid(itemNav._getNavigationItemsOfGrid());
100✔
209
        tableRef.current.querySelector(`[row-key="${rowKey}"]`).focus();
100✔
210
      }, 0);
211
      prevRowKey.current = rowKey;
100✔
212
    }
213
  };
214

215
  const visibleChildren = () =>
3,941✔
216
    children.filter((item) => {
772✔
217
      return !!item?.props && !item?.props?.hidden;
2,396✔
218
    });
219

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

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

237
    return filteredChildren.map((child, index) => {
3,941✔
238
      return cloneElement<FilterGroupItemInternalProps>(child, {
11,692✔
239
        'data-index': index
240
      });
241
    });
242
  };
243

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

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

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

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

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

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

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

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

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

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

375
  useEffect(() => {
1,973✔
376
    if (selectedFilters) {
2,603✔
377
      selectionChangePayloadRef.current = { selectedFilterKeys: selectedFilters };
1,126✔
378
    }
491✔
379
  }, [selectedFilters]);
380

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

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

240✔
400
      fireOnFiltersDialogSelectionChange(_selected, selectedKeys, prevSelected);
70✔
401
    }
402
  };
70✔
403

404
  useEffect(() => {
1,973✔
405
    if (forceRequired && forceRequired.target) {
624✔
406
      const { prevSelected, selectedKeys, selected: _selected, required, target } = forceRequired;
2,208✔
407
      required.forEach((requiredString) => {
864✔
408
        _selected.add(requiredString);
480✔
409
      });
240✔
410

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

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

421
  const renderGroups = () => {
1,973✔
422
    const groups = {};
74✔
423
    Children.forEach(renderChildren(), (child) => {
2,042✔
424
      const childGroups = child.props.groupName ?? 'default';
206✔
425
      if (groups[childGroups]) {
206✔
426
        groups[childGroups].push(child);
154✔
427
      } else {
132✔
428
        groups[childGroups] = [child];
132✔
429
      }
430
    });
110✔
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) => {
74✔
436
        return (
191!
437
          <Panel
438
            headerText={item === 'default' ? basicText : item}
110✔
439
            className={classNames.groupPanel}
440
            key={`${item === 'default' ? basicText : item}${index}`}
220✔
441
          >
442
            <Table
110✔
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
              }
220✔
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
        );
1,968✔
464
      });
598✔
465
  };
144✔
466

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

473
  return (
1,973✔
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}
3,393✔
508
              </Title>
509
            }
510
            endContent={
511
              showRestoreButton && (
3,403✔
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) => {
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}
1,968✔
574
              aria-live="polite"
575
              className={classNames.showValuesBtn}
576
            >
577
              {showValues ? hideValuesText : showValuesText}
1,973✔
578
            </Button>
579
            <SegmentedButton onSelectionChange={handleViewChange}>
580
              <SegmentedButtonItem
581
                icon={listIcon}
582
                data-id="list"
583
                selected={isListView}
584
                accessibleName={listViewText}
585
                tooltip={listViewText}
586
              />
587
              <SegmentedButtonItem
588
                icon={group2Icon}
589
                data-id="group"
590
                selected={!isListView}
591
                accessibleName={groupViewText}
592
                tooltip={groupViewText}
593
              />
594
            </SegmentedButton>
595
          </FlexBox>
596
          <FlexBox className={classNames.searchInputContainer}>
597
            <Input
598
              id={`${uniqueId}-fb-dialog-search`}
599
              noTypeahead
600
              placeholder={searchForFiltersText}
601
              onInput={handleSearch}
602
              showClearIcon
603
              icon={<Icon name={searchIcon} />}
604
              ref={dialogSearchRef}
605
              className={classNames.searchInput}
606
              data-component-name="FilterBarDialogSearchInput"
607
            />
608
          </FlexBox>
2,042✔
609
        </FlexBox>
610
        <Table
611
          ref={tableRef}
612
          className={!isListView && classNames.inactiveTable}
4,015✔
613
          data-component-name="FilterBarDialogTable"
1,968✔
614
          data-is-grouped={!isListView}
615
          data-with-value={`${showValues}`}
616
          nodata={!isListView ? <span /> : undefined}
1,973✔
617
          tabIndex={!isListView ? -1 : undefined}
1,973✔
618
          features={
619
            <>
620
              <TableSelection mode={TableSelectionMode.Multiple} onChange={handleCheckBoxChange} selected={selected} />
621
            </>
1,968✔
622
          }
623
          headerRow={
624
            <TableHeaderRow
2,872✔
625
              data-component-name={!isListView ? 'FilterBarDialogGroupTableHeaderRow' : 'FilterBarDialogTableHeaderRow'}
1,973✔
626
            >
627
              <TableHeaderCell>{filterText}</TableHeaderCell>
628
              {!showValues && <TableHeaderCell className={classNames.tHactive}>{activeText}</TableHeaderCell>}
6,744✔
629
            </TableHeaderRow>
630
          }
2,042✔
631
        >
632
          {isListView && renderChildren()}
7,315✔
633
        </Table>
634
        {!isListView && renderGroups()}
2,047✔
635
      </Dialog>
636
      {showRestoreButton && messageBoxOpen && (
3,453✔
637
        <MessageBox
638
          open
639
          type={MessageBoxType.Warning}
640
          actions={[MessageBoxAction.OK, MessageBoxAction.Cancel]}
641
          onClose={handleMessageBoxClose}
642
          data-component-name="FilterBarDialogResetMessageBox"
643
        >
644
          {i18nBundle.getText(FILTER_DIALOG_RESET_WARNING)}
645
        </MessageBox>
646
      )}
647
    </FilterBarDialogContext.Provider>
648
  );
649
};
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