-
Notifications
You must be signed in to change notification settings - Fork 11.9k
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
Implemented aligment by senior unit in time axis. #4267
Implemented aligment by senior unit in time axis. #4267
Conversation
src/scales/scale.time.js
Outdated
@@ -315,15 +331,26 @@ module.exports = function(Chart) { | |||
|
|||
var maxTicks = me.getLabelCapacity(minTimestamp || dataMin); | |||
var unit = timeOpts.unit || determineUnit(timeOpts.minUnit, minTimestamp || dataMin, maxTimestamp || dataMax, maxTicks); | |||
var units = Object.keys(interval); | |||
var seniorUnit = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd probably call it majorUnit
vs seniorUnit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wll be changed in next pull request
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hurskiy-andriy can you change that in this PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry. Changed in this PR.
test/specs/scale.time.tests.js
Outdated
@@ -218,7 +218,7 @@ describe('Time scale tests', function() { | |||
|
|||
// Counts down because the lines are drawn top to bottom | |||
expect(xScale.ticks[0]).toEqualOneOf(['Nov 19, 1981', 'Nov 20, 1981', 'Nov 21, 1981']); // handle time zone changes | |||
expect(xScale.ticks[1]).toEqualOneOf(['Nov 19, 1981', 'Nov 20, 1981', 'Nov 21, 1981']); // handle time zone changes | |||
// expect(xScale.ticks[1]).toEqualOneOf(['Nov 19, 1981', 'Nov 20, 1981', 'Nov 21, 1981']); // handle time zone changes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this be changed so that it passes? e.g. by changing the expected values to 'Dec 1, 1981' or whatever it is that it's expecting now
or if there's not a reasonable way to fix it, it's probably better to delete it rather than leaving commented out code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Uncommented and changed in next pull request
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hurskiy-andriy same here!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed in this PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Uncommented in last commit
src/scales/scale.time.js
Outdated
@@ -315,15 +331,26 @@ module.exports = function(Chart) { | |||
|
|||
var maxTicks = me.getLabelCapacity(minTimestamp || dataMin); | |||
var unit = timeOpts.unit || determineUnit(timeOpts.minUnit, minTimestamp || dataMin, maxTimestamp || dataMax, maxTicks); | |||
var units = Object.keys(interval); | |||
var seniorUnit = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed
src/scales/scale.time.js
Outdated
ticks.push(options.min !== undefined ? options.min : niceRange.min); | ||
var cur = moment(niceRange.min); | ||
var startTick = options.min !== undefined ? options.min : niceRange.min; | ||
var seniorUnitStart = moment(startTick).add(1, options.seniorUnit).startOf(options.seniorUnit); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what will happen if options.seniorUnit
is not set? That would be the default case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If options.seniorUnit
is not set - there will be no alignment by majorUnit.
Added check of options.seniorUnit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We discussed in the feature request #4187 that the alignment always be on. Can you auto-detect what the majorUnit is? You should be able to use determineUnit
and interval
to do so. I think this would be much simpler for the user
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we remove options.majorUnit
? I don't think the user should be able to change it as it just adds additional complexity
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, you're correct. Thanks for clarifying
test/specs/scale.time.tests.js
Outdated
@@ -231,7 +232,7 @@ describe('Time scale tests', function() { | |||
|
|||
var scale = createScale(mockData, config); | |||
scale.update(2500, 200); | |||
expect(scale.ticks).toEqual(['Jan 1, 8PM', 'Jan 1, 9PM', 'Jan 1, 10PM', 'Jan 1, 11PM', 'Jan 2, 12AM', 'Jan 2, 1AM', 'Jan 2, 2AM', 'Jan 2, 3AM', 'Jan 2, 4AM', 'Jan 2, 5AM', 'Jan 2, 6AM', 'Jan 2, 7AM', 'Jan 2, 8AM', 'Jan 2, 9AM', 'Jan 2, 10AM', 'Jan 2, 11AM', 'Jan 2, 12PM', 'Jan 2, 1PM', 'Jan 2, 2PM', 'Jan 2, 3PM', 'Jan 2, 4PM', 'Jan 2, 5PM', 'Jan 2, 6PM', 'Jan 2, 7PM', 'Jan 2, 8PM', 'Jan 2, 9PM']); | |||
expect(scale.ticks).toEqual(['8PM', '9PM', '10PM', '11PM', 'Jan 2', '1AM', '2AM', '3AM', '4AM', '5AM', '6AM', '7AM', '8AM', '9AM', '10AM', '11AM', '12PM', '1PM', '2PM', '3PM', '4PM', '5PM', '6PM', '7PM', '8PM', '9PM']); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should either be one PR with both the alignment and formatting changes. Or one PR with only alignment changes and one PR with only formatting changes.
This PR says it's the alignment changes and that #4268 is the formatting changes. But you're making formatting changes here which is really confusing.
src/scales/scale.time.js
Outdated
var alignedTick = startTick; | ||
ticks.push(startTick); | ||
if (startTick !== alignedTick + startFraction && | ||
options.majorUnit && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you indent continuation lines +2 tabs? I.e. this line and the next two lines. That way it's clearer what's part of the if and what's inside it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that will make ESLint happy!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, well if it makes ESLint fail, then nevermind. @simonbrunel do you have any convention about whether the &&
should be at the end of the line or the beginning of the next line?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm used to &&
and ||
at the end but it's not enforced by the linter.
This code should fit in one line and be more readable:
startTick !== alignedTick + startFraction
eq.startFraction !== 0
majorUnit
should be cached because it's used many time- same for
timeOpts
var majorUnit = options.majorUnit;
var timeOpts = options.timeOpts;
//...
if (majorUnit && startFraction && !timeOpts.round && !timeOpts.isoWeekday) {
// ...
}
src/scales/scale.time.js
Outdated
unit: unit, | ||
timeOpts: timeOpts, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's weird that here we pass in timeOpts
and on the next line we pass in timeOpts.isoWeekday
. we should maybe change this to timeOpts.round
for consistency?
src/scales/scale.time.js
Outdated
me.displayFormat = timeOpts.displayFormats[unit]; | ||
me.seniorDisplayFormat = timeOpts.displayFormats[majorUnit]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should probably call this majorDisplayFormat
for consistency
src/scales/scale.time.js
Outdated
var startTick = options.min !== undefined ? options.min : niceRange.min; | ||
var majorUnitStart = startTick; | ||
if (options.majorUnit) { | ||
majorUnitStart = moment(startTick).add(1, options.majorUnit).startOf(options.majorUnit); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if startTick is already at a majorUnit then I think you'll end up at the next one. do you really want something like:
majorUnitStart = moment(startTick).startOf(options.majorUnit) == moment(startTick) ? moment(startTick) : moment(startTick).add(1, options.majorUnit).startOf(options.majorUnit);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assignment startTick
to majorUnitStart
on line #179 is performed instead of else
block of if
statement at line #180
In other words we can write:
var majorUnitStart; if (options.majorUnit) { majorUnitStart = moment(startTick).add(1, options.majorUnit).startOf(options.majorUnit); } else { majorUnitStart = startTick; }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to be rounding up here? I would have guessed we'd round down. In which case we wouldn't add 1 and you could ignore my previous comment
src/scales/scale.time.js
Outdated
var startFraction = startRange % (interval[options.unit].size * stepSize); | ||
var alignedTick = startTick; | ||
ticks.push(startTick); | ||
if (startTick !== alignedTick + startFraction && options.majorUnit && !options.timeOpts.round && !options.timeOpts.isoWeekday) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure why you need startTick !== alignedTick + startFraction
. Is this equivalent to startTick !== majorUnitStart
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For sure, it's equivalent to if (startFraction && ...
(see my previous comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If startTick
is not equal to time that aligned by stepSize
accordingly to majorUnit
we need to find first aligned tick so then we can push ticks with stepSize
and there will be ticks aligned to majorUnit
.
In other words if we have:
startTick
== 20:12;
majorUnit
== "hour";
sterSize
== "3 hour";
majorUnitStart
will equal to "00:00", first aligned tick will equal to "21:00" and startFraction
will equal to "48 minutes".
With this value of startFraction
we can determine alignedTick
(tick which aligned by stepSize
accordingly to majorUnit
(21:00, 00:00, 03:00 etc..))
When we perform startTick !== alignedTick + startFraction
at line #187 we check if we need to push aligned tick (21:00) or startTick
(20:12) is already aligned.
So startFraction
is not difference between startTick
(20:12) and majorUnitStart
(00:00) but difference between startTick
(20:12) and first aligned tick (21:00)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hurskiy-andriy if var alignedTick = startTick;
(line 185) then:
if (startTick !== alignedTick + startFraction && ...
eq.
if (startTick !== startTick + startFraction && ...
eq.
if (startFraction !== 0 && ...
eq.
if (startFraction && ...
Am I right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@simonbrunel yes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hurskiy-andriy I'm not following your example. I don't understand why majorUnitStart
will be equal to "00:00". Wouldn't it be equal to "21:00"?
majorUnitStart = moment(startTick).add(1, options.majorUnit).startOf(options.majorUnit);
majorUnitStart = (20.12 + 1hr).startOf(hour) = 21.12.startOf(hour) = 21:00
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@benmccann Sorry, my mistake. In example above unit
== "hour" and majorUnit
== "day"
@hurskiy-andriy can you rebase both your PRs? |
@benmccann rebase |
…ck 'if' statement formatting. Removed isoWeekday from generateTick ooptions due to redundancy.
76a3d19
to
deee58f
Compare
Yes, rebase each branch against master, so that they can be merged. It looks like you've already done that |
The samples (e.g. samples/scales/time/line-point-data.html and samples/scales/time/combo.html) look much nicer with this change. The one improvement I would suggest is to also align the lefthand and righthand side of the chart just as we do for the points on the interior of the chart. E.g. on samples/scales/time/line-point-data.html it would be nice to make the lefthand side "May 27, 6AM" and the righthand side "Jun 1, 12PM" Doing so would also fix some existing bugs such as #4293 |
src/helpers/helpers.time.js
Outdated
var cur = moment(niceRange.min); | ||
var startTick = options.min !== undefined ? options.min : niceRange.min; | ||
var majorUnitStart = startTick; | ||
if (options.majorUnit) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
options.majorUnit
is used quite often in this method, I would cache it (var majorUnit = options.majorUnit
) to increase minification.
… first and last ticks to time steps.
test/specs/scale.time.tests.js
Outdated
@@ -94,7 +94,7 @@ describe('Time scale tests', function() { | |||
displayFormat: false, | |||
minUnit: 'millisecond', | |||
displayFormats: { | |||
millisecond: 'h:mm:ss.SSS a', // 11:20:01.123 AM | |||
millisecond: 'h:mm:ss.SSS a', // 11:20:01.123 AM, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't need a comma at the end of this line
src/helpers/helpers.time.js
Outdated
var majorUnitStart = startTick; | ||
var majorUnit = options.majorUnit; | ||
if (majorUnit) { | ||
majorUnitStart = moment(startTick).add(1, majorUnit).startOf(majorUnit); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you could combine a few lines by doing:
var majorUnitStart = majorUnit ? moment(startTick).add(1, majorUnit).startOf(majorUnit) : startTick;
var startFraction = startRange % stepValue; | ||
var alignedTick = startTick; | ||
if (startFraction && majorUnit && !options.timeOpts.round && !options.timeOpts.isoWeekday) { | ||
alignedTick += startFraction - stepValue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is alignedTick += startFraction - stepValue
equal to alignedTick -= startFraction
? If so, that might be a more straightforward way of writing it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I startFraction
will equal to 0 - we haven't pass condition on line above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure how that's related to my question...
Thanks @hurskiy-andriy This looks great! The samples render so nicely now :-) I left just a couple very small comments, but otherwise looks good to me |
src/helpers/helpers.time.js
Outdated
if (majorUnit) { | ||
majorUnitStart = moment(startTick).add(1, majorUnit).startOf(majorUnit); | ||
} | ||
var majorUnitStart = majorUnit ? majorUnitStart = moment(startTick).add(1, majorUnit).startOf(majorUnit) : startTick; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line has majorUnitStart =
twice. You only need it once:
var majorUnitStart = majorUnit ? moment(startTick).add(1, majorUnit).startOf(majorUnit) : startTick;
Implemented alignment by major unit in the time scale. This allows showing the first tick of a larger unit like days in a special way and is part of the basis of the time series scale.
Time axis ticks are aligned by senior units of determined base units (for example: if units determined as "hour", senior units will be "day"). See #4187
Check samples:
samples/scales/time/combo.html
and
samples/scales/time/line-point-data.html