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

mendersoftware / mender-server / 10483

pending completion
10483

push

gitlab-ci

web-flow
Merge pull request #1091 from mzedel/test/e2e-stricter

Test/e2e stricter

3867 of 5388 branches covered (71.77%)

Branch coverage included in aggregate %.

6844 of 8304 relevant lines covered (82.42%)

68.07 hits per line

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

68.18
/frontend/src/js/components/devices/dialogs/CustomColumnsDialogContent.tsx
1
// Copyright 2022 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14
import { useCallback, useEffect, useMemo, useState } from 'react';
15

16
// material ui
17
import { Clear as ClearIcon, DragHandle as DragHandleIcon } from '@mui/icons-material';
18
import { DialogContent, FormControl, IconButton, ListItem } from '@mui/material';
19

20
import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd';
21
import { ATTRIBUTE_SCOPES } from '@northern.tech/store/constants';
22

23
import AttributeAutoComplete, { getOptionLabel } from '../widgets/AttributeAutocomplete';
24

25
const DraggableListItem = ({ item, index, onRemove }) => {
2✔
26
  // eslint-disable-next-line react-hooks/exhaustive-deps
27
  const title = useMemo(() => getOptionLabel(item), [item.key, item.scope, item.title]);
39✔
28

29
  const onClick = () => onRemove(item, index);
39✔
30

31
  return (
39✔
32
    <Draggable draggableId={item.key} index={index}>
33
      {provided => (
34
        <ListItem className="flexbox space-between margin-right-large" ref={provided.innerRef} {...provided.draggableProps}>
39✔
35
          <div>{title}</div>
36
          <div className="flexbox space-between" style={{ width: 80 }}>
37
            <div {...provided.dragHandleProps} className="flexbox centered">
38
              <DragHandleIcon />
39
            </div>
40
            <IconButton onClick={onClick} size="small">
41
              <ClearIcon color="disabled" />
42
            </IconButton>
43
          </div>
44
        </ListItem>
45
      )}
46
    </Draggable>
47
  );
48
};
49

50
const filterAttributes = (list, attribute) => list.filter(item => !(item.key === attribute.key && item.scope === attribute.scope));
55✔
51

52
export const Content = ({ attributes, columnHeaders, idAttribute, selectedAttributes, setSelectedAttributes, ...props }) => {
2✔
53
  const [attributeOptions, setAttributeOptions] = useState([]);
12✔
54

55
  useEffect(() => {
12✔
56
    const { attributeOptions, selectedAttributes } = columnHeaders.reduce(
3✔
57
      (accu, { attribute, title }, index) => {
58
        // we skip the first/ id column + exclude the status column from customization
59
        if (index && attribute.name && !(attribute.name === 'status' && attribute.scope === ATTRIBUTE_SCOPES.identity)) {
16✔
60
          const currentAttribute = { ...attribute, key: attribute.name, id: `${attribute.scope}-${attribute.name}`, title };
10✔
61
          accu.selectedAttributes.push(currentAttribute);
10✔
62
          accu.attributeOptions = filterAttributes(accu.attributeOptions, currentAttribute);
10✔
63
        }
64
        return accu;
16✔
65
      },
66
      {
67
        attributeOptions: [...attributes.filter(item => !([idAttribute.attribute, 'status'].includes(item.key) && item.scope === ATTRIBUTE_SCOPES.identity))],
19✔
68
        selectedAttributes: []
69
      }
70
    );
71
    setSelectedAttributes(selectedAttributes);
3✔
72
    setAttributeOptions(attributeOptions);
3✔
73
    // eslint-disable-next-line react-hooks/exhaustive-deps
74
  }, [JSON.stringify(attributes), JSON.stringify(columnHeaders), idAttribute.attribute, setSelectedAttributes]);
75

76
  const onDragEnd = ({ destination, source }) => {
12✔
77
    if (!destination) {
×
78
      return;
×
79
    }
80
    const result = [...selectedAttributes];
×
81
    const [removed] = result.splice(source.index, 1);
×
82
    result.splice(destination.index, 0, removed);
×
83
    setSelectedAttributes(result);
×
84
  };
85

86
  const onRemove = (attribute, index) => {
12✔
87
    let selection = [];
×
88
    let removed = attribute;
×
89
    if (index !== undefined) {
×
90
      selection = [...selectedAttributes];
×
91
      const [removedAttribute] = selection.splice(index, 1);
×
92
      removed = removedAttribute;
×
93
    } else {
94
      selection = filterAttributes(selectedAttributes, attribute);
×
95
    }
96
    setSelectedAttributes(selection);
×
97
    setAttributeOptions([...attributeOptions, removed]);
×
98
  };
99

100
  const onSelect = useCallback(
12✔
101
    attribute => {
102
      if (attribute.key) {
1!
103
        const existingAttribute = attributeOptions.find(item => item.key === attribute.key && item.scope === attribute.scope) || attribute;
5!
104
        setSelectedAttributes(current => [
1✔
105
          ...current,
106
          { ...existingAttribute, title: existingAttribute.value ?? existingAttribute.key, id: `${attribute.scope}-${attribute.key}` }
2✔
107
        ]);
108
        setAttributeOptions(filterAttributes(attributeOptions, attribute));
1✔
109
      }
110
    },
111
    // eslint-disable-next-line react-hooks/exhaustive-deps
112
    [JSON.stringify(attributeOptions), setSelectedAttributes]
113
  );
114

115
  return (
12✔
116
    <DialogContent>
117
      <p>You can select columns of inventory data to display in the device list table. Drag to change the order.</p>
118
      <DragDropContext onDragEnd={onDragEnd}>
119
        <Droppable droppableId="droppable-list" direction="vertical">
120
          {provided => (
121
            <div ref={provided.innerRef} {...provided.droppableProps} {...props}>
12✔
122
              {selectedAttributes.map((item, index) => (
123
                <DraggableListItem item={item} index={index} key={item.key} onRemove={onRemove} />
39✔
124
              ))}
125
              {provided.placeholder}
126
            </div>
127
          )}
128
        </Droppable>
129
      </DragDropContext>
130
      <FormControl>
131
        <AttributeAutoComplete attributes={attributeOptions} label="Add a column" onRemove={onRemove} onSelect={onSelect} />
132
      </FormControl>
133
    </DialogContent>
134
  );
135
};
136

137
export default Content;
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