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

SAP / ui5-webcomponents-react / 9803930041

05 Jul 2024 06:14AM CUT coverage: 80.971% (-0.02%) from 80.988%
9803930041

Pull #6012

github

web-flow
Merge 8926a08d0 into 10e50a2f7
Pull Request #6012: refactor(FilterGroupItem): api alignment

2624 of 3829 branches covered (68.53%)

0 of 10 new or added lines in 3 files covered. (0.0%)

1 existing line in 1 file now uncovered.

4736 of 5849 relevant lines covered (80.97%)

65867.55 hits per line

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

2.76
/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, useIsomorphicId, useStylesheet } from '@ui5/webcomponents-react-base';
9
import type { Dispatch, ReactElement, RefObject, SetStateAction } from 'react';
10
import { Children, cloneElement, useEffect, useReducer, useRef, useState } from 'react';
11
import { createPortal } from 'react-dom';
12
import {
13
  FlexBoxDirection,
14
  FlexBoxJustifyContent,
15
  MessageBoxAction,
16
  MessageBoxType,
17
  ToolbarStyle
18
} from '../../enums/index.js';
19
import {
20
  ACTIVE,
21
  ALL,
22
  BASIC,
23
  CANCEL,
24
  FIELDS_BY_ATTRIBUTE,
25
  FILTER,
26
  FILTER_DIALOG_RESET_WARNING,
27
  FILTERS,
28
  GROUP_VIEW,
29
  HIDE_VALUES,
30
  LIST_VIEW,
31
  MANDATORY,
32
  OK,
33
  RESET,
34
  SEARCH_FOR_FILTERS,
35
  SHOW_VALUES,
36
  VISIBLE,
37
  VISIBLE_AND_ACTIVE
38
} from '../../i18n/i18n-defaults.js';
39
import { addCustomCSSWithScoping } from '../../internal/addCustomCSSWithScoping.js';
40
import type { OnReorderParams } from '../../internal/FilterBarDialogContext.js';
41
import { FilterBarDialogContext } from '../../internal/FilterBarDialogContext.js';
42
import { useCanRenderPortal } from '../../internal/ssr.js';
43
import { stopPropagation } from '../../internal/stopPropagation.js';
44
import type { Ui5CustomEvent } from '../../types/index.js';
45
import type { DialogDomRef, SegmentedButtonPropTypes, TableRowDomRef } from '../../webComponents/index.js';
46
import {
47
  Bar,
48
  Button,
49
  Dialog,
50
  Icon,
51
  Input,
52
  Option,
53
  Panel,
54
  SegmentedButton,
55
  SegmentedButtonItem,
56
  Select,
57
  Table,
58
  TableHeaderCell,
59
  TableHeaderRow,
60
  TableSelection,
61
  Title
62
} from '../../webComponents/index.js';
63
import type { FilterGroupItemInternalProps } from '../FilterGroupItem/types.js';
64
import { FlexBox } from '../FlexBox/index.js';
65
import { MessageBox } from '../MessageBox/index.js';
66
import { Toolbar } from '../Toolbar/index.js';
67
import { ToolbarSpacer } from '../ToolbarSpacer/index.js';
68
import { classNames, styleData } from './FilterBarDialog.module.css.js';
69
import type { FilterBarPropTypes } from './types.js';
70
import { filterValue, syncRef } from './utils.js';
71

72
addCustomCSSWithScoping(
398✔
73
  'ui5-table',
74
  `
75
:host([data-component-name="FilterBarDialogTable"][data-is-grouped]) #nodata-row {
76
  display: none;
77
}
78
`
79
);
80

81
addCustomCSSWithScoping(
398✔
82
  'ui5-table-header-row',
83
  `
84
:host([data-component-name="FilterBarDialogTableHeaderRow"]) :first-child {
85
  visibility: hidden;
86
}
87
`
88
);
89

90
type ActiveFilterAttributes = 'all' | 'visible' | 'active' | 'visibleAndActive' | 'mandatory';
91
const getActiveFilters = (
398✔
92
  activeFilterAttribute: ActiveFilterAttributes,
93
  filter: ReactElement<FilterGroupItemInternalProps>
94
) => {
95
  switch (activeFilterAttribute) {
×
96
    case 'all':
97
      return true;
×
98
    case 'visible':
NEW
99
      return filter.props?.hiddenInFilterBar !== true;
×
100
    case 'active':
101
      return filter.props?.active;
×
102
    case 'visibleAndActive':
NEW
103
      return filter.props?.hiddenInFilterBar !== true && filter.props?.active;
×
104
    case 'mandatory':
105
      return filter.props?.required;
×
106
    default:
107
      return true;
×
108
  }
109
};
110

