Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
N
nexpie-grafana-theme
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Registry
Registry
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kornkitt Poolsup
nexpie-grafana-theme
Commits
23c9da61
Commit
23c9da61
authored
May 15, 2018
by
David Kaltschmidt
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fixed custom dates for react timepicker
* added jest tests for timepicker component
parent
eadaff61
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
147 additions
and
16 deletions
+147
-16
public/app/containers/Explore/TimePicker.jest.tsx
+74
-0
public/app/containers/Explore/TimePicker.tsx
+69
-16
public/sass/pages/_explore.scss
+4
-0
No files found.
public/app/containers/Explore/TimePicker.jest.tsx
0 → 100644
View file @
23c9da61
import
React
from
'react'
;
import
{
shallow
}
from
'enzyme'
;
import
sinon
from
'sinon'
;
import
*
as
rangeUtil
from
'app/core/utils/rangeutil'
;
import
TimePicker
,
{
DEFAULT_RANGE
,
parseTime
}
from
'./TimePicker'
;
describe
(
'<TimePicker />'
,
()
=>
{
it
(
'renders closed with default values'
,
()
=>
{
const
rangeString
=
rangeUtil
.
describeTimeRange
(
DEFAULT_RANGE
);
const
wrapper
=
shallow
(<
TimePicker
/>);
expect
(
wrapper
.
find
(
'.timepicker-rangestring'
).
text
()).
toBe
(
rangeString
);
expect
(
wrapper
.
find
(
'.gf-timepicker-dropdown'
).
exists
()).
toBe
(
false
);
});
it
(
'renders with relative range'
,
()
=>
{
const
range
=
{
from
:
'now-7h'
,
to
:
'now'
,
};
const
rangeString
=
rangeUtil
.
describeTimeRange
(
range
);
const
wrapper
=
shallow
(<
TimePicker
range=
{
range
}
isOpen
/>);
expect
(
wrapper
.
find
(
'.timepicker-rangestring'
).
text
()).
toBe
(
rangeString
);
expect
(
wrapper
.
state
(
'fromRaw'
)).
toBe
(
range
.
from
);
expect
(
wrapper
.
state
(
'toRaw'
)).
toBe
(
range
.
to
);
expect
(
wrapper
.
find
(
'.timepicker-from'
).
props
().
value
).
toBe
(
range
.
from
);
expect
(
wrapper
.
find
(
'.timepicker-to'
).
props
().
value
).
toBe
(
range
.
to
);
});
it
(
'renders with epoch (millies) range converted to ISO-ish'
,
()
=>
{
const
range
=
{
from
:
'1'
,
to
:
'1000'
,
};
const
rangeString
=
rangeUtil
.
describeTimeRange
({
from
:
parseTime
(
range
.
from
),
to
:
parseTime
(
range
.
to
),
});
const
wrapper
=
shallow
(<
TimePicker
range=
{
range
}
isUtc
isOpen
/>);
expect
(
wrapper
.
state
(
'fromRaw'
)).
toBe
(
'1970-01-01 00:00:00'
);
expect
(
wrapper
.
state
(
'toRaw'
)).
toBe
(
'1970-01-01 00:00:01'
);
expect
(
wrapper
.
find
(
'.timepicker-rangestring'
).
text
()).
toBe
(
rangeString
);
expect
(
wrapper
.
find
(
'.timepicker-from'
).
props
().
value
).
toBe
(
'1970-01-01 00:00:00'
);
expect
(
wrapper
.
find
(
'.timepicker-to'
).
props
().
value
).
toBe
(
'1970-01-01 00:00:01'
);
});
it
(
'moves ranges forward and backward by half the range on arrow click'
,
()
=>
{
const
range
=
{
from
:
'2000'
,
to
:
'4000'
,
};
const
rangeString
=
rangeUtil
.
describeTimeRange
({
from
:
parseTime
(
range
.
from
),
to
:
parseTime
(
range
.
to
),
});
const
onChangeTime
=
sinon
.
spy
();
const
wrapper
=
shallow
(<
TimePicker
range=
{
range
}
isUtc
isOpen
onChangeTime=
{
onChangeTime
}
/>);
expect
(
wrapper
.
state
(
'fromRaw'
)).
toBe
(
'1970-01-01 00:00:02'
);
expect
(
wrapper
.
state
(
'toRaw'
)).
toBe
(
'1970-01-01 00:00:04'
);
expect
(
wrapper
.
find
(
'.timepicker-rangestring'
).
text
()).
toBe
(
rangeString
);
expect
(
wrapper
.
find
(
'.timepicker-from'
).
props
().
value
).
toBe
(
'1970-01-01 00:00:02'
);
expect
(
wrapper
.
find
(
'.timepicker-to'
).
props
().
value
).
toBe
(
'1970-01-01 00:00:04'
);
wrapper
.
find
(
'.timepicker-left'
).
simulate
(
'click'
);
expect
(
onChangeTime
.
calledOnce
).
toBe
(
true
);
expect
(
wrapper
.
state
(
'fromRaw'
)).
toBe
(
'1970-01-01 00:00:01'
);
expect
(
wrapper
.
state
(
'toRaw'
)).
toBe
(
'1970-01-01 00:00:03'
);
wrapper
.
find
(
'.timepicker-right'
).
simulate
(
'click'
);
expect
(
wrapper
.
state
(
'fromRaw'
)).
toBe
(
'1970-01-01 00:00:02'
);
expect
(
wrapper
.
state
(
'toRaw'
)).
toBe
(
'1970-01-01 00:00:04'
);
});
});
public/app/containers/Explore/TimePicker.tsx
View file @
23c9da61
...
...
@@ -4,22 +4,43 @@ import moment from 'moment';
import
*
as
dateMath
from
'app/core/utils/datemath'
;
import
*
as
rangeUtil
from
'app/core/utils/rangeutil'
;
const
DATE_FORMAT
=
'YYYY-MM-DD HH:mm:ss'
;
export
const
DEFAULT_RANGE
=
{
from
:
'now-6h'
,
to
:
'now'
,
};
export
function
parseTime
(
value
,
isUtc
=
false
,
asString
=
false
)
{
if
(
value
.
indexOf
(
'now'
)
!==
-
1
)
{
return
value
;
}
if
(
!
isNaN
(
value
))
{
const
epoch
=
parseInt
(
value
);
const
m
=
isUtc
?
moment
.
utc
(
epoch
)
:
moment
(
epoch
);
return
asString
?
m
.
format
(
DATE_FORMAT
)
:
m
;
}
return
undefined
;
}
export
default
class
TimePicker
extends
PureComponent
<
any
,
any
>
{
dropdownEl
:
any
;
constructor
(
props
)
{
super
(
props
);
const
fromRaw
=
props
.
range
?
props
.
range
.
from
:
DEFAULT_RANGE
.
from
;
const
toRaw
=
props
.
range
?
props
.
range
.
to
:
DEFAULT_RANGE
.
to
;
const
range
=
{
from
:
parseTime
(
fromRaw
),
to
:
parseTime
(
toRaw
),
};
this
.
state
=
{
fromRaw
:
p
rops
.
range
?
props
.
range
.
from
:
DEFAULT_RANGE
.
from
,
isOpen
:
false
,
isUtc
:
false
,
rangeString
:
rangeUtil
.
describeTimeRange
(
props
.
range
||
DEFAULT_RANGE
),
fromRaw
:
p
arseTime
(
fromRaw
,
props
.
isUtc
,
true
)
,
isOpen
:
props
.
isOpen
,
isUtc
:
props
.
isUtc
,
rangeString
:
rangeUtil
.
describeTimeRange
(
range
),
refreshInterval
:
''
,
toRaw
:
p
rops
.
range
?
props
.
range
.
to
:
DEFAULT_RANGE
.
to
,
toRaw
:
p
arseTime
(
toRaw
,
props
.
isUtc
,
true
)
,
};
}
...
...
@@ -49,14 +70,15 @@ export default class TimePicker extends PureComponent<any, any> {
}
const
rangeString
=
rangeUtil
.
describeTimeRange
(
range
);
to
=
moment
.
utc
(
to
);
from
=
moment
.
utc
(
from
);
// No need to convert to UTC again
to
=
moment
(
to
);
from
=
moment
(
from
);
this
.
setState
(
{
rangeString
,
fromRaw
:
from
,
toRaw
:
to
,
fromRaw
:
from
.
format
(
DATE_FORMAT
)
,
toRaw
:
to
.
format
(
DATE_FORMAT
)
,
},
()
=>
{
onChangeTime
({
to
,
from
});
...
...
@@ -76,6 +98,27 @@ export default class TimePicker extends PureComponent<any, any> {
});
};
handleClickApply
=
()
=>
{
const
{
onChangeTime
}
=
this
.
props
;
const
{
toRaw
,
fromRaw
}
=
this
.
state
;
const
range
=
{
from
:
dateMath
.
parse
(
fromRaw
,
false
),
to
:
dateMath
.
parse
(
toRaw
,
true
),
};
const
rangeString
=
rangeUtil
.
describeTimeRange
(
range
);
this
.
setState
(
{
isOpen
:
false
,
rangeString
,
},
()
=>
{
if
(
onChangeTime
)
{
onChangeTime
(
range
);
}
}
);
};
handleClickLeft
=
()
=>
this
.
move
(
-
1
);
handleClickPicker
=
()
=>
{
this
.
setState
(
state
=>
({
...
...
@@ -118,7 +161,7 @@ export default class TimePicker extends PureComponent<any, any> {
const
timeOptions
=
this
.
getTimeOptions
();
return
(
<
div
ref=
{
this
.
dropdownRef
}
className=
"gf-timepicker-dropdown"
>
<
form
name=
"timeForm"
className=
"gf-timepicker-absolute-section"
>
<
div
className=
"gf-timepicker-absolute-section"
>
<
h3
className=
"section-heading"
>
Custom range
</
h3
>
<
label
className=
"small"
>
From:
</
label
>
...
...
@@ -126,7 +169,7 @@ export default class TimePicker extends PureComponent<any, any> {
<
div
className=
"gf-form max-width-28"
>
<
input
type=
"text"
className=
"gf-form-input input-large"
className=
"gf-form-input input-large
timepicker-from
"
value=
{
fromRaw
}
onChange=
{
this
.
handleChangeFrom
}
/>
...
...
@@ -136,7 +179,12 @@ export default class TimePicker extends PureComponent<any, any> {
<
label
className=
"small"
>
To:
</
label
>
<
div
className=
"gf-form-inline"
>
<
div
className=
"gf-form max-width-28"
>
<
input
type=
"text"
className=
"gf-form-input input-large"
value=
{
toRaw
}
onChange=
{
this
.
handleChangeTo
}
/>
<
input
type=
"text"
className=
"gf-form-input input-large timepicker-to"
value=
{
toRaw
}
onChange=
{
this
.
handleChangeTo
}
/>
</
div
>
</
div
>
...
...
@@ -146,7 +194,12 @@ export default class TimePicker extends PureComponent<any, any> {
<select className="gf-form-input input-medium" ng-options="f.value as f.text for f in ctrl.refresh.options"></select>
</div>
</div> */
}
</
form
>
<
div
className=
"gf-form"
>
<
button
className=
"btn gf-form-btn btn-secondary"
onClick=
{
this
.
handleClickApply
}
>
Apply
</
button
>
</
div
>
</
div
>
<
div
className=
"gf-timepicker-relative-section"
>
<
h3
className=
"section-heading"
>
Quick ranges
</
h3
>
...
...
@@ -172,16 +225,16 @@ export default class TimePicker extends PureComponent<any, any> {
return
(
<
div
className=
"timepicker"
>
<
div
className=
"navbar-buttons"
>
<
button
className=
"btn navbar-button navbar-button--tight"
onClick=
{
this
.
handleClickLeft
}
>
<
button
className=
"btn navbar-button navbar-button--tight
timepicker-left
"
onClick=
{
this
.
handleClickLeft
}
>
<
i
className=
"fa fa-chevron-left"
/>
</
button
>
<
button
className=
"btn navbar-button gf-timepicker-nav-btn"
onClick=
{
this
.
handleClickPicker
}
>
<
i
className=
"fa fa-clock-o"
/>
<
span
>
{
rangeString
}
</
span
>
<
span
className=
"timepicker-rangestring"
>
{
rangeString
}
</
span
>
{
isUtc
?
<
span
className=
"gf-timepicker-utc"
>
UTC
</
span
>
:
null
}
{
refreshInterval
?
<
span
className=
"text-warning"
>
Refresh every
{
refreshInterval
}
</
span
>
:
null
}
</
button
>
<
button
className=
"btn navbar-button navbar-button--tight"
onClick=
{
this
.
handleClickRight
}
>
<
button
className=
"btn navbar-button navbar-button--tight
timepicker-right
"
onClick=
{
this
.
handleClickRight
}
>
<
i
className=
"fa fa-chevron-right"
/>
</
button
>
</
div
>
...
...
public/sass/pages/_explore.scss
View file @
23c9da61
...
...
@@ -36,6 +36,10 @@
.timepicker
{
display
:
flex
;
&
-rangestring
{
margin-left
:
0
.5em
;
}
}
.run-icon
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment