Skip to content

Commit

Permalink
Add renderIfTrue and renderIfFalse convenience functions
Browse files Browse the repository at this point in the history
Both provide a shortcut to render content if a given
boolean flow's value is `true` or `false` respectively.
  • Loading branch information
haukesomm committed Mar 20, 2024
1 parent 7669d2a commit 90daf96
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 9 deletions.
38 changes: 38 additions & 0 deletions core/src/jsMain/kotlin/dev/fritz2/core/rendercontext.kt
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,44 @@ interface RenderContext : WithJob, WithScope {
}
}

/**
* Renders the data of a boolean [Flow] only if it's value is `true`.
*
* @receiver [Flow] containing the data
* @param into target to mount content to. If not set a child div is added to the [Tag] this method is called on
* @param content [RenderContext] for rendering the data to the DOM
*
* @see renderIf
* @see renderFalse
*/
fun Flow<Boolean>.renderTrue(
into: Tag<HTMLElement>? = null,
content: Tag<*>.() -> Unit
) {
renderIf(predicate = { it }, into) { _ ->
content()
}
}

/**
* Renders the data of a boolean [Flow] only if it's value is `false`.
*
* @receiver [Flow] containing the data
* @param into target to mount content to. If not set a child div is added to the [Tag] this method is called on
* @param content [RenderContext] for rendering the data to the DOM
*
* @see renderIf
* @see renderTrue
*/
fun Flow<Boolean>.renderFalse(
into: Tag<HTMLElement>? = null,
content: Tag<*>.() -> Unit
) {
renderIf(predicate = { !it }, into) { _ ->
content()
}
}

/**
* Renders the non-null data of a [Flow].
*
Expand Down
78 changes: 78 additions & 0 deletions core/src/jsTest/kotlin/dev/fritz2/core/rendercontext.kt
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,84 @@ class RenderContextTests {
assertNull(div(div2))
}

@Test
fun testRenderIfTrueFunction() = runTest {
val store = storeOf(true)

val id = Id.next()
val expectedContentIfTrue = "rendered"
val expectedContentIfFalse = ""


render {
div(id = id) {
store.data.renderTrue(into = this) {
+expectedContentIfTrue
}
}
}


delay(100)

val divContent = document.getElementById(id)?.textContent
assertEquals(
expected = expectedContentIfTrue,
actual = divContent,
message = "Content must be present"
)


store.update(false)
delay(100)

val divContentAfterUpdate = document.getElementById(id)?.textContent
assertEquals(
expected = expectedContentIfFalse,
actual = divContentAfterUpdate,
message = "Content must be absent"
)
}

@Test
fun testRenderIfFalseFunction() = runTest {
val store = storeOf(false)

val id = Id.next()
val expectedContentIfFalse = "rendered"
val expectedContentIfTrue = ""


render {
div(id = id) {
store.data.renderFalse(into = this) {
+expectedContentIfFalse
}
}
}


delay(100)

val divContent = document.getElementById(id)?.textContent
assertEquals(
expected = expectedContentIfFalse,
actual = divContent,
message = "Content must be present"
)


store.update(true)
delay(100)

val divContentAfterUpdate = document.getElementById(id)?.textContent
assertEquals(
expected = expectedContentIfTrue,
actual = divContentAfterUpdate,
message = "Content must be absent"
)
}

@Test
fun testRenderNotNullFunction() = runTest {
val store = storeOf<String?>(null)
Expand Down
20 changes: 11 additions & 9 deletions www/src/pages/docs/30_Render HTML.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,15 +257,17 @@ As you already know, all [state handling](/docs/fundamentals/#state-handling) is
Based upon the `data`-property, which provides a `Flow` of the store's generic data type, there are a variety of
`render*`-functions which can be used to create *reactive* UIs:

| Render-Function | Additional parameters | Description | Default Tag |
|----------------------------|-----------------------|-------------------------------------------------------------------------------------------------------------------------------|-------------|
| `Flow<T>.render` | - | Creates a mount-point providing the whole store's data value `T` inside `content` expression | `div` |
| `Flow<T>.renderIf` | predicate | Creates a mount-point providing the whole store's data value `T` inside `content` expression when `predicate` is `true` | `div` |
| `Flow<T>.renderNotNull` | - | Creates a mount-point providing the whole store's data value `T` inside `content` expression when `T` is not `null` | `div` |
| `Flow<T>.renderIs` | klass | Creates a mount-point providing the whole store's data value `T` inside `content` expression when `T` is of type `klass` | `div` |
| `Flow<String>.renderText` | - | Creates a mount-point creating a text-node | `span` |
| `Flow<List<T>>.renderEach` | - | Creates a mount-point optimizing changes by `T.equals`. Provides a `T` inside the `content` expression. Use for value objects | `div` |
| `Flow<List<T>>.renderEach` | idProvider | Creates a mount-point optimizing changes by `idProvider`. Provides a `T` inside the `content` expression. Use for entities | `div` |
| Render-Function | Additional parameters | Description | Default Tag |
|-----------------------------|-----------------------|-------------------------------------------------------------------------------------------------------------------------------|-------------|
| `Flow<T>.render` | - | Creates a mount-point providing the whole store's data value `T` inside `content` expression | `div` |
| `Flow<T>.renderIf` | predicate | Creates a mount-point providing the whole store's data value `T` inside `content` expression when `predicate` is `true` | `div` |
| `Flow<Boolean>.renderTrue` | - | Creates a mount-point rendering the `content` expression with no store data provided when the flow's value is `true` | `div` |
| `Flow<Boolean>.renderFalse` | - | Creates a mount-point rendering the `content` expression with no store data provided when the flow's value is `false` | `div` |
| `Flow<T>.renderNotNull` | - | Creates a mount-point providing the whole store's data value `T` inside `content` expression when `T` is not `null` | `div` |
| `Flow<T>.renderIs` | klass | Creates a mount-point providing the whole store's data value `T` inside `content` expression when `T` is of type `klass` | `div` |
| `Flow<String>.renderText` | - | Creates a mount-point creating a text-node | `span` |
| `Flow<List<T>>.renderEach` | - | Creates a mount-point optimizing changes by `T.equals`. Provides a `T` inside the `content` expression. Use for value objects | `div` |
| `Flow<List<T>>.renderEach` | idProvider | Creates a mount-point optimizing changes by `idProvider`. Provides a `T` inside the `content` expression. Use for entities | `div` |

There is one more `renderEach` variant which is defined as an extension to a `Store` instead of a `Flow`.
This special variant and its application are described in the
Expand Down

0 comments on commit 90daf96

Please sign in to comment.