Flesh out D3 time series charts #657
Reference in New Issue
Block a user
Delete Branch "jose/line-charts"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Depends on https://github.com/maybe-finance/maybe/pull/651 which might make this hard to review because it includes changes from that PR as well. Sorry about that!
This PR consolidates our
trendlineandline-chartStimulus controllers into a singletime-series-chartcontroller. These controllers weren't immediately compatible, so this also cleans things up by identifying the common patterns and abstracting them into reusable methods, all inside the same stimulus controller.The Figma designs don't have change data for percent values:

I think the user loses information in this case. So I kept the scalar and percent change portion of the tooltip. If we want to remove it, it's a matter of simply including a conditional in the tooltip template method. But percent, integer, and money types are all supported by this PR in any case.
Finally, my preview screenshots were taken before I realized we'd like to show the x-axis labels as
day month year. That part of the feature is not pictured, but it is implemented.Demo
https://github.com/maybe-finance/maybe/assets/31393016/04eaec59-dc1c-4834-b033-32c35269dbdc
Previews
Trendline tooltip
EDIT: Whoops! Looks like we don't want tooltips on trendlines. I don't actually mind it though? Seems useful to have. In any case, turning them off is a simple bool param away. Let me know!
Trendline Trends
-ve Trend
+ve Trend
Flat Trend
Liability -ve Trend
Percentage Trend
Empty State
Requirements
Functional Requirements
trendline_controller.jsand aline_chart_controller.js. We should consolidate these into a singleline_chart_controllerthat is configurable. Options will include:TimeSeries.new([]).to_jsonvalue, which can be an instance ofMoneyor a regular integer (this is already implemented). As part of this issue, we need to make sure the chart can handle bothMoneyvalues and regular integers (including percentages) in the tooltip.TimeSeries.new([])), it should display the "empty" state design.Design Requirements
/claim #643
@josefarias no worries on the extra changes at all. I've read through them closely enough to distinguish between those changes and the D3 changes.
I have some local work in-progress that plays off #651 to simplify all our
nilchecks and reworks some of the tests to simplify requirements. Unfortunately I don't have the availability to open that up today for collaboration, so you can go ahead and assume those changes will be independent of the bounty.I'll leave a comment on the main issue, but I'll be available to review bounties late tomorrow evening or first thing Monday morning.
Great work on this!
You should also add a configuration option which will be used to tell the chart that it should render only precentage values in the tooltip as shown in the design(the last card) as per this comment:
https://github.com/maybe-finance/maybe/pull/652#issuecomment-2066668131
Thanks @syedbarimanjan! And thanks for the heads up!
I added a note about that percentage-only design in the PR description. It's easy to add, but I want to make sure that's what we actually want, since the user loses information in that case. Let me know @zachgoll!
This is ready for specific feedback.
@josefarias looks awesome, this is great! A few responses below:
Also, I went ahead and merged in the time series refactor, but hadn't realized you made a few incremental changes on that here. Let me know if you'd like me to revert that commit so you don't have to resolve the conflicts. My main reasoning for merging that was for a cleaner history / documentation purposes.
Thanks @zachgoll! Totally, makes sense to merge that other PR first. Conflicts should be no problem. I'll resolve them (and implement that bit about the percent change) once I'm online in a couple of hours if that works for you.
Looked through everything and think we're all set with this!
One of the things I was contemplating as an improvement to the
TimeSeriesclass is to allow for individual data points to havetrendvalue equal tonil. This better aligns with the domain as there is no such thing as a "null trend"—it either exists (there are 2 data points to compare) or doesn't. I think this will clean up several of thosenilchecks you had mentioned. That will require a few small checks in the D3 controller for the presence of the trend, but I'll handle that all in one PR. Just wanted to let you know of those expected changes.@@ -0,0 +1,518 @@import { Controller } from "@hotwired/stimulus"Very small naming nitpick that I've been thinking about in regards to the time series classes in general:
What do you think about introducing the concept of "Data" and "Datum", where
datais a collection ofdatumanddatumreads as a "piece of data"?@zachgoll changes have been made and conflicts resolved.
That's interesting about the
nilvalues in trends. I think that makes sense but I'd have to see the code to be sure. I think we're missing a duck type or two in some places.Like, ideally we'd be able to call i.e.
.valueor.trendon anything and have it just work in both JS and Ruby. Regardless of the underlying data being Money, Numeric, BigDecimal, or NilClass.In the case of values, I think we can handle the normalization on the ruby side and have both integers and Money instances have the same shape, then include currency at the top level. So:
And the presence or absence of
currencywill tell JS what kind of scalar we're dealing with.I'm also unsure if the presence of
nilvalues in a time series points to a problem with data integrity — how can we build a continuous time series if there's missing data points? I suspect this is best handled further upstream where missing points are intelligently assigned a value so trend calculations don't have to be aware of a value being potentially missing. But that's just a hunch, and an ill-informed one at that. Need more familiarity with the domain.I'm conflating multiple parts of the codebase — the BigDecimal check only happens in
Money, the#valueshape discrepancy only happens inTimeSeries::Value(I think), and thenilvalues thing only happens in trends.I think this is all good to keep in mind as we build. No need to solve for everything right now. The right abstractions will present themselves, but I think they might be in-line with the above.
@josefarias
Agree with this.
I was mostly referring to the following cases, but I suppose these could be handled as special cases upstream and we just pass the same value for
currentandprevious. When I say "there can't be anilTrend" I'm mostly referring to the idea that regardless of who fills in thepreviousvalue (whether that beTrendor upstream), there must be apreviousvalue to be considered a "Trend" (which I think you're also saying in regards to data integrity here).100% agree. Between
TimeSeries,Trend, andValueGroup, I think there's a slight tweak somewhere to be made that will allow for a lot more flexibility.