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

mendersoftware / mender-server / 10423

11 Nov 2025 04:53PM UTC coverage: 74.435% (-0.1%) from 74.562%
10423

push

gitlab-ci

web-flow
Merge pull request #1071 from mendersoftware/dependabot/npm_and_yarn/frontend/main/development-dependencies-92732187be

3868 of 5393 branches covered (71.72%)

Branch coverage included in aggregate %.

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

176 existing lines in 95 files now uncovered.

64605 of 86597 relevant lines covered (74.6%)

7.74 hits per line

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

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

2✔
17
import { Typography, useTheme } from '@mui/material';
2✔
18
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
2✔
19
import { makeStyles } from 'tss-react/mui';
2✔
20

2✔
21
import dayjs from 'dayjs';
2✔
22

2✔
23
const useStyles = makeStyles()(theme => ({
10✔
24
  container: {
2✔
25
    alignItems: 'start',
2✔
26
    width: 'min-content'
2✔
27
  },
2✔
28
  inputs: {
2✔
29
    gap: theme.spacing(2),
2✔
30
    flexWrap: 'wrap',
2✔
31
    [theme.breakpoints.up('md')]: {
2✔
32
      flexWrap: 'nowrap'
2✔
33
    }
2✔
34
  }
2✔
35
}));
2✔
36

2✔
37
const ensureStartOfDay = date => {
9✔
38
  const momentDate = typeof date === 'string' ? dayjs(date.replace('Z', '')) : dayjs(date);
2!
39
  return `${momentDate.format().split('T')[0]}T00:00:00.000`;
2✔
40
};
2✔
41

2✔
42
const ensureEndOfDay = date => {
9✔
43
  const momentDate = typeof date === 'string' ? dayjs(date.replace('Z', '')) : dayjs(date);
2!
44
  return `${momentDate.format().split('T')[0]}T23:59:59.999`;
2✔
45
};
2✔
46

2✔
47
const no = () => false;
14✔
48

2✔
49
interface HasHelptextParams {
2✔
50
  endDate: string;
2✔
51
  startDate: string;
2✔
52
}
2✔
53

2✔
54
export const TimeframePicker = ({
9✔
55
  hasHelperText = no,
2✔
56
  helperText,
2✔
57
  tonight: propsTonight
2✔
58
}: {
2✔
59
  hasHelperText?: (params: HasHelptextParams) => boolean;
2✔
60
  helperText?: string;
2✔
61
  tonight: string;
2✔
62
}) => {
2✔
63
  const [tonight] = useState(dayjs(propsTonight));
123✔
64
  const [maxStartDate, setMaxStartDate] = useState(tonight);
123✔
65
  const [minEndDate, setMinEndDate] = useState(tonight);
123✔
66
  const [showsHelptext, setShowsHelptext] = useState(false);
123✔
67
  const { classes } = useStyles();
123✔
68

2✔
69
  const theme = useTheme();
123✔
70
  const isNextTheme = !theme.palette.background.lightgrey;
123✔
71

2✔
72
  const { control, setValue, watch, getValues } = useFormContext();
123✔
73

2✔
74
  const startDate = watch('startDate');
123✔
75
  const endDate = watch('endDate');
123✔
76

2✔
77
  useEffect(() => {
123✔
78
    const currentEndDate = getValues('endDate');
23✔
79
    const now = new Date().toISOString().replace('Z', '');
23✔
80
    if (startDate > currentEndDate) {
23!
81
      setValue('endDate', ensureEndOfDay(startDate));
2✔
82
    } else if (currentEndDate > now) {
23✔
83
      setValue('endDate', now);
13✔
84
    }
2✔
85
    setMinEndDate(dayjs(startDate));
23✔
86
  }, [startDate, getValues, setValue]);
2✔
87

2✔
88
  useEffect(() => {
123✔
89
    const currentStartDate = getValues('startDate');
25✔
90
    if (endDate < currentStartDate) {
25!
91
      setValue('startDate', ensureStartOfDay(endDate));
2✔
92
    }
2✔
93
    setMaxStartDate(dayjs(endDate));
25✔
94
  }, [endDate, getValues, setValue]);
2✔
95

2✔
96
  useEffect(() => {
123✔
97
    setShowsHelptext(hasHelperText({ startDate, endDate }));
28✔
98
  }, [endDate, hasHelperText, startDate]);
2✔
99

2✔
100
  const handleChangeStartDate = date => ensureStartOfDay(date);
123✔
101

2✔
102
  const handleChangeEndDate = date => ensureEndOfDay(date);
123✔
103

2✔
104
  return (
123✔
105
    <div className={`flexbox column ${classes.container}`}>
2✔
106
      <div className={`flexbox ${classes.inputs}`}>
2✔
107
        <Controller
2✔
108
          name="startDate"
2✔
109
          control={control}
2✔
110
          render={({ field: { onChange, value } }) => (
2✔
111
            <DatePicker
123✔
112
              disableFuture
2✔
113
              slotProps={{
2✔
114
                textField: props => ({
214✔
115
                  size: isNextTheme ? 'small' : 'medium',
2✔
116
                  inputProps: {
2✔
117
                    ...props.inputProps,
2✔
118
                    'aria-label': 'From'
2✔
119
                  }
2✔
120
                })
2✔
121
              }}
2✔
122
              format="YYYY-MM-DD"
2✔
123
              yearsOrder="desc"
2✔
124
              label="From"
2✔
125
              maxDate={maxStartDate}
2✔
UNCOV
126
              onChange={e => onChange(handleChangeStartDate(e))}
2✔
127
              value={value ? dayjs(value) : null}
2✔
128
            />
2✔
129
          )}
2✔
130
        />
2✔
131
        <Controller
2✔
132
          name="endDate"
2✔
133
          control={control}
2✔
134
          render={({ field: { onChange, value } }) => (
2✔
135
            <DatePicker
123✔
136
              disableFuture
2✔
137
              slotProps={{
2✔
138
                textField: props => ({
236✔
139
                  size: isNextTheme ? 'small' : 'medium',
2✔
140
                  inputProps: {
2✔
141
                    ...props.inputProps,
2✔
142
                    'aria-label': 'To'
2✔
143
                  }
2✔
144
                })
2✔
145
              }}
2✔
146
              format="YYYY-MM-DD"
2✔
147
              yearsOrder="desc"
2✔
148
              label="To"
2✔
149
              minDate={minEndDate}
2✔
UNCOV
150
              onChange={e => onChange(handleChangeEndDate(e))}
2✔
151
              value={value ? dayjs(value) : dayjs()}
2✔
152
            />
2✔
153
          )}
2✔
154
        />
2✔
155
      </div>
2✔
156
      {showsHelptext && (
2✔
157
        <Typography className="margin-left-small margin-top-x-small" color="info" variant="body2">
2✔
158
          {helperText}
2✔
159
        </Typography>
2✔
160
      )}
2✔
161
    </div>
2✔
162
  );
2✔
163
};
2✔
164

2✔
165
export default TimeframePicker;
2✔
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