Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sensor chart ux improvements #447

Merged
merged 34 commits into from Jun 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
76499dc
Show updated sensor data and annotations together
Flix6x Jun 20, 2022
51aff4e
Show spinner while fetching new data
Flix6x Jun 20, 2022
bf6c5c7
Switch from id-based styling to class-based styling
Flix6x Jun 20, 2022
9fd91f1
Move styling to css, and lower spinner
Flix6x Jun 20, 2022
546cb65
Simplify and streamline datepicker fontsize
Flix6x Jun 20, 2022
39e3cef
Streamline datepicker margins
Flix6x Jun 20, 2022
4dae1a0
Add margins and side panel activated on hover
Flix6x Jun 20, 2022
f64b6e5
Correct margins and padding of side panel to allow for custom ranges …
Flix6x Jun 20, 2022
cf13a78
Show single month
Flix6x Jun 20, 2022
6939384
Fewer custom ranges
Flix6x Jun 20, 2022
8fb6406
Side panel rounded similar to buttons rather than similar to cards
Flix6x Jun 20, 2022
1ccfcc9
Align box shadows of cards and calendar
Flix6x Jun 20, 2022
37521cc
Non-transparent cards
Flix6x Jun 20, 2022
48e36e9
Simplified padding notation
Flix6x Jun 20, 2022
5d3db6e
Move chart actions buttons away from the card's corner (negative margin)
Flix6x Jun 20, 2022
6384e24
Rotate y-axis labels to improve legibility
Flix6x Jun 20, 2022
c6478e1
Remove sensor chart title if the same information is already containe…
Flix6x Jun 20, 2022
97b2bc9
Move unit to right side of tooltip
Flix6x Jun 21, 2022
c8e5dd5
Style predefined datetime ranges
Flix6x Jun 21, 2022
a12acf6
Raise column to top without requiring flex display
Flix6x Jun 21, 2022
2208986
Rename sidepanel class and separate styling specific to the sidepanel…
Flix6x Jun 21, 2022
50fb0d5
Show spinner only while the promise is being fulfilled
Flix6x Jun 21, 2022
ff62e43
Cancel previous request when the user makes a new request
Flix6x Jun 21, 2022
2c9c0ad
Do not let spinner block the full page height, so the sensor table na…
Flix6x Jun 21, 2022
e266ba9
Change header and label colors inside the sidepanel to contrast again…
Flix6x Jun 21, 2022
fb3d9e0
Style navbar logo to have a consistent height and adjust the width of…
Flix6x Jun 22, 2022
20726b2
Actually load intended font
Flix6x Jun 22, 2022
e650208
Enforce separation of time axis labels
Flix6x Jun 22, 2022
0fe334e
Add return type annotation and docs: applying chart defaults returns …
Flix6x Jun 22, 2022
b25d23d
Resolve hover glitch when exiting either the list of months or the li…
Flix6x Jun 23, 2022
1370f86
Enable swiping for left sidepanel
Flix6x Jun 23, 2022
9d8d627
Stop using redundant litepicker plugin, which was messing with calend…
Flix6x Jun 23, 2022
d21cf3f
Fix test
Flix6x Jun 20, 2022
a6c1c9b
Changelog entry
Flix6x Jun 23, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions documentation/changelog.rst
Expand Up @@ -8,6 +8,7 @@ v0.11.0 | June XX, 2022
New features
-------------
* Individual sensor charts show available annotations [see `PR #428 <http://www.github.com/FlexMeasures/flexmeasures/pull/428>`_]
* Collapsible sidepanel (hover/swipe) used for date selection on sensor charts, and various styling improvements [see `PR #447 <http://www.github.com/FlexMeasures/flexmeasures/pull/447>`_]

