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

Add the link to homepage to the header if homepageLink is provided #3235

Merged
merged 8 commits into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,13 @@ plugins {
kotlin("jvm")
id("org.jetbrains.dokka")
}

allprojects {
tasks.withType<org.jetbrains.dokka.gradle.AbstractDokkaTask> {
pluginsMapConfiguration.set(
mapOf(
"org.jetbrains.dokka.base.DokkaBase" to """{ "homepageLink" : "https://github.com/Kotlin/dokka/tree/master/integration-tests/gradle/projects/it-multimodule-0/" }"""
)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,21 @@ class MultiModule0IntegrationTest : AbstractGradleIntegrationTest() {
"Expected moduleC being mentioned in -modules.html"
)

val htmlsWithHomepageLink = outputDir.walkTopDown().filter {
it.isFile && it.extension == "html" && it.name != "navigation.html"
}.toList()

assertEquals(16, htmlsWithHomepageLink.size)

htmlsWithHomepageLink.forEach {
assertTrue(
it.readText().contains(
"""https://github.com/Kotlin/dokka/tree/master/integration-tests/gradle/projects/it-multimodule-0/"""
),
"File ${it.absolutePath} doesn't contain link to homepage"
)
}

val gfmOutputDir = File(projectDir, "moduleA/build/dokka/gfmMultiModule")
assertTrue(gfmOutputDir.isDirectory, "Missing dokka GFM output directory")

Expand Down
11 changes: 7 additions & 4 deletions plugins/base/api/base.api
Original file line number Diff line number Diff line change
Expand Up @@ -105,27 +105,30 @@ public final class org/jetbrains/dokka/base/DokkaBaseConfiguration : org/jetbrai
public static final field mergeImplicitExpectActualDeclarationsDefault Z
public static final field separateInheritedMembersDefault Z
public fun <init> ()V
public fun <init> (Ljava/util/List;Ljava/util/List;ZLjava/lang/String;ZLjava/io/File;)V
public synthetic fun <init> (Ljava/util/List;Ljava/util/List;ZLjava/lang/String;ZLjava/io/File;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/util/List;Ljava/util/List;ZLjava/lang/String;ZLjava/io/File;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/util/List;Ljava/util/List;ZLjava/lang/String;ZLjava/io/File;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Ljava/util/List;
public final fun component2 ()Ljava/util/List;
public final fun component3 ()Z
public final fun component4 ()Ljava/lang/String;
public final fun component5 ()Z
public final fun component6 ()Ljava/io/File;
public final fun copy (Ljava/util/List;Ljava/util/List;ZLjava/lang/String;ZLjava/io/File;)Lorg/jetbrains/dokka/base/DokkaBaseConfiguration;
public static synthetic fun copy$default (Lorg/jetbrains/dokka/base/DokkaBaseConfiguration;Ljava/util/List;Ljava/util/List;ZLjava/lang/String;ZLjava/io/File;ILjava/lang/Object;)Lorg/jetbrains/dokka/base/DokkaBaseConfiguration;
public final fun component7 ()Ljava/lang/String;
public final fun copy (Ljava/util/List;Ljava/util/List;ZLjava/lang/String;ZLjava/io/File;Ljava/lang/String;)Lorg/jetbrains/dokka/base/DokkaBaseConfiguration;
public static synthetic fun copy$default (Lorg/jetbrains/dokka/base/DokkaBaseConfiguration;Ljava/util/List;Ljava/util/List;ZLjava/lang/String;ZLjava/io/File;Ljava/lang/String;ILjava/lang/Object;)Lorg/jetbrains/dokka/base/DokkaBaseConfiguration;
public fun equals (Ljava/lang/Object;)Z
public final fun getCustomAssets ()Ljava/util/List;
public final fun getCustomStyleSheets ()Ljava/util/List;
public final fun getFooterMessage ()Ljava/lang/String;
public final fun getHomepageLink ()Ljava/lang/String;
public final fun getMergeImplicitExpectActualDeclarations ()Z
public final fun getSeparateInheritedMembers ()Z
public final fun getTemplatesDir ()Ljava/io/File;
public fun hashCode ()I
public final fun setCustomAssets (Ljava/util/List;)V
public final fun setCustomStyleSheets (Ljava/util/List;)V
public final fun setFooterMessage (Ljava/lang/String;)V
public final fun setHomepageLink (Ljava/lang/String;)V
public final fun setMergeImplicitExpectActualDeclarations (Z)V
public final fun setSeparateInheritedMembers (Z)V
public final fun setTemplatesDir (Ljava/io/File;)V
Expand Down
3 changes: 2 additions & 1 deletion plugins/base/src/main/kotlin/DokkaBaseConfiguration.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ public data class DokkaBaseConfiguration(
var separateInheritedMembers: Boolean = separateInheritedMembersDefault,
var footerMessage: String = defaultFooterMessage,
var mergeImplicitExpectActualDeclarations: Boolean = mergeImplicitExpectActualDeclarationsDefault,
var templatesDir: File? = defaultTemplatesDir
var templatesDir: File? = defaultTemplatesDir,
var homepageLink: String? = null,
) : ConfigurableBlock {
public companion object {
public val defaultFooterMessage: String = "© ${Year.now().value} Copyright"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ public object AssetsInstaller : PageTransformer {
"images/copy-successful-icon.svg",
"images/theme-toggle.svg",
"images/burger.svg",
"images/homepage.svg",

// navigation icons
"images/nav-icons/abstract-class.svg",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,16 @@ public class DefaultTemplateModelFactory(
return mapper
}

override fun buildSharedModel(): TemplateMap = mapOf<String, Any>(
"footerMessage" to (configuration?.footerMessage?.takeIf { it.isNotEmpty() }
?: DokkaBaseConfiguration.defaultFooterMessage)
)
override fun buildSharedModel(): TemplateMap {
val mapper = mutableMapOf<String, Any>()

mapper["footerMessage"] =
(configuration?.footerMessage?.takeIf(String::isNotBlank) ?: DokkaBaseConfiguration.defaultFooterMessage)

configuration?.homepageLink?.takeIf(String::isNotBlank)?.let { mapper["homepageLink"] = it }

return mapper
}

private val DisplaySourceSet.comparableKey
get() = sourceSetIDs.merged.let { it.scopeId + it.sourceSetName }
Expand All @@ -107,6 +113,7 @@ public class DefaultTemplateModelFactory(
rel = LinkRel.stylesheet,
href = if (resource.isAbsolute) resource else "$pathToRoot$resource"
)

resource.URIExtension == "js" ->
script(
type = ScriptType.textJavaScript,
Expand All @@ -117,6 +124,7 @@ public class DefaultTemplateModelFactory(
else
async = true
}

resource.isImage() -> link(href = if (resource.isAbsolute) resource else "$pathToRoot$resource")
else -> null
}
Expand All @@ -125,6 +133,7 @@ public class DefaultTemplateModelFactory(
append(resourceHtml)
}
}

}

private class PrintDirective(val generateData: () -> String) : TemplateDirectiveModel {
Expand All @@ -144,7 +153,10 @@ private class PrintDirective(val generateData: () -> String) : TemplateDirective
}
}

private class TemplateDirective(val configuration: DokkaConfiguration, val pathToRoot: String) : TemplateDirectiveModel {
private class TemplateDirective(
val configuration: DokkaConfiguration,
val pathToRoot: String
) : TemplateDirectiveModel {
override fun execute(
env: Environment,
params: MutableMap<Any?, Any?>?,
Expand All @@ -170,6 +182,7 @@ private class TemplateDirective(val configuration: DokkaConfiguration, val pathT
Context(env, body)
)
}

"projectName" -> {
body ?: throw TemplateModelException(
"No directive body $commandName command."
Expand All @@ -183,6 +196,7 @@ private class TemplateDirective(val configuration: DokkaConfiguration, val pathT
Context(env, body)
)
}

else -> throw TemplateModelException(
"The parameter $PARAM_NAME $commandName is unknown"
)
Expand Down
5 changes: 5 additions & 0 deletions plugins/base/src/main/resources/dokka/images/homepage.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 31 additions & 1 deletion plugins/base/src/main/resources/dokka/styles/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ td:first-child {
/* --- Navigation controls --- */
.navigation-controls {
display: flex;
margin-left: 4px;
}

@media (min-width: 760px) {
Expand All @@ -365,7 +366,6 @@ td:first-child {
display: block;
border-radius: 50%;
background-color: inherit;
margin-left: 4px;
padding: 0;
border: none;
cursor: pointer;
Expand Down Expand Up @@ -394,6 +394,36 @@ td:first-child {
}
/* /--- Navigation THEME --- */

/* --- Navigation HOMEPAGE --- */
.navigation-controls--homepage {
height: 40px;
width: 40px;
display: block;
border-radius: 50%;
cursor: pointer;
}

.navigation-controls--homepage a::before {
height: 100%;
width: 20px;
margin-left: 10px;
display: block;
content: "";
background: url("../images/homepage.svg");
background-size: 100% 100%;
}

.navigation-controls--homepage:hover {
background: var(--white-10);
}

@media (max-width: 759px) {
.navigation-controls--homepage {
display: none;
}
}
/* /--- Navigation HOMEPAGE --- */

.navigation .platform-selector:not([data-active]) {
color: #fff;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
<@source_set_selector.display/>
</div>
<div class="navigation-controls">
<#if homepageLink?has_content>
<div class="navigation-controls--btn navigation-controls--homepage" id="homepage-link" role="button"><a href="${homepageLink}"></a></div>
Copy link
Member

Choose a reason for hiding this comment

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

I have not checked but it seems to be <a href="${homepageLink}"> <div class="navigation-controls--btn navigation-controls--homepage" id="homepage-link" role="button"/></a>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Im not an html-expert, but at least my variant works. If to switch tags, it doesn't work at my side (may be need to adapt more code)
I think it could be rechecked in scope of #3300

</#if>
<button class="navigation-controls--btn navigation-controls--theme" id="theme-toggle-button" type="button">switch theme</button>
<div class="navigation-controls--btn navigation-controls--search" id="searchBar" role="button">search in API</div>
</div>
Expand Down
102 changes: 102 additions & 0 deletions plugins/base/src/test/kotlin/renderers/html/HeaderTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/

package renderers.html

import org.jetbrains.dokka.DokkaConfiguration
import org.jetbrains.dokka.DokkaConfigurationImpl
import org.jetbrains.dokka.PluginConfigurationImpl
import org.jetbrains.dokka.base.DokkaBase
import org.jetbrains.dokka.base.DokkaBaseConfiguration
import org.jetbrains.dokka.base.templating.toJsonString
import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
import org.jetbrains.dokka.pages.RootPageNode
import org.jetbrains.dokka.plugability.DokkaContext
import org.jsoup.Jsoup
import utils.TestOutputWriter
import utils.TestOutputWriterPlugin
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull

class HeaderTest : BaseAbstractTest() {
private val configuration = dokkaConfiguration {
sourceSets {
sourceSet {
name = "jvm"
sourceRoots = listOf("src/jvm")
}
sourceSet {
name = "js"
sourceRoots = listOf("src/js")
}
}
}

@Test
fun `should include homepage link if homepageLink is provided`() {
testRendering(
DokkaBaseConfiguration(homepageLink = "https://github.com/Kotlin/dokka/")
) { _, _, writer ->
val renderedContent = navigationElement(writer)

val sourceLinkElement =
assertNotNull(renderedContent.getElementById("homepage-link"), "Source link element not found")
val aElement = assertNotNull(sourceLinkElement.selectFirst("a"))
assertEquals("https://github.com/Kotlin/dokka/", aElement.attr("href"))
}
}

@Test
fun `should not include homepage link by default`() {
testRendering(null) { _, _, writer ->
val renderedContent = navigationElement(writer)
assertNull(renderedContent.getElementById("homepage-link"), "Source link element found")
}
}

private fun testRendering(
baseConfiguration: DokkaBaseConfiguration?,
block: (RootPageNode, DokkaContext, writer: TestOutputWriter) -> Unit
) {
fun configuration(): DokkaConfigurationImpl {
baseConfiguration ?: return configuration
return configuration.copy(
pluginsConfiguration = listOf(
PluginConfigurationImpl(
DokkaBase::class.java.canonicalName,
DokkaConfiguration.SerializationFormat.JSON,
toJsonString(baseConfiguration)
)
)
)
}

val writerPlugin = TestOutputWriterPlugin()
testInline(
"""
|/src/jvm/Test.kt
|fun test() {}
|/src/js/Test.kt
|fun test() {}
""",
configuration(),
pluginOverrides = listOf(writerPlugin)
) {
renderingStage = { node, context ->
block(node, context, writerPlugin.writer)
}
}
}

private fun navigationElement(writer: TestOutputWriter) =
writer
.contents
.getValue("index.html")
.let(Jsoup::parse)
.select(".navigation")
.single()

}