111
const compareObjects = (firstObj, secondObj) =>
398✔
112
  Object.keys(firstObj).find((first) =>
×
113
    Object.keys(secondObj).every((second) => firstObj[second] !== secondObj[first])
×
114
  );
115

116
interface FilterDialogPropTypes {
117
  filterBarRefs: any;
118
  open: boolean;
119
  handleDialogClose: (event: Ui5CustomEvent<DialogDomRef>) => void;
120
  children: ReactElement<FilterGroupItemInternalProps>[];
121
  showRestoreButton: boolean;
122
  handleRestoreFilters: (e, source, filterElements) => void;
123
  handleDialogSave: (e, newRefs, updatedToggledFilters, orderedChildren) => void;
124
  handleSearchValueChange: Dispatch<SetStateAction<string>>;
125
  handleSelectionChange?: FilterBarPropTypes['onFiltersDialogSelectionChange'];
126
  handleDialogSearch?: (event: CustomEvent<{ value: string; element: HTMLElement }>) => void;
127
  handleDialogCancel?: (event: Ui5CustomEvent<HTMLElement>) => void;
128
  portalContainer: Element;
129
  onAfterFiltersDialogOpen: (event: Ui5CustomEvent<DialogDomRef>) => void;
130
  dialogRef: RefObject<DialogDomRef>;
131
  enableReordering?: FilterBarPropTypes['enableReordering'];
132
  isPhone?: boolean;
133
}
134

