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

mendersoftware / gui / 944676341

pending completion
944676341

Pull #3875

gitlab-ci

mzedel
chore: aligned snapshots with updated design

Signed-off-by: Manuel Zedel <manuel.zedel@northern.tech>
Pull Request #3875: MEN-5414

4469 of 6446 branches covered (69.33%)

230 of 266 new or added lines in 43 files covered. (86.47%)

1712 existing lines in 161 files now uncovered.

8406 of 10170 relevant lines covered (82.65%)

196.7 hits per line

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

80.9
/src/js/components/releases/dialogs/artifactinformationform.js
1
// Copyright 2020 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 React, { useEffect, useRef, useState } from 'react';
15

16
import { FormControl, Input, InputLabel, TextField } from '@mui/material';
17

18
import { TIMEOUTS } from '../../../constants/appConstants';
19
import { onboardingSteps } from '../../../constants/onboardingConstants';
20
import { useDebounce } from '../../../utils/debouncehook';
21
import { getOnboardingComponentFor } from '../../../utils/onboardingmanager';
22
import useWindowSize from '../../../utils/resizehook';
23
import ChipSelect from '../../common/chipselect';
24
import { DOCSTIPS, DocsTooltip } from '../../common/docslink';
25
import { InfoHintContainer } from '../../common/info-hint';
26
import { HELPTOOLTIPS, MenderHelpTooltip } from '../../helptips/helptooltips';
27
import { FileInformation } from './addartifact';
28

29
const defaultVersion = '1.0.0';
7✔
30

31
export const VersionInformation = ({ creation = {}, onRemove, updateCreation }) => {
7!
32
  const { file, fileSystem: propFs, name, softwareName: propName, softwareVersion: version = '', type } = creation;
2!
33
  const [fileSystem, setFileSystem] = useState(propFs);
2✔
34
  const [softwareName, setSoftwareName] = useState(propName || name.replace('.', '-'));
2✔
35
  const [softwareVersion, setSoftwareVersion] = useState(version || defaultVersion);
2✔
36

37
  useEffect(() => {
2✔
38
    updateCreation({ finalStep: true });
1✔
39
  }, []);
40

41
  useEffect(() => {
2✔
42
    updateCreation({ fileSystem, softwareName, softwareVersion, isValid: fileSystem && softwareName && softwareVersion });
1✔
43
  }, [fileSystem, softwareName, softwareVersion]);
44

45
  return (
2✔
46
    <>
47
      <FileInformation file={file} type={type} onRemove={onRemove} />
48
      <h4>Version information</h4>
49
      <div className="flexbox column">
50
        {[
51
          { key: 'fileSystem', title: 'Software filesystem', setter: setFileSystem, value: fileSystem },
52
          { key: 'softwareName', title: 'Software name', setter: setSoftwareName, value: softwareName },
53
          { key: 'softwareVersion', title: 'Software version', setter: setSoftwareVersion, value: softwareVersion }
54
        ].map(({ key, title, setter, value: currentValue }, index) => (
55
          <TextField autoFocus={!index} fullWidth key={key} label={title} onChange={({ target: { value } }) => setter(value)} value={currentValue} />
6✔
56
        ))}
57
      </div>
58
    </>
59
  );
60
};
61

62
const checkDestinationValidity = destination => (destination.length ? /^(?:\/|[a-z]+:\/\/)/.test(destination) : true);
58✔
63

