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: interactive legends #5305

Merged
merged 43 commits into from
Nov 19, 2019
Merged

feat: interactive legends #5305

merged 43 commits into from
Nov 19, 2019

Conversation

arvind
Copy link
Member

@arvind arvind commented Aug 15, 2019

This PR supersedes #4732 and makes legends interactive when there exists a selection that has been projected over the legend's domain. If the legend only partially matches the selection's projection (e.g., a color legend for Origin when the selection is projected over ["Origin", "Cylinders"]) the legend is still interactive and only partially populates the selection predicate (i.e., clicking "Europe" will select all "Europe" cars without any "Cylinders" specified.)

Kapture 2019-08-15 at 17 00 59

/cc @djbarnwal

@domoritz
Copy link
Member

🎉 I'm very excited about this

@arvind arvind force-pushed the djbarnwal-db/legendInteractive branch from b05535e to 2646d20 Compare August 16, 2019 17:52
@arvind
Copy link
Member Author

arvind commented Aug 16, 2019

Alright, I'm nearing the end with this feature PR. Before I finalize the design via unit tests, I'd like to get some eyes and hands on how interactive legends work and confirm that the current design is desirable.

To that end, here are some interesting specifications to try. I'm purposely not including any GIFs so as not to prime you.

Single selection projected over the legend domain
{
  "$schema": "https://vega.github.io/schema/vega-lite/v3.json",
  "description": "A scatterplot showing horsepower and miles per gallons.",
  "data": {"url": "data/cars.json"},
  "mark": "circle",
  "selection": {
    "foo": {"type": "single", "fields": ["Origin"]}
  },
  "encoding": {
    "x": {"field": "Horsepower", "type": "quantitative"},
    "y": {"field": "Miles_per_Gallon", "type": "quantitative"},
    "color": {
      "condition": {"selection": "foo", "field": "Origin", "type": "nominal"},
      "value": "grey"
    }
  }
}
Multi selection projected over the legend domain
{
  "$schema": "https://vega.github.io/schema/vega-lite/v3.json",
  "description": "A scatterplot showing horsepower and miles per gallons.",
  "data": {"url": "data/cars.json"},
  "mark": "circle",
  "selection": {
    "foo": {"type": "multi", "fields": ["Origin"]}
  },
  "encoding": {
    "x": {"field": "Horsepower", "type": "quantitative"},
    "y": {"field": "Miles_per_Gallon", "type": "quantitative"},
    "color": {
      "condition": {"selection": "foo", "field": "Origin", "type": "nominal"},
      "value": "grey"
    }
  }
}
Multi selection projected over several fields, only one of which includes the legend domain
{
  "$schema": "https://vega.github.io/schema/vega-lite/v3.json",
  "description": "A scatterplot showing horsepower and miles per gallons.",
  "data": {"url": "data/cars.json"},
  "mark": "circle",
  "selection": {
    "foo": {"type": "multi", "fields": ["Origin", "Cylinders"]}
  },
  "encoding": {
    "x": {"field": "Horsepower", "type": "quantitative"},
    "y": {"field": "Miles_per_Gallon", "type": "quantitative"},
    "color": {
      "condition": {"selection": "foo", "field": "Origin", "type": "nominal"},
      "value": "grey"
    }
  }
}
Multi selection projected over several fields, where each field has a corresponding legend
{
  "$schema": "htts://vega.github.io/schema/vega-lite/v3.json",
  "description": "A scatterplot showing horsepower and miles per gallons.",
  "data": {"url": "data/cars.json"},
  "mark": "circle",
  "selection": {
    "foo": {"type": "multi", "fields": ["Origin", "Cylinders"]}
  },
  "encoding": {
    "x": {"field": "Horsepower", "type": "quantitative"},
    "y": {"field": "Miles_per_Gallon", "type": "quantitative"},
    "color": {
      "condition": {"selection": "foo", "field": "Origin", "type": "nominal"},
      "value": "grey"
    },
    "size": {
      "condition": {"selection": "foo", "field": "Cylinders", "type": "nominal"},
      "value": 0
    }
  }
}
Multiple selections each that map to one legend
{
  "$schema": "htts://vega.github.io/schema/vega-lite/v3.json",
  "description": "A scatterplot showing horsepower and miles per gallons.",
  "data": {"url": "data/cars.json"},
  "mark": "circle",
  "selection": {
    "foo": {"type": "multi", "fields": ["Origin"]},
    "bar": {"type": "multi", "fields": ["Cylinders"]}
  },
  "encoding": {
    "x": {"field": "Horsepower", "type": "quantitative"},
    "y": {"field": "Miles_per_Gallon", "type": "quantitative"},
    "color": {
      "condition": {"selection": "foo", "field": "Origin", "type": "nominal"},
      "value": "grey"
    },
    "size": {
      "condition": {"selection": "bar", "field": "Cylinders", "type": "nominal"},
      "value": 0
    }
  }
}
Interactive Legends when unioning a selection across multiple views
{
  "$schema": "https://vega.github.io/schema/vega-lite/v3.json",
  "repeat": {
    "row": ["Horsepower", "Acceleration", "Miles_per_Gallon"],
    "column": ["Miles_per_Gallon", "Acceleration", "Horsepower"]
  },
  "spec": {
    "data": {"url": "data/cars.json"},
    "mark": "circle",
    "selection": {
      "brush": {
        "type": "multi", "fields": ["Origin"],
        "resolve": "union"
      }
    },
    "encoding": {
      "x": {"field": {"repeat": "column"}, "type": "quantitative"},
      "y": {
        "field": {"repeat": "row"},
        "type": "quantitative",
        "axis": {"minExtent": 30}
      },
      "color": {
        "condition": {
          "selection": "brush",
          "field": "Origin",
          "type": "nominal"
        },
        "value": "grey"
      }
    }
  }
}

