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

custom tip format #1823

Merged
merged 9 commits into from
Aug 26, 2023
Merged

custom tip format #1823

merged 9 commits into from
Aug 26, 2023

Conversation

mbostock
Copy link
Member

@mbostock mbostock commented Aug 22, 2023

This implements a new format option for the tip mark that specifies how to format values. The option is specified as an object whose property names are channel names and whose values are formats; each format may either be a shorthand string for a number or time format, or a function that takes a channel value and zero-based index and returns the corresponding formatted string.

For example, to customize the number format of the x channel:

format: {x: ".2f"}

Equivalently:

format: {x: (d) => d.toFixed(2)}

To suppress a channel (similar to #1822), set the corresponding format to null:

format: {x: null}

As before, if you want the tip to display a paragraph of text, you can use the title channel; if you want to add additional name-value pairs to the tip, you can declare extra channels using the channels option; if you want to change the label, you can either change the corresponding channel or scale label. This PR improves the channels option slightly, allowing the label option to be declared alongside a channel #1734, and defaulting the channel label to its name when expressed as a shorthand string (e.g., channels: {Sport: "sport"} now uses the label “Sport” instead of “sport”.)

Lastly, the tip mark option now supports passing arbitrary options through to the derived tip mark, including the format option. The tip mark option also now takes a pointer option to control which pointer mode is used (x, y, or xy, for pointerX, pointerY, or pointer respectively).

Fixes #1612.
Fixes #1733.
Fixes #1734.

@Fil
Copy link
Contributor

Fil commented Aug 22, 2023

Love the API design.

color, opacity… can we have symbol too, maybe? :-) [see expanded comment below ]

@mkfreeman
Copy link
Contributor

mkfreeman commented Aug 23, 2023

Very exciting! Would it be possible to pass up the format option from other marks in the inferTip function? The first thing I tried to do was use a tip formatter with another mark where tip: true.

I think it would be a big win for new-ish users to be able to format their tips without ejecting from tip: true to adding another tip mark (might also make it easier to add the UI in the chart cell?).

Unrelated: notebook for playing around with.

@mbostock
Copy link
Member Author

Yes @mkfreeman; see the top description.

@mkfreeman
Copy link
Contributor

Does it make sense to put the swatch at the beginning of each line (to match legends)? Just wanted to compare:

Start Middle end
beginning middle end

@mbostock
Copy link
Member Author

@mkfreeman I’m not changing the display functionality of tips in this PR; I’m just giving more control how the contents of the tip are specified. See #1767 for a more general issue, but feel free to open another issue if you want to suggest changes to the default design of tips.

@mkfreeman
Copy link
Contributor

@mbostock thanks, will take a look at that issue!

@mbostock mbostock force-pushed the mbostock/custom-tip-format branch from 5388248 to ff3c49f Compare August 24, 2023 18:47
@mbostock
Copy link
Member Author

mbostock commented Aug 24, 2023

I’m wondering if I over-engineered this by adding the ability to derive anonymous channels as part of the format. I could do something that is more focused on just how the channels are formatted. For example, instead of this:

format: [{channel: "x", format: ".2f"}]

maybe you’d say:

format: {x: ".2f"}

and it wouldn’t change which channels were displayed, but it would change how the x channel is formatted (or perhaps how any channel bound to the x scale is formatted? not sure). And maybe you could suppress a channel by saying:

format: {x: ".2f", y: null}

I think that might be enough. 🤔

Edit: I have now done this.

@mbostock mbostock force-pushed the mbostock/custom-tip-format branch 2 times, most recently from 75b972a to 6743bb5 Compare August 25, 2023 03:19
@mbostock mbostock force-pushed the mbostock/custom-tip-format branch from 6743bb5 to 7b4d5ed Compare August 25, 2023 03:21
@mbostock mbostock marked this pull request as ready for review August 25, 2023 03:29
@mbostock
Copy link
Member Author