135
export const FilterDialog = (props: FilterDialogPropTypes) => {
398✔
136
  const {
137
    filterBarRefs,
138
    open,
139
    handleDialogClose,
140
    children,
141
    showRestoreButton,
142
    handleRestoreFilters,
143
    handleDialogSave,
144
    handleSelectionChange,
145
    handleDialogSearch,
146
    handleDialogCancel,
147
    onAfterFiltersDialogOpen,
148
    portalContainer,
149
    dialogRef,
150
    enableReordering,
151
    isPhone
152
  } = props;
×
153
  useStylesheet(styleData, 'FilterBarDialog');
×
154
  const uniqueId = useIsomorphicId();
×
155
  const [searchString, setSearchString] = useState('');
×
156
  const [toggledFilters, setToggledFilters] = useState({});
×
157
  const dialogRefs = useRef({});
×
158
  const dialogSearchRef = useRef(null);
×
159
  const [showValues, toggleValues] = useReducer((prev) => !prev, false);
×
160
  const [messageBoxOpen, setMessageBoxOpen] = useState(false);
×
161

162
  const [forceRequired, setForceRequired] = useState<undefined | TableRowDomRef>();
×
163
  const [showBtnsOnHover, setShowBtnsOnHover] = useState(true);
×
164
  const [isListView, setIsListView] = useState(true);
×
165
  const [filteredAttribute, setFilteredAttribute] = useState<ActiveFilterAttributes>('all');
×
166
  const [currentReorderedItem, setCurrentReorderedItem] = useState<OnReorderParams | Record<string, never>>({});
×
167
  const tableRef = useRef(null);
×
168
  const okBtnRef = useRef(null);
×
169
  const handleReorder = (e: OnReorderParams) => {
×
170
    setCurrentReorderedItem(e);
×
171
  };
172

173
  const prevOderId = useRef(undefined);
×
174
  const handleFocusFallback = () => {
×
175
    const orderId = currentReorderedItem?.target?.dataset.orderId;
×
176
    if (orderId && tableRef.current && orderId !== prevOderId.current) {
×
177
      // we have to retrigger the internal item navigation logic after reordering,
178
      // otherwise keyboard nav and general focus handling is not working properly
179
      setTimeout(() => {
×
180
        const itemNav = tableRef.current._itemNavigation;
×
181
        itemNav._getItems = () => Array.from(tableRef.current.querySelectorAll('[ui5-table-row]'));
×
182
        itemNav.setCurrentItem(tableRef.current.querySelector(`[data-order-id="${orderId}"]`));
×
183
      });
184
      prevOderId.current = orderId;
×
185
    }
186
  };
187

188
  const i18nBundle = useI18nBundle('@ui5/webcomponents-react');
×
189

190
  const basicText = i18nBundle.getText(BASIC);
×
191
  const cancelText = i18nBundle.getText(CANCEL);
×
192
  const okText = i18nBundle.getText(OK);
×
193
  const searchForFiltersText = i18nBundle.getText(SEARCH_FOR_FILTERS);
×
194
  const filtersTitle = i18nBundle.getText(FILTERS);
×
195
  const resetText = i18nBundle.getText(RESET);
×
196
  const allText = i18nBundle.getText(ALL);
×
197
  const activeText = i18nBundle.getText(ACTIVE);
×
198
  const visibleText = i18nBundle.getText(VISIBLE);
×
199
  const visibleAndActiveText = i18nBundle.getText(VISIBLE_AND_ACTIVE);
×
200
  const mandatoryText = i18nBundle.getText(MANDATORY);
×
201
  const listViewText = i18nBundle.getText(LIST_VIEW);
×
202
  const groupViewText = i18nBundle.getText(GROUP_VIEW);
×
203
  const showValuesText = i18nBundle.getText(SHOW_VALUES);
×
204
  const hideValuesText = i18nBundle.getText(HIDE_VALUES);
×
205
  const filterText = i18nBundle.getText(FILTER);
×
206
  const fieldsByAttributeText = i18nBundle.getText(FIELDS_BY_ATTRIBUTE);
×
207

208
  const visibleChildren = () =>
×
209
    children.filter((item) => {
×
NEW
210
      return !!item?.props && !item?.props?.hidden;
×
211
    });
212

213
  const [orderedChildren, setOrderedChildren] = useState([]);
×
214

215
  useEffect(() => {
×
216
    if (children.length) {
×
217
      setOrderedChildren(visibleChildren());
×
218
    }
219
  }, [children]);
220

221
  const renderChildren = () => {
×
222
    const searchStringLower = searchString.toLowerCase();
×
223
    const filteredChildren =
224
      searchStringLower.length > 0 || filteredAttribute !== 'all'
×
225
        ? orderedChildren.filter(
226
            (item) =>
×
227
              (searchStringLower === '' || item.props.label?.toLowerCase().includes(searchStringLower)) &&
228
              getActiveFilters(filteredAttribute, item)
229
          )
230
        : orderedChildren;
231

232
    return filteredChildren.map((child, index) => {
×
233
      const filterBarItemRef = filterBarRefs.current[child.key];
×
234
      let isSelected =
NEW
235
        child.props.hiddenInFilterBar !== true || child.props.required || child.type.displayName !== 'FilterGroupItem';
×
236
      if (toggledFilters.hasOwnProperty(child.key)) {
×
237
        isSelected = toggledFilters[child.key];
×
238
      }
239

240
      const filterItemProps = filterBarItemRef ? filterValue(filterBarItemRef, child) : {};
×
241

242
      return cloneElement<FilterGroupItemInternalProps>(child, {
×
243
        'data-selected': isSelected,
244
        'data-react-key': child.key,
245
        'data-index': index,
246
        children: {
247
          ...child.props.children,
248
          props: {
249
            ...(child.props.children.props || {}),
×
250
            ...filterItemProps
251
          },
252
          ref: (node) => {
253
            if (node) {
×
254
              dialogRefs.current[child.key] = node;
×
255
              syncRef(child.props.children.ref, node);
×
256
            }
257
          }
258
        }
259
      });
260
    });
261
  };
262

263
  const handleSearch = (e) => {
×
264
    if (typeof handleDialogSearch === 'function') {
×
265
      handleDialogSearch(enrichEventWithDetails(e, { value: e.target.value, element: e.target }));
×
266
    }
267
    setSearchString(e.target.value);
×
268
  };
269
  const handleSave = (e) => {
×
270
    const orderedChildrenIds = enableReordering ? orderedChildren.map((child) => child.props.orderId) : [];
×
271
    handleDialogSave(e, dialogRefs.current, toggledFilters, orderedChildrenIds);
×
272
  };
273

274
  const handleClose = (e) => {
×
275
    setToggledFilters({});
×
276
    stopPropagation(e);
×
277
    if (typeof handleDialogCancel === 'function') {
×
278
      handleDialogCancel(e);
×
279
    }
280
    handleDialogClose(e);
×
281
  };
282

283
  const handleCancel = (e) => {
×
284
    if (handleDialogCancel) {
×
285
      handleDialogCancel(e);
×
286
    }
287
    handleDialogClose(e);
×
288
  };
289

290
  const handleRestore = () => {
×
291
    setMessageBoxOpen(true);
×
292
  };
293
  const handleViewChange: SegmentedButtonPropTypes['onSelectionChange'] = (e) => {
×
294
    const selectedItem = e.detail.selectedItems.at(0);
×
295
    setIsListView(selectedItem.dataset.id === 'list');
×
296
  };
297

298
  const handleMessageBoxClose = (e) => {
×
299
    if (e.detail.action === 'OK') {
×
300
      setToggledFilters({});
×
301
      setOrderedChildren(visibleChildren());
×
302
      handleRestoreFilters(e, 'dialog', { filters: Array.from(dialogRef.current.querySelectorAll('ui5-table-row')) });
×
303
    }
304
    setMessageBoxOpen(false);
×
305
    okBtnRef.current.focus();
×
306
  };
307

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

351
  useEffect(() => {
×
352
    if (updatedIndex != null) {
×
353
      prevOderId.current = undefined;
×
354
    }
355
  }, [updatedIndex]);
356

357
  const handleAttributeFilterChange = (e) => {
×
358
    setFilteredAttribute(e.detail.selectedOption.dataset.id);
×
359
  };
360

361
  const handleCheckBoxChange = (e) => {
×
362
    if (e.target.hasAttribute('ui5-table')) {
×
363
      // preventDefault should only be called if the target is the table, otherwise bubbled `selection-change` events
364
      // also prevent their default behavior (e.g. the event of the MultiComboBox)
365
      e.preventDefault();
×
366
      const prevRowsByKey = e.detail.previouslySelectedRows.reduce(
×
367
        (acc, prevSelRow) => ({ ...acc, [prevSelRow.dataset.reactKey]: prevSelRow }),
×
368
        {}
369
      );
370
      const rowsByKey = e.detail.selectedRows.reduce(
×
371
        (acc, selRow) => ({ ...acc, [selRow.dataset.reactKey]: selRow }),
×
372
        {}
373
      );
374

375
      const changedRowKey =
376
        e.detail.previouslySelectedRows > e.detail.selectedRows
×
377
          ? compareObjects(prevRowsByKey, rowsByKey)
378
          : compareObjects(rowsByKey, prevRowsByKey);
379

380
      const element = rowsByKey[changedRowKey] || prevRowsByKey[changedRowKey];
×
381

382
      // todo: workaround until specific rows can be disabled
383
      if (element.dataset?.required === 'true') {
×
384
        setForceRequired(element);
×
385
        return;
×
386
      }
387

388
      if (typeof handleSelectionChange === 'function') {
×
389
        handleSelectionChange(enrichEventWithDetails(e, { element, checked: element.selected }));
×
390
      }
391

392
      setToggledFilters((prev) => {
×
393
        return { ...prev, [changedRowKey]: element.selected };
×
394
      });
395
    }
396
  };
397

398
  useEffect(() => {
×
399
    if (forceRequired) {
×
400
      forceRequired.setAttribute('selected', 'true');
×
401
      setForceRequired(undefined);
×
402
    }
403
  }, [forceRequired]);
404

405
  const canRenderPortal = useCanRenderPortal();
×
406
  if (!canRenderPortal) {
×
407
    return null;
×
408
  }
409

410
  const renderGroups = () => {
×
411
    const groups = {};
×
412
    Children.forEach(renderChildren(), (child) => {
×
413
      const childGroups = child.props.groupName ?? 'default';
×
414
      if (groups[childGroups]) {
×
415
        groups[childGroups].push(child);
×
416
      } else {
417
        groups[childGroups] = [child];
×
418
      }
419
    });
420

421
    const filterGroups = Object.keys(groups)
×
422
      .sort((x, y) => (x === 'default' ? -1 : y === 'role' ? 1 : 0))
×
423
      .map((item, index) => {
424
        const selectedRows = groups[item].map((child) => child.props['data-react-key']).join(' ');
×
425
        return (
×
426
          <Panel
427
            headerText={item === 'default' ? basicText : item}
×
428
            className={classNames.groupPanel}
429
            key={`${item === 'default' ? basicText : item}${index}`}
×
430
          >
431
            <Table
432
              className={classNames.tableInGroup}
433
              data-component-name="FilterBarDialogPanelTable"
434
              features={
435
                <TableSelection
436
                  mode={TableSelectionMode.Multiple}
437
                  selected={selectedRows}
438
                  onChange={handleCheckBoxChange}
439
                />
440
              }
441
              headerRow={
442
                <TableHeaderRow className={classNames.groupedTableHeader}>
443
                  <TableHeaderCell>{filterText}</TableHeaderCell>
444
                  {!showValues && <TableHeaderCell className={classNames.tHactive}>{activeText}</TableHeaderCell>}
×
445
                </TableHeaderRow>
446
              }
447
            >
448
              {groups[item]}
449
            </Table>
450
          </Panel>
451
        );
452
      });
453
    return filterGroups;
×
454
  };
455

456
  const currentReorderedItemOrderId = currentReorderedItem?.orderId;
×
457

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