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(xy): add null bars to geometry indexing #1226

Merged

Conversation

markov00
Copy link
Member

@markov00 markov00 commented Jun 29, 2021

Summary

The showNullValues prop is now available in the Settings.tooltip to allow displaying null Y values in the tooltip.
This should goes hand in hand with the right tooltip formatter to catch and correctly format these null values.

Jul-06-2021 12-37-30

Details

This PR replace: #1206 reducing the scope to only show null values, keeping the current typings intact.

The PR consist of removing the null values checks in the geometry generation phase, moving every geometry (visible or not) within the geometry index map (the map used by the tooltip to detect the cursor-geometry collisions). The geometries with null values are not pushed within the arrays of renderable geometries.

Issues

close #950

Checklist

  • The proper chart type label was added (e.g. :xy, :partition) if the PR involves a specific chart type
  • The proper feature label was added (e.g. :interactions, :axis) if the PR involves a specific chart feature
  • Whenever possible, please check if the closing issue is connected to a running GH project
  • Any consumer-facing exports were added to packages/charts/src/index.ts (and stories only import from ../src except for test data & storybook)
  • 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

@markov00 markov00 added :interactions Interactions related issue :tooltip Related to hover tooltip :xy Bar/Line/Area chart related labels Jul 6, 2021
@markov00 markov00 marked this pull request as ready for review July 6, 2021 12:49
@markov00 markov00 requested a review from nickofthyme July 6, 2021 12:49
@markov00 markov00 changed the title refactor: add null bars to geometry indexing feat(xy): add null bars to geometry indexing Jul 6, 2021
const absMinHeight = Math.abs(minBarHeight);

// safeguard against null y values
let height = isNil(y0Scaled) || isNil(y) ? 0 : y0Scaled - y;
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor: this could be substituted with height = y0Scaled - y || 0; using isNil doesn't add a lot of documentational value as the types already reveal they can be undefined or null. Still, it's a subjective call.

Also, as the row below sets y to zero if either is nil (in row 86) maybe reordering helps simplify a bit (eg. setting y and y0Scaled to 0 before; in which case the subtraction will result zero)

Copy link
Member Author

Choose a reason for hiding this comment

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

I can't replace it with height = y0Scaled - y || 0 because y0Scaled and y can be null and TS rise a warning/error here

@@ -125,7 +124,7 @@ export function renderBars(
const width = clamp(seriesStyle.rect.widthPixel ?? xScale.bandwidth, minPixelWidth, maxPixelWidth);
const x = xScaled + xScale.bandwidth * orderIndex + xScale.bandwidth / 2 - width / 2;

const originalY1Value = stackMode === StackMode.Percentage ? y1 - (y0 ?? 0) : initialY1;
const originalY1Value = stackMode === StackMode.Percentage ? (isNil(y1) ? null : y1 - (y0 ?? 0)) : initialY1;
Copy link
Contributor

Choose a reason for hiding this comment

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

May be tangential to this PR, but GeometryValue has x and y as any, and maybe it could be tightened up. If it's not possible to do it globally, it could start by perhaps mapping to NaN instead of null so that eventually, y in GeometryValue is always a number

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes agree, we can push this into a separate PR I think

Comment on lines +198 to +200
if (y1 !== null && initialY1 !== null && filled?.y1 === undefined) {
barGeometries.push(barGeometry);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Should indexedGeometryMap.set(barGeometry) still be called unconditionally? If that were conditionalized, this predicate could go to the top of the forEach.

Btw. this function is almost 200 lines long with a bunch of let and co-dependent mutations and non-obvious type narrowings to number, and I don't comprehend it enough to do a good review, would be great to refactor before, or as part of feature work otherwise it'll be more and more complicated

Copy link
Member Author

Choose a reason for hiding this comment

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

indexedGeometryMap.set(barGeometry) needs to be called unconditionally to take the advantage of the current picking logic.
Definitely a refactoring here is required!

Copy link
Collaborator

@nickofthyme nickofthyme left a comment

Choose a reason for hiding this comment

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

Changes LGTM. This is a really clean solution! 🎉

Comment on lines +758 to +759
export function getShowNullValues(settings: SettingsSpec): TooltipProps['showNullValues'] {
const { tooltip } = settings;
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit

Suggested change
export function getShowNullValues(settings: SettingsSpec): TooltipProps['showNullValues'] {
const { tooltip } = settings;
export function getShowNullValues({ tooltip }: SettingsSpec): TooltipProps['showNullValues'] {

stories/interactions/18_null_values.tsx Show resolved Hide resolved
stories/interactions/interactions.stories.tsx Show resolved Hide resolved
}

const absMinHeight = Math.abs(minBarHeight);
let height = y0Scaled - y;
if (absMinHeight !== undefined && height !== 0 && Math.abs(height) < absMinHeight) {
Copy link
Contributor

Choose a reason for hiding this comment

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

absMinHeight is always a number, so this part of the condition can be removed.

Also, using a sign rather than retaining the if(height < 0) means that the height !== 0 check is easy to make redundant:

    const heightDelta = absMinHeight - Math.abs(height);
    if (heightDelta > 0) {
      const heightSign = Math.sign(height);
      height = heightSign * absMinHeight;
      y -= heightSign * heightDelta;
    }

@@ -81,12 +77,15 @@ export function renderBars(
y0Scaled = y0 === null ? yScale.scale(0) : yScale.scale(y0);
Copy link
Contributor

@monfera monfera Jul 12, 2021

Choose a reason for hiding this comment

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

The let / if imperative construct for y0Scaled may be replaced with a single assignment:

    // prettier-ignore
    const y0Scaled = yScale.type === ScaleType.Log
      ? (y0 ?? 0) > 0 ? yScale.scale(y0) : yScale.range[yScale.isInverted ? 1 : 0]
      : yScale.scale(y0 || 0);

It's not super pretty, there may be ways to more deeply simplify though

@monfera monfera self-requested a review July 12, 2021 21:38
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.

LGTM, my comments are only about how the render function could be simplified or at least made easier to understand

@markov00
Copy link
Member Author

LGTM, my comments are only about how the render function could be simplified or at least made easier to understand

Thanks @monfera I will consider the simplification in a subsequent PR

@markov00 markov00 merged commit 20b81a9 into elastic:master Jul 14, 2021
@markov00 markov00 deleted the 2021_06_29-show_null_values_on_tooltip branch July 14, 2021 14:54
nickofthyme pushed a commit that referenced this pull request Jul 14, 2021
# [33.0.0](v32.0.0...v33.0.0) (2021-07-14)

### Features

* **xy:** add null bars to geometry indexing ([#1226](#1226)) ([20b81a9](20b81a9)), closes [#950](#950)
* **xy:** hide labels that protrude the bar geometry ([#1233](#1233)) ([be1fb3d](be1fb3d)), closes [#1234](#1234)

### BREAKING CHANGES

* **xy:** an API change is introduced: `hideClippedValue` is removed in favor of `overflowConstraints?: Array<LabelOverflowConstraint>;`. The array can contain one or multiple overflow constraints enumerated as `LabelOverflowConstraint`
@nickofthyme
Copy link
Collaborator

🎉 This PR is included in version 33.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
:interactions Interactions related issue released Issue released publicly :tooltip Related to hover tooltip :xy Bar/Line/Area chart related
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add option to show tooltip for empty buckets
3 participants