Let me know what you think. Besides bugs, do interactive legends work as you expect them to or are there cases where their behavior is unexpected?

/cc @djbarnwal, @domoritz, @kanitw, @jheer

@arvind arvind added this to the 4.0 milestone Aug 16, 2019
@domoritz
Copy link
Member

domoritz commented Aug 16, 2019

  • This is awesome!
  • I think the second spec should use multi, not single as the title implies.
  • In the multi-selection, I first wanted to use CMD since I use that on my mac to toggle selections but it didn't work. This obviously isn't something unique to this PR.
  • I expected shift+click to also de-select a previously selected items (toggle) but it didn't work for me. Is this supposed to work? It works in the chart but not the legend.
  • The way multiple legends over a single projection work is really nice!

This is necessary when a selection is projected over several fields.
Using vlSelectionTest will cause all legend items to appear unselected,
even if one of the values is within in the selection. Instead, we now
test only for the legend item itself, via the top-level resolved signal.
Augmenting existing signal logic with legend events yields buggy
behavior when toggling values from the store based on unit name. It is
also less efficient in multi-view cases, as each individual view would
modify the store based on legend events. Instead, the legend selection
transform adds a top-level signal to coordinate all legends for a given
selection. Moreover, it allows users to selectively disable interactive
legend processing (i.e., "legends": false in a selection definition).
@arvind arvind force-pushed the djbarnwal-db/legendInteractive branch from e531677 to f96e699 Compare September 2, 2019 18:25
@arvind arvind force-pushed the djbarnwal-db/legendInteractive branch from c98e8aa to d799578 Compare September 2, 2019 19:15
@domoritz
Copy link
Member

Are we changing the design based on @jheer's comment in #1657 (comment)?

@domoritz domoritz self-requested a review November 13, 2019 22:50
Copy link
Member

@domoritz domoritz left a comment

Choose a reason for hiding this comment

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

See my comments. Otherwise, it looks good. Feel free to merge when this is done.

src/compile/selection/transforms/legends.ts Show resolved Hide resolved
"encode": {"symbols": {"update": {"opacity": {"value": 0.7}}}}
"encode": {
"symbols": {
"name": "gender_legend_symbols",
Copy link
Member

Choose a reason for hiding this comment

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

This chart isn't interactive so it shouldn't have a name for the legend, right?

@domoritz domoritz force-pushed the djbarnwal-db/legendInteractive branch from 55d8462 to edf24d5 Compare November 13, 2019 22:54
@domoritz domoritz force-pushed the djbarnwal-db/legendInteractive branch from edf24d5 to 473eefa Compare November 13, 2019 23:45
@arvind arvind force-pushed the djbarnwal-db/legendInteractive branch from 9ba36ec to d002977 Compare November 17, 2019 20:47
@domoritz domoritz force-pushed the djbarnwal-db/legendInteractive branch from 76bdf54 to 47cec23 Compare November 18, 2019 05:27
@domoritz
Copy link
Member

This spec leads to an error "Converting circular structure to JSON"

{
  "$schema": "https://vega.github.io/schema/vega-lite/v4.json",
  "data": {"url": "data/cars.json"},
  "selection": {
    "sel": {
      "type": "multi", "fields": ["Origin"], "bind": "legend"
    }
  },
  "mark": "point",
  "encoding": {
    "x": {"field": "Horsepower", "type": "quantitative"},
    "y": {"field": "Miles_per_Gallon", "type": "quantitative"},
    "shape": {"field": "Origin", "type": "nominal"},
    "color": {
      "condition": {"selection": "sel", "field": "Origin", "type": "nominal"},
      "value": "gray"
    }
  }
}

@domoritz
Copy link
Member

In this spec below, I would expect the shape to depend on the selection. Instead, Vega-Lite filters the data by the selection. I think the actual behavior makes a lot more sense but it's not what the spec says, no?

{
  "$schema": "https://vega.github.io/schema/vega-lite/v4.json",
  "data": {"url": "data/cars.json"},
  "selection": {
    "sel": {
      "type": "multi", "fields": ["Origin"], "bind": "legend"
    }
  },
  "mark": "point",
  "encoding": {
    "x": {"field": "Horsepower", "type": "quantitative"},
    "y": {"field": "Miles_per_Gallon", "type": "quantitative"},
    "shape": {
      "condition": {"selection": "sel", "field": "Origin", "type": "nominal"},
      "value": "rectangle"
    }
  }
}

@domoritz
Copy link
Member

domoritz commented Nov 18, 2019

Fixed in 17bb58d but I'm not happy with the fix.

@domoritz domoritz self-requested a review November 18, 2019 17:17
Copy link
Member

@domoritz domoritz left a comment

Choose a reason for hiding this comment

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

Looks good except #5305 (comment)

src/compile/selection/transforms/legends.ts Outdated Show resolved Hide resolved
src/compile/selection/transforms/legends.ts Outdated Show resolved Hide resolved
src/compile/legend/encode.ts Outdated Show resolved Hide resolved
src/compile/legend/parse.ts Outdated Show resolved Hide resolved
src/compile/selection/transforms/legends.ts Outdated Show resolved Hide resolved
Co-Authored-By: Dominik Moritz <[email protected]>
@arvind arvind merged commit 89108f0 into master Nov 19, 2019
@arvind arvind deleted the djbarnwal-db/legendInteractive branch November 19, 2019 05:58
@kanitw kanitw modified the milestones: 4.1, 4.0 Dec 4, 2019
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.

4 participants