64
export const ArtifactInformation = ({ advanceOnboarding, creation = {}, deviceTypes = [], onboardingState, onRemove, updateCreation }) => {
7!
65
  const { destination = '', file, name = '', selectedDeviceTypes = [], type } = creation;
36✔
66
  const deviceTypeRef = useRef();
36✔
67
  const releaseNameRef = useRef();
36✔
68
  const destinationRef = useRef();
36✔
69
  // eslint-disable-next-line no-unused-vars
70
  const size = useWindowSize();
36✔
71

72
  const debouncedName = useDebounce(name, TIMEOUTS.debounceDefault);
36✔
73

74
  useEffect(() => {
36✔
75
    const nextDestination = onboardingState.complete ? destination : '/data/www/localhost/htdocs';
2✔
76
    updateCreation({
2✔
77
      destination: nextDestination,
78
      isValid: checkDestinationValidity(nextDestination) && selectedDeviceTypes.length && name,
4!
79
      finalStep: false
80
    });
81
  }, []);
82

83
  useEffect(() => {
36✔
84
    if (debouncedName.length) {
2✔
85
      advanceOnboarding(onboardingSteps.UPLOAD_NEW_ARTIFACT_DIALOG_RELEASE_NAME);
1✔
86
    }
87
  }, [debouncedName]);
88

89
  const onSelectionChanged = ({ currentValue = '', selection = [] }) => {
36!
90
    if (currentValue.length > 3) {
3!
UNCOV
91
      advanceOnboarding(onboardingSteps.UPLOAD_NEW_ARTIFACT_DIALOG_DEVICE_TYPE);
×
92
    }
93
    updateCreation({
3✔
94
      customDeviceTypes: currentValue,
95
      isValid: (currentValue.length || selection.length) && name && destination,
8✔
96
      selectedDeviceTypes: selection
97
    });
98
  };
99

100
  const onDestinationChange = ({ target: { value } }) =>
36✔
101
    updateCreation({ destination: value, isValid: checkDestinationValidity(value) && selectedDeviceTypes.length && name });
20!
102

103
  let onboardingComponent = null;
36✔
104
  let extraOnboardingComponent = null;
36✔
105
  if (!onboardingState.complete && deviceTypeRef.current && releaseNameRef.current) {
36!
UNCOV
106
    const deviceTypeAnchor = {
×
107
      left: deviceTypeRef.current.parentElement.parentElement.offsetLeft + deviceTypeRef.current.parentElement.parentElement.clientWidth,
108
      top:
109
        deviceTypeRef.current.parentElement.parentElement.offsetTop +
110
        deviceTypeRef.current.parentElement.offsetTop +
111
        deviceTypeRef.current.parentElement.parentElement.clientHeight / 2
112
    };
UNCOV
113
    const releaseNameAnchor = {
×
114
      left: releaseNameRef.current.parentElement.parentElement.offsetLeft + releaseNameRef.current.parentElement.parentElement.clientWidth,
115
      top:
116
        releaseNameRef.current.parentElement.parentElement.offsetTop + releaseNameRef.current.parentElement.offsetTop + releaseNameRef.current.clientHeight / 2
117
    };
UNCOV
118
    const destinationAnchor = {
×
119
      left: destinationRef.current.parentElement.parentElement.offsetLeft + destinationRef.current.parentElement.parentElement.clientWidth,
120
      top: destinationRef.current.parentElement.parentElement.offsetTop + destinationRef.current.parentElement.parentElement.clientHeight / 2
121
    };
UNCOV
122
    extraOnboardingComponent = getOnboardingComponentFor(
×
123
      onboardingSteps.UPLOAD_NEW_ARTIFACT_DIALOG_DESTINATION,
124
      onboardingState,
125
      { anchor: destinationAnchor, place: 'right' },
126
      extraOnboardingComponent
127
    );
UNCOV
128
    onboardingComponent = getOnboardingComponentFor(
×
129
      onboardingSteps.UPLOAD_NEW_ARTIFACT_DIALOG_RELEASE_NAME,
130
      onboardingState,
131
      { anchor: releaseNameAnchor, place: 'right' },
132
      onboardingComponent
133
    );
UNCOV
134
    onboardingComponent = getOnboardingComponentFor(
×
135
      onboardingSteps.UPLOAD_NEW_ARTIFACT_DIALOG_DEVICE_TYPE,
136
      onboardingState,
137
      { anchor: deviceTypeAnchor, place: 'right' },
138
      onboardingComponent
139
    );
140
  }
141

142
  const isValidDestination = checkDestinationValidity(destination);
36✔
143
  return (
36✔
144
    <div className="flexbox column">
145
      <FileInformation file={file} type={type} onRemove={onRemove} />
146
      <TextField
147
        autoFocus={true}
148
        error={!isValidDestination}
149
        fullWidth
150
        helperText={!isValidDestination && <div className="warning">Destination has to be an absolute path</div>}
45✔
151
        inputProps={{ style: { marginTop: 16 } }}
152
        InputLabelProps={{ shrink: true }}
153
        label="Destination directory where the file will be installed on your devices"
154
        onChange={onDestinationChange}
155
        placeholder="Example: /opt/installed-by-single-file"
156
        inputRef={destinationRef}
157
        value={destination}
158
      />
159
      <h4>Artifact information</h4>
160
      <FormControl>
161
        <InputLabel htmlFor="release-name" style={{ alignItems: 'center', display: 'flex' }}>
162
          Release name
163
          <InfoHintContainer>
164
            <MenderHelpTooltip id={HELPTOOLTIPS.releaseName.id} />
165
            <DocsTooltip id={DOCSTIPS.releases.id} />
166
          </InfoHintContainer>
167
        </InputLabel>
168
        <Input
169
          defaultValue={name}
170
          className="release-name-input"
171
          id="release-name"
172
          placeholder="A descriptive name for the software"
173
          onChange={e => updateCreation({ name: e.target.value })}
12✔
174
          inputRef={releaseNameRef}
175
        />
176
      </FormControl>
177
      <ChipSelect
178
        id="compatible-device-type-selection"
179
        inputRef={deviceTypeRef}
180
        label="Device types compatible"
181
        onChange={onSelectionChanged}
182
        placeholder="Enter all device types this software is compatible with"
183
        selection={selectedDeviceTypes}
184
        options={deviceTypes}
185
      />
186
      {onboardingComponent}
187
      {extraOnboardingComponent}
188
    </div>
189
  );
190
};
191

192
const steps = [ArtifactInformation, VersionInformation];
7✔
193

194
export const ArtifactInformationForm = ({ activeStep, ...remainder }) => {
7✔
195
  const Component = steps[activeStep];
38✔
196
  return <Component {...remainder} />;
38✔
197
};
198

199
export default ArtifactInformationForm;
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