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

SAP / ui5-webcomponents-react / 6429818523

06 Oct 2023 09:19AM CUT coverage: 87.716% (-0.1%) from 87.829%
6429818523

Pull #5123

github

web-flow
Merge 9bbf9066c into 6a3247e3e
Pull Request #5123: feat(VariantManagement): add `onManageViewsCancel` and `onSaveViewCancel` events

2771 of 3724 branches covered (0.0%)

18 of 22 new or added lines in 2 files covered. (81.82%)

5 existing lines in 1 file now uncovered.

5084 of 5796 relevant lines covered (87.72%)

16286.19 hits per line

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

85.92
/packages/main/src/components/VariantManagement/ManageViewsDialog.tsx
1
import { isPhone, isTablet } from '@ui5/webcomponents-base/dist/Device.js';
2
import searchIcon from '@ui5/webcomponents-icons/dist/search.js';
3
import { enrichEventWithDetails, ThemingParameters, useI18nBundle } from '@ui5/webcomponents-react-base';
4
import type { MouseEventHandler, ReactNode } from 'react';
5
import React, { Children, useEffect, useRef, useState } from 'react';
6
import { createPortal } from 'react-dom';
7
import { createUseStyles } from 'react-jss';
8
import { BarDesign, FlexBoxAlignItems, FlexBoxDirection, ButtonDesign } from '../../enums/index.js';
9
import {
10
  APPLY_AUTOMATICALLY,
11
  CANCEL,
12
  CREATED_BY,
13
  DEFAULT,
14
  MANAGE_VIEWS,
15
  SAVE,
16
  SHARING,
17
  VIEW,
18
  SEARCH
19
} from '../../i18n/i18n-defaults.js';
20
import { useCanRenderPortal } from '../../internal/ssr.js';
21
import { cssVarVersionInfoPrefix } from '../../internal/utils.js';
22
import { Bar } from '../../webComponents/Bar/index.js';
23
import { Button } from '../../webComponents/Button/index.js';
24
import { Dialog } from '../../webComponents/Dialog/index.js';
25
import type { InputDomRef } from '../../webComponents/index.js';
26
import { Icon, Input } from '../../webComponents/index.js';
27
import { Table } from '../../webComponents/Table/index.js';
28
import { TableColumn } from '../../webComponents/TableColumn/index.js';
29
import { FlexBox } from '../FlexBox/index.js';
30
import { ManageViewsTableRows } from './ManageViewsTableRows.js';
31
import type { VariantManagementPropTypes } from './types.js';
32
import type { VariantItemPropTypes } from './VariantItem.js';
33

34
const _popupDefaultHeaderHeight = `var(${cssVarVersionInfoPrefix}popup_default_header_height)`;
391✔
35
const _popupHeaderFontFamily = `var(${cssVarVersionInfoPrefix}popup_header_font_family)`;
391✔
36

37
const styles = {
391✔
38
  manageViewsDialog: {
39
    width: isPhone() || isTablet() ? '100%' : '70vw',
1,173!
40
    '&::part(content), &::part(header)': {
41
      padding: 0
42
    },
43
    '&::part(footer)': {
44
      padding: 0,
45
      borderBlockStart: 'none'
46
    }
47
  },
48
  headerText: {
49
    margin: 0,
50
    textAlign: 'center',
51
    alignSelf: 'start',
52
    minHeight: _popupDefaultHeaderHeight,
53
    maxHeight: _popupDefaultHeaderHeight,
54
    lineHeight: _popupDefaultHeaderHeight,
55
    textOverflow: 'ellipsis',
56
    overflow: 'hidden',
57
    whiteSpace: 'nowrap',
58
    maxWidth: '100%',
59
    display: 'inline-block',
60
    paddingInlineStart: '1rem',
61
    fontFamily: `"72override",${_popupHeaderFontFamily}`,
62
    fontSize: '1rem'
63
  },
64
  search: { width: 'calc(100% - 2rem)', marginBlockEnd: '0.5rem' },
65
  inputIcon: { cursor: 'pointer', color: ThemingParameters.sapContent_IconColor }
66
};
67

68
const useStyles = createUseStyles(styles, { name: 'ManageViewsDialog' });
391✔
69

70
interface ManageViewsDialogPropTypes {
71
  children: ReactNode | ReactNode[];
72
  onAfterClose: any;
73
  handleSaveManageViews: (
74
    e: MouseEventHandler<HTMLElement>,
75
    payload: {
76
      updatedRows: any;
77
      defaultView: string;
78
      deletedRows: any;
79
    }
80
  ) => void;
81
  showShare: boolean;
82
  showApplyAutomatically: boolean;
83
  showSetAsDefault: boolean;
84
  showCreatedBy: boolean;
85
  variantNames: string[];
86
  portalContainer: Element;
87
  showOnlyFavorites?: boolean;
88
  onManageViewsCancel?: VariantManagementPropTypes['onManageViewsCancel'];
89
}
90

