Skip to content

Commit

Permalink
Footer customisation (#1691)
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcinAman authored Jan 14, 2021
1 parent 4a0af56 commit c04295a
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 29 deletions.
14 changes: 14 additions & 0 deletions docs/src/doc/docs/user_guide/base-specific/frontend.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Configuration specific to HTML format

!!! important
Concepts specified below apply only to configuration of the Base Plugin (that contains Html format)
and needs to be applied via pluginsConfiguration and not on the root one.

## Modifying assets

It is possible to change static assets that are used to generate dokka's HTML.
Expand All @@ -19,6 +23,16 @@ Dokka uses 3 stylesheets:
User can choose to add or override those files.
Resources will be overridden when in `pluginConfiguration` block there is a resource with the same name.

## Modifying footer

Dokka supports custom messages in the footer via `footerMessage` string property on base plugin configuration.
Keep in mind that this value will be pased exactly to the output html, so it has to be valid and escaped correctly.

## Separating inherited members

By setting a boolean property `separateInheritedMembers` dokka will split inherited members (like functions, properties etc.)
from ones declared in viewed class. Separated members will have it's own tabs on the page.

### Examples
In order to override a logo and style it accordingly a simple css file named `logo-styles.css` is needed:
```css
Expand Down
7 changes: 5 additions & 2 deletions plugins/base/src/main/kotlin/DokkaBaseConfiguration.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ package org.jetbrains.dokka.base

import org.jetbrains.dokka.plugability.ConfigurableBlock
import java.io.File
import java.time.Year

data class DokkaBaseConfiguration(
var customStyleSheets: List<File> = defaultCustomStyleSheets,
var customAssets: List<File> = defaultCustomAssets,
var separateInheritedMembers: Boolean = separateInheritedMembersDefault
): ConfigurableBlock {
var separateInheritedMembers: Boolean = separateInheritedMembersDefault,
var footerMessage: String = defaultFooterMessage
) : ConfigurableBlock {
companion object {
val defaultFooterMessage = "© ${Year.now().value} Copyright"
val defaultCustomStyleSheets: List<File> = emptyList()
val defaultCustomAssets: List<File> = emptyList()
const val separateInheritedMembersDefault: Boolean = false
Expand Down
65 changes: 41 additions & 24 deletions plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import kotlinx.html.*
import kotlinx.html.stream.createHTML
import org.jetbrains.dokka.DokkaSourceSetID
import org.jetbrains.dokka.base.DokkaBase
import org.jetbrains.dokka.base.DokkaBaseConfiguration
import org.jetbrains.dokka.base.DokkaBaseConfiguration.Companion.defaultFooterMessage
import org.jetbrains.dokka.base.renderers.DefaultRenderer
import org.jetbrains.dokka.base.renderers.TabSortingStrategy
import org.jetbrains.dokka.base.renderers.html.command.consumers.ImmediateResolutionTagConsumer
Expand All @@ -20,16 +22,14 @@ import org.jetbrains.dokka.model.properties.PropertyContainer
import org.jetbrains.dokka.model.sourceSetIDs
import org.jetbrains.dokka.model.withDescendants
import org.jetbrains.dokka.pages.*
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.plugability.plugin
import org.jetbrains.dokka.plugability.query
import org.jetbrains.dokka.plugability.querySingle
import org.jetbrains.dokka.plugability.*
import org.jetbrains.dokka.utilities.htmlEscape
import java.net.URI

open class HtmlRenderer(
context: DokkaContext
) : DefaultRenderer<FlowContent>(context) {
private val configuration = configuration<DokkaBase, DokkaBaseConfiguration>(context)

private val sourceSetDependencyMap: Map<DokkaSourceSetID, List<DokkaSourceSetID>> =
context.configuration.sourceSets.map { sourceSet ->
Expand Down Expand Up @@ -107,8 +107,13 @@ open class HtmlRenderer(
}
node.hasStyle(TextStyle.Paragraph) -> p(additionalClasses) { childrenCallback() }
node.hasStyle(TextStyle.Block) -> div(additionalClasses) { childrenCallback() }
node.isAnchorable -> buildAnchor(node.anchor!!, node.anchorLabel!!, node.sourceSetsFilters) { childrenCallback() }
node.extra[InsertTemplateExtra] != null -> node.extra[InsertTemplateExtra]?.let { templateCommand(it.command) } ?: Unit
node.isAnchorable -> buildAnchor(
node.anchor!!,
node.anchorLabel!!,
node.sourceSetsFilters
) { childrenCallback() }
node.extra[InsertTemplateExtra] != null -> node.extra[InsertTemplateExtra]?.let { templateCommand(it.command) }
?: Unit
else -> childrenCallback()
}
}
Expand Down Expand Up @@ -147,7 +152,7 @@ open class HtmlRenderer(
}
}

fun FlowContent.withHtml(content: String): Unit = when (this){
fun FlowContent.withHtml(content: String): Unit = when (this) {
is HTMLTag -> unsafe { +content }
else -> div { unsafe { +content } }
}
Expand Down Expand Up @@ -225,13 +230,14 @@ open class HtmlRenderer(
sourceSet.sourceSetIDs.all.flatMap { sourceSetDependencyMap[it].orEmpty() }
.any { sourceSetId -> sourceSetId in sourceSets.sourceSetIDs }
}.map {
it to createHTML(prettyPrint = false).prepareForTemplates().div(classes = "content sourceset-depenent-content") {
if (counter++ == 0) attributes["data-active"] = ""
attributes["data-togglable"] = it.sourceSetIDs.merged.toString()
unsafe {
+html
it to createHTML(prettyPrint = false).prepareForTemplates()
.div(classes = "content sourceset-depenent-content") {
if (counter++ == 0) attributes["data-active"] = ""
attributes["data-togglable"] = it.sourceSetIDs.merged.toString()
unsafe {
+html
}
}
}
}
}
}
Expand Down Expand Up @@ -423,15 +429,15 @@ open class HtmlRenderer(
div {
toRender.filter { it !is ContentLink && !it.hasStyle(ContentStyle.RowTitle) }
.takeIf { it.isNotEmpty() }?.let {
if (ContentKind.shouldBePlatformTagged(contextNode.dci.kind) && contextNode.sourceSets.size == 1)
createPlatformTags(contextNode)
if (ContentKind.shouldBePlatformTagged(contextNode.dci.kind) && contextNode.sourceSets.size == 1)
createPlatformTags(contextNode)

div("title") {
it.forEach {
it.build(this, pageContext, sourceSetRestriction)
div("title") {
it.forEach {
it.build(this, pageContext, sourceSetRestriction)
}
}
}
}
}
}
}
Expand All @@ -450,7 +456,9 @@ open class HtmlRenderer(
.forEach {
span("inline-flex") {
it.build(this, pageContext, sourceSetRestriction)
if(it is ContentLink && !anchorDestination.isNullOrBlank()) buildAnchorCopyButton(anchorDestination)
if (it is ContentLink && !anchorDestination.isNullOrBlank()) buildAnchorCopyButton(
anchorDestination
)
}
}
}
Expand All @@ -472,7 +480,7 @@ open class HtmlRenderer(
toRender: List<ContentNode>,
pageContext: ContentPage,
sourceSetRestriction: Set<DisplaySourceSet>?,
){
) {
toRender.filter { it !is ContentLink }.takeIf { it.isNotEmpty() }?.let {
it.forEach {
span(classes = if (it.dci.kind == ContentKind.Comment) "brief-comment" else "") {
Expand Down Expand Up @@ -573,7 +581,12 @@ open class HtmlRenderer(
}
}

private fun FlowContent.buildAnchor(anchor: String, anchorLabel: String, sourceSets: String, content: FlowContent.() -> Unit) {
private fun FlowContent.buildAnchor(
anchor: String,
anchorLabel: String,
sourceSets: String,
content: FlowContent.() -> Unit
) {
a {
attributes["data-name"] = anchor
attributes["anchor-label"] = anchorLabel
Expand Down Expand Up @@ -772,9 +785,13 @@ open class HtmlRenderer(
span("go-to-top-icon") {
a(href = "#content")
}
span { text("© 2020 Copyright") }
span {
configuration?.footerMessage?.takeIf { it.isNotEmpty() }
?.let { unsafe { raw(it) } }
?: text(defaultFooterMessage)
}
span("pull-right") {
span { text("Sponsored and developed by ") }
span { text("Generated by ") }
a(href = "https://github.com/Kotlin/dokka") {
span { text("dokka") }
span(classes = "padded-icon")
Expand Down
44 changes: 44 additions & 0 deletions plugins/base/src/test/kotlin/renderers/html/CustomFooterTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
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.renderers.html.HtmlRenderer
import org.jetbrains.dokka.base.templating.toJsonString
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
import org.junit.jupiter.api.Test
import renderers.testPage
import utils.A
import utils.Div
import utils.Span
import utils.match

class CustomFooterTest : HtmlRenderingOnlyTestBase() {
@Test
fun `should include message from custom footer`() {
val page = testPage { }
HtmlRenderer(context).render(page)
renderedContent.match(
Span(A()),
Span(Div("Custom message")),
Span(Span("Generated by "), A(Span("dokka"), Span()))
)
}

override val configuration: DokkaConfigurationImpl
get() = super.configuration.copy(
pluginsConfiguration = listOf(
PluginConfigurationImpl(
DokkaBase::class.java.canonicalName,
DokkaConfiguration.SerializationFormat.JSON,
toJsonString(DokkaBaseConfiguration(footerMessage = """<div style="color: red">Custom message</div>"""))
)
)
)

override val renderedContent: Element
get() = files.contents.getValue("test-page.html").let { Jsoup.parse(it) }.select(".footer").single()
}
27 changes: 27 additions & 0 deletions plugins/base/src/test/kotlin/renderers/html/FooterMessageTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package renderers.html

import org.jetbrains.dokka.base.DokkaBaseConfiguration.Companion.defaultFooterMessage
import org.jetbrains.dokka.base.renderers.html.HtmlRenderer
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
import org.junit.jupiter.api.Test
import renderers.testPage
import utils.A
import utils.Span
import utils.match

class FooterMessageTest : HtmlRenderingOnlyTestBase() {
@Test
fun `should include defaultFooter`() {
val page = testPage { }
HtmlRenderer(context).render(page)
renderedContent.match(
Span(A()),
Span(defaultFooterMessage),
Span(Span("Generated by "), A(Span("dokka"), Span()))
)
}

override val renderedContent: Element
get() = files.contents.getValue("test-page.html").let { Jsoup.parse(it) }.select(".footer").single()
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,19 @@ abstract class HtmlRenderingOnlyTestBase : RenderingOnlyTestBase<Element>() {
)

val files = TestOutputWriter()

open val configuration = DokkaConfigurationImpl(
sourceSets = listOf(js, jvm, native)
)

override val context = MockContext(
DokkaBase().outputWriter to { _ -> files },
DokkaBase().locationProviderFactory to ::DokkaLocationProviderFactory,
DokkaBase().htmlPreprocessors to { _ -> RootCreator },
DokkaBase().externalLocationProviderFactory to { ::JavadocExternalLocationProviderFactory },
DokkaBase().externalLocationProviderFactory to { ::DefaultExternalLocationProviderFactory },
DokkaBase().tabSortingStrategy to { DefaultTabSortingStrategy() },
testConfiguration = DokkaConfigurationImpl(
sourceSets = listOf(js, jvm, native)
)
testConfiguration = configuration
)

override val renderedContent: Element by lazy {
Expand Down

0 comments on commit c04295a

Please sign in to comment.