Browse Source
* web: Add more tests for the event handlers * web: Inputs refactoring (wip) * web: Add `Options` for `addEventListener` * web: Add basic methods and properties to the SyntheticInputEvent to align it with `org.w3c.dom.events.Event` * web: Add a test for `capture = true` Event Listener Option * web: Update PR to make contain only relevant changes + add specific functions for Inputs * web: Update PR to align with master after rebase Co-authored-by: Oleksandr Karpovich <oleksandr.karpovich@jetbrains.com>pull/822/head
Oleksandr Karpovich
3 years ago
committed by
GitHub
24 changed files with 1367 additions and 150 deletions
@ -0,0 +1,93 @@
|
||||
/* |
||||
* Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers. |
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||
*/ |
||||
|
||||
package androidx.compose.web.attributes |
||||
|
||||
import org.jetbrains.compose.web.attributes.* |
||||
import org.jetbrains.compose.web.events.GenericWrappedEvent |
||||
import org.jetbrains.compose.web.events.WrappedCheckBoxInputEvent |
||||
import org.jetbrains.compose.web.events.WrappedRadioInputEvent |
||||
import org.jetbrains.compose.web.events.WrappedTextInputEvent |
||||
import org.w3c.dom.HTMLElement |
||||
import org.w3c.dom.HTMLInputElement |
||||
import org.w3c.dom.events.Event |
||||
import org.w3c.dom.events.EventTarget |
||||
|
||||
class SyntheticInputEvent<ValueType, Element : HTMLElement>( |
||||
val value: ValueType, |
||||
val target: Element, |
||||
val nativeEvent: Event |
||||
) { |
||||
|
||||
val bubbles: Boolean = nativeEvent.bubbles |
||||
val cancelable: Boolean = nativeEvent.cancelable |
||||
val composed: Boolean = nativeEvent.composed |
||||
val currentTarget: HTMLElement? = nativeEvent.currentTarget.unsafeCast<HTMLInputElement?>() |
||||
val eventPhase: Short = nativeEvent.eventPhase |
||||
val defaultPrevented: Boolean = nativeEvent.defaultPrevented |
||||
val timestamp: Number = nativeEvent.timeStamp |
||||
val type: String = nativeEvent.type |
||||
val isTrusted: Boolean = nativeEvent.isTrusted |
||||
|
||||
fun preventDefault(): Unit = nativeEvent.preventDefault() |
||||
fun stopPropagation(): Unit = nativeEvent.stopPropagation() |
||||
fun stopImmediatePropagation(): Unit = nativeEvent.stopImmediatePropagation() |
||||
fun composedPath(): Array<EventTarget> = nativeEvent.composedPath() |
||||
} |
||||
|
||||
class InputAttrsBuilder<T>(val inputType: InputType<T>) : AttrsBuilder<HTMLInputElement>() { |
||||
|
||||
fun onInput(options: Options = Options.DEFAULT, listener: (SyntheticInputEvent<T, HTMLInputElement>) -> Unit) { |
||||
addEventListener(INPUT, options) { |
||||
val value = inputType.inputValue(it.nativeEvent) |
||||
listener(SyntheticInputEvent(value, it.nativeEvent.target as HTMLInputElement, it.nativeEvent)) |
||||
} |
||||
} |
||||
|
||||
@Deprecated( |
||||
message = "It's not reliable as it can be applied to any input type.", |
||||
replaceWith = ReplaceWith("onInput(options, listener)"), |
||||
level = DeprecationLevel.WARNING |
||||
) |
||||
fun onTextInput(options: Options = Options.DEFAULT, listener: (WrappedTextInputEvent) -> Unit) { |
||||
listeners.add(TextInputEventListener(options, listener)) |
||||
} |
||||
|
||||
@Deprecated( |
||||
message = "It's not reliable as it can be applied to any input type.", |
||||
replaceWith = ReplaceWith("onInput(options, listener)"), |
||||
level = DeprecationLevel.WARNING |
||||
) |
||||
fun onCheckboxInput( |
||||
options: Options = Options.DEFAULT, |
||||
listener: (WrappedCheckBoxInputEvent) -> Unit |
||||
) { |
||||
listeners.add(CheckBoxInputEventListener(options, listener)) |
||||
} |
||||
|
||||
@Deprecated( |
||||
message = "It's not reliable as it can be applied to any input type.", |
||||
replaceWith = ReplaceWith("onInput(options, listener)"), |
||||
level = DeprecationLevel.WARNING |
||||
) |
||||
fun onRadioInput( |
||||
options: Options = Options.DEFAULT, |
||||
listener: (WrappedRadioInputEvent) -> Unit |
||||
) { |
||||
listeners.add(RadioInputEventListener(options, listener)) |
||||
} |
||||
|
||||
@Deprecated( |
||||
message = "It's not reliable as it can be applied to any input type.", |
||||
replaceWith = ReplaceWith("onInput(options, listener)"), |
||||
level = DeprecationLevel.WARNING |
||||
) |
||||
fun onRangeInput( |
||||
options: Options = Options.DEFAULT, |
||||
listener: (GenericWrappedEvent<*>) -> Unit |
||||
) { |
||||
listeners.add(WrappedEventListener(INPUT, options, listener)) |
||||
} |
||||
} |
@ -0,0 +1,32 @@
|
||||
/* |
||||
* Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers. |
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||
*/ |
||||
|
||||
package androidx.compose.web.attributes |
||||
|
||||
import org.jetbrains.compose.web.attributes.* |
||||
import org.jetbrains.compose.web.events.WrappedTextInputEvent |
||||
import org.w3c.dom.HTMLTextAreaElement |
||||
|
||||
class TextAreaAttrsBuilder : AttrsBuilder<HTMLTextAreaElement>() { |
||||
|
||||
fun onInput( |
||||
options: Options = Options.DEFAULT, |
||||
listener: (SyntheticInputEvent<String, HTMLTextAreaElement>) -> Unit |
||||
) { |
||||
addEventListener(INPUT, options) { |
||||
val text = it.nativeEvent.target.asDynamic().value.unsafeCast<String>() |
||||
listener(SyntheticInputEvent(text, it.nativeEvent.target as HTMLTextAreaElement, it.nativeEvent)) |
||||
} |
||||
} |
||||
|
||||
@Deprecated( |
||||
message = "It's not reliable as it can be applied to any input type.", |
||||
replaceWith = ReplaceWith("onInput(options, listener)"), |
||||
level = DeprecationLevel.WARNING |
||||
) |
||||
fun onTextInput(options: Options = Options.DEFAULT, listener: (WrappedTextInputEvent) -> Unit) { |
||||
listeners.add(TextInputEventListener(options, listener)) |
||||
} |
||||
} |
@ -0,0 +1,192 @@
|
||||
package org.jetbrains.compose.web.dom |
||||
|
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.runtime.NonRestartableComposable |
||||
import androidx.compose.web.attributes.InputAttrsBuilder |
||||
import org.jetbrains.compose.web.attributes.* |
||||
|
||||
private fun InputAttrsBuilder<String>.applyAttrsWithStringValue( |
||||
value: String, |
||||
attrsBuilder: InputAttrsBuilder<String>.() -> Unit |
||||
) { |
||||
if (value.isNotEmpty()) value(value) |
||||
attrsBuilder() |
||||
} |
||||
|
||||
@Composable |
||||
@NonRestartableComposable |
||||
fun CheckboxInput(checked: Boolean = false, attrsBuilder: InputAttrsBuilder<Boolean>.() -> Unit = {}) { |
||||
Input( |
||||
type = InputType.Checkbox, |
||||
attrs = { |
||||
if (checked) checked() |
||||
this.attrsBuilder() |
||||
} |
||||
) |
||||
} |
||||
|
||||
@Composable |
||||
@NonRestartableComposable |
||||
fun DateInput(value: String = "", attrsBuilder: InputAttrsBuilder<String>.() -> Unit = {}) { |
||||
Input(type = InputType.Date, attrs = { applyAttrsWithStringValue(value, attrsBuilder) }) |
||||
} |
||||
|
||||
@Composable |
||||
@NonRestartableComposable |
||||
fun DateTimeLocalInput(value: String = "", attrsBuilder: InputAttrsBuilder<String>.() -> Unit = {}) { |
||||
Input(type = InputType.DateTimeLocal, attrs = { applyAttrsWithStringValue(value, attrsBuilder) }) |
||||
} |
||||
|
||||
@Composable |
||||
@NonRestartableComposable |
||||
fun EmailInput(value: String = "", attrsBuilder: InputAttrsBuilder<String>.() -> Unit = {}) { |
||||
Input(type = InputType.Email, attrs = { applyAttrsWithStringValue(value, attrsBuilder) }) |
||||
} |
||||
|
||||
@Composable |
||||
@NonRestartableComposable |
||||
fun FileInput(value: String = "", attrsBuilder: InputAttrsBuilder<String>.() -> Unit = {}) { |
||||
Input(type = InputType.File, attrs = { applyAttrsWithStringValue(value, attrsBuilder) }) |
||||
} |
||||
|
||||
@Composable |
||||
@NonRestartableComposable |
||||
fun HiddenInput(attrsBuilder: InputAttrsBuilder<String>.() -> Unit = {}) { |
||||
Input(type = InputType.Hidden, attrs = attrsBuilder) |
||||
} |
||||
|
||||
@Composable |
||||
@NonRestartableComposable |
||||
fun MonthInput(value: String = "", attrsBuilder: InputAttrsBuilder<String>.() -> Unit = {}) { |
||||
Input(type = InputType.Month, attrs = { applyAttrsWithStringValue(value, attrsBuilder) }) |
||||
} |
||||
|
||||
@Composable |
||||
@NonRestartableComposable |
||||
fun NumberInput( |
||||
value: Number? = null, |
||||
min: Number? = null, |
||||
max: Number? = null, |
||||
attrsBuilder: InputAttrsBuilder<Number?>.() -> Unit = {} |
||||
) { |
||||
Input( |
||||
type = InputType.Number, |
||||
attrs = { |
||||
if (value != null) value(value.toString()) |
||||
if (min != null) min(min.toString()) |
||||
if (max != null) max(max.toString()) |
||||
attrsBuilder() |
||||
} |
||||
) |
||||
} |
||||
|
||||
@Composable |
||||
@NonRestartableComposable |
||||
fun PasswordInput(value: String = "", attrsBuilder: InputAttrsBuilder<String>.() -> Unit = {}) { |
||||
Input(type = InputType.Password, attrs = { applyAttrsWithStringValue(value, attrsBuilder) }) |
||||
} |
||||
|
||||
@Composable |
||||
@NonRestartableComposable |
||||
fun RadioInput(checked: Boolean = false, attrsBuilder: InputAttrsBuilder<Boolean>.() -> Unit = {}) { |
||||
Input( |
||||
type = InputType.Radio, |
||||
attrs = { |
||||
if (checked) checked() |
||||
attrsBuilder() |
||||
} |
||||
) |
||||
} |
||||
|
||||
@Composable |
||||
@NonRestartableComposable |
||||
fun RangeInput( |
||||
value: Number? = null, |
||||
min: Number? = null, |
||||
max: Number? = null, |
||||
step: Number = 1, |
||||
attrsBuilder: InputAttrsBuilder<Number?>.() -> Unit = {} |
||||
) { |
||||
Input( |
||||
type = InputType.Range, |
||||
attrs = { |
||||
if (value != null) value(value.toString()) |
||||
if (min != null) min(min.toString()) |
||||
if (max != null) max(max.toString()) |
||||
step(step) |
||||
attrsBuilder() |
||||
} |
||||
) |
||||
} |
||||
|
||||
@Composable |
||||
@NonRestartableComposable |
||||
fun SearchInput(value: String = "", attrsBuilder: InputAttrsBuilder<String>.() -> Unit = {}) { |
||||
Input(type = InputType.Search, attrs = { applyAttrsWithStringValue(value, attrsBuilder) }) |
||||
} |
||||
|
||||
@Composable |
||||
@NonRestartableComposable |
||||
fun SubmitInput(attrsBuilder: InputAttrsBuilder<Unit>.() -> Unit = {}) { |
||||
Input(type = InputType.Submit, attrs = attrsBuilder) |
||||
} |
||||
|
||||
@Composable |
||||
@NonRestartableComposable |
||||
fun TelInput(value: String = "", attrsBuilder: InputAttrsBuilder<String>.() -> Unit = {}) { |
||||
Input(type = InputType.Tel, attrs = { applyAttrsWithStringValue(value, attrsBuilder) }) |
||||
} |
||||
|
||||
@Composable |
||||
@NonRestartableComposable |
||||
fun TextInput(value: String = "", attrsBuilder: InputAttrsBuilder<String>.() -> Unit = {}) { |
||||
Input(type = InputType.Text, attrs = { applyAttrsWithStringValue(value, attrsBuilder) }) |
||||
} |
||||
|
||||
@Composable |
||||
@NonRestartableComposable |
||||
fun TimeInput(value: String = "", attrsBuilder: InputAttrsBuilder<String>.() -> Unit = {}) { |
||||
Input(type = InputType.Time, attrs = { applyAttrsWithStringValue(value, attrsBuilder) }) |
||||
} |
||||
|
||||
@Composable |
||||
@NonRestartableComposable |
||||
fun UrlInput(value: String = "", attrsBuilder: InputAttrsBuilder<String>.() -> Unit = {}) { |
||||
Input(type = InputType.Url, attrs = { applyAttrsWithStringValue(value, attrsBuilder) }) |
||||
} |
||||
|
||||
@Composable |
||||
@NonRestartableComposable |
||||
fun WeekInput(value: String = "", attrsBuilder: InputAttrsBuilder<String>.() -> Unit = {}) { |
||||
Input(type = InputType.Week, attrs = { applyAttrsWithStringValue(value, attrsBuilder) }) |
||||
} |
||||
|
||||
@Composable |
||||
fun <K> Input( |
||||
type: InputType<K>, |
||||
attrs: InputAttrsBuilder<K>.() -> Unit |
||||
) { |
||||
TagElement( |
||||
elementBuilder = ElementBuilder.Input, |
||||
applyAttrs = { |
||||
val inputAttrsBuilder = InputAttrsBuilder(type) |
||||
inputAttrsBuilder.type(type) |
||||
inputAttrsBuilder.attrs() |
||||
this.copyFrom(inputAttrsBuilder) |
||||
}, |
||||
content = null |
||||
) |
||||
} |
||||
|
||||
@Composable |
||||
fun <K> Input(type: InputType<K>) { |
||||
TagElement( |
||||
elementBuilder = ElementBuilder.Input, |
||||
applyAttrs = { |
||||
val inputAttrsBuilder = InputAttrsBuilder(type) |
||||
inputAttrsBuilder.type(type) |
||||
this.copyFrom(inputAttrsBuilder) |
||||
}, |
||||
content = null |
||||
) |
||||
} |
@ -0,0 +1,388 @@
|
||||
package org.jetbrains.compose.web.core.tests.elements |
||||
|
||||
import org.jetbrains.compose.web.core.tests.runTest |
||||
import org.jetbrains.compose.web.dom.* |
||||
import org.w3c.dom.HTMLInputElement |
||||
import kotlin.test.Test |
||||
import kotlin.test.assertEquals |
||||
|
||||
class InputsGenerateCorrectHtmlTests { |
||||
|
||||
@Test |
||||
fun checkBoxInput() = runTest { |
||||
composition { |
||||
CheckboxInput(checked = true) { |
||||
id("checkboxId") |
||||
} |
||||
} |
||||
|
||||
val checkboxInput = root.firstChild as HTMLInputElement |
||||
|
||||
assertEquals("checkbox", checkboxInput.getAttribute("type")) |
||||
assertEquals("checkboxId", checkboxInput.getAttribute("id")) |
||||
assertEquals(true, checkboxInput.checked) |
||||
} |
||||
|
||||
@Test |
||||
fun checkBoxInputWithDefaults() = runTest { |
||||
composition { CheckboxInput() } |
||||
|
||||
val checkboxInput = root.firstChild as HTMLInputElement |
||||
|
||||
assertEquals("checkbox", checkboxInput.getAttribute("type")) |
||||
assertEquals(null, checkboxInput.getAttribute("id")) |
||||
assertEquals(false, checkboxInput.checked) |
||||
} |
||||
|
||||
@Test |
||||
fun dateInput() = runTest { |
||||
composition { |
||||
DateInput(value = "2021-10-10") { |
||||
id("dateInputId") |
||||
} |
||||
} |
||||
|
||||
val dateInput = root.firstChild as HTMLInputElement |
||||
|
||||
assertEquals("date", dateInput.getAttribute("type")) |
||||
assertEquals("dateInputId", dateInput.getAttribute("id")) |
||||
assertEquals("2021-10-10", dateInput.value) |
||||
} |
||||
|
||||
@Test |
||||
fun dateInputWithDefaults() = runTest { |
||||
composition { DateInput() } |
||||
|
||||
val dateInput = root.firstChild as HTMLInputElement |
||||
|
||||
assertEquals("date", dateInput.getAttribute("type")) |
||||
assertEquals(null, dateInput.getAttribute("id")) |
||||
assertEquals("", dateInput.value) |
||||
} |
||||
|
||||
@Test |
||||
fun emailInput() = runTest { |
||||
composition { |
||||
EmailInput(value = "user@mail.com") { |
||||
id("emailInputId") |
||||
} |
||||
} |
||||
|
||||
val emailInput = root.firstChild as HTMLInputElement |
||||
|
||||
assertEquals("email", emailInput.getAttribute("type")) |
||||
assertEquals("emailInputId", emailInput.getAttribute("id")) |
||||
assertEquals("user@mail.com", emailInput.value) |
||||
} |
||||
|
||||
@Test |
||||
fun emailInputWithDefaults() = runTest { |
||||
composition { EmailInput() } |
||||
|
||||
val emailInput = root.firstChild as HTMLInputElement |
||||
|
||||
assertEquals("email", emailInput.getAttribute("type")) |
||||
assertEquals(null, emailInput.getAttribute("id")) |
||||
assertEquals("", emailInput.value) |
||||
} |
||||
|
||||
@Test |
||||
fun monthInput() = runTest { |
||||
composition { |
||||
MonthInput(value = "2017-06") { |
||||
id("monthInputId") |
||||
} |
||||
} |
||||
|
||||
val monthInput = root.firstChild as HTMLInputElement |
||||
|
||||
assertEquals("month", monthInput.getAttribute("type")) |
||||
assertEquals("monthInputId", monthInput.getAttribute("id")) |
||||
assertEquals("2017-06", monthInput.value) |
||||
} |
||||
|
||||
@Test |
||||
fun monthInputWithDefaults() = runTest { |
||||
composition { MonthInput() } |
||||
|
||||
val monthInput = root.firstChild as HTMLInputElement |
||||
|
||||
assertEquals("month", monthInput.getAttribute("type")) |
||||
assertEquals(null, monthInput.getAttribute("id")) |
||||
assertEquals("", monthInput.value) |
||||
} |
||||
|
||||
@Test |
||||
fun numberInput() = runTest { |
||||
composition { |
||||
NumberInput(value = 100, min = 10, max = 200) { |
||||
id("numberInputId") |
||||
} |
||||
} |
||||
|
||||
val numberInput = root.firstChild as HTMLInputElement |
||||
|
||||
assertEquals("number", numberInput.getAttribute("type")) |
||||
assertEquals("numberInputId", numberInput.getAttribute("id")) |
||||
assertEquals("200", numberInput.getAttribute("max")) |
||||
assertEquals("10", numberInput.getAttribute("min")) |
||||
assertEquals(100, numberInput.valueAsNumber.toInt()) |
||||
} |
||||
|
||||
@Test |
||||
fun numberInputWithDefaults() = runTest { |
||||
composition { |
||||
NumberInput { |
||||
id("numberInputId") |
||||
} |
||||
} |
||||
|
||||
val numberInput = root.firstChild as HTMLInputElement |
||||
|
||||
assertEquals("number", numberInput.getAttribute("type")) |
||||
assertEquals("numberInputId", numberInput.getAttribute("id")) |
||||
assertEquals(null, numberInput.getAttribute("max")) |
||||
assertEquals(null, numberInput.getAttribute("min")) |
||||
assertEquals("", numberInput.value) |
||||
} |
||||
|
||||
|
||||
@Test |
||||
fun passwordInput() = runTest { |
||||
composition { |
||||
PasswordInput(value = "somepassword") { |
||||
id("passwordInputId") |
||||
} |
||||
} |
||||
|
||||
val passwordInput = root.firstChild as HTMLInputElement |
||||
|
||||
assertEquals("password", passwordInput.getAttribute("type")) |
||||
assertEquals("passwordInputId", passwordInput.getAttribute("id")) |
||||
assertEquals("somepassword", passwordInput.value) |
||||
} |
||||
|
||||
@Test |
||||
fun passwordInputWithDefaults() = runTest { |
||||
composition { PasswordInput() } |
||||
|
||||
val passwordInput = root.firstChild as HTMLInputElement |
||||
|
||||
assertEquals("password", passwordInput.getAttribute("type")) |
||||
assertEquals(null, passwordInput.getAttribute("id")) |
||||
assertEquals("", passwordInput.value) |
||||
} |
||||
|
||||
@Test |
||||
fun radioInput() = runTest { |
||||
composition { |
||||
RadioInput(checked = true) { |
||||
id("radioInputId") |
||||
} |
||||
} |
||||
|
||||
val radioInput = root.firstChild as HTMLInputElement |
||||
|
||||
assertEquals("radio", radioInput.getAttribute("type")) |
||||
assertEquals("radioInputId", radioInput.getAttribute("id")) |
||||
assertEquals(true, radioInput.checked) |
||||
} |
||||
|
||||
@Test |
||||
fun radioInputWithDefaults() = runTest { |
||||
composition { RadioInput() } |
||||
|
||||
val radioInput = root.firstChild as HTMLInputElement |
||||
|
||||
assertEquals("radio", radioInput.getAttribute("type")) |
||||
assertEquals(null, radioInput.getAttribute("id")) |
||||
assertEquals(false, radioInput.checked) |
||||
} |
||||
|
||||
@Test |
||||
fun rangeInput() = runTest { |
||||
composition { |
||||
RangeInput(value = 20, min = 10, max = 30, step = 2) { |
||||
id("rangeInputId") |
||||
} |
||||
} |
||||
|
||||
val rangeInput = root.firstChild as HTMLInputElement |
||||
|
||||
assertEquals("range", rangeInput.getAttribute("type")) |
||||
assertEquals("rangeInputId", rangeInput.getAttribute("id")) |
||||
assertEquals("10", rangeInput.getAttribute("min")) |
||||
assertEquals("30", rangeInput.getAttribute("max")) |
||||
assertEquals("2", rangeInput.getAttribute("step")) |
||||
assertEquals(20, rangeInput.valueAsNumber.toInt()) |
||||
} |
||||
|
||||
@Test |
||||
fun rangeInputWithDefaults() = runTest { |
||||
composition { RangeInput() } |
||||
|
||||
val rangeInput = root.firstChild as HTMLInputElement |
||||
|
||||
assertEquals("range", rangeInput.getAttribute("type")) |
||||
assertEquals(null, rangeInput.getAttribute("id")) |
||||
assertEquals(null, rangeInput.getAttribute("min")) |
||||
assertEquals(null, rangeInput.getAttribute("max")) |
||||
assertEquals("1", rangeInput.getAttribute("step")) |
||||
} |
||||
|
||||
@Test |
||||
fun searchInput() = runTest { |
||||
composition { |
||||
SearchInput(value = "Search Term") { |
||||
id("searchInputId") |
||||
} |
||||
} |
||||
|
||||
val searchInput = root.firstChild as HTMLInputElement |
||||
|
||||
assertEquals("search", searchInput.getAttribute("type")) |
||||
assertEquals("searchInputId", searchInput.getAttribute("id")) |
||||
assertEquals("Search Term", searchInput.value) |
||||
} |
||||
|
||||
@Test |
||||
fun searchInputWithDefaults() = runTest { |
||||
composition { SearchInput() } |
||||
|
||||
val searchInput = root.firstChild as HTMLInputElement |
||||
|
||||
assertEquals("search", searchInput.getAttribute("type")) |
||||
assertEquals(null, searchInput.getAttribute("id")) |
||||
assertEquals("", searchInput.value) |
||||
} |
||||
|
||||
@Test |
||||
fun telInput() = runTest { |
||||
composition { |
||||
TelInput(value = "0123456789") { |
||||
id("telInputId") |
||||
} |
||||
} |
||||
|
||||
val textInput = root.firstChild as HTMLInputElement |
||||
|
||||
assertEquals("tel", textInput.getAttribute("type")) |
||||
assertEquals("telInputId", textInput.getAttribute("id")) |
||||
assertEquals("0123456789", textInput.value) |
||||
} |
||||
|
||||
@Test |
||||
fun telInputWithDefaults() = runTest { |
||||
composition { TelInput() } |
||||
|
||||
val textInput = root.firstChild as HTMLInputElement |
||||
|
||||
assertEquals("tel", textInput.getAttribute("type")) |
||||
assertEquals(null, textInput.getAttribute("id")) |
||||
assertEquals("", textInput.value) |
||||
} |
||||
|
||||
@Test |
||||
fun textInput() = runTest { |
||||
composition { |
||||
TextInput(value = "Some value") { |
||||
id("textInputId") |
||||
} |
||||
} |
||||
|
||||
val textInput = root.firstChild as HTMLInputElement |
||||
|
||||
assertEquals("text", textInput.getAttribute("type")) |
||||
assertEquals("textInputId", textInput.getAttribute("id")) |
||||
assertEquals("Some value", textInput.value) |
||||
} |
||||
|
||||
@Test |
||||
fun textInputWithDefaults() = runTest { |
||||
composition { TextInput() } |
||||
|
||||
val textInput = root.firstChild as HTMLInputElement |
||||
|
||||
assertEquals("text", textInput.getAttribute("type")) |
||||
assertEquals(null, textInput.getAttribute("id")) |
||||
assertEquals("", textInput.value) |
||||
} |
||||
|
||||
@Test |
||||
fun timeInput() = runTest { |
||||
composition { |
||||
TimeInput(value = "12:20") { |
||||
id("timeInputId") |
||||
} |
||||
} |
||||
|
||||
val textInput = root.firstChild as HTMLInputElement |
||||
|
||||
assertEquals("time", textInput.getAttribute("type")) |
||||
assertEquals("timeInputId", textInput.getAttribute("id")) |
||||
assertEquals("12:20", textInput.value) |
||||
} |
||||
|
||||
@Test |
||||
fun timeInputWithDefaults() = runTest { |
||||
composition { TimeInput() } |
||||
|
||||
val textInput = root.firstChild as HTMLInputElement |
||||
|
||||
assertEquals("time", textInput.getAttribute("type")) |
||||
assertEquals(null, textInput.getAttribute("id")) |
||||
assertEquals("", textInput.value) |
||||
} |
||||
|
||||
@Test |
||||
fun urlInput() = runTest { |
||||
composition { |
||||
UrlInput(value = "http://127.0.0.1") { |
||||
id("urlInputId") |
||||
} |
||||
} |
||||
|
||||
val textInput = root.firstChild as HTMLInputElement |
||||
|
||||
assertEquals("url", textInput.getAttribute("type")) |
||||
assertEquals("urlInputId", textInput.getAttribute("id")) |
||||
assertEquals("http://127.0.0.1", textInput.value) |
||||
} |
||||
|
||||
@Test |
||||
fun urlInputWithDefaults() = runTest { |
||||
composition { UrlInput() } |
||||
|
||||
val textInput = root.firstChild as HTMLInputElement |
||||
|
||||
assertEquals("url", textInput.getAttribute("type")) |
||||
assertEquals(null, textInput.getAttribute("id")) |
||||
assertEquals("", textInput.value) |
||||
} |
||||
|
||||
@Test |
||||
fun weekInput() = runTest { |
||||
composition { |
||||
WeekInput(value = "2017-W01") { |
||||
id("weekInputId") |
||||
} |
||||
} |
||||
|
||||
val textInput = root.firstChild as HTMLInputElement |
||||
|
||||
assertEquals("week", textInput.getAttribute("type")) |
||||
assertEquals("weekInputId", textInput.getAttribute("id")) |
||||
assertEquals("2017-W01", textInput.value) |
||||
} |
||||
|
||||
@Test |
||||
fun weekInputWithDefaults() = runTest { |
||||
composition { WeekInput() } |
||||
|
||||
val textInput = root.firstChild as HTMLInputElement |
||||
|
||||
assertEquals("week", textInput.getAttribute("type")) |
||||
assertEquals(null, textInput.getAttribute("id")) |
||||
assertEquals("", textInput.value) |
||||
} |
||||
} |
@ -0,0 +1,106 @@
|
||||
package org.jetbrains.compose.web.sample.tests |
||||
|
||||
import androidx.compose.runtime.getValue |
||||
import androidx.compose.runtime.mutableStateOf |
||||
import androidx.compose.runtime.remember |
||||
import androidx.compose.runtime.setValue |
||||
import org.jetbrains.compose.web.attributes.* |
||||
import org.jetbrains.compose.web.css.* |
||||
import org.jetbrains.compose.web.css.selectors.attr |
||||
import org.jetbrains.compose.web.dom.* |
||||
import org.jetbrains.compose.web.dom.ElementBuilder.Companion.Div |
||||
|
||||
class EventsTests { |
||||
|
||||
val doubleClickUpdatesText by testCase { |
||||
var state by remember { mutableStateOf("") } |
||||
|
||||
TestText(state) |
||||
|
||||
Div( |
||||
attrs = { |
||||
id("box") |
||||
style { |
||||
width(100.px) |
||||
height(100.px) |
||||
backgroundColor("red") |
||||
} |
||||
onDoubleClick { state = "Double Click Works!" } |
||||
} |
||||
) { |
||||
Text("Clickable Box") |
||||
} |
||||
} |
||||
|
||||
val focusInAndFocusOutUpdateTheText by testCase { |
||||
var state by remember { mutableStateOf("") } |
||||
|
||||
TestText(state) |
||||
|
||||
Input(type = InputType.Text, attrs = { |
||||
id("focusableInput") |
||||
onFocusIn { |
||||
state = "focused" |
||||
} |
||||
onFocusOut { |
||||
state = "not focused" |
||||
} |
||||
}) |
||||
} |
||||
|
||||
val focusAndBlurUpdateTheText by testCase { |
||||
var state by remember { mutableStateOf("") } |
||||
|
||||
TestText(state) |
||||
|
||||
Input(type = InputType.Text, attrs = { |
||||
id("focusableInput") |
||||
onFocus { |
||||
state = "focused" |
||||
} |
||||
onBlur { |
||||
state = "blured" |
||||
} |
||||
}) |
||||
} |
||||
|
||||
val scrollUpdatesText by testCase { |
||||
var state by remember { mutableStateOf("") } |
||||
|
||||
TestText(state) |
||||
|
||||
Div( |
||||
attrs = { |
||||
id("box") |
||||
style { |
||||
property("overflow-y", "scroll") |
||||
height(200.px) |
||||
backgroundColor(Color.RGB(220, 220, 220)) |
||||
} |
||||
onScroll { |
||||
state = "Scrolled" |
||||
} |
||||
} |
||||
) { |
||||
repeat(500) { |
||||
P { |
||||
Text("Scrollable content in Div - $it") |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
val selectEventUpdatesText by testCase { |
||||
var state by remember { mutableStateOf("None") } |
||||
|
||||
P(attrs = { style { height(50.px) } }) { TestText(state) } |
||||
|
||||
Input(type = InputType.Text, attrs = { |
||||
value("This is a text to be selected") |
||||
id("selectableText") |
||||
onSelect { |
||||
state = "Text Selected" |
||||
} |
||||
}) |
||||
} |
||||
} |
@ -0,0 +1,174 @@
|
||||
/* |
||||
* Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers. |
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||
*/ |
||||
|
||||
package org.jetbrains.compose.web.tests.integration |
||||
|
||||
import org.jetbrains.compose.web.tests.integration.common.BaseIntegrationTests |
||||
import org.jetbrains.compose.web.tests.integration.common.ResolveDrivers |
||||
import org.jetbrains.compose.web.tests.integration.common.openTestPage |
||||
import org.jetbrains.compose.web.tests.integration.common.waitTextToBe |
||||
import org.openqa.selenium.By |
||||
import org.openqa.selenium.Keys |
||||
import org.openqa.selenium.interactions.Actions |
||||
import org.openqa.selenium.WebDriver |
||||
|
||||
class EventTests : BaseIntegrationTests() { |
||||
|
||||
@ResolveDrivers |
||||
fun `double click updates text`(driver: WebDriver) { |
||||
driver.openTestPage("doubleClickUpdatesText") |
||||
|
||||
val box = driver.findElement(By.id("box")) |
||||
|
||||
val actions = Actions(driver) |
||||
|
||||
actions |
||||
.doubleClick(box) |
||||
.perform() |
||||
|
||||
driver.waitTextToBe(value = "Double Click Works!", textId = "txt") |
||||
} |
||||
|
||||
@ResolveDrivers |
||||
fun `focusin and focusout update the text`(driver: WebDriver) { |
||||
driver.openTestPage("focusInAndFocusOutUpdateTheText") |
||||
|
||||
driver.waitTextToBe(value = "", textId = "txt") |
||||
|
||||
val input = driver.findElement(By.id("focusableInput")) |
||||
|
||||
val actions = Actions(driver) |
||||
|
||||
actions.moveToElement(input) |
||||
.click() |
||||
.perform() |
||||
|
||||
driver.waitTextToBe(value = "focused", textId = "txt") |
||||
|
||||
val actions2 = Actions(driver) |
||||
|
||||
actions2.moveToElement(driver.findElement(By.id("txt"))) |
||||
.click() |
||||
.perform() |
||||
|
||||
driver.waitTextToBe(value = "not focused", textId = "txt") |
||||
} |
||||
|
||||
@ResolveDrivers |
||||
fun `focus and blur update the text`(driver: WebDriver) { |
||||
driver.openTestPage("focusAndBlurUpdateTheText") |
||||
|
||||
driver.waitTextToBe(value = "", textId = "txt") |
||||
|
||||
val input = driver.findElement(By.id("focusableInput")) |
||||
|
||||
val actions = Actions(driver) |
||||
|
||||
actions.moveToElement(input) |
||||
.click() |
||||
.perform() |
||||
|
||||
driver.waitTextToBe(value = "focused", textId = "txt") |
||||
|
||||
val actions2 = Actions(driver) |
||||
|
||||
actions2.moveToElement(driver.findElement(By.id("txt"))) |
||||
.click() |
||||
.perform() |
||||
|
||||
driver.waitTextToBe(value = "blured", textId = "txt") |
||||
} |
||||
|
||||
@ResolveDrivers |
||||
fun `scroll updates the text`(driver: WebDriver) { |
||||
driver.openTestPage("scrollUpdatesText") |
||||
|
||||
driver.waitTextToBe(value = "", textId = "txt") |
||||
|
||||
val box = driver.findElement(By.id("box")) |
||||
|
||||
val actions = Actions(driver) |
||||
actions.moveToElement(box) |
||||
.click() |
||||
.sendKeys(Keys.ARROW_DOWN) |
||||
.perform() |
||||
|
||||
driver.waitTextToBe(value = "Scrolled", textId = "txt") |
||||
} |
||||
|
||||
@ResolveDrivers |
||||
fun `select event update the txt`(driver: WebDriver) { |
||||
driver.openTestPage("selectEventUpdatesText") |
||||
driver.waitTextToBe(value = "None") |
||||
|
||||
val selectableText = driver.findElement(By.id("selectableText")) |
||||
|
||||
val action = Actions(driver) |
||||
|
||||
action.moveToElement(selectableText,3,3) |
||||
.click().keyDown(Keys.SHIFT) |
||||
.moveToElement(selectableText,200, 0) |
||||
.click().keyUp(Keys.SHIFT) |
||||
.build() |
||||
.perform() |
||||
|
||||
driver.waitTextToBe(value = "Text Selected") |
||||
} |
||||
|
||||
@ResolveDrivers |
||||
fun `stopImmediatePropagation prevents consequent listeners from being called`(driver: WebDriver) { |
||||
driver.openTestPage("stopOnInputImmediatePropagationWorks") |
||||
driver.waitTextToBe(value = "None") |
||||
|
||||
val checkBox = driver.findElement(By.id("checkbox")) |
||||
val radioButtonToStopImmediatePropagation = driver.findElement(By.id("radioBtn")) |
||||
|
||||
checkBox.click() |
||||
driver.waitTextToBe(value = "onInput2") |
||||
|
||||
radioButtonToStopImmediatePropagation.click() |
||||
driver.waitTextToBe(value = "None") |
||||
|
||||
checkBox.click() |
||||
driver.waitTextToBe(value = "onInput1") |
||||
} |
||||
|
||||
@ResolveDrivers |
||||
fun `preventDefault works as expected`(driver: WebDriver) { |
||||
driver.openTestPage("preventDefaultWorks") |
||||
|
||||
driver.waitTextToBe(value = "None") |
||||
driver.waitTextToBe(textId = "txt2", value = "None") |
||||
|
||||
val checkBox = driver.findElement(By.id("checkbox")) |
||||
checkBox.click() |
||||
|
||||
driver.waitTextToBe(value = "Clicked but check should be prevented") |
||||
driver.waitTextToBe(textId = "txt2", value = "None") |
||||
} |
||||
|
||||
@ResolveDrivers |
||||
fun `stopPropagation works as expected`(driver: WebDriver) { |
||||
driver.openTestPage("stopPropagationWorks") |
||||
|
||||
driver.waitTextToBe(value = "None") |
||||
driver.waitTextToBe(textId = "txt2", value = "None") |
||||
|
||||
val checkBox = driver.findElement(By.id("checkbox")) |
||||
val radioButtonToStopImmediatePropagation = driver.findElement(By.id("radioBtn")) |
||||
|
||||
checkBox.click() |
||||
driver.waitTextToBe(value = "childInput") |
||||
driver.waitTextToBe(textId = "txt2", value = "div caught an input") |
||||
|
||||
radioButtonToStopImmediatePropagation.click() |
||||
driver.waitTextToBe(value = "None") |
||||
driver.waitTextToBe(textId = "txt2", value = "None") |
||||
|
||||
checkBox.click() |
||||
driver.waitTextToBe(value = "childInput") |
||||
driver.waitTextToBe(textId = "txt2", value = "None") |
||||
} |
||||
} |
Loading…
Reference in new issue