Bugfixes
-----------
Expand Down
10 changes: 7 additions & 3 deletions flexmeasures/data/models/charts/belief_charts.py
Expand Up @@ -9,13 +9,14 @@ def bar_chart(
unit = sensor.unit if sensor.unit else "a.u."
event_value_field_definition = dict(
title=f"{capitalize(sensor.sensor_type)} ({unit})",
format=".3s",
format=[".3s", unit],
formatType="quantityWithUnitFormat",
stack=None,
**FIELD_DEFINITIONS["event_value"],
)
chart_specs = {
"description": "A simple bar chart.",
"title": capitalize(sensor.name),
"title": capitalize(sensor.name) if sensor.name != sensor.sensor_type else None,
"mark": "bar",
"encoding": {
"x": FIELD_DEFINITIONS["event_start"],
Expand All @@ -25,7 +26,10 @@ def bar_chart(
"opacity": {"value": 0.7},
"tooltip": [
FIELD_DEFINITIONS["full_date"],
event_value_field_definition,
{
**event_value_field_definition,
**dict(title=f"{capitalize(sensor.sensor_type)}"),
},
FIELD_DEFINITIONS["source"],
],
},
Expand Down
6 changes: 5 additions & 1 deletion flexmeasures/data/models/charts/defaults.py
Expand Up @@ -17,11 +17,13 @@
field="event_start",
type="temporal",
title=None,
axis={"labelOverlap": True, "labelSeparation": 1},
),
"event_end": dict(
field="event_end",
type="temporal",
title=None,
axis={"labelOverlap": True, "labelSeparation": 1},
),
"event_value": dict(
field="event_value",
Expand Down Expand Up @@ -105,6 +107,7 @@
titleFontSize=FONT_SIZE,
labelFontSize=FONT_SIZE,
),
axisY={"titleAngle": 0, "titleAlign": "left", "titleY": -15, "titleX": -40},
title=dict(
fontSize=FONT_SIZE,
),
Expand All @@ -128,7 +131,8 @@

def apply_chart_defaults(fn):
@wraps(fn)
def decorated_chart_specs(*args, **kwargs):
def decorated_chart_specs(*args, **kwargs) -> dict:
""":returns: dict with vega-lite specs, even when applied to an Altair chart."""
dataset_name = kwargs.pop("dataset_name", None)
include_annotations = kwargs.pop("include_annotations", None)
if isinstance(fn, Callable):
Expand Down
2 changes: 1 addition & 1 deletion flexmeasures/data/models/charts/test_chart_defaults.py
Expand Up @@ -6,4 +6,4 @@
def test_default_encodings():
"""Check default encodings for valid vega-lite specifications."""
for field_name, field_definition in FIELD_DEFINITIONS.items():
assert alt.StringFieldDefWithCondition(**field_definition)
assert alt.PositionFieldDef(**field_definition)
4 changes: 1 addition & 3 deletions flexmeasures/data/models/time_series.py
Expand Up @@ -366,9 +366,7 @@ def chart(
# Set up chart specification
if dataset_name is None:
dataset_name = "sensor_" + str(self.id)
self.sensor_type = (
self.name
) # todo remove this placeholder when sensor types are modelled
self.sensor_type = self.get_attribute("sensor_type", self.name)
chart_specs = chart_type_to_chart_specs(
chart_type,
sensor=self,
Expand Down
147 changes: 104 additions & 43 deletions flexmeasures/ui/static/css/flexmeasures.css
Expand Up @@ -33,7 +33,30 @@
--delete-color: var(--red);
}

@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap');
/* devanagari */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/poppins/v20/pxiEyp8kv8JHgFVrJJbecmNE.woff2) format('woff2');
unicode-range: U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200C-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB;
}
/* latin-ext */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/poppins/v20/pxiEyp8kv8JHgFVrJJnecmNE.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/poppins/v20/pxiEyp8kv8JHgFVrJJfecg.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

body {
padding-top: 100px; /* This default is overridden by flexmeasures.js to support a fluid navbar */
Expand All @@ -43,6 +66,12 @@ body *:not(.fa, .glyphicon) {
font-family: 'Poppins', sans-serif;
}

@media (min-width: 768px) {
.on-top-md {
z-index: 1010;
}
}

h1 {
font-size: 35px;
color: var(--primary-color);
Expand All @@ -63,6 +92,12 @@ h1, h2, h3 {
font-weight: 600;
color: var(--primary-color);
}
.sidepanel h1,
.sidepanel h2,
.sidepanel h3,
.sidepanel label {
color: var(--white) !important;
}

hr {
border-top: none;
Expand Down Expand Up @@ -108,8 +143,14 @@ form button[type="submit"] {
}

#spinner {
padding-top: 50px !important;
padding-bottom: 50px !important;
padding-top: 200px;
text-align: center;
position: absolute;
z-index: 10;
font-size: 8px;
top: 0;
left: 0;
width: 100%;
}

.legend {
Expand Down Expand Up @@ -170,7 +211,6 @@ p.error {
/* --- Nav Bar --- */

.navbar-tool-name {
margin-right: 30px;
color: var(--secondary-color);
}

Expand Down Expand Up @@ -220,8 +260,8 @@ p.error {

.navbar-brand {
display: inline-block;
width: 200px;
height: 100%;
padding: 10px 15px;
}

.navbar-brand a {
Expand All @@ -233,10 +273,6 @@ p.error {
color: var(--secondary-color) !important;
}

.navbar-brand a span img {
width: 200px;
}

.navbar-default .navbar-collapse, .navbar-default .navbar-form {
border-color: var(--primary-border-color);
}
Expand Down Expand Up @@ -264,8 +300,8 @@ p.error {

@media (min-width: 768px) {
.navbar-nav>li>a {
padding-top: 28px;
padding-bottom: 28px;
padding-top: 20px;
padding-bottom: 20px;
}

.navbar-default .dropdown-menu a {
Expand Down Expand Up @@ -301,8 +337,7 @@ p.error {
}

#navbar-logo {
height: 50px;
position: fixed; top: 0; left: 0;
height: 40px;
}

/* --- End Nav Bar --- */
Expand Down Expand Up @@ -877,13 +912,48 @@ body .dataTables_wrapper .dataTables_paginate .paginate_button.current:hover {
/* ---- End Modal ---- */


/* ---- Date picker ---- */
/* ---- Side panel ---- */

#datepicker {
margin-top: 50px;
margin-bottom: 30px;
@media (min-width: 768px) {
.sidepanel-container {
z-index: 20;
}
.sidepanel {
background: var(--nav-default-background-color);
width: calc(var(--litepicker-day-width) * 9);
margin: 15px;
padding: 20px 15px;
transition: .3s;
-webkit-transition: .3s;
-moz-transition: .3s;
-ms-transition: .3s;
-o-transition: .3s;
}
.left-sidepanel {
border-top-left-radius: 0px;
border-bottom-left-radius: 0px;
border-top-right-radius: 15px;
border-bottom-right-radius: 15px;
}
.sidepanel-container > .left-sidepanel:not(.sidepanel-show) {
transform: translateX(-106%);
}
@media (hover: hover) {
.sidepanel-container:hover > .left-sidepanel,
.sidepanel-container:focus-within > .left-sidepanel {
transform: translateX(-30px);
}
}
@media (hover: none) {
.sidepanel-container > .left-sidepanel.sidepanel-show {
transform: translateX(-30px);
}
}
}


/* ---- Date picker ---- */

.datetimepicker input {
width: 100%;
}
Expand All @@ -897,7 +967,6 @@ body .dataTables_wrapper .dataTables_paginate .paginate_button.current:hover {
outline: none;
border-radius: 4px;
border: 1px solid #ddd;
font-size: 13px;
color: var(--nav-default-color);
background: var(--nav-default-background-color);
-webkit-border-radius: 4px;
Expand All @@ -907,8 +976,9 @@ body .dataTables_wrapper .dataTables_paginate .paginate_button.current:hover {
}

.container__predefined-ranges {
padding: 20px;
margin-right: 10px;
padding: 0px;
margin-top: 15px;
justify-content: space-around;
box-shadow: 0 0 10px rgba(0,0,0,.1) !important;
border-radius: 6px !important;
-webkit-border-radius: 6px !important;
Expand All @@ -918,12 +988,17 @@ body .dataTables_wrapper .dataTables_paginate .paginate_button.current:hover {
}

.container__predefined-ranges button {
font-size: 14px;
font-weight: 500;
cursor: pointer !important;
border-right: 1px solid var(--primary-border-color) !important;
flex: 1 1 auto;
}
.container__predefined-ranges > button:last-child {
border-right: none !important;
}

.container__months {
box-shadow: 0 0 10px rgba(0,0,0,.1) !important;
border-radius: 6px !important;
-webkit-border-radius: 6px !important;
-moz-border-radius: 6px !important;
Expand All @@ -932,22 +1007,13 @@ body .dataTables_wrapper .dataTables_paginate .paginate_button.current:hover {
justify-content: center;
}

.month-item-weekdays-row {
font-size: 14px;
}

.week-number {
font-size: 14px !important;
}

.litepicker .container__days .day-item.is-today {
color: var(--nav-current-color);
background: var(--secondary-transparent);
}

.litepicker .container__days .day-item {
color: var(--litepicker-day-color);
font-size: 14px;
cursor: pointer;
width: 38px;
height: 38px;
Expand Down Expand Up @@ -1058,11 +1124,9 @@ body .dataTables_wrapper .dataTables_paginate .paginate_button.current:hover {
fill: var(--secondary-color) !important;
}

@media (max-width:525px) {
@media (max-width: 525px) {
#datepicker .container__main {
flex-wrap: wrap;
margin-left: 15px;
margin-right: 15px;
}

#datepicker .container__predefined-ranges {
Expand All @@ -1077,19 +1141,20 @@ body .dataTables_wrapper .dataTables_paginate .paginate_button.current:hover {

/* --- Sensor Data --- */

.sensor-data .sensor-chart-main {
padding: 0;
}

#sensorchart {
padding: 20px 15px 20px 15px;
.card {
background: var(--white);
margin: 15px;
padding: 20px 15px;
box-shadow: 0 0 10px rgba(0,0,0,.1);
border-radius: 6px;
-webkit-border-radius: 6px;
-moz-border-radius: 6px;
-ms-border-radius: 6px;
-o-border-radius: 6px;
}
.card.vega-embed summary {
transform: translate(-15px, 15px);
}

.role-title-text text {
font-size: 20px;
Expand All @@ -1100,10 +1165,6 @@ body .dataTables_wrapper .dataTables_paginate .paginate_button.current:hover {
margin: 0 !important;
}

#sensorchart {
margin-bottom: 40px;
}

#vg-tooltip-element {
font-size: 16px;
}
Expand Down
11 changes: 11 additions & 0 deletions flexmeasures/ui/static/js/flexmeasures.js
Expand Up @@ -310,3 +310,14 @@ function submit_market() {
function submit_sensor_type() {
$("#sensor_type-form").attr("action", empty_location).submit();
}

/** Tooltips: Register custom formatter for quantities incl. units
Usage:
{
'format': [<d3-format>, <sensor unit>],
'formatType': 'quantityWithUnitFormat'
}
*/
vega.expressionFunction('quantityWithUnitFormat', function(datum, params) {
return d3.format(params[0])(datum) + " " + params[1];
});