From 01e21a47d4199bf8de992e336c46e1cda041f9bc Mon Sep 17 00:00:00 2001 From: tretikoff Date: Thu, 5 Aug 2021 14:58:09 +0300 Subject: [PATCH] Add more tests for public CssBuilder integration and minor fixes --- .../kotlin/kotlinx/css/CssBuilder.kt | 7 +- .../src/jsMain/kotlin/styled/StyledCss.kt | 82 ++--- .../src/jsTest/kotlin/TestUtils.kt | 18 +- .../src/jsTest/kotlin/test/AtRulesTest.kt | 88 +++++ .../src/jsTest/kotlin/test/ElementTest.kt | 122 +++++-- .../kotlin/test/PseudoClassRulesTest.kt | 2 +- .../jsTest/kotlin/test/PseudoElementsTest.kt | 74 +++++ .../kotlin/test/RelativeSelectorsTest.kt | 312 ++++++++++++++++++ .../src/jsTest/kotlin/test/StyleSheetTest.kt | 28 +- .../src/jsTest/kotlin/test/TestBase.kt | 15 +- .../test/styleSheets/SimpleStyleSheet.kt | 21 ++ .../test/styleSheets/StaticStyleSheet.kt | 15 + 12 files changed, 686 insertions(+), 98 deletions(-) create mode 100644 kotlin-styled-next/src/jsTest/kotlin/test/AtRulesTest.kt create mode 100644 kotlin-styled-next/src/jsTest/kotlin/test/PseudoElementsTest.kt create mode 100644 kotlin-styled-next/src/jsTest/kotlin/test/RelativeSelectorsTest.kt create mode 100644 kotlin-styled-next/src/jsTest/kotlin/test/styleSheets/SimpleStyleSheet.kt create mode 100644 kotlin-styled-next/src/jsTest/kotlin/test/styleSheets/StaticStyleSheet.kt diff --git a/kotlin-css/src/commonMain/kotlin/kotlinx/css/CssBuilder.kt b/kotlin-css/src/commonMain/kotlin/kotlinx/css/CssBuilder.kt index 96cb6a7a1837..0f16d3557590 100644 --- a/kotlin-css/src/commonMain/kotlin/kotlinx/css/CssBuilder.kt +++ b/kotlin-css/src/commonMain/kotlin/kotlinx/css/CssBuilder.kt @@ -133,6 +133,9 @@ interface CssBuilder : StyledElement, RuleContainer { } } + /** + * Add custom property to CSS [declarations]. If the variable name is in the camelCase, it turns it to snake-case + */ fun setCustomProperty(name: String, value: CssValue) { put("--$name", value.value) } @@ -144,8 +147,8 @@ interface CssBuilder : StyledElement, RuleContainer { fun max(v1: LinearDimension, v2: LinearDimension): LinearDimension = LinearDimension("max($v1, $v2)") - fun clamp(min: LinearDimension, max: LinearDimension, preferred: LinearDimension): LinearDimension = - LinearDimension("clamp($min, $max, $preferred)") + fun clamp(min: LinearDimension, preferred: LinearDimension, max: LinearDimension): LinearDimension = + LinearDimension("clamp($min, $preferred, $max)") // Operator overrides operator fun RuleSet.unaryPlus() diff --git a/kotlin-styled-next/src/jsMain/kotlin/styled/StyledCss.kt b/kotlin-styled-next/src/jsMain/kotlin/styled/StyledCss.kt index 42fb3acfff89..d9c14aa87934 100644 --- a/kotlin-styled-next/src/jsMain/kotlin/styled/StyledCss.kt +++ b/kotlin-styled-next/src/jsMain/kotlin/styled/StyledCss.kt @@ -43,17 +43,13 @@ internal open class StyledCss( } } - private fun buildRules(outerSelector: String = ""): List { - return rules.filter { (selector) -> !withAmpersand(selector) && !withMedia(selector) } - .flatMap { (selector, _, css) -> - val delimiter = if (isPseudoClass(selector)) "" else " " - css.getCssRules(selector.split(",").joinToString { "$outerSelector$delimiter${it.trim()}" }) - } - } - - private fun isPseudoClass(selector: Selector) = selector.trim().startsWith(":") - private fun withAmpersand(selector: Selector) = selector.contains("&") private fun withMedia(selector: Selector) = selector.trim().startsWith("@media") + private fun withContainer(selector: Selector) = selector.trim().startsWith("@container") + private fun withAmpersand(selector: Selector) = selector.contains("&") + private fun withFontFace(selector: Selector) = selector.trim().startsWith("@font-face") + private fun withSupports(selector: Selector) = selector.trim().startsWith("@supports") + private fun withCustomHandle(selector: Selector) = + withMedia(selector) || withContainer(selector) || withSupports(selector) || withAmpersand(selector) || withFontFace(selector) private var memoizedHashCode: Int? = null override fun hashCode(): Int { @@ -79,45 +75,55 @@ internal open class StyledCss( */ fun getCssRules(outerSelector: String?, indent: String = ""): List { val result = mutableListOf() - val (ampersandRules, rules) = rules.partition { withAmpersand(it.selector) } - if (rules.isNotEmpty() || declarations.isNotEmpty()) { - if (outerSelector != null) { - result.add( - buildString { - append("$indent$outerSelector {\n") - append(declarations) - append("$indent}\n") - result.addAll(buildRules(outerSelector)) - } - ) - } else { - result.addAll(buildRules()) - } - } - - rules.filter { withMedia(it.selector) }.forEach { (selector, _, css) -> + val (rules, handleRules) = rules.partition { !withCustomHandle(it.selector) } + if (declarations.isNotEmpty() && outerSelector != null) { result.add( buildString { - append("$indent$selector {\n") - css.getCssRules(outerSelector, " $indent").forEach { appendLine(it); } + append("$indent$outerSelector {\n") + append(declarations) append("$indent}\n") } ) } + result.addAll(buildRules(rules, outerSelector ?: "")) - ampersandRules.forEach { - result.add( - buildString { - val selector = resolveRelativeSelector(it.selector, outerSelector!!) - result.addAll(it.css.getCssRules(selector, indent)) - } - ) + handleRules.forEach { (selector, _, css) -> + val resolvedSelector = resolveRelativeSelector(selector, outerSelector) + if (withMedia(resolvedSelector)) { + result.add( + buildString { + append("$indent$selector {\n") + css.getCssRules(outerSelector, " $indent").forEach { appendLine(it); } + append("$indent}\n") + } + ) + } else if (withContainer(resolvedSelector) || withSupports(resolvedSelector)) { + result.add( + buildString { + append("$indent$selector {\n") + css.getCssRules(" $indent").forEach { appendLine(it); } + append("$indent}\n") + } + ) + } else { + result.addAll(css.getCssRules(resolvedSelector)) + } } return result } - private fun resolveRelativeSelector(selector: Selector, selfClassName: ClassName) = selfClassName.split(",") - .joinToString { selector.replace("&", it.trim()) } + private fun resolveRelativeSelector(selector: Selector, selfClassName: ClassName?): Selector { + if (selfClassName == null) return selector + return selfClassName.split(",").joinToString { selector.replace("&", it.trim()) } + } +} + +private fun isPseudoClass(selector: Selector) = selector.trim().startsWith(":") +private fun buildRules(rules: List, outerSelector: String): List { + return rules.flatMap { (selector, _, css) -> + val delimiter = if (isPseudoClass(selector)) "" else " " + css.getCssRules(selector.split(",").joinToString { "$outerSelector$delimiter${it.trim()}" }) + } } private fun Rule.toStyledRule(parent: RuleContainer, block: RuleSet = this.block): StyledRule { diff --git a/kotlin-styled-next/src/jsTest/kotlin/TestUtils.kt b/kotlin-styled-next/src/jsTest/kotlin/TestUtils.kt index 3c828806c00b..3ed040c3d948 100644 --- a/kotlin-styled-next/src/jsTest/kotlin/TestUtils.kt +++ b/kotlin-styled-next/src/jsTest/kotlin/TestUtils.kt @@ -9,9 +9,7 @@ import kotlinx.dom.clear import org.w3c.dom.Element import org.w3c.dom.HTMLElement import org.w3c.dom.HTMLStyleElement -import org.w3c.dom.css.CSSRuleList -import org.w3c.dom.css.CSSStyleDeclaration -import org.w3c.dom.css.CSSStyleSheet +import org.w3c.dom.css.* import react.ComponentType import react.RProps import react.createElement @@ -49,8 +47,18 @@ class TestScope : CoroutineScope by testScope { return getStylesheet().cssRules } - fun Element.getStyle(): CSSStyleDeclaration { - return window.getComputedStyle(this) + fun Element.getStyle(pseudoElt: String? = null): CSSStyleDeclaration { + return window.getComputedStyle(this, pseudoElt) + } + + fun Element.color(pseudoElt: String? = null): String { + return getStyle(pseudoElt).color + } + + fun CSSRuleList.forEach(block: (rule: CSSRule?) -> Unit) { + for (i in 0 until this.length) { + block(this[i]) + } } fun assertChildrenCount(n: Int) { diff --git a/kotlin-styled-next/src/jsTest/kotlin/test/AtRulesTest.kt b/kotlin-styled-next/src/jsTest/kotlin/test/AtRulesTest.kt new file mode 100644 index 000000000000..61ad6a36b747 --- /dev/null +++ b/kotlin-styled-next/src/jsTest/kotlin/test/AtRulesTest.kt @@ -0,0 +1,88 @@ +package test + +import kotlinx.css.* +import react.RProps +import react.dom.div +import react.fc +import runTest +import styled.css +import styled.styledDiv +import styled.styledSpan +import kotlin.test.Test +import kotlin.test.assertEquals + +/** + * Check every public interface function in [CssBuilder] + */ +class AtRulesTest : TestBase() { + @Test + fun supports() = runTest { + val query = "(color: $firstColor)" + val styledComponent = fc { + styledDiv { + css { + supports(query) { + "div" { + color = firstColor + } + } + } + div {} + } + } + val element = clearAndInject(styledComponent) + assertEquals(firstColor.toString(), element.childAt(0).color()) + assertCssInjected("@supports $query", listOf("color" to firstColor.toString())) + } + + @Test + fun fontFace() = runTest { + val styledComponent = fc { + styledDiv { + css { + fontFace { + fontFamily = "Roboto" + } + } + } + } + clearAndInject(styledComponent) + assertCssInjected("@font-face", listOf("font-family" to "Roboto")) + } + + @Test + fun retina() = runTest { + val styledComponent = fc { + styledDiv { + css { + fontSize = 15.px + retina { + fontSize = 18.px + } + } + } + } + clearAndInject(styledComponent) + assertCssInjected("@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi)", listOf("font-size" to "18px")) + } + + @Test + fun media() = runTest { + val query = "only screen and (max-width: 600px)" + val styledComponent = fc { + styledSpan { + css { + media(query) { + textTransform = TextTransform.capitalize + } + } + } + } + clearAndInject(styledComponent) + assertCssInjected( + "@media $query", listOf( + "text-transform" to "capitalize", + ) + ) + } +} \ No newline at end of file diff --git a/kotlin-styled-next/src/jsTest/kotlin/test/ElementTest.kt b/kotlin-styled-next/src/jsTest/kotlin/test/ElementTest.kt index 7b616b5a0531..36d61bc663a7 100644 --- a/kotlin-styled-next/src/jsTest/kotlin/test/ElementTest.kt +++ b/kotlin-styled-next/src/jsTest/kotlin/test/ElementTest.kt @@ -118,9 +118,10 @@ class ElementTest : TestBase() { } } } - clearAndInject(first) - inject(second) + val firstElement = clearAndInject(first) + val secondElement = inject(second) val rules = getStylesheet().cssRules + assertEquals(firstElement.className, secondElement.className) assertEquals(1, rules.length) } @@ -224,26 +225,6 @@ class ElementTest : TestBase() { assertEquals(firstColor.toString(), element.childAt(1).getStyle().color) } - @Test - fun mediaRuleTest() = runTest { - val query = "only screen and (max-width: 600px)" - val styledComponent = fc { - styledSpan { - css { - media(query) { - textTransform = TextTransform.capitalize - } - } - } - } - clearAndInject(styledComponent) - assertCssInjected( - "@media $query", listOf( - "text-transform" to "capitalize", - ) - ) - } - @Test fun animationTest() = runTest { val styledComponent = fc { @@ -282,4 +263,101 @@ class ElementTest : TestBase() { ) ) } + + + @Test + fun setCustomProperty() = runTest { + val styledComponent = fc { + styledDiv { + css { + setCustomProperty("first-color", firstColor) + } + styledSpan { + css { + color = Color("first-color".toCustomProperty()) + } + } + } + } + val element = clearAndInject(styledComponent) + assertEquals(firstColor.toString(), element.getStyle().getPropertyValue("--first-color").trim()) + assertEquals(firstColor.toString(), element.childAt(0).color()) + } + + @Test + fun min() = runTest { + val styledComponent = fc { + styledDiv { + css { + paddingLeft = min(1.px, 2.px) + } + } + } + val element = clearAndInject(styledComponent) + assertEquals("1px", element.getStyle().paddingLeft) + } + + @Test + fun max() = runTest { + val styledComponent = fc { + styledDiv { + css { + paddingLeft = max(1.px, 2.px) + } + } + } + val element = clearAndInject(styledComponent) + assertEquals("2px", element.getStyle().paddingLeft) + } + + @Test + fun clamp() = runTest { + val styledComponent = fc { + styledDiv { + css { + paddingTop = clamp(2.px, 3.px, 4.px) // value inside bounds + paddingLeft = clamp(2.px, 1.px, 4.px) // value lesser than lower bound + paddingRight = clamp(2.px, 5.px, 4.px) // value greater than higher bound + } + } + } + val element = clearAndInject(styledComponent) + assertEquals("3px", element.getStyle().paddingTop) + assertEquals("2px", element.getStyle().paddingLeft) + assertEquals("4px", element.getStyle().paddingRight) + } + + @Test + fun root() = runTest { + val css = CssBuilder().apply { + root { + color = firstColor + } + } + injectGlobal(css) + val styledComponent = fc { + div {} + } + val element = clearAndInject(styledComponent) + assertEquals(firstColor.toString(), element.color()) + } + + @Test + fun compareTo() = runTest { + val styledComponent = fc { + styledDiv { + css { + color = firstColor + this > "div" { + color = secondColor + } + } + div {} + span { div {} } + } + } + val element = clearAndInject(styledComponent) + assertEquals(firstColor.toString(), element.color()) + assertEquals(secondColor.toString(), element.childAt(0).color()) + } } \ No newline at end of file diff --git a/kotlin-styled-next/src/jsTest/kotlin/test/PseudoClassRulesTest.kt b/kotlin-styled-next/src/jsTest/kotlin/test/PseudoClassRulesTest.kt index 042d78aa6c4a..c997687a883d 100644 --- a/kotlin-styled-next/src/jsTest/kotlin/test/PseudoClassRulesTest.kt +++ b/kotlin-styled-next/src/jsTest/kotlin/test/PseudoClassRulesTest.kt @@ -256,7 +256,7 @@ class PseudoClassRulesTest : TestBase() { } } val element = clearAndInject(styledComponent) - assertEquals(firstColor.toString(), element.getStyle().color) + assertEquals(firstColor.toString(), element.color()) // We can't set :hover with plain js (https://stackoverflow.com/questions/4347116/trigger-css-hover-with-js) assertCssInjected("${element.className}:hover", listOf("color" to secondColor.toString())) diff --git a/kotlin-styled-next/src/jsTest/kotlin/test/PseudoElementsTest.kt b/kotlin-styled-next/src/jsTest/kotlin/test/PseudoElementsTest.kt new file mode 100644 index 000000000000..3124802eb2c2 --- /dev/null +++ b/kotlin-styled-next/src/jsTest/kotlin/test/PseudoElementsTest.kt @@ -0,0 +1,74 @@ +package test + +import kotlinx.browser.window +import kotlinx.css.CssBuilder +import kotlinx.css.color +import kotlinx.html.ButtonType +import kotlinx.html.InputType +import react.RProps +import react.dom.* +import react.fc +import runTest +import styled.* +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals + +/** + * Check every public interface function in [CssBuilder] + */ +class PseudoElementsTest : TestBase() { + @Test + fun afterPseudoElement() = runTest { + val styledComponent = fc { + styledDiv { + css { + after { + color = firstColor + } + } + } + } + val element = clearAndInject(styledComponent) + assertEquals(firstColor.toString(), element.color(":after")) + assertNotEquals(firstColor.toString(), element.color()) + } + + @Test + fun beforePseudoElement() = runTest { + val styledComponent = fc { + styledDiv { + css { + before { + color = firstColor + } + } + } + } + val element = clearAndInject(styledComponent) + assertEquals(firstColor.toString(), element.color(":before")) + assertNotEquals(firstColor.toString(), element.color()) + } + + @Test + fun placeholder() = runTest { + val styledComponent = fc { + styledInput { + attrs { + placeholder = "sdf" + } + css { + placeholder { + color = firstColor + } + } + } + } + val element = clearAndInject(styledComponent) + // Checking computed css not working for placeholder pseudo element in chrome, https://bugs.chromium.org/p/chromium/issues/detail?id=850744 + if (window.navigator.userAgent.lowercase().contains("firefox")) { + assertEquals(firstColor.toString(), element.getStyle("::placeholder").color) + } + assertCssInjected("${element.className}::placeholder", listOf("color" to firstColor.toString())) + } +} \ No newline at end of file diff --git a/kotlin-styled-next/src/jsTest/kotlin/test/RelativeSelectorsTest.kt b/kotlin-styled-next/src/jsTest/kotlin/test/RelativeSelectorsTest.kt new file mode 100644 index 000000000000..341052901f77 --- /dev/null +++ b/kotlin-styled-next/src/jsTest/kotlin/test/RelativeSelectorsTest.kt @@ -0,0 +1,312 @@ +package test + +import kotlinx.css.CssBuilder +import kotlinx.css.backgroundColor +import kotlinx.css.color +import react.RProps +import react.dom.div +import react.dom.span +import react.fc +import runTest +import styled.css +import styled.styledDiv +import styled.styledSpan +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals + +/** + * Check every public interface function in [CssBuilder] + */ +class RelativeSelectorsTest : TestBase() { + @Test + fun children() = runTest { + val styledComponent = fc { + styledDiv { + css { + color = firstColor + children { + color = secondColor + } + } + div {} + span { div {} } + div {} + } + } + val element = clearAndInject(styledComponent) + assertEquals(secondColor.toString(), element.childAt(0).getStyle().color) + assertEquals(secondColor.toString(), element.childAt(1).getStyle().color) + assertEquals(secondColor.toString(), element.childAt(1).childAt(0).getStyle().color) + assertEquals(secondColor.toString(), element.childAt(2).getStyle().color) + } + + @Test + fun descendantsSelector() = runTest { + val styledComponent = fc { + styledDiv { + css { + color = firstColor + backgroundColor = secondColor + descendants("div") { + color = secondColor + } + } + span { div {} } + div {} + } + } + val element = clearAndInject(styledComponent) + assertNotEquals(secondColor.toString(), element.color()) + assertEquals(firstColor.toString(), element.childAt(0).color()) + assertEquals(secondColor.toString(), element.childAt(0).childAt(0).color()) + assertEquals(secondColor.toString(), element.childAt(1).color()) + } + + @Test + fun descendants() = runTest { + val styledComponent = fc { + styledDiv { + css { + color = firstColor + descendants { + color = secondColor + } + } + span { div {} } + div {} + } + } + val element = clearAndInject(styledComponent) + assertNotEquals(secondColor.toString(), element.color()) + assertEquals(secondColor.toString(), element.childAt(0).color()) + assertEquals(secondColor.toString(), element.childAt(0).childAt(0).color()) + assertEquals(secondColor.toString(), element.childAt(1).color()) + } + + @Test + fun ancestorHover() = runTest { + val styledComponent = fc { + styledDiv { + +"ancestor" + styledSpan { + css { + color = firstColor + ancestorHover(".ancestor") { + color = secondColor + } + } + } + } + } + val element = clearAndInject(styledComponent) + assertCssInjected(".ancestor:hover .${element.childAt(0).className}", listOf("color" to secondColor.toString())) + } + + + @Test + fun child() = runTest { + val styledComponent = fc { + styledDiv { + css { + color = firstColor + child("div") { + color = secondColor + } + } + div {} + span { div {} } + div {} + } + } + val element = clearAndInject(styledComponent) + assertEquals(secondColor.toString(), element.childAt(0).getStyle().color) + assertEquals(firstColor.toString(), element.childAt(1).getStyle().color) + assertEquals(firstColor.toString(), element.childAt(1).childAt(0).getStyle().color) + assertEquals(secondColor.toString(), element.childAt(2).getStyle().color) + } + + @Test + fun sibling() = runTest { + val styledComponent = fc { + div { + span {} + styledDiv { + css { + sibling("div") { + color = firstColor + } + } + } + styledDiv { css { color = secondColor } } + div {} + } + } + val element = clearAndInject(styledComponent) + assertNotEquals(firstColor.toString(), element.color()) + assertNotEquals(firstColor.toString(), element.childAt(0).color()) + assertNotEquals(firstColor.toString(), element.childAt(1).color()) + assertEquals(firstColor.toString(), element.childAt(2).color()) + assertEquals(firstColor.toString(), element.childAt(3).color()) + } + + @Test + fun adjacentSibling() = runTest { + val styledComponent = fc { + div { + span {} + styledDiv { + css { + adjacentSibling("div") { + color = secondColor + } + color = firstColor + } + } + div {} + div {} + } + } + val element = clearAndInject(styledComponent) + assertNotEquals(secondColor.toString(), element.color()) + assertNotEquals(firstColor.toString(), element.childAt(0).color()) + assertEquals(firstColor.toString(), element.childAt(1).color()) + assertEquals(secondColor.toString(), element.childAt(2).color()) + assertNotEquals(secondColor.toString(), element.childAt(3).color()) + } + + @Test + fun universal() = runTest { + val styledComponent = fc { + styledDiv { + css { + universal { + color = firstColor + } + } + span { div {} } + div {} + } + } + val element = clearAndInject(styledComponent) + assertNotEquals(firstColor.toString(), element.color()) + assertEquals(firstColor.toString(), element.childAt(0).color()) + assertEquals(firstColor.toString(), element.childAt(0).childAt(0).color()) + assertEquals(firstColor.toString(), element.childAt(1).color()) + } + + @Test + fun unaryPlus() = runTest { + val styledComponent = fc { + styledDiv { + css { + ".$firstClassName" { + color = firstColor + } + ".$secondClassName" { + backgroundColor = firstColor + } + } + styledSpan { + css { +firstClassName } + } + styledSpan { + css { +arrayOf(firstClassName, secondClassName) } + } + styledSpan { + css { + +setOf(firstClassName, secondClassName) + } + } + } + } + + val element = clearAndInject(styledComponent) + assertEquals(firstColor.toString(), element.childAt(0).color()) + assertNotEquals(firstColor.toString(), element.childAt(0).getStyle().backgroundColor) + + assertEquals(firstColor.toString(), element.childAt(1).color()) + assertEquals(firstColor.toString(), element.childAt(1).getStyle().backgroundColor) + assertEquals(firstColor.toString(), element.childAt(2).color()) + assertEquals(firstColor.toString(), element.childAt(2).getStyle().backgroundColor) + } + + @Test + fun not() = runTest { + val styledComponent = fc { + styledDiv { + css { + children { + "div" { + color = firstColor + }.not() + } + } + styledDiv { } + styledSpan { } + } + } + val element = clearAndInject(styledComponent) + assertNotEquals(firstColor.toString(), element.childAt(0).color()) + assertEquals(firstColor.toString(), element.childAt(1).color()) + } + + @Test + fun classNameNot() = runTest { + val styledComponent = fc { + styledDiv { + css { + "div" { + ".$firstClassName" { + color = firstColor + }.not() + } + } + styledDiv { css { +firstClassName } } + styledDiv { } + } + } + val element = clearAndInject(styledComponent) + assertNotEquals(firstColor.toString(), element.childAt(0).color()) + assertEquals(firstColor.toString(), element.childAt(1).color()) + } + + @Test + fun prefix() = runTest { + val styledComponent = fc { + styledDiv { + css { +firstClassName } + styledDiv { + css { + color = firstColor + prefix(".$firstClassName") { + color = secondColor + } + } + } + } + } + val element = clearAndInject(styledComponent) + assertEquals(secondColor.toString(), element.childAt(0).color()) + } + + @Test + fun ruleUnaryPlus() = runTest { + val styledComponent = fc { + styledDiv { + css { + +"someExternalClass1" { + color = firstColor + } + +"someExternalClass2" { + backgroundColor = secondColor + } + +"someExternalClass1" + } + } + } + val element = clearAndInject(styledComponent) + assertEquals(firstColor.toString(), element.color()) + assertNotEquals(secondColor.toString(), element.getStyle().backgroundColor) + } +} \ No newline at end of file diff --git a/kotlin-styled-next/src/jsTest/kotlin/test/StyleSheetTest.kt b/kotlin-styled-next/src/jsTest/kotlin/test/StyleSheetTest.kt index dd14dcdd38a2..8629764c25b5 100644 --- a/kotlin-styled-next/src/jsTest/kotlin/test/StyleSheetTest.kt +++ b/kotlin-styled-next/src/jsTest/kotlin/test/StyleSheetTest.kt @@ -5,39 +5,15 @@ import react.RProps import react.fc import runTest import styled.GlobalStyles -import styled.StyleSheet import styled.css import styled.styledSpan +import test.styleSheets.SimpleStyleSheet +import test.styleSheets.StaticStyleSheet import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertContains import kotlin.test.assertEquals -private class SimpleStyleSheet : StyleSheet("SimpleStyleSheet") { - val simpleProperty by css { - minHeight = 66.px - padding(0.px) - } - - val specificProperty by css { - specific { - padding(80.px) - border = "none" - } - } -} - -private class StaticStyleSheet : StyleSheet("StaticStyleSheet", isStatic = true) { - val property1 by css { - alignContent = Align.end - } - - val property2 by css { - padding(40.px) - minHeight = 50.px - } -} - class StyleSheetTest : TestBase() { private lateinit var simpleStyleSheet: SimpleStyleSheet private lateinit var staticStyleSheet: StaticStyleSheet diff --git a/kotlin-styled-next/src/jsTest/kotlin/test/TestBase.kt b/kotlin-styled-next/src/jsTest/kotlin/test/TestBase.kt index 436a99841e7f..837db3f85aa4 100644 --- a/kotlin-styled-next/src/jsTest/kotlin/test/TestBase.kt +++ b/kotlin-styled-next/src/jsTest/kotlin/test/TestBase.kt @@ -22,10 +22,17 @@ open class TestBase { protected val secondColor = rgb(2, 2, 2) protected val thirdColor = rgb(3, 3, 3) + protected val firstClassName = "firstClassName" + protected val secondClassName = "secondClassName" + /** * Assert that injected CSS for [selector] contains all of the [declarations] */ protected fun TestScope.assertCssInjected(selector: String, declarations: List>) { + assertCssInjected(selector, declarations.map { (property, value) -> "$property: $value" }) + } + + protected fun TestScope.assertCssInjected(selector: String, strings: Iterable) { val rules = getStylesheet().cssRules val checkedCss = StringBuilder() for (i in 0 until rules.length) { @@ -34,10 +41,9 @@ open class TestBase { if (css == null || selector !in css) continue css.let { - declarations.forEach { (property, value) -> - val declaration = "$property: $value" - assertTrue("Could not find declaration $declaration in $it") { - declaration in css + strings.forEach { + assertTrue("Could not find declaration $it in $css") { + it in css } } } @@ -55,6 +61,7 @@ open class TestBase { GlobalStyles.scheduledRules.clear() GlobalStyles.injectedStyleSheetRules.clear() GlobalStyles.injectedKeyframes.clear() + GlobalStyles.styledClasses.clear() } protected suspend fun TestScope.clearAndInject(styledComponent: Component): Element { diff --git a/kotlin-styled-next/src/jsTest/kotlin/test/styleSheets/SimpleStyleSheet.kt b/kotlin-styled-next/src/jsTest/kotlin/test/styleSheets/SimpleStyleSheet.kt new file mode 100644 index 000000000000..f9017c4915ef --- /dev/null +++ b/kotlin-styled-next/src/jsTest/kotlin/test/styleSheets/SimpleStyleSheet.kt @@ -0,0 +1,21 @@ +package test.styleSheets + +import kotlinx.css.border +import kotlinx.css.minHeight +import kotlinx.css.padding +import kotlinx.css.px +import styled.StyleSheet + +internal class SimpleStyleSheet : StyleSheet("SimpleStyleSheet") { + val simpleProperty by css { + minHeight = 66.px + padding(0.px) + } + + val specificProperty by css { + specific { + padding(80.px) + border = "none" + } + } +} \ No newline at end of file diff --git a/kotlin-styled-next/src/jsTest/kotlin/test/styleSheets/StaticStyleSheet.kt b/kotlin-styled-next/src/jsTest/kotlin/test/styleSheets/StaticStyleSheet.kt new file mode 100644 index 000000000000..5fd132bbe800 --- /dev/null +++ b/kotlin-styled-next/src/jsTest/kotlin/test/styleSheets/StaticStyleSheet.kt @@ -0,0 +1,15 @@ +package test.styleSheets + +import kotlinx.css.* +import styled.StyleSheet + +internal class StaticStyleSheet : StyleSheet("StaticStyleSheet", isStatic = true) { + val property1 by css { + alignContent = Align.end + } + + val property2 by css { + padding(40.px) + minHeight = 50.px + } +}