Skip to content

Commit

Permalink
Hide tooltips using theme(tooltips='blank') (#1014)
Browse files Browse the repository at this point in the history
* Hide general and side tooltips via `theme(tooltips='blank')`.

* Specified format should be applied to non-hidden axis tooltips.

* Code cleanup.

* Update future_changes.md.
  • Loading branch information
OLarionova-HORIS authored Feb 12, 2024
1 parent c61353e commit 020b1e3
Show file tree
Hide file tree
Showing 6 changed files with 227 additions and 117 deletions.
3 changes: 2 additions & 1 deletion future_changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@
- geom_errorbar(): wrong positioning of tooltips [[#992](https://github.com/JetBrains/lets-plot/issues/992)].
- geom_path(): tooltip position interpolation [[#855](https://github.com/JetBrains/lets-plot/issues/855)].
- Stacked bar-chart annotation: labels go out of the plot when zooming-in using coord_cartesian(xlim, ylim) [[#981](https://github.com/JetBrains/lets-plot/issues/981)].
- Facets: "free scales" options are ignored by discrete axis [[#955](https://github.com/JetBrains/lets-plot/issues/955)].
- Facets: "free scales" options are ignored by discrete axis [[#955](https://github.com/JetBrains/lets-plot/issues/955)].
- How to hide only main tooltip? [[LPK-#232](https://github.com/JetBrains/lets-plot-kotlin/issues/232)].
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ interface TooltipsTheme {
fun textStyle(): ThemeTextStyle
fun titleStyle(): ThemeTextStyle
fun labelStyle(): ThemeTextStyle

fun show(): Boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,8 @@ internal class DefaultTooltipsTheme(
ThemeTextStyle(family, FontFace.BOLD + face, size, color)
}
}

override fun show(): Boolean {
return !isElemBlank(listOf(TOOLTIP_RECT))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import org.jetbrains.letsPlot.core.plot.base.theme.Theme
import org.jetbrains.letsPlot.core.plot.base.tooltip.GeomTargetLocator
import org.jetbrains.letsPlot.core.plot.base.util.afterOrientation
import org.jetbrains.letsPlot.core.plot.builder.VarBinding
import org.jetbrains.letsPlot.core.plot.builder.tooltip.TooltipSpecification
import org.jetbrains.letsPlot.core.plot.builder.tooltip.conf.GeomInteraction
import org.jetbrains.letsPlot.core.plot.builder.tooltip.conf.GeomInteractionBuilder
import org.jetbrains.letsPlot.core.plot.builder.tooltip.conf.GeomTooltipSetup
Expand Down Expand Up @@ -91,19 +92,34 @@ object GeomInteractionUtil {
hiddenAesList -
axisWithNoLabels

val sideTooltipAes = createSideTooltipAesList(
layerConfig.geomProto.geomKind
).afterOrientation(yOrientation)

val axisAesFromFunctionTypeAfterOrientation = axisAesFromFunctionKind.afterOrientation(yOrientation)
val layerRendersAesAfterOrientation = layerConfig.renderedAes.afterOrientation(yOrientation)
val tooltipAes = createTooltipAesList(
layerConfig,
scaleMap,
layerRendersAesAfterOrientation,
axisAesFromFunctionTypeAfterOrientation,
hiddenAesList
)
val tooltipAes: List<Aes<*>>
val sideTooltipAes: List<Aes<*>>
val tooltipSpecification: TooltipSpecification

if (theme.tooltips().show()) {
val axisAesFromFunctionTypeAfterOrientation = axisAesFromFunctionKind.afterOrientation(yOrientation)
val layerRendersAesAfterOrientation = layerConfig.renderedAes.afterOrientation(yOrientation)
tooltipAes = createTooltipAesList(
layerConfig,
scaleMap,
layerRendersAesAfterOrientation,
axisAesFromFunctionTypeAfterOrientation,
hiddenAesList
)
sideTooltipAes = createSideTooltipAesList(layerConfig.geomProto.geomKind).afterOrientation(yOrientation)
tooltipSpecification = layerConfig.tooltips
} else {
tooltipAes = emptyList()
sideTooltipAes = emptyList()
// Need to keep specified formats to use for non-hidden tooltips:
tooltipSpecification = TooltipSpecification(
valueSources = layerConfig.tooltips.valueSources,
tooltipLinePatterns = null,
tooltipProperties = TooltipSpecification.TooltipProperties.NONE,
tooltipTitle = null,
disableSplitting = false
)
}

val builder = GeomInteractionBuilder(
locatorLookupSpace = tooltipSetup.locatorLookupSpace,
Expand All @@ -114,7 +130,7 @@ object GeomInteractionUtil {
)

return builder
.tooltipLinesSpec(layerConfig.tooltips)
.tooltipLinesSpec(tooltipSpecification)
.tooltipConstants(createConstantAesList(layerConfig))
.enableCrosshair(isCrosshairEnabled(layerConfig))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import org.jetbrains.letsPlot.core.spec.Option.Plot.SCALES
import org.jetbrains.letsPlot.core.spec.Option.PlotBase.MAPPING
import org.jetbrains.letsPlot.core.spec.Option.Scale.AES
import org.jetbrains.letsPlot.core.spec.Option.Scale.SCALE_MAPPER_KIND
import org.jetbrains.letsPlot.core.spec.Option.Theme.TOOLTIP_RECT
import org.jetbrains.letsPlot.core.spec.front.GeomInteractionUtil
import kotlin.test.Test
import kotlin.test.assertEquals
Expand Down Expand Up @@ -231,7 +232,7 @@ class GeomInteractionBuilderCreationTest {
}

@Test
fun `use 'theme' to control tooltips`() {
fun `use 'theme' to control axis tooltips`() {
run {
// default: X axis tooltip + Y value in the general tooltip
val tooltipLines = histogramInteractionBuilder(data, themeOpts = emptyMap()).tooltipLines
Expand All @@ -244,11 +245,11 @@ class GeomInteractionBuilderCreationTest {
}
run {
// if axis tooltip is hidden - remove value also from the general tooltip
val hideTooltips = mapOf(
val hideAxisTooltips = mapOf(
AXIS_TOOLTIP + "_x" to ELEMENT_BLANK,
AXIS_TOOLTIP + "_y" to ELEMENT_BLANK,
)
val tooltipLines = histogramInteractionBuilder(data, themeOpts = hideTooltips).tooltipLines
val tooltipLines = histogramInteractionBuilder(data, themeOpts = hideAxisTooltips).tooltipLines

val axis = getAesListInAxisTooltip(tooltipLines)
assertNoTooltipForAes(Aes.X, axis)
Expand All @@ -272,6 +273,47 @@ class GeomInteractionBuilderCreationTest {
}
}

@Test
fun `use 'theme' to control general and side tooltips`() {
val mappedData = data + mapOf(
Aes.FILL.name to listOf(4.0)
)

run { // default
val tooltipLines = geomInteractionBuilder(
mappedData,
geom = Option.GeomName.BOX_PLOT,
themeOpts = emptyMap()
).tooltipLines

val axis = getAesListInAxisTooltip(tooltipLines)
assertTooltipForAes(Aes.X, axis)

val general = getAesListInGeneralTooltip(tooltipLines)
assertTooltipForAes(Aes.FILL, general)

val side = getAesListInSideTooltips(tooltipLines)
assertAesList(listOf(Aes.YMAX, Aes.UPPER, Aes.MIDDLE, Aes.LOWER, Aes.YMIN), side)
}

run { // theme(tooltip='blank') => hide general and side tooltips, axis tooltips are visible
val tooltipLines = geomInteractionBuilder(
mappedData,
geom = Option.GeomName.BOX_PLOT,
themeOpts = mapOf(TOOLTIP_RECT to ELEMENT_BLANK)
).tooltipLines

val axis = getAesListInAxisTooltip(tooltipLines)
assertTooltipForAes(Aes.X, axis)

val general = getAesListInGeneralTooltip(tooltipLines)
assertTrue(general.isEmpty())

val side = getAesListInSideTooltips(tooltipLines)
assertTrue(side.isEmpty())
}
}

@Test
fun `quantile should be in tooltip if it is mapped to FILL`() {

Expand Down Expand Up @@ -352,16 +394,17 @@ class GeomInteractionBuilderCreationTest {
return createGeomInteractionBuilder(plotOpts)
}

private fun histogramInteractionBuilder(
private fun geomInteractionBuilder(
mappedData: Map<String, Any>,
themeOpts: Map<String, Any> = emptyMap()
themeOpts: Map<String, Any> = emptyMap(),
geom: String
): GeomInteractionBuilder {
val plotOpts = mutableMapOf(
Meta.KIND to Meta.Kind.PLOT,
MAPPING to mappedData,
LAYERS to listOf(
mapOf(
GEOM to Option.GeomName.HISTOGRAM
GEOM to geom
)
)
)
Expand All @@ -371,6 +414,11 @@ class GeomInteractionBuilderCreationTest {
)
}

private fun histogramInteractionBuilder(
mappedData: Map<String, Any>,
themeOpts: Map<String, Any> = emptyMap()
) = geomInteractionBuilder(mappedData, themeOpts, Option.GeomName.HISTOGRAM)

private fun createGeomInteractionBuilder(
plotOpts: MutableMap<String, Any>,
theme: Theme = DefaultTheme.minimal2()
Expand Down Expand Up @@ -407,6 +455,12 @@ class GeomInteractionBuilderCreationTest {
}
}

private fun getAesListInSideTooltips(tooltipLines: List<LinePattern>): List<Aes<*>> {
return tooltipLines.flatMap { line ->
line.fields.filterIsInstance<MappingField>().filter { it.isSide && !it.isAxis }.map(MappingField::aes)
}
}

private fun assertAesList(
expectedList: List<Aes<*>>,
actualList: List<Aes<*>>
Expand Down
Loading

0 comments on commit 020b1e3

Please sign in to comment.