aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--plugins/base/src/main/kotlin/renderers/html/NavigationDataProvider.kt24
-rw-r--r--plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt7
-rw-r--r--plugins/base/src/test/kotlin/renderers/html/NavigationIconTest.kt12
-rw-r--r--plugins/base/src/test/kotlin/renderers/html/NavigationTest.kt228
-rw-r--r--plugins/base/src/test/kotlin/utils/HtmlUtils.kt11
5 files changed, 259 insertions, 23 deletions
diff --git a/plugins/base/src/main/kotlin/renderers/html/NavigationDataProvider.kt b/plugins/base/src/main/kotlin/renderers/html/NavigationDataProvider.kt
index 647ba687..958488ef 100644
--- a/plugins/base/src/main/kotlin/renderers/html/NavigationDataProvider.kt
+++ b/plugins/base/src/main/kotlin/renderers/html/NavigationDataProvider.kt
@@ -73,18 +73,28 @@ abstract class NavigationDataProvider {
}
private fun ContentPage.navigableChildren(): List<NavigationNode> {
- return if (this !is ClasslikePageNode) {
+ return if (this is ClasslikePage) {
+ return this.navigableChildren()
+ } else {
children
.filterIsInstance<ContentPage>()
.map { visit(it) }
.sortedBy { it.name.toLowerCase() }
- } else if (documentables.any { it is DEnum }) {
- // no sorting for enum entries, should be the same as in source code
- children
- .filter { child -> child is WithDocumentables && child.documentables.any { it is DEnumEntry } }
- .map { visit(it as ContentPage) }
+ }
+ }
+
+ private fun ClasslikePage.navigableChildren(): List<NavigationNode> {
+ // Classlikes should only have other classlikes as navigable children
+ val navigableChildren = children
+ .filterIsInstance<ClasslikePage>()
+ .map { visit(it) }
+
+ val isEnumPage = documentables.any { it is DEnum }
+ return if (isEnumPage) {
+ // no sorting for enum entries, should be the same order as in source code
+ navigableChildren
} else {
- emptyList()
+ navigableChildren.sortedBy { it.name.toLowerCase() }
}
}
}
diff --git a/plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt b/plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt
index e5183699..87808add 100644
--- a/plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt
+++ b/plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt
@@ -49,8 +49,7 @@ class NavigationPage(
}
}
buildLink(node.dri, node.sourceSets.toList()) {
- // special condition for Enums as it has children enum entries in navigation
- val withIcon = node.icon != null && (node.children.isEmpty() || node.isEnum())
+ val withIcon = node.icon != null
if (withIcon) {
// in case link text is so long that it needs to have word breaks,
// and it stretches to two or more lines, make sure the icon
@@ -69,10 +68,6 @@ class NavigationPage(
node.children.withIndex().forEach { (n, p) -> visit(p, "$navId-$n", renderer) }
}
}
-
- private fun NavigationNode.isEnum(): Boolean {
- return icon == NavigationNodeIcon.ENUM_CLASS || icon == NavigationNodeIcon.ENUM_CLASS_KT
- }
}
data class NavigationNode(
diff --git a/plugins/base/src/test/kotlin/renderers/html/NavigationIconTest.kt b/plugins/base/src/test/kotlin/renderers/html/NavigationIconTest.kt
index f2c1fca8..a7a7bacf 100644
--- a/plugins/base/src/test/kotlin/renderers/html/NavigationIconTest.kt
+++ b/plugins/base/src/test/kotlin/renderers/html/NavigationIconTest.kt
@@ -1,13 +1,11 @@
package renderers.html
import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
-import org.jsoup.Jsoup
-import org.jsoup.nodes.Element
-import org.jsoup.select.Elements
import org.junit.jupiter.api.Test
-import utils.TestOutputWriter
import utils.TestOutputWriterPlugin
import kotlin.test.assertEquals
+import utils.navigationHtml
+import utils.selectNavigationGrid
class NavigationIconTest : BaseAbstractTest() {
@@ -277,10 +275,4 @@ class NavigationIconTest : BaseAbstractTest() {
}
}
}
-
- private fun TestOutputWriter.navigationHtml(): Element = contents.getValue("navigation.html").let { Jsoup.parse(it) }
-
- private fun Elements.selectNavigationGrid(): Element {
- return this.select("div.overview").select("span.nav-link-grid").single()
- }
}
diff --git a/plugins/base/src/test/kotlin/renderers/html/NavigationTest.kt b/plugins/base/src/test/kotlin/renderers/html/NavigationTest.kt
new file mode 100644
index 00000000..104246cb
--- /dev/null
+++ b/plugins/base/src/test/kotlin/renderers/html/NavigationTest.kt
@@ -0,0 +1,228 @@
+package renderers.html
+
+import org.jetbrains.dokka.base.renderers.html.NavigationNodeIcon
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jsoup.nodes.Element
+import org.junit.jupiter.api.Test
+import utils.TestOutputWriterPlugin
+import kotlin.test.assertEquals
+import utils.navigationHtml
+
+class NavigationTest : BaseAbstractTest() {
+
+ private val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ }
+ }
+ }
+
+ @Test
+ fun `should have expandable classlikes`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ testInline(
+ """
+ |/src/main/kotlin/com/example/WithInner.kt
+ |package com.example
+ |
+ |class WithInner {
+ | // in-class functions should not be in navigation
+ | fun a() {}
+ | fun b() {}
+ | fun c() {}
+ |
+ | class InnerClass {}
+ | interface InnerInterface {}
+ | enum class InnerEnum {}
+ | object InnerObject {}
+ | annotation class InnerAnnotation {}
+ | companion object CompanionObject {}
+ |}
+ """.trimIndent(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ val content = writerPlugin.writer.navigationHtml().select("div.sideMenuPart")
+ assertEquals(9, content.size)
+
+ // Navigation menu should be the following, sorted by name:
+ // - root
+ // - com.example
+ // - WithInner
+ // - CompanionObject
+ // - InnerAnnotation
+ // - InnerClass
+ // - InnerEnum
+ // - InnerInterface
+ // - InnerObject
+
+ content[0].assertNavigationLink(
+ id = "root-nav-submenu",
+ text = "root",
+ address = "index.html",
+ )
+
+ content[1].assertNavigationLink(
+ id = "root-nav-submenu-0",
+ text = "com.example",
+ address = "root/com.example/index.html",
+ )
+
+ content[2].assertNavigationLink(
+ id = "root-nav-submenu-0-0",
+ text = "WithInner",
+ address = "root/com.example/-with-inner/index.html",
+ icon = NavigationNodeIcon.CLASS_KT
+ )
+
+ content[3].assertNavigationLink(
+ id = "root-nav-submenu-0-0-0",
+ text = "CompanionObject",
+ address = "root/com.example/-with-inner/-companion-object/index.html",
+ icon = NavigationNodeIcon.OBJECT
+ )
+
+ content[4].assertNavigationLink(
+ id = "root-nav-submenu-0-0-1",
+ text = "InnerAnnotation",
+ address = "root/com.example/-with-inner/-inner-annotation/index.html",
+ icon = NavigationNodeIcon.ANNOTATION_CLASS_KT
+ )
+
+ content[5].assertNavigationLink(
+ id = "root-nav-submenu-0-0-2",
+ text = "InnerClass",
+ address = "root/com.example/-with-inner/-inner-class/index.html",
+ icon = NavigationNodeIcon.CLASS_KT
+ )
+
+ content[6].assertNavigationLink(
+ id = "root-nav-submenu-0-0-3",
+ text = "InnerEnum",
+ address = "root/com.example/-with-inner/-inner-enum/index.html",
+ icon = NavigationNodeIcon.ENUM_CLASS_KT
+ )
+
+ content[7].assertNavigationLink(
+ id = "root-nav-submenu-0-0-4",
+ text = "InnerInterface",
+ address = "root/com.example/-with-inner/-inner-interface/index.html",
+ icon = NavigationNodeIcon.INTERFACE_KT
+ )
+
+ content[8].assertNavigationLink(
+ id = "root-nav-submenu-0-0-5",
+ text = "InnerObject",
+ address = "root/com.example/-with-inner/-inner-object/index.html",
+ icon = NavigationNodeIcon.OBJECT
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `should be able to have deeply nested classlikes`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ testInline(
+ """
+ |/src/main/kotlin/com/example/DeeplyNested.kt
+ |package com.example
+ |
+ |class DeeplyNested {
+ | class FirstLevelClass {
+ | interface SecondLevelInterface {
+ | object ThirdLevelObject {
+ | annotation class FourthLevelAnnotation {}
+ | }
+ | }
+ | }
+ |}
+ """.trimIndent(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ val content = writerPlugin.writer.navigationHtml().select("div.sideMenuPart")
+ assertEquals(7, content.size)
+
+ // Navigation menu should be the following
+ // - root
+ // - com.example
+ // - DeeplyNested
+ // - FirstLevelClass
+ // - SecondLevelInterface
+ // - ThirdLevelObject
+ // - FourthLevelAnnotation
+
+ content[0].assertNavigationLink(
+ id = "root-nav-submenu",
+ text = "root",
+ address = "index.html",
+ )
+
+ content[1].assertNavigationLink(
+ id = "root-nav-submenu-0",
+ text = "com.example",
+ address = "root/com.example/index.html",
+ )
+
+ content[2].assertNavigationLink(
+ id = "root-nav-submenu-0-0",
+ text = "DeeplyNested",
+ address = "root/com.example/-deeply-nested/index.html",
+ icon = NavigationNodeIcon.CLASS_KT
+ )
+
+ content[3].assertNavigationLink(
+ id = "root-nav-submenu-0-0-0",
+ text = "FirstLevelClass",
+ address = "root/com.example/-deeply-nested/-first-level-class/index.html",
+ icon = NavigationNodeIcon.CLASS_KT
+ )
+
+ content[4].assertNavigationLink(
+ id = "root-nav-submenu-0-0-0-0",
+ text = "SecondLevelInterface",
+ address = "root/com.example/-deeply-nested/-first-level-class/-second-level-interface/index.html",
+ icon = NavigationNodeIcon.INTERFACE_KT
+ )
+
+ content[5].assertNavigationLink(
+ id = "root-nav-submenu-0-0-0-0-0",
+ text = "ThirdLevelObject",
+ address = "root/com.example/-deeply-nested/-first-level-class/-second-level-interface/" +
+ "-third-level-object/index.html",
+ icon = NavigationNodeIcon.OBJECT
+ )
+
+ content[6].assertNavigationLink(
+ id = "root-nav-submenu-0-0-0-0-0-0",
+ text = "FourthLevelAnnotation",
+ address = "root/com.example/-deeply-nested/-first-level-class/-second-level-interface/" +
+ "-third-level-object/-fourth-level-annotation/index.html",
+ icon = NavigationNodeIcon.ANNOTATION_CLASS_KT
+ )
+ }
+ }
+ }
+
+ private fun Element.assertNavigationLink(
+ id: String, text: String, address: String, icon: NavigationNodeIcon? = null
+ ) {
+ assertEquals(id, this.id())
+
+ val link = this.selectFirst("a")
+ checkNotNull(link)
+ assertEquals(text, link.text())
+ assertEquals(address, link.attr("href"))
+ if (icon != null) {
+ val iconStyles =
+ this.selectFirst("div.overview span.nav-link-grid")?.child(0)?.classNames()?.toList() ?: emptyList()
+ assertEquals(3, iconStyles.size)
+ assertEquals("nav-link-child", iconStyles[0])
+ assertEquals(icon.style(), "${iconStyles[1]} ${iconStyles[2]}")
+ }
+ }
+}
diff --git a/plugins/base/src/test/kotlin/utils/HtmlUtils.kt b/plugins/base/src/test/kotlin/utils/HtmlUtils.kt
new file mode 100644
index 00000000..bfba882a
--- /dev/null
+++ b/plugins/base/src/test/kotlin/utils/HtmlUtils.kt
@@ -0,0 +1,11 @@
+package utils
+
+import org.jsoup.Jsoup
+import org.jsoup.nodes.Element
+import org.jsoup.select.Elements
+
+internal fun TestOutputWriter.navigationHtml(): Element = contents.getValue("navigation.html").let { Jsoup.parse(it) }
+
+internal fun Elements.selectNavigationGrid(): Element {
+ return this.select("div.overview").select("span.nav-link-grid").single()
+}