Browse Source
* web: update change and beforeInput event listeners * web: update Clipboard events: copy, cut, paste * web: update Keyboard events: keyup, keydown * web: update select event (only for input and textarea) * web: update FocusEvents: focus, blur, focusin, focusout * web: update Touch Events * web: remove search event listener as it's not supported in Firefox * web: update Animation Events * web: update scroll event * web: rename WrappedEventListener to SyntheticEventListener * refactor internal event listeners to avoid redundant lambdas * remove WrappedEvent.kt * move SyntheticInputEvent.kt to `events` package * Add tests for Select element events * remove import from another changelist * web: add Form events: submit and reset * fix PR discussions * web: rename `attrsBuilder` to `attrs` Co-authored-by: Oleksandr Karpovich <oleksandr.karpovich@jetbrains.com>pull/905/head
Oleksandr Karpovich
3 years ago
committed by
GitHub
32 changed files with 1278 additions and 500 deletions
@ -1,33 +0,0 @@ |
|||||||
/* |
|
||||||
* 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 androidx.compose.web.events.SyntheticEvent |
|
||||||
import org.jetbrains.compose.web.attributes.AttrsBuilder |
|
||||||
import org.jetbrains.compose.web.attributes.InputType |
|
||||||
import org.jetbrains.compose.web.attributes.Options |
|
||||||
import org.w3c.dom.HTMLInputElement |
|
||||||
import org.w3c.dom.events.Event |
|
||||||
import org.w3c.dom.events.EventTarget |
|
||||||
|
|
||||||
class SyntheticInputEvent<ValueType, Element : EventTarget>( |
|
||||||
val value: ValueType, |
|
||||||
nativeEvent: Event |
|
||||||
) : SyntheticEvent<Element>( |
|
||||||
nativeEvent = nativeEvent |
|
||||||
) |
|
||||||
|
|
||||||
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)) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,156 @@ |
|||||||
|
package org.jetbrains.compose.web.attributes |
||||||
|
|
||||||
|
import org.jetbrains.compose.web.events.SyntheticInputEvent |
||||||
|
import androidx.compose.web.events.SyntheticDragEvent |
||||||
|
import androidx.compose.web.events.SyntheticEvent |
||||||
|
import androidx.compose.web.events.SyntheticMouseEvent |
||||||
|
import androidx.compose.web.events.SyntheticWheelEvent |
||||||
|
import org.jetbrains.compose.web.attributes.EventsListenerBuilder.Companion.CHANGE |
||||||
|
import org.jetbrains.compose.web.attributes.EventsListenerBuilder.Companion.INPUT |
||||||
|
import org.jetbrains.compose.web.attributes.EventsListenerBuilder.Companion.SELECT |
||||||
|
import org.jetbrains.compose.web.events.* |
||||||
|
import org.w3c.dom.DragEvent |
||||||
|
import org.w3c.dom.TouchEvent |
||||||
|
import org.w3c.dom.clipboard.ClipboardEvent |
||||||
|
import org.w3c.dom.events.* |
||||||
|
|
||||||
|
open class SyntheticEventListener<T : SyntheticEvent<*>> internal constructor( |
||||||
|
val event: String, |
||||||
|
val options: Options, |
||||||
|
val listener: (T) -> Unit |
||||||
|
) : EventListener { |
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST") |
||||||
|
override fun handleEvent(event: Event) { |
||||||
|
listener(SyntheticEvent<EventTarget>(event).unsafeCast<T>()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class Options { |
||||||
|
// TODO: add options for addEventListener |
||||||
|
|
||||||
|
companion object { |
||||||
|
val DEFAULT = Options() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
internal class AnimationEventListener( |
||||||
|
event: String, |
||||||
|
options: Options, |
||||||
|
listener: (SyntheticAnimationEvent) -> Unit |
||||||
|
) : SyntheticEventListener<SyntheticAnimationEvent>( |
||||||
|
event, options, listener |
||||||
|
) { |
||||||
|
override fun handleEvent(event: Event) { |
||||||
|
listener(SyntheticAnimationEvent(event, event.unsafeCast<AnimationEventDetails>())) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
internal class MouseEventListener( |
||||||
|
event: String, |
||||||
|
options: Options, |
||||||
|
listener: (SyntheticMouseEvent) -> Unit |
||||||
|
) : SyntheticEventListener<SyntheticMouseEvent>(event, options, listener) { |
||||||
|
override fun handleEvent(event: Event) { |
||||||
|
listener(SyntheticMouseEvent(event.unsafeCast<MouseEvent>())) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
internal class MouseWheelEventListener( |
||||||
|
event: String, |
||||||
|
options: Options, |
||||||
|
listener: (SyntheticWheelEvent) -> Unit |
||||||
|
) : SyntheticEventListener<SyntheticWheelEvent>(event, options, listener) { |
||||||
|
override fun handleEvent(event: Event) { |
||||||
|
listener(SyntheticWheelEvent(event.unsafeCast<WheelEvent>())) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
internal class KeyboardEventListener( |
||||||
|
event: String, |
||||||
|
options: Options, |
||||||
|
listener: (SyntheticKeyboardEvent) -> Unit |
||||||
|
) : SyntheticEventListener<SyntheticKeyboardEvent>(event, options, listener) { |
||||||
|
override fun handleEvent(event: Event) { |
||||||
|
listener(SyntheticKeyboardEvent(event.unsafeCast<KeyboardEvent>())) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
internal class FocusEventListener( |
||||||
|
event: String, |
||||||
|
options: Options, |
||||||
|
listener: (SyntheticFocusEvent) -> Unit |
||||||
|
) : SyntheticEventListener<SyntheticFocusEvent>(event, options, listener) { |
||||||
|
override fun handleEvent(event: Event) { |
||||||
|
listener(SyntheticFocusEvent(event.unsafeCast<FocusEvent>())) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
internal class TouchEventListener( |
||||||
|
event: String, |
||||||
|
options: Options, |
||||||
|
listener: (SyntheticTouchEvent) -> Unit |
||||||
|
) : SyntheticEventListener<SyntheticTouchEvent>(event, options, listener) { |
||||||
|
override fun handleEvent(event: Event) { |
||||||
|
listener(SyntheticTouchEvent(event.unsafeCast<TouchEvent>())) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
internal class DragEventListener( |
||||||
|
event: String, |
||||||
|
options: Options, |
||||||
|
listener: (SyntheticDragEvent) -> Unit |
||||||
|
) : SyntheticEventListener<SyntheticDragEvent>(event, options, listener) { |
||||||
|
override fun handleEvent(event: Event) { |
||||||
|
listener(SyntheticDragEvent(event.unsafeCast<DragEvent>())) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
internal class ClipboardEventListener( |
||||||
|
event: String, |
||||||
|
options: Options, |
||||||
|
listener: (SyntheticClipboardEvent) -> Unit |
||||||
|
) : SyntheticEventListener<SyntheticClipboardEvent>(event, options, listener) { |
||||||
|
override fun handleEvent(event: Event) { |
||||||
|
listener(SyntheticClipboardEvent(event.unsafeCast<ClipboardEvent>())) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
internal class InputEventListener<InputValueType, Target: EventTarget>( |
||||||
|
eventName: String = INPUT, |
||||||
|
options: Options, |
||||||
|
val inputType: InputType<InputValueType>, |
||||||
|
listener: (SyntheticInputEvent<InputValueType, Target>) -> Unit |
||||||
|
) : SyntheticEventListener<SyntheticInputEvent<InputValueType, Target>>( |
||||||
|
eventName, options, listener |
||||||
|
) { |
||||||
|
override fun handleEvent(event: Event) { |
||||||
|
val value = inputType.inputValue(event) |
||||||
|
listener(SyntheticInputEvent(value, event)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
internal class ChangeEventListener<InputValueType, Target: EventTarget>( |
||||||
|
options: Options, |
||||||
|
val inputType: InputType<InputValueType>, |
||||||
|
listener: (SyntheticChangeEvent<InputValueType, Target>) -> Unit |
||||||
|
) : SyntheticEventListener<SyntheticChangeEvent<InputValueType, Target>>( |
||||||
|
CHANGE, options, listener |
||||||
|
) { |
||||||
|
override fun handleEvent(event: Event) { |
||||||
|
val value = inputType.inputValue(event) |
||||||
|
listener(SyntheticChangeEvent(value, event)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
internal class SelectEventListener<Target: EventTarget>( |
||||||
|
options: Options, |
||||||
|
listener: (SyntheticSelectEvent<Target>) -> Unit |
||||||
|
) : SyntheticEventListener<SyntheticSelectEvent<Target>>( |
||||||
|
SELECT, options, listener |
||||||
|
) { |
||||||
|
override fun handleEvent(event: Event) { |
||||||
|
listener(SyntheticSelectEvent(event, event.target.unsafeCast<SelectionInfoDetails>())) |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -1,23 +0,0 @@ |
|||||||
/* |
|
||||||
* 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.AttrsBuilder |
|
||||||
import org.jetbrains.compose.web.attributes.Options |
|
||||||
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)) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,168 +0,0 @@ |
|||||||
package 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.WrappedClipboardEvent |
|
||||||
import org.jetbrains.compose.web.events.WrappedDragEvent |
|
||||||
import org.jetbrains.compose.web.events.WrappedEventImpl |
|
||||||
import org.jetbrains.compose.web.events.WrappedFocusEvent |
|
||||||
import org.jetbrains.compose.web.events.WrappedInputEvent |
|
||||||
import org.jetbrains.compose.web.events.WrappedKeyboardEvent |
|
||||||
import org.jetbrains.compose.web.events.WrappedMouseEvent |
|
||||||
import org.jetbrains.compose.web.events.WrappedPointerEvent |
|
||||||
import org.jetbrains.compose.web.events.WrappedRadioInputEvent |
|
||||||
import org.jetbrains.compose.web.events.WrappedTextInputEvent |
|
||||||
import org.jetbrains.compose.web.events.WrappedTouchEvent |
|
||||||
import org.jetbrains.compose.web.events.WrappedWheelEvent |
|
||||||
import org.w3c.dom.DragEvent |
|
||||||
import org.w3c.dom.TouchEvent |
|
||||||
import org.w3c.dom.clipboard.ClipboardEvent |
|
||||||
import org.w3c.dom.events.Event |
|
||||||
import org.w3c.dom.events.FocusEvent |
|
||||||
import org.w3c.dom.events.InputEvent |
|
||||||
import org.w3c.dom.events.KeyboardEvent |
|
||||||
import org.w3c.dom.events.MouseEvent |
|
||||||
import org.w3c.dom.events.WheelEvent |
|
||||||
import org.w3c.dom.pointerevents.PointerEvent |
|
||||||
|
|
||||||
open class WrappedEventListener<T : GenericWrappedEvent<*>>( |
|
||||||
val event: String, |
|
||||||
val options: Options, |
|
||||||
val listener: (T) -> Unit |
|
||||||
) : org.w3c.dom.events.EventListener { |
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST") |
|
||||||
override fun handleEvent(event: Event) { |
|
||||||
listener(WrappedEventImpl(event) as T) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
class Options { |
|
||||||
// TODO: add options for addEventListener |
|
||||||
|
|
||||||
companion object { |
|
||||||
val DEFAULT = Options() |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
internal class MouseEventListener( |
|
||||||
event: String, |
|
||||||
options: Options, |
|
||||||
listener: (WrappedMouseEvent) -> Unit |
|
||||||
) : WrappedEventListener<WrappedMouseEvent>(event, options, listener) { |
|
||||||
override fun handleEvent(event: Event) { |
|
||||||
listener(WrappedMouseEvent(event as MouseEvent)) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
internal class MouseWheelEventListener( |
|
||||||
event: String, |
|
||||||
options: Options, |
|
||||||
listener: (WrappedWheelEvent) -> Unit |
|
||||||
) : WrappedEventListener<WrappedWheelEvent>(event, options, listener) { |
|
||||||
override fun handleEvent(event: Event) { |
|
||||||
listener(WrappedWheelEvent(event as WheelEvent)) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
internal class KeyboardEventListener( |
|
||||||
event: String, |
|
||||||
options: Options, |
|
||||||
listener: (WrappedKeyboardEvent) -> Unit |
|
||||||
) : WrappedEventListener<WrappedKeyboardEvent>(event, options, listener) { |
|
||||||
override fun handleEvent(event: Event) { |
|
||||||
listener(WrappedKeyboardEvent(event as KeyboardEvent)) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
internal class FocusEventListener( |
|
||||||
event: String, |
|
||||||
options: Options, |
|
||||||
listener: (WrappedFocusEvent) -> Unit |
|
||||||
) : WrappedEventListener<WrappedFocusEvent>(event, options, listener) { |
|
||||||
override fun handleEvent(event: Event) { |
|
||||||
listener(WrappedFocusEvent(event as FocusEvent)) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
internal class TouchEventListener( |
|
||||||
event: String, |
|
||||||
options: Options, |
|
||||||
listener: (WrappedTouchEvent) -> Unit |
|
||||||
) : WrappedEventListener<WrappedTouchEvent>(event, options, listener) { |
|
||||||
override fun handleEvent(event: Event) { |
|
||||||
listener(WrappedTouchEvent(event as TouchEvent)) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
internal class DragEventListener( |
|
||||||
event: String, |
|
||||||
options: Options, |
|
||||||
listener: (WrappedDragEvent) -> Unit |
|
||||||
) : WrappedEventListener<WrappedDragEvent>(event, options, listener) { |
|
||||||
override fun handleEvent(event: Event) { |
|
||||||
listener(WrappedDragEvent(event as DragEvent)) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
internal class PointerEventListener( |
|
||||||
event: String, |
|
||||||
options: Options, |
|
||||||
listener: (WrappedPointerEvent) -> Unit |
|
||||||
) : WrappedEventListener<WrappedPointerEvent>(event, options, listener) { |
|
||||||
override fun handleEvent(event: Event) { |
|
||||||
listener(WrappedPointerEvent(event as PointerEvent)) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
internal class ClipboardEventListener( |
|
||||||
event: String, |
|
||||||
options: Options, |
|
||||||
listener: (WrappedClipboardEvent) -> Unit |
|
||||||
) : WrappedEventListener<WrappedClipboardEvent>(event, options, listener) { |
|
||||||
override fun handleEvent(event: Event) { |
|
||||||
listener(WrappedClipboardEvent(event as ClipboardEvent)) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
internal class InputEventListener( |
|
||||||
event: String, |
|
||||||
options: Options, |
|
||||||
listener: (WrappedInputEvent) -> Unit |
|
||||||
) : WrappedEventListener<WrappedInputEvent>(event, options, listener) { |
|
||||||
override fun handleEvent(event: Event) { |
|
||||||
listener(WrappedInputEvent(event as InputEvent)) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
internal class RadioInputEventListener( |
|
||||||
options: Options, |
|
||||||
listener: (WrappedRadioInputEvent) -> Unit |
|
||||||
) : WrappedEventListener<WrappedRadioInputEvent>(EventsListenerBuilder.INPUT, options, listener) { |
|
||||||
override fun handleEvent(event: Event) { |
|
||||||
val checked = event.target.asDynamic().checked as Boolean |
|
||||||
listener(WrappedRadioInputEvent(event, checked)) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
internal class CheckBoxInputEventListener( |
|
||||||
options: Options, |
|
||||||
listener: (WrappedCheckBoxInputEvent) -> Unit |
|
||||||
) : WrappedEventListener<WrappedCheckBoxInputEvent>( |
|
||||||
EventsListenerBuilder.INPUT, options, listener |
|
||||||
) { |
|
||||||
override fun handleEvent(event: Event) { |
|
||||||
val checked = event.target.asDynamic().checked as Boolean |
|
||||||
listener(WrappedCheckBoxInputEvent(event, checked)) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
internal class TextInputEventListener( |
|
||||||
options: Options, |
|
||||||
listener: (WrappedTextInputEvent) -> Unit |
|
||||||
) : WrappedEventListener<WrappedTextInputEvent>(EventsListenerBuilder.INPUT, options, listener) { |
|
||||||
override fun handleEvent(event: Event) { |
|
||||||
val text = event.target.asDynamic().value as String |
|
||||||
listener(WrappedTextInputEvent(event as InputEvent, text)) |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,53 @@ |
|||||||
|
/* |
||||||
|
* 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.attributes.builders |
||||||
|
|
||||||
|
import androidx.compose.web.events.SyntheticEvent |
||||||
|
import org.jetbrains.compose.web.attributes.* |
||||||
|
import org.jetbrains.compose.web.events.SyntheticChangeEvent |
||||||
|
import org.jetbrains.compose.web.events.SyntheticInputEvent |
||||||
|
import org.jetbrains.compose.web.events.SyntheticSelectEvent |
||||||
|
import org.w3c.dom.HTMLInputElement |
||||||
|
|
||||||
|
class InputAttrsBuilder<ValueType>( |
||||||
|
val inputType: InputType<ValueType> |
||||||
|
) : AttrsBuilder<HTMLInputElement>() { |
||||||
|
|
||||||
|
fun onInvalid( |
||||||
|
options: Options = Options.DEFAULT, |
||||||
|
listener: (SyntheticEvent<HTMLInputElement>) -> Unit |
||||||
|
) { |
||||||
|
addEventListener(INVALID, options, listener) |
||||||
|
} |
||||||
|
|
||||||
|
fun onInput( |
||||||
|
options: Options = Options.DEFAULT, |
||||||
|
listener: (SyntheticInputEvent<ValueType, HTMLInputElement>) -> Unit |
||||||
|
) { |
||||||
|
listeners.add(InputEventListener(eventName = INPUT, options, inputType, listener)) |
||||||
|
} |
||||||
|
|
||||||
|
fun onChange( |
||||||
|
options: Options = Options.DEFAULT, |
||||||
|
listener: (SyntheticChangeEvent<ValueType, HTMLInputElement>) -> Unit |
||||||
|
) { |
||||||
|
listeners.add(ChangeEventListener(options, inputType, listener)) |
||||||
|
} |
||||||
|
|
||||||
|
fun onBeforeInput( |
||||||
|
options: Options = Options.DEFAULT, |
||||||
|
listener: (SyntheticInputEvent<ValueType, HTMLInputElement>) -> Unit |
||||||
|
) { |
||||||
|
listeners.add(InputEventListener(eventName = BEFOREINPUT, options, inputType, listener)) |
||||||
|
} |
||||||
|
|
||||||
|
fun onSelect( |
||||||
|
options: Options = Options.DEFAULT, |
||||||
|
listener: (SyntheticSelectEvent<HTMLInputElement>) -> Unit |
||||||
|
) { |
||||||
|
listeners.add(SelectEventListener(options, listener)) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,58 @@ |
|||||||
|
/* |
||||||
|
* 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.AttrsBuilder |
||||||
|
import org.jetbrains.compose.web.attributes.EventsListenerBuilder.Companion.CHANGE |
||||||
|
import org.jetbrains.compose.web.attributes.EventsListenerBuilder.Companion.INPUT |
||||||
|
import org.jetbrains.compose.web.attributes.Options |
||||||
|
import org.jetbrains.compose.web.attributes.SyntheticEventListener |
||||||
|
import org.jetbrains.compose.web.events.SyntheticChangeEvent |
||||||
|
import org.jetbrains.compose.web.events.SyntheticInputEvent |
||||||
|
import org.w3c.dom.HTMLSelectElement |
||||||
|
import org.w3c.dom.events.Event |
||||||
|
|
||||||
|
class SelectAttrsBuilder : AttrsBuilder<HTMLSelectElement>() { |
||||||
|
|
||||||
|
fun onInput( |
||||||
|
options: Options = Options.DEFAULT, |
||||||
|
listener: (SyntheticInputEvent<String?, HTMLSelectElement>) -> Unit |
||||||
|
) { |
||||||
|
listeners.add(SelectInputEventListener(INPUT, options, listener)) |
||||||
|
} |
||||||
|
|
||||||
|
fun onChange( |
||||||
|
options: Options = Options.DEFAULT, |
||||||
|
listener: (SyntheticChangeEvent<String?, HTMLSelectElement>) -> Unit |
||||||
|
) { |
||||||
|
listeners.add(SelectChangeEventListener(options, listener)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private class SelectInputEventListener( |
||||||
|
eventName: String = INPUT, |
||||||
|
options: Options = Options.DEFAULT, |
||||||
|
listener: (SyntheticInputEvent<String?, HTMLSelectElement>) -> Unit |
||||||
|
) : SyntheticEventListener<SyntheticInputEvent<String?, HTMLSelectElement>>( |
||||||
|
eventName, options, listener |
||||||
|
) { |
||||||
|
override fun handleEvent(event: Event) { |
||||||
|
val value = event.target?.asDynamic().value?.toString() |
||||||
|
listener(SyntheticInputEvent(value, event)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private class SelectChangeEventListener( |
||||||
|
options: Options = Options.DEFAULT, |
||||||
|
listener: (SyntheticChangeEvent<String?, HTMLSelectElement>) -> Unit |
||||||
|
): SyntheticEventListener<SyntheticChangeEvent<String?, HTMLSelectElement>>( |
||||||
|
CHANGE, options, listener |
||||||
|
) { |
||||||
|
override fun handleEvent(event: Event) { |
||||||
|
val value = event.target?.asDynamic().value?.toString() |
||||||
|
listener(SyntheticChangeEvent(value, event)) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,43 @@ |
|||||||
|
/* |
||||||
|
* 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.attributes.builders |
||||||
|
|
||||||
|
import org.jetbrains.compose.web.attributes.* |
||||||
|
import org.jetbrains.compose.web.events.SyntheticChangeEvent |
||||||
|
import org.jetbrains.compose.web.events.SyntheticSelectEvent |
||||||
|
import org.jetbrains.compose.web.events.SyntheticInputEvent |
||||||
|
import org.w3c.dom.HTMLTextAreaElement |
||||||
|
|
||||||
|
class TextAreaAttrsBuilder : AttrsBuilder<HTMLTextAreaElement>() { |
||||||
|
|
||||||
|
fun onInput( |
||||||
|
options: Options = Options.DEFAULT, |
||||||
|
listener: (SyntheticInputEvent<String, HTMLTextAreaElement>) -> Unit |
||||||
|
) { |
||||||
|
listeners.add(InputEventListener(INPUT, options, InputType.Text, listener)) |
||||||
|
} |
||||||
|
|
||||||
|
fun onChange( |
||||||
|
options: Options = Options.DEFAULT, |
||||||
|
listener: (SyntheticChangeEvent<String, HTMLTextAreaElement>) -> Unit |
||||||
|
) { |
||||||
|
listeners.add(ChangeEventListener(options, InputType.Text, listener)) |
||||||
|
} |
||||||
|
|
||||||
|
fun onBeforeInput( |
||||||
|
options: Options = Options.DEFAULT, |
||||||
|
listener: (SyntheticInputEvent<String, HTMLTextAreaElement>) -> Unit |
||||||
|
) { |
||||||
|
listeners.add(InputEventListener(BEFOREINPUT, options, InputType.Text, listener)) |
||||||
|
} |
||||||
|
|
||||||
|
fun onSelect( |
||||||
|
options: Options = Options.DEFAULT, |
||||||
|
listener: (SyntheticSelectEvent<HTMLTextAreaElement>) -> Unit |
||||||
|
) { |
||||||
|
listeners.add(SelectEventListener(options, listener)) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
package org.jetbrains.compose.web.events |
||||||
|
|
||||||
|
import androidx.compose.web.events.SyntheticEvent |
||||||
|
import org.w3c.dom.events.Event |
||||||
|
import org.w3c.dom.events.EventTarget |
||||||
|
|
||||||
|
class SyntheticAnimationEvent internal constructor( |
||||||
|
nativeEvent: Event, |
||||||
|
animationEventDetails: AnimationEventDetails |
||||||
|
) : SyntheticEvent<EventTarget>(nativeEvent) { |
||||||
|
|
||||||
|
val animationName: String = animationEventDetails.animationName |
||||||
|
val elapsedTime: Number = animationEventDetails.elapsedTime |
||||||
|
val pseudoElement: String = animationEventDetails.pseudoElement |
||||||
|
} |
||||||
|
|
||||||
|
internal external interface AnimationEventDetails { |
||||||
|
val animationName: String |
||||||
|
val elapsedTime: Number |
||||||
|
val pseudoElement: String |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
package org.jetbrains.compose.web.events |
||||||
|
|
||||||
|
import androidx.compose.web.events.SyntheticEvent |
||||||
|
import org.w3c.dom.HTMLElement |
||||||
|
import org.w3c.dom.events.Event |
||||||
|
import org.w3c.dom.events.EventTarget |
||||||
|
|
||||||
|
class SyntheticChangeEvent<Value, Element : EventTarget> internal constructor( |
||||||
|
val value: Value, |
||||||
|
nativeEvent: Event, |
||||||
|
) : SyntheticEvent<Element>(nativeEvent) |
@ -0,0 +1,21 @@ |
|||||||
|
package org.jetbrains.compose.web.events |
||||||
|
|
||||||
|
import androidx.compose.web.events.SyntheticEvent |
||||||
|
import org.w3c.dom.DataTransfer |
||||||
|
import org.w3c.dom.clipboard.ClipboardEvent |
||||||
|
import org.w3c.dom.events.EventTarget |
||||||
|
|
||||||
|
class SyntheticClipboardEvent internal constructor( |
||||||
|
nativeEvent: ClipboardEvent |
||||||
|
) : SyntheticEvent<EventTarget>(nativeEvent) { |
||||||
|
|
||||||
|
val clipboardData: DataTransfer? = nativeEvent.clipboardData |
||||||
|
|
||||||
|
fun getData(format: String): String? { |
||||||
|
return clipboardData?.getData(format) |
||||||
|
} |
||||||
|
|
||||||
|
fun setData(format: String, data: String) { |
||||||
|
clipboardData?.setData(format, data) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
package org.jetbrains.compose.web.events |
||||||
|
|
||||||
|
import androidx.compose.web.events.SyntheticEvent |
||||||
|
import org.w3c.dom.events.EventTarget |
||||||
|
import org.w3c.dom.events.FocusEvent |
||||||
|
|
||||||
|
class SyntheticFocusEvent internal constructor( |
||||||
|
nativeEvent: FocusEvent, |
||||||
|
) : SyntheticEvent<EventTarget>(nativeEvent) { |
||||||
|
|
||||||
|
val relatedTarget: EventTarget? = nativeEvent.relatedTarget |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
package org.jetbrains.compose.web.events |
||||||
|
|
||||||
|
import androidx.compose.web.events.SyntheticEvent |
||||||
|
import org.w3c.dom.DataTransfer |
||||||
|
import org.w3c.dom.events.Event |
||||||
|
import org.w3c.dom.events.EventTarget |
||||||
|
|
||||||
|
// @param nativeEvent: Event - we don't use [org.w3c.dom.events.InputEvent] here, |
||||||
|
// since for cases it can be just [org.w3c.dom.events.Event] |
||||||
|
class SyntheticInputEvent<ValueType, Element : EventTarget> internal constructor( |
||||||
|
val value: ValueType, |
||||||
|
nativeEvent: Event |
||||||
|
) : SyntheticEvent<Element>( |
||||||
|
nativeEvent = nativeEvent |
||||||
|
) { |
||||||
|
val data: String? = nativeEvent.asDynamic().data?.unsafeCast<String>() |
||||||
|
val dataTransfer: DataTransfer? = nativeEvent.asDynamic().dataTransfer?.unsafeCast<DataTransfer>() |
||||||
|
val inputType: String? = nativeEvent.asDynamic().inputType?.unsafeCast<String>() |
||||||
|
val isComposing: Boolean = nativeEvent.asDynamic().isComposing?.unsafeCast<Boolean>() ?: false |
||||||
|
} |
@ -0,0 +1,44 @@ |
|||||||
|
package org.jetbrains.compose.web.events |
||||||
|
|
||||||
|
import androidx.compose.web.events.SyntheticEvent |
||||||
|
import org.w3c.dom.events.EventTarget |
||||||
|
import org.w3c.dom.events.KeyboardEvent |
||||||
|
|
||||||
|
class SyntheticKeyboardEvent internal constructor( |
||||||
|
nativeEvent: KeyboardEvent |
||||||
|
) : SyntheticEvent<EventTarget>(nativeEvent) { |
||||||
|
|
||||||
|
private val keyboardEvent = nativeEvent |
||||||
|
|
||||||
|
val altKey: Boolean = nativeEvent.altKey |
||||||
|
val code: String = nativeEvent.code |
||||||
|
val ctrlKey: Boolean = nativeEvent.ctrlKey |
||||||
|
val isComposing: Boolean = nativeEvent.isComposing |
||||||
|
val key: String = nativeEvent.key |
||||||
|
val locale: String = nativeEvent.asDynamic().locale.toString() |
||||||
|
val location: Int = nativeEvent.location |
||||||
|
val metaKey: Boolean = nativeEvent.metaKey |
||||||
|
val repeat: Boolean = nativeEvent.repeat |
||||||
|
val shiftKey: Boolean = nativeEvent.shiftKey |
||||||
|
|
||||||
|
fun getModifierState(keyArg: String): Boolean = keyboardEvent.getModifierState(keyArg) |
||||||
|
|
||||||
|
fun getNormalizedKey(): String = key.let { |
||||||
|
normalizedKeys[it] ?: it |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private val normalizedKeys = mapOf( |
||||||
|
"Esc" to "Escape", |
||||||
|
"Spacebar" to " ", |
||||||
|
"Left" to "ArrowLeft", |
||||||
|
"Up" to "ArrowUp", |
||||||
|
"Right" to "ArrowRight", |
||||||
|
"Down" to "ArrowDown", |
||||||
|
"Del" to "Delete", |
||||||
|
"Apps" to "ContextMenu", |
||||||
|
"Menu" to "ContextMenu", |
||||||
|
"Scroll" to "ScrollLock", |
||||||
|
"MozPrintableKey" to "Unidentified", |
||||||
|
) |
||||||
|
// Firefox bug for Windows key https://bugzilla.mozilla.org/show_bug.cgi?id=1232918 |
@ -0,0 +1,26 @@ |
|||||||
|
package org.jetbrains.compose.web.events |
||||||
|
|
||||||
|
import androidx.compose.web.events.SyntheticEvent |
||||||
|
import org.w3c.dom.events.Event |
||||||
|
import org.w3c.dom.events.EventTarget |
||||||
|
|
||||||
|
class SyntheticSelectEvent<Element : EventTarget> internal constructor( |
||||||
|
nativeEvent: Event, |
||||||
|
selectionInfoDetails: SelectionInfoDetails |
||||||
|
) : SyntheticEvent<Element>(nativeEvent) { |
||||||
|
|
||||||
|
val selectionStart: Int = selectionInfoDetails.selectionStart |
||||||
|
val selectionEnd: Int = selectionInfoDetails.selectionEnd |
||||||
|
|
||||||
|
|
||||||
|
fun selection(): String { |
||||||
|
return nativeEvent.target.asDynamic().value.unsafeCast<String?>()?.substring( |
||||||
|
selectionStart, selectionEnd |
||||||
|
) ?: "" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
internal external interface SelectionInfoDetails { |
||||||
|
val selectionStart: Int |
||||||
|
val selectionEnd: Int |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
package org.jetbrains.compose.web.events |
||||||
|
|
||||||
|
import androidx.compose.web.events.SyntheticEvent |
||||||
|
import org.w3c.dom.events.Event |
||||||
|
import org.w3c.dom.events.EventTarget |
||||||
|
|
||||||
|
class SyntheticSubmitEvent internal constructor( |
||||||
|
nativeEvent: Event |
||||||
|
) : SyntheticEvent<EventTarget>(nativeEvent) |
@ -0,0 +1,18 @@ |
|||||||
|
package org.jetbrains.compose.web.events |
||||||
|
|
||||||
|
import androidx.compose.web.events.SyntheticEvent |
||||||
|
import org.w3c.dom.TouchEvent |
||||||
|
import org.w3c.dom.TouchList |
||||||
|
import org.w3c.dom.events.EventTarget |
||||||
|
|
||||||
|
class SyntheticTouchEvent( |
||||||
|
nativeEvent: TouchEvent, |
||||||
|
) : SyntheticEvent<EventTarget>(nativeEvent) { |
||||||
|
|
||||||
|
val altKey: Boolean = nativeEvent.altKey |
||||||
|
val changedTouches: TouchList = nativeEvent.changedTouches |
||||||
|
val ctrlKey: Boolean = nativeEvent.ctrlKey |
||||||
|
val metaKey: Boolean = nativeEvent.metaKey |
||||||
|
val shiftKey: Boolean = nativeEvent.shiftKey |
||||||
|
val touches: TouchList = nativeEvent.touches |
||||||
|
} |
@ -1,107 +0,0 @@ |
|||||||
package org.jetbrains.compose.web.events |
|
||||||
|
|
||||||
import org.w3c.dom.DragEvent |
|
||||||
import org.w3c.dom.TouchEvent |
|
||||||
import org.w3c.dom.clipboard.ClipboardEvent |
|
||||||
import org.w3c.dom.events.CompositionEvent |
|
||||||
import org.w3c.dom.events.Event |
|
||||||
import org.w3c.dom.events.FocusEvent |
|
||||||
import org.w3c.dom.events.InputEvent |
|
||||||
import org.w3c.dom.events.KeyboardEvent |
|
||||||
import org.w3c.dom.events.MouseEvent |
|
||||||
import org.w3c.dom.events.WheelEvent |
|
||||||
import org.w3c.dom.pointerevents.PointerEvent |
|
||||||
|
|
||||||
interface GenericWrappedEvent<T : Event> { |
|
||||||
val nativeEvent: T |
|
||||||
} |
|
||||||
|
|
||||||
interface WrappedEvent : GenericWrappedEvent<Event> |
|
||||||
|
|
||||||
internal open class WrappedMouseEvent( |
|
||||||
override val nativeEvent: MouseEvent |
|
||||||
) : GenericWrappedEvent<MouseEvent> { |
|
||||||
|
|
||||||
// MouseEvent doesn't support movementX and movementY on IE6-11, and it's OK for now. |
|
||||||
val movementX: Double |
|
||||||
get() = nativeEvent.asDynamic().movementX as Double |
|
||||||
val movementY: Double |
|
||||||
get() = nativeEvent.asDynamic().movementY as Double |
|
||||||
} |
|
||||||
|
|
||||||
internal open class WrappedWheelEvent( |
|
||||||
override val nativeEvent: WheelEvent |
|
||||||
) : GenericWrappedEvent<WheelEvent> |
|
||||||
|
|
||||||
open class WrappedInputEvent( |
|
||||||
override val nativeEvent: Event |
|
||||||
) : GenericWrappedEvent<Event> |
|
||||||
|
|
||||||
open class WrappedKeyboardEvent( |
|
||||||
override val nativeEvent: KeyboardEvent |
|
||||||
) : GenericWrappedEvent<KeyboardEvent> { |
|
||||||
|
|
||||||
fun getNormalizedKey(): String = nativeEvent.key.let { |
|
||||||
normalizedKeys[it] ?: it |
|
||||||
} |
|
||||||
|
|
||||||
companion object { |
|
||||||
private val normalizedKeys = mapOf( |
|
||||||
"Esc" to "Escape", |
|
||||||
"Spacebar" to " ", |
|
||||||
"Left" to "ArrowLeft", |
|
||||||
"Up" to "ArrowUp", |
|
||||||
"Right" to "ArrowRight", |
|
||||||
"Down" to "ArrowDown", |
|
||||||
"Del" to "Delete", |
|
||||||
"Apps" to "ContextMenu", |
|
||||||
"Menu" to "ContextMenu", |
|
||||||
"Scroll" to "ScrollLock", |
|
||||||
"MozPrintableKey" to "Unidentified", |
|
||||||
) |
|
||||||
// Firefox bug for Windows key https://bugzilla.mozilla.org/show_bug.cgi?id=1232918 |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
open class WrappedFocusEvent( |
|
||||||
override val nativeEvent: FocusEvent |
|
||||||
) : GenericWrappedEvent<FocusEvent> |
|
||||||
|
|
||||||
open class WrappedTouchEvent( |
|
||||||
override val nativeEvent: TouchEvent |
|
||||||
) : GenericWrappedEvent<TouchEvent> |
|
||||||
|
|
||||||
open class WrappedCompositionEvent( |
|
||||||
override val nativeEvent: CompositionEvent |
|
||||||
) : GenericWrappedEvent<CompositionEvent> |
|
||||||
|
|
||||||
open class WrappedDragEvent( |
|
||||||
override val nativeEvent: DragEvent |
|
||||||
) : GenericWrappedEvent<DragEvent> |
|
||||||
|
|
||||||
open class WrappedPointerEvent( |
|
||||||
override val nativeEvent: PointerEvent |
|
||||||
) : GenericWrappedEvent<PointerEvent> |
|
||||||
|
|
||||||
open class WrappedClipboardEvent( |
|
||||||
override val nativeEvent: ClipboardEvent |
|
||||||
) : GenericWrappedEvent<ClipboardEvent> |
|
||||||
|
|
||||||
class WrappedTextInputEvent( |
|
||||||
nativeEvent: Event, |
|
||||||
val inputValue: String |
|
||||||
) : WrappedInputEvent(nativeEvent) |
|
||||||
|
|
||||||
class WrappedCheckBoxInputEvent( |
|
||||||
override val nativeEvent: Event, |
|
||||||
val checked: Boolean |
|
||||||
) : GenericWrappedEvent<Event> |
|
||||||
|
|
||||||
class WrappedRadioInputEvent( |
|
||||||
override val nativeEvent: Event, |
|
||||||
val checked: Boolean |
|
||||||
) : GenericWrappedEvent<Event> |
|
||||||
|
|
||||||
class WrappedEventImpl( |
|
||||||
override val nativeEvent: Event |
|
||||||
) : WrappedEvent |
|
@ -0,0 +1,75 @@ |
|||||||
|
package androidx.compose.web.sample.tests |
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable |
||||||
|
import androidx.compose.runtime.mutableStateOf |
||||||
|
import androidx.compose.runtime.remember |
||||||
|
import androidx.compose.runtime.getValue |
||||||
|
import androidx.compose.runtime.setValue |
||||||
|
import org.jetbrains.compose.web.attributes.name |
||||||
|
import org.jetbrains.compose.web.dom.* |
||||||
|
import org.jetbrains.compose.web.events.SyntheticChangeEvent |
||||||
|
import org.jetbrains.compose.web.events.SyntheticInputEvent |
||||||
|
import org.jetbrains.compose.web.sample.tests.TestText |
||||||
|
import org.jetbrains.compose.web.sample.tests.testCase |
||||||
|
import org.w3c.dom.HTMLSelectElement |
||||||
|
import org.w3c.dom.asList |
||||||
|
|
||||||
|
class SelectElementTests { |
||||||
|
|
||||||
|
@Composable |
||||||
|
private fun ChooseAPetSelect( |
||||||
|
canSelectMultiple: Boolean = false, |
||||||
|
onInput: (SyntheticInputEvent<String?, HTMLSelectElement>) -> Unit, |
||||||
|
onChange: (SyntheticChangeEvent<String?, HTMLSelectElement>) -> Unit |
||||||
|
) { |
||||||
|
Label(forId = "pet-select") { |
||||||
|
Text("Choose a pet:") |
||||||
|
} |
||||||
|
|
||||||
|
Select(multiple = canSelectMultiple, attrs = { |
||||||
|
name("pets") |
||||||
|
id("pet-select") |
||||||
|
|
||||||
|
onInput { onInput(it) } |
||||||
|
onChange { onChange(it) } |
||||||
|
|
||||||
|
}) { |
||||||
|
Option(value = "") { Text("--Please choose an option--") } |
||||||
|
Option(value = "dog") { Text("Dog") } |
||||||
|
Option(value = "cat") { Text("Cat") } |
||||||
|
Option(value = "hamster") { Text("Hamster") } |
||||||
|
Option(value = "parrot") { Text("Parrot") } |
||||||
|
Option(value = "spider") { Text("Spider") } |
||||||
|
Option(value = "goldfish") { Text("Goldfish") } |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
val selectDispatchesInputAndChangeAndBeforeInputEvents by testCase { |
||||||
|
var onInputState by remember { mutableStateOf("None") } |
||||||
|
var onChangeState by remember { mutableStateOf("None") } |
||||||
|
|
||||||
|
P { TestText(value = onInputState, id = "txt_oninput") } |
||||||
|
P { TestText(value = onChangeState, id = "txt_onchange") } |
||||||
|
|
||||||
|
ChooseAPetSelect( |
||||||
|
onInput = { onInputState = it.value ?: "" }, |
||||||
|
onChange = { onChangeState = it.value ?: "" } |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
val selectMultipleItems by testCase { |
||||||
|
var selectedItemsText by remember { mutableStateOf("None") } |
||||||
|
|
||||||
|
P { TestText(value = selectedItemsText) } |
||||||
|
|
||||||
|
ChooseAPetSelect( |
||||||
|
canSelectMultiple = true, |
||||||
|
onInput = {}, |
||||||
|
onChange = { |
||||||
|
selectedItemsText = it.target.selectedOptions.asList().joinToString(separator = ", ") { |
||||||
|
it.asDynamic().value |
||||||
|
} |
||||||
|
} |
||||||
|
) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
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.WebDriver |
||||||
|
import org.openqa.selenium.support.ui.Select |
||||||
|
|
||||||
|
class SelectElementTests : BaseIntegrationTests() { |
||||||
|
|
||||||
|
@ResolveDrivers |
||||||
|
fun selectDispatchesInputAndChangeAndBeforeInputEvents(driver: WebDriver) { |
||||||
|
driver.openTestPage("selectDispatchesInputAndChangeAndBeforeInputEvents") |
||||||
|
driver.waitTextToBe(textId = "txt_oninput", value = "None") |
||||||
|
driver.waitTextToBe(textId = "txt_onchange", value = "None") |
||||||
|
|
||||||
|
// Commented code does force onChange, but doesn't force onInput for some reason |
||||||
|
// val select = Select(driver.findElement(By.name("pets"))) |
||||||
|
// select.selectByIndex(0) |
||||||
|
|
||||||
|
// Code below works properly: forces both onInput and onChange |
||||||
|
driver.findElement(By.name("pets")).sendKeys("Dog") |
||||||
|
|
||||||
|
driver.waitTextToBe(textId = "txt_onchange", value = "dog") |
||||||
|
driver.waitTextToBe(textId = "txt_oninput", value = "dog") |
||||||
|
} |
||||||
|
|
||||||
|
@ResolveDrivers |
||||||
|
fun selectMultipleItems(driver: WebDriver) { |
||||||
|
driver.openTestPage("selectMultipleItems") |
||||||
|
driver.waitTextToBe(value = "None") |
||||||
|
|
||||||
|
val select = Select(driver.findElement(By.name("pets"))) |
||||||
|
select.selectByIndex(1) |
||||||
|
|
||||||
|
driver.waitTextToBe(value = "dog") |
||||||
|
|
||||||
|
select.selectByIndex(2) |
||||||
|
driver.waitTextToBe(value = "dog, cat") |
||||||
|
|
||||||
|
select.selectByIndex(3) |
||||||
|
driver.waitTextToBe(value = "dog, cat, hamster") |
||||||
|
|
||||||
|
select.deselectByIndex(2) |
||||||
|
driver.waitTextToBe(value = "dog, hamster") |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue