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

feat(axis): option to hide duplicate axes #370

Merged
merged 7 commits into from
Sep 19, 2019

Conversation

nickofthyme
Copy link
Collaborator

@nickofthyme nickofthyme commented Sep 10, 2019

Summary

Adds prop hideDuplicateAxes to settings to hide axes based on tick labels, position and title.

  • Refactor axes render function.

Axis will be hidden if all of the following conditions are true:

  • The first and last tick labels (with applied tickFormat) match
  • The length of the ticks match
  • The titles match, can be both undefined
  • The position on the spec match

closes #368

Screen Recording 2019-09-10 at 05 37 PM

Checklist

  • Any consumer-facing exports were added to src/index.ts (and stories only import from ../src except for test data & storybook)
  • This was checked for cross-browser compatibility, including a check against IE11
  • Proper documentation or storybook story was added for features that require explanation or tutorials
  • Unit tests were updated or added to match the most common scenarios
  • Each commit follows the convention

* Option to hide axes based on tick labels, position and title.
* Refactor axes render function.

closes elastic#368
@nickofthyme nickofthyme added :axis Axis related issue enhancement New feature or request :TSVB Kibana TSVB related labels Sep 10, 2019
@codecov-io
Copy link

codecov-io commented Sep 10, 2019

Codecov Report

❗ No coverage uploaded for pull request base (master@af4805f). Click here to learn what that means.
The diff coverage is 94.44%.

Impacted file tree graph

@@            Coverage Diff            @@
##             master     #370   +/-   ##
=========================================
  Coverage          ?   98.25%           
=========================================
  Files             ?       38           
  Lines             ?     2801           
  Branches          ?      661           
=========================================
  Hits              ?     2752           
  Misses            ?       44           
  Partials          ?        5
Impacted Files Coverage Δ
src/specs/settings.tsx 94.56% <100%> (ø)
src/chart_types/xy_chart/store/chart_state.ts 97.45% <93.75%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update af4805f...2474776. Read the comment docs.


let hasDuplicate = false;
tickMap.forEach(({ tickLabels: axisTickLabels }, axisId) => {
if (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of a let hasDuplicate and forEach, let's use a tickmap.some(...)

Copy link
Collaborator Author

@nickofthyme nickofthyme Sep 12, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya would totally agree unfortunately this is a Map not an Array which doesn't have the some prototype. 😞

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah indeed! We could do tickMap.keys().some(...) tickMap.entries().some(...) but these would already visit all elements; a for or while loop feeding from an iterator could bail early but probably not worth the hassle.

@@ -844,6 +845,35 @@ export class ChartStore {
this.annotationSpecs.delete(annotationId);
}

isDuplicateAxis(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On an initial look, this method isn't using this, it would be better to turn it into a regular function const isDuplicateAxis = ():({}) = {} completely outside anything that's to do with objects/classes. Even if such functions use this here and there, it'd be advantageous to represent logic as that; we can still pass this.foo or whatever upon invocation. This lets us lighten up our objects, make the logic reusable and refactorable (even into another file if needed), and help us reduce object orientation.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure I agree with that idea and we can make an effort to use that here and going forward, also in components.

const ids = [...axesVisibleTicks.keys()];

return ids.reduce(
(acc, id) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd usually retain reduce for slightly more complex cases. Here it's essentially a ids.filter().map() except that you want to avoid the recalculation of ticks, axisSpec, axisTicksDimensions and axisPosition. But the result already carries these, so we could use ids.map(id => {... return {..., configured: ticks && axisSpec && axisTicksDimensions && axisPosition}}).filter(({configured}) => configured) - not sure if configured is the right name - or just make the map return null when a condition fails and then .filter(identity) where const identity = d => d

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok that's fine, I just tend to use reduce to avoid multiple loops. What is the performance difference for large arrays using 1 loop vs 2+?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok so it looks like ts is not smart enough to realize the filter checks the fields from the map.get are defined.

image

image

I would need to return a Partial or do some typescript trickery

Image 2019-09-12 at 9 11 28 AM

Are you ok keeping this as a reduce for this case?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure thing, and I think we don't really have guidelines for this level of coding questions anyway

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely, esp. if the retained set can be large.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 1f27b60

Copy link
Contributor

@monfera monfera left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functionality looks good to me, does what the issue asks, code looks good, pending CI and PR checkbox ticks. As I'm a new reviewer in this code base pls. @markov00 also take a look.

src/chart_types/xy_chart/store/chart_state.ts Show resolved Hide resolved
Copy link
Member

@markov00 markov00 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's also add a check on the number of desired ticks in the axis spec before merge this

ticks: axesVisibleTicks.get(id),
axisSpec: axesSpecs.get(id),
axisTicksDimensions: axesTicksDimensions.get(id),
axisPosition: axesPositions.get(id),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can map this to something that respect the props of the <Axis> component like {key, ticks, axisSpec, axisTickDimensions, axisPosition }` so then you can just render it like

<Axis
   {...axis}
   chartTheme={chartTheme}
   debug={debug}
   chartDimensions={chartDimensions}
/>

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see 2474776

) {
const spec = specMap.get(axisId);

if (spec && spec.position === position && ((!title && !spec.title) || title === spec.title)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we simplify this using spec && spec.position === position && title == spec.title?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see 2474776

@markov00 markov00 self-requested a review September 16, 2019 15:22
Copy link
Member

@markov00 markov00 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

left a comment to address

@@ -134,7 +134,7 @@ export const isDuplicateAxis = (
) {
const spec = specMap.get(axisId);

if (spec && spec.position === position && ((!title && !spec.title) || title === spec.title)) {
if (spec && spec.position === position && title === spec.title) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's a double equal here if we want to mark as duplicate if one title is null and the other undefined (that in our case we can threat as no value)

Suggested change
if (spec && spec.position === position && title === spec.title) {
if (spec && spec.position === position && title == spec.title) {

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The value cannot be null based on the types correct?

Nonetheless, I also don't like double equals in javascript because it is loose equality, and generally a bad practice. It also can cause unwanted conversions producing inaccurate results. I'd like to enforce this throughout the code eventually.

I think the explicit equality adding twice the code to clearly indicate the comparison is worth it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep that value cannot be null, but I was thinking on the case the chart used in a JS world (people can do strange things there).
You are right about the loose equality, I use that only in rare case checking when checking for null and undefined, in that case you are right, we can get some strange results. So please ignore my comment here

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To your point about people using this library in javaScript and not minding the types.

I 100% agree, but I think if we want to address that there are numerous places where we assume values to be provided/required. I think the Theme is a perfect example where we assume options are provided on the type were in js they may not.

chartDimensions={chartDimensions}
/>
return axes.map(({ key, ...axisProps }) => (
<Axis {...axisProps} key={key} chartTheme={chartTheme} debug={debug} chartDimensions={chartDimensions} />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you extracted the key a bit more explicit on the code?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry what do you mean by this?

I tried spreading the props with the key included and I get a linting error not adding the key to mapd child. Is that what you mean?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, sorry :(
strange that the ts linter doesn't know about that

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya seems like they can get the keys of the object to verify

image

@markov00
Copy link
Member

jenkins, test this please

@nickofthyme nickofthyme merged commit ada2ddc into elastic:master Sep 19, 2019
@nickofthyme nickofthyme deleted the feat/remove-duplicate-axes branch September 19, 2019 16:38
markov00 pushed a commit that referenced this pull request Sep 19, 2019
# [12.1.0](v12.0.2...v12.1.0) (2019-09-19)

### Features

* **axis:** option to hide duplicate axes ([#370](#370)) ([ada2ddc](ada2ddc)), closes [#368](#368)
@markov00
Copy link
Member

🎉 This PR is included in version 12.1.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@markov00 markov00 added the released Issue released publicly label Sep 19, 2019
AMoo-Miki pushed a commit to AMoo-Miki/OpenSearch-Dashboards that referenced this pull request Feb 10, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
:axis Axis related issue enhancement New feature or request released Issue released publicly :TSVB Kibana TSVB related
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Prop to coalesce duplicate Axes
4 participants