91
export const ManageViewsDialog = (props: ManageViewsDialogPropTypes) => {
391✔
92
  const {
93
    children,
94
    onAfterClose,
95
    handleSaveManageViews,
96
    showShare,
97
    showApplyAutomatically,
98
    showSetAsDefault,
99
    showCreatedBy,
100
    variantNames,
101
    portalContainer,
102
    showOnlyFavorites,
103
    onManageViewsCancel
104
  } = props;
815✔
105
  const i18nBundle = useI18nBundle('@ui5/webcomponents-react');
815✔
106
  const cancelText = i18nBundle.getText(CANCEL);
815✔
107
  const saveText = i18nBundle.getText(SAVE);
815✔
108
  const viewHeaderText = i18nBundle.getText(VIEW);
815✔
109
  const sharingHeaderText = i18nBundle.getText(SHARING);
815✔
110
  const defaultHeaderText = i18nBundle.getText(DEFAULT);
815✔
111
  const applyAutomaticallyHeaderText = i18nBundle.getText(APPLY_AUTOMATICALLY);
815✔
112
  const createdByHeaderText = i18nBundle.getText(CREATED_BY);
815✔
113
  const manageViewsText = i18nBundle.getText(MANAGE_VIEWS);
815✔
114
  const searchText = i18nBundle.getText(SEARCH);
815✔
115

116
  const [changedVariantNames, setChangedVariantNames] = useState(new Map());
815✔
117
  const [invalidVariants, setInvalidVariants] = useState<Record<string, InputDomRef & { isInvalid?: boolean }>>({});
815✔
118

119
  const classes = useStyles();
815✔
120

121
  const columns = (
122
    <>
815✔
123
      {showOnlyFavorites && <TableColumn key="favorite-variant-item" />}
1,086✔
124
      <TableColumn>{viewHeaderText}</TableColumn>
125
      {showShare && (
1,630✔
126
        <TableColumn demandPopin minWidth={600}>
127
          {sharingHeaderText}
128
        </TableColumn>
129
      )}
130
      {showSetAsDefault && (
1,630✔
131
        <TableColumn demandPopin minWidth={600} popinText={defaultHeaderText}>
132
          {defaultHeaderText}
133
        </TableColumn>
134
      )}
135
      {showApplyAutomatically && (
1,630✔
136
        <TableColumn demandPopin minWidth={600} popinText={applyAutomaticallyHeaderText}>
137
          {applyAutomaticallyHeaderText}
138
        </TableColumn>
139
      )}
140
      {showCreatedBy && (
1,630✔
141
        <TableColumn demandPopin minWidth={600} popinText={createdByHeaderText}>
142
          {createdByHeaderText}
143
        </TableColumn>
144
      )}
145
      <TableColumn key="delete-variant-item" />
146
    </>
147
  );
148

149
  const [childrenProps, setChildrenProps] = useState(
815✔
150
    Children.map(children, (child) => {
151
      if (!React.isValidElement(child)) {
2,310!
152
        return {};
×
153
      }
154
      return child.props;
2,310✔
155
    })
156
  );
157
  useEffect(() => {
815✔
158
    setChildrenProps(
154✔
159
      Children.map(children, (child) => {
160
        if (!React.isValidElement(child)) {
457!
161
          return {};
×
162
        }
163
        return child.props;
457✔
164
      })
165
    );
166
  }, [children]);
167

168
  const [filteredProps, setFilteredProps] = useState(childrenProps);
815✔
169
  useEffect(() => {
815✔
170
    setFilteredProps(childrenProps);
256✔
171
  }, [childrenProps]);
172

173
  const [defaultView, setDefaultView] = useState<string>();
815✔
174

175
  const changedTableRows = useRef({});
815✔
176
  const handleTableRowChange = (e, payload) => {
815✔
177
    if (payload) {
260✔
178
      changedTableRows.current[payload.currentVariant] = {
260✔
179
        ...(changedTableRows.current[payload.currentVariant] ?? {}),
300✔
180
        ...payload
181
      };
182
    }
183
  };
184
  const deletedTableRows = useRef(new Set([]));
815✔
185
  const handleDelete = (e) => {
815✔
186
    deletedTableRows.current.add(e.target.dataset.children);
8✔
187
    setChildrenProps((prev) =>
8✔
188
      prev
8✔
189
        .filter((item) => item.children !== e.target.dataset.children)
20✔
190
        .map((item) => {
191
          if (changedTableRows.current.hasOwnProperty(item.children)) {
12!
192
            return { ...item, ...changedTableRows.current[item.children] };
×
193
          }
194
          return item;
12✔
195
        })
196
    );
197
  };
198

199
  const handleSave = (e) => {
815✔
200
    if (Object.keys(invalidVariants).length === 0) {
69✔
201
      handleSaveManageViews(e, {
48✔
202
        updatedRows: changedTableRows.current,
203
        defaultView,
204
        deletedRows: deletedTableRows.current
205
      });
206
    } else {
207
      Object.values(invalidVariants)[0].focus();
21✔
208
    }
209
  };
210

211
  const handleClose = (e) => {
815✔
212
    if (e.detail.escPressed) {
15!
213
      handleCancel(e);
15✔
214
    } else {
NEW
215
      onAfterClose(e);
×
216
    }
217
  };
218

219
  const handleCancel = (e) => {
815✔
220
    if (typeof onManageViewsCancel === 'function') {
46✔
221
      onManageViewsCancel(
30✔
222
        enrichEventWithDetails(e, {
223
          invalidVariants
224
        })
225
      );
226
    }
227
    setInvalidVariants((prev) => {
46✔
NEW
228
      Object.values(prev).forEach((item) => {
×
NEW
229
        item.isInvalid = false;
×
230
      });
NEW
231
      return {};
×
232
    });
233
    onAfterClose(e);
46✔
234
  };
235

236
  const handleSearchInput = (e) => {
815✔
237
    const lowerCaseVal = e.target.value.toLowerCase();
×
238
    setFilteredProps(
×
239
      childrenProps.filter(
240
        (item) =>
241
          item.children?.toLowerCase()?.includes(lowerCaseVal) || item.author?.toLowerCase()?.includes(lowerCaseVal)
×
242
      )
243
    );
244
  };
245

246
  const canRenderPortal = useCanRenderPortal();
815✔
247
  if (!canRenderPortal) {
815✔
248
    return null;
94✔
249
  }
250

251
  return createPortal(
721✔
252
    <Dialog
253
      open
254
      className={classes.manageViewsDialog}
255
      data-component-name="VariantManagementManageViewsDialog"
256
      onAfterClose={onAfterClose}
257
      onBeforeClose={handleClose}
258
      headerText={manageViewsText}
259
      header={
260
        <FlexBox direction={FlexBoxDirection.Column} style={{ width: '100%' }} alignItems={FlexBoxAlignItems.Center}>
261
          <h2 className={classes.headerText}>{manageViewsText}</h2>
262
          <Input
263
            className={classes.search}
264
            placeholder={searchText}
265
            showClearIcon
266
            icon={<Icon name={searchIcon} className={classes.inputIcon} />}
267
            onInput={handleSearchInput}
268
          />
269
        </FlexBox>
270
      }
271
      resizable
272
      footer={
273
        <Bar
274
          design={BarDesign.Footer}
275
          endContent={
276
            <>
277
              <Button design={ButtonDesign.Emphasized} onClick={handleSave}>
278
                {saveText}
279
              </Button>
280
              <Button design={ButtonDesign.Transparent} onClick={handleCancel}>
281
                {cancelText}
282
              </Button>
283
            </>
284
          }
285
        />
286
      }
287
    >
288
      <Table columns={columns} stickyColumnHeader role="table">
289
        {filteredProps.map((itemProps: VariantItemPropTypes) => {
290
          return (
1,957✔
291
            <ManageViewsTableRows
292
              {...itemProps}
293
              setInvalidVariants={setInvalidVariants}
294
              setChangedVariantNames={setChangedVariantNames}
295
              changedVariantNames={changedVariantNames}
296
              variantNames={variantNames}
297
              handleRowChange={handleTableRowChange}
298
              handleDelete={handleDelete}
299
              defaultView={defaultView}
300
              setDefaultView={setDefaultView}
301
              showShare={showShare}
302
              showApplyAutomatically={showApplyAutomatically}
303
              showSetAsDefault={showSetAsDefault}
304
              showCreatedBy={showCreatedBy}
305
              key={itemProps?.children}
306
              showOnlyFavorites={showOnlyFavorites}
307
            />
308
          );
309
        })}
310
      </Table>
311
    </Dialog>,
312
    portalContainer ?? document.body
1,442✔
313
  );
314
};
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