After much exploration, I’ve decided to pare this PR down to its essentials, focusing solely on formatting channel values and suppressing channels. The other functionality (declaring additional channels, and overriding labels) is already covered by the existing API, so I didn’t want to introduce a competing mechanism.

src/marks/tip.js Outdated Show resolved Hide resolved
src/plot.js Outdated Show resolved Hide resolved
@Fil
Copy link
Contributor

Fil commented Aug 25, 2023

Great!! Besides the missing documentation, two minor (non-blocking) remarks:

  1. I was tempted to say format: {x: true} to just use the standard format (for instance, when I wanted to revert {x: null}).

  2. Looks like this enables to set the order of the fields by setting the order in the channels options, and possibly suppressing the corresponding standard channels. For example:

Plot.dot(penguins, {
  x: "body_mass_g",
  y: "culmen_length_mm",
  fill: "species",
  channels: {
    culmen_length_mm: "culmen_length_mm",
    species: { value: "species", label: "species", scale: "color" },
    body_mass_g: "body_mass_g"
  },
  tip: { format: { fill: null, culmen_length_mm: ".1f", x: null } }
}).plot()

results in the order (culmen_length_mm, species, body_mass_g)

Capture d’écran 2023-08-25 à 11 07 14

I feel that this is a bit arbitrary and hacky (and also bad performance-wise). Since it's something that I think users will want to do anyways, it would be nicer to design this such that it followed the order given in the format instead. This would be an additional reason to support {x: true}, btw.

@Fil
Copy link
Contributor

Fil commented Aug 25, 2023

To expand my remark about symbols: it would be great to have a symbol (where there is a swatch) for channels that use the symbol scale. And the combo color+symbol should apply too. Again not a blocker, I can try to do this separately.

@mbostock
Copy link
Member Author

Let’s do the symbol swatch separately.

The order of channels is hard and not addressed in this PR. The formats are sparse and I think using “true” as a format is weird — that doesn’t work for a tickFormat, for example. I think channel order might need to be a separate PR or option too, but I’ll think about it today. (I’ve already spent a lot of time on this and want to move forward to other things for 1.0.)

@mbostock
Copy link
Member Author

I could change it to do whatever is listed in the format option first, followed by any remaining channels (in arbitrary order). That seems like a reasonable improvement although it might complicate the code some. We will see! I can probably address the improvement I mentioned above at the same time. But then I want to move on!

@mbostock mbostock requested a review from Fil August 25, 2023 16:39
@mbostock
Copy link
Member Author

The format option now determines the order in which channels are shown, and you can set the format to true (or equivalently undefined) if you just want to use it to specify the order. I also added the optimization I described above to materialize the defaults, fixed a bug with facet settings (facet and facetAnchor) for the tip mark option, and removed the data pseudochannel.

Still need documentation and tests, I guess. Maybe that could be a followup PR? 😬

src/marks/tip.js Outdated Show resolved Hide resolved
src/marks/tip.js Outdated Show resolved Hide resolved
@mbostock mbostock merged commit 0dcec9f into main Aug 26, 2023
@mbostock mbostock deleted the mbostock/custom-tip-format branch August 26, 2023 00:38
@mbostock mbostock mentioned this pull request Aug 26, 2023
@dperetti
Copy link

Is it supposed to work for default channels? I'm not able to suppress / reorder default those, only works for extra channels.

@mbostock
Copy link
Member Author

@dperetti Yes, it does. Please open a discussion here or in the Observable Forum if you’d like help, and please include a live code example to demonstrate. Thank you.

@dperetti
Copy link

Thanks, finally got it! Turns out the channels were x and y, not the keys in the data object. This was too much convolution for my aging brain.

chaichontat pushed a commit to chaichontat/plot that referenced this pull request Jan 14, 2024
* custom tip format

* format order; materialize defaults; fix facets

* revert data values, for now

* fix default tip pointer

* fix paired channels

* fix paired channel order

* fix crash with inferred tick format

* pReTTier

* oops, time zones!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Channel label option Custom format for the tip mark?
4 participants