From 2c4da443b96e655032ae7b053fe40ae33c9f6fea Mon Sep 17 00:00:00 2001 From: Oleksandr Karpovich Date: Tue, 22 Feb 2022 14:46:51 +0100 Subject: [PATCH] fix Style composable and CSSRulesHolderState (#1885) Co-authored-by: Oleksandr Karpovich --- .../kotlin/com/sample/content/CodeSnippets.kt | 1 + .../kotlin/com/sample/content/IntroSection.kt | 1 - .../jetbrains/compose/web/css/StyleSheet.kt | 10 +-- .../compose/web/elements/Elements.kt | 2 +- .../src/jsTest/kotlin/CSSStylesheetTests.kt | 77 +++++++++++++++++++ 5 files changed, 83 insertions(+), 8 deletions(-) diff --git a/examples/web-landing/src/jsMain/kotlin/com/sample/content/CodeSnippets.kt b/examples/web-landing/src/jsMain/kotlin/com/sample/content/CodeSnippets.kt index 73bd8df478..98ee83942c 100644 --- a/examples/web-landing/src/jsMain/kotlin/com/sample/content/CodeSnippets.kt +++ b/examples/web-landing/src/jsMain/kotlin/com/sample/content/CodeSnippets.kt @@ -263,6 +263,7 @@ fun FormattedCodeSnippet(code: String, language: String = "kotlin") { backgroundColor(Color("transparent")) } }) { + @Suppress("DEPRECATION") DomSideEffect(code) { it.setHighlightedCode(code) } diff --git a/examples/web-landing/src/jsMain/kotlin/com/sample/content/IntroSection.kt b/examples/web-landing/src/jsMain/kotlin/com/sample/content/IntroSection.kt index 70f4a6db79..5245b82b9c 100644 --- a/examples/web-landing/src/jsMain/kotlin/com/sample/content/IntroSection.kt +++ b/examples/web-landing/src/jsMain/kotlin/com/sample/content/IntroSection.kt @@ -2,7 +2,6 @@ package com.sample.content import androidx.compose.runtime.* import org.jetbrains.compose.web.attributes.ATarget -import org.jetbrains.compose.web.attributes.AttrsBuilder import org.jetbrains.compose.web.attributes.target import org.jetbrains.compose.web.css.* import org.jetbrains.compose.web.dom.* diff --git a/web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/StyleSheet.kt b/web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/StyleSheet.kt index 590e5de6da..d1fe54332c 100644 --- a/web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/StyleSheet.kt +++ b/web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/StyleSheet.kt @@ -1,9 +1,7 @@ package org.jetbrains.compose.web.css import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.setValue +import androidx.compose.runtime.mutableStateListOf import org.jetbrains.compose.web.ExperimentalComposeWebStyleApi import org.jetbrains.compose.web.css.selectors.CSSSelector import org.jetbrains.compose.web.dom.Style @@ -11,11 +9,11 @@ import kotlin.properties.ReadOnlyProperty import kotlin.reflect.KProperty class CSSRulesHolderState : CSSRulesHolder { - override var cssRules: CSSRuleDeclarationList by mutableStateOf(listOf()) + override val cssRules = mutableStateListOf() + override fun add(cssRule: CSSRuleDeclaration) { - @Suppress("SuspiciousCollectionReassignment") - cssRules += cssRule + cssRules.add(cssRule) } } diff --git a/web/core/src/jsMain/kotlin/org/jetbrains/compose/web/elements/Elements.kt b/web/core/src/jsMain/kotlin/org/jetbrains/compose/web/elements/Elements.kt index 4d5fed5061..bf3242a09f 100644 --- a/web/core/src/jsMain/kotlin/org/jetbrains/compose/web/elements/Elements.kt +++ b/web/core/src/jsMain/kotlin/org/jetbrains/compose/web/elements/Elements.kt @@ -947,7 +947,7 @@ fun Style( } }, ) { - DisposableEffect(cssRules) { + DisposableEffect(cssRules, cssRules.size) { val cssStylesheet = scopeElement.sheet as? CSSStyleSheet cssStylesheet?.setCSSRules(cssRules) onDispose { diff --git a/web/core/src/jsTest/kotlin/CSSStylesheetTests.kt b/web/core/src/jsTest/kotlin/CSSStylesheetTests.kt index 3413948088..ac4c01b729 100644 --- a/web/core/src/jsTest/kotlin/CSSStylesheetTests.kt +++ b/web/core/src/jsTest/kotlin/CSSStylesheetTests.kt @@ -5,12 +5,17 @@ package org.jetbrains.compose.web.core.tests +import androidx.compose.runtime.mutableStateOf import org.jetbrains.compose.web.css.* import org.jetbrains.compose.web.dom.Div import org.jetbrains.compose.web.dom.stringPresentation import org.w3c.dom.HTMLElement import org.w3c.dom.get import org.jetbrains.compose.web.testutils.* +import org.w3c.dom.HTMLDivElement +import org.w3c.dom.HTMLStyleElement +import org.w3c.dom.css.CSSStyleSheet +import org.w3c.dom.css.get import kotlin.test.* object AppCSSVariables { @@ -320,4 +325,76 @@ class CSSVariableTests { } } } + + @Test + fun testChildStylesheetsGetApplied() = runTest { + val showContent = mutableStateOf(false) + composition { + Style(TestRootStylesheet2) + if (showContent.value) { + Div(attrs = { + classes(ChildStylesheet2.content) + }) {} + } + } + + val styleElem = root.children[0] as HTMLStyleElement + val cssStyleSheet = styleElem.sheet as CSSStyleSheet + assertEquals(1, cssStyleSheet.cssRules.length) + assertEquals(".cls1 { padding: 10px; }", cssStyleSheet.cssRules[0]!!.cssText) + + showContent.value = true + waitForRecompositionComplete() + + with(root.children[1] as HTMLDivElement) { + assertEquals("content", getAttribute("class")) + } + + // 2nd recomposition occurs since TestRootStylesheet2 was changed during recomposition + // when ChildStylesheet2 was initialised after showContent was set into "true" + waitForRecompositionComplete() + + val styleElem2 = root.children[0] as HTMLStyleElement + val cssStyleSheet2 = styleElem2.sheet as CSSStyleSheet + assertEquals(".cls1 { padding: 10px; }", cssStyleSheet2.cssRules[0]!!.cssText) + assertEquals(2, cssStyleSheet2.cssRules.length) + assertEquals(".content { color: red; }", cssStyleSheet2.cssRules[1]!!.cssText) + } + + @Test + fun testChildStylesheetsGetApplied2() = runTest { + composition { + Style(TestRootStylesheet) + Div(attrs = { classes(ChildStylesheet.content) }) + } + + val styleElem = root.children[0] as HTMLStyleElement + val cssStyleSheet = styleElem.sheet as CSSStyleSheet + assertEquals(2, cssStyleSheet.cssRules.length) + assertEquals(".cls1 { padding: 10px; }", cssStyleSheet.cssRules[0]!!.cssText) + assertEquals(".content { color: red; }", cssStyleSheet.cssRules[1]!!.cssText) + } +} + +private object TestRootStylesheet : StyleSheet(usePrefix = false) { + val cls1 by style { + padding(10.px) + } +} +private object ChildStylesheet : StyleSheet(TestRootStylesheet, usePrefix = false) { + val content by style { + color(Color.red) + } +} + +// Use duplicates for another test to ensure uninitialized objects will be used +private object TestRootStylesheet2 : StyleSheet(usePrefix = false) { + val cls1 by style { + padding(10.px) + } +} +private object ChildStylesheet2 : StyleSheet(TestRootStylesheet2, usePrefix = false) { + val content by style { + color(Color.red) + } }