diff --git a/R/ggplotly.R b/R/ggplotly.R
index d0c32fae2a..556aa12326 100644
--- a/R/ggplotly.R
+++ b/R/ggplotly.R
@@ -806,7 +806,9 @@ gg2list <- function(p, width = NULL, height = NULL,
# do some stuff that should be done once for the entire plot
if (i == 1) {
- axisTickText <- longest_element(axisObj$ticktext)
+ # Split ticktext elements by "\n" to account for linebreaks
+ axisTickText <- strsplit(as.character(axisObj$ticktext), split = "\n", fixed = TRUE)
+ axisTickText <- longest_element(unlist(axisTickText))
side <- if (xy == "x") "b" else "l"
# account for axis ticks, ticks text, and titles in plot margins
# (apparently ggplot2 doesn't support axis.title/axis.text margins)
diff --git a/tests/figs/axes/ticktext-linebreaks-no-linebreaks.svg b/tests/figs/axes/ticktext-linebreaks-no-linebreaks.svg
new file mode 100644
index 0000000000..afee904c2d
--- /dev/null
+++ b/tests/figs/axes/ticktext-linebreaks-no-linebreaks.svg
@@ -0,0 +1 @@
+
diff --git a/tests/figs/axes/ticktext-linebreaks-one-cat.svg b/tests/figs/axes/ticktext-linebreaks-one-cat.svg
new file mode 100644
index 0000000000..566a45677f
--- /dev/null
+++ b/tests/figs/axes/ticktext-linebreaks-one-cat.svg
@@ -0,0 +1 @@
+
diff --git a/tests/figs/axes/ticktext-linebreaks.svg b/tests/figs/axes/ticktext-linebreaks.svg
new file mode 100644
index 0000000000..34fc751204
--- /dev/null
+++ b/tests/figs/axes/ticktext-linebreaks.svg
@@ -0,0 +1 @@
+
diff --git a/tests/testthat/test-ticktext-linebreaks.R b/tests/testthat/test-ticktext-linebreaks.R
new file mode 100644
index 0000000000..f1db6b9560
--- /dev/null
+++ b/tests/testthat/test-ticktext-linebreaks.R
@@ -0,0 +1,64 @@
+context("axes")
+
+# Compute margin
+comp_margin <- function(gg, axisTickText) {
+ plot <- ggfun("plot_clone")(gg)
+ theme <- ggfun("plot_theme")(plot)
+ elements <- names(which(sapply(theme, inherits, "element")))
+ for (i in elements) {
+ theme[[i]] <- ggplot2::calc_element(i, theme)
+ }
+
+ pm <- unitConvert(theme$plot.margin, "pixels")
+ gglayout <- list(
+ margin = list(t = pm[[1]], r = pm[[2]], b = pm[[3]], l = pm[[4]])
+ )
+ axisTitle <- theme[["axis.title.y"]]
+ axisObj <- list(
+ ticktext = "djdfkjdfdklj",
+ tickfont = text2font(theme[["axis.text"]], "width"),
+ ticklen = unitConvert(theme$axis.ticks.length, "pixels", "width")
+ )
+
+ gglayout$margin[["l"]] + axisObj$ticklen +
+ bbox(axisTickText, 0, axisObj$tickfont$size)[["width"]] +
+ bbox("y", 90, unitConvert(axisTitle, "pixels", "width"))[["width"]]
+}
+
+expect_margin <- function(L, gg, ticktext) {
+ margin_l <- comp_margin(gg, ticktext)
+ expect_equal(round(L$layout$margin$l, 10), round(margin_l, 10))
+}
+
+# Linebreaks
+d <- data.frame(x = c(1, 2, 3), y = c("ticktext\nlong_ticktext\nticktext", "ticktext", "ticktext"))
+gg <- ggplot(d, aes(x, y)) + geom_bar(stat = "identity")
+
+test_that("ggplotly takes account of linebreaks in ticktext", {
+ # Visual Test
+ L <- expect_doppelganger_built(gg, "ticktext-linebreaks")
+ # ggplotly returns correct margin
+ expect_margin(L, gg, "long_ticktext")
+})
+
+# Linebreaks one category
+d <- data.frame(x = c(1), y = c("ticktext\nlong_ticktext\nticktext"))
+gg <- ggplot(d, aes(x, y)) + geom_bar(stat = "identity")
+
+test_that("ggplotly takes account of linebreaks in ticktext with only one category", {
+ # Visual Test
+ L <- expect_doppelganger_built(gg, "ticktext-linebreaks-one-cat")
+ # ggplotly returns correct margin
+ expect_margin(L, gg, "long_ticktext")
+})
+
+# No linebreaks
+d <- data.frame(x = c(1, 2, 3), y = c("ticktext long_ticktext ticktext", "ticktext", "ticktext"))
+gg <- ggplot(d, aes(x, y)) + geom_bar(stat = "identity")
+
+test_that("ggplotly works with no linebreaks in ticktext", {
+ # Visual Test
+ L <- expect_doppelganger_built(gg, "ticktext-linebreaks-no-linebreaks")
+ # ggplotly returns correct margin
+ expect_margin(L, gg, "ticktext long_ticktext ticktext")
+})