diff options
15 files changed, 699 insertions, 1 deletions
@@ -62,7 +62,7 @@ Spinner input component. # Package pl.treksoft.kvision.form.text -Text input components, with password, text area and rich text area support. +Text input components, with password, text area, rich text area and typeahead support. # Package pl.treksoft.kvision.form.time diff --git a/build.gradle b/build.gradle index 32afc552..db61ad2b 100644 --- a/build.gradle +++ b/build.gradle @@ -176,6 +176,7 @@ dokka { 'kvision-modules/kvision-moment/src/main/kotlin', 'kvision-modules/kvision-tabulator/src/main/kotlin', 'kvision-modules/kvision-pace/src/main/kotlin', + 'kvision-modules/kvision-bootstrap-typeahead/src/main/kotlin', 'kvision-modules/kvision-remote/src/main/kotlin', 'kvision-modules/kvision-bootstrap-select-remote/src/main/kotlin', 'kvision-modules/kvision-tabulator-remote/src/main/kotlin', diff --git a/kvision-modules/kvision-bootstrap-typeahead/build.gradle b/kvision-modules/kvision-bootstrap-typeahead/build.gradle new file mode 100644 index 00000000..e9162b54 --- /dev/null +++ b/kvision-modules/kvision-bootstrap-typeahead/build.gradle @@ -0,0 +1,9 @@ +apply from: "../shared.gradle" + +kotlinFrontend { + + npm { + dependency("eam_bootstrap-3-typeahead", "0.0.2") + } + +} diff --git a/kvision-modules/kvision-bootstrap-typeahead/package.json.d/project.info b/kvision-modules/kvision-bootstrap-typeahead/package.json.d/project.info new file mode 100644 index 00000000..a212d31d --- /dev/null +++ b/kvision-modules/kvision-bootstrap-typeahead/package.json.d/project.info @@ -0,0 +1,3 @@ +{ + "description": "KVision Typeahead module" +} diff --git a/kvision-modules/kvision-bootstrap-typeahead/src/main/kotlin/pl/treksoft/kvision/KVManagerTypeahead.kt b/kvision-modules/kvision-bootstrap-typeahead/src/main/kotlin/pl/treksoft/kvision/KVManagerTypeahead.kt new file mode 100644 index 00000000..dd9e5a43 --- /dev/null +++ b/kvision-modules/kvision-bootstrap-typeahead/src/main/kotlin/pl/treksoft/kvision/KVManagerTypeahead.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017-present Robert Jaros + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package pl.treksoft.kvision + +internal val kVManagerTypeaheadInit = KVManagerTypeahead.init() + +/** + * Internal singleton object which initializes and configures KVision typeahead module. + */ +internal object KVManagerTypeahead { + init { + require("eam_bootstrap-3-typeahead/bootstrap3-typeahead.min.js") + } + + internal fun init() {} +} diff --git a/kvision-modules/kvision-bootstrap-typeahead/src/main/kotlin/pl/treksoft/kvision/form/text/TaAjaxOptions.kt b/kvision-modules/kvision-bootstrap-typeahead/src/main/kotlin/pl/treksoft/kvision/form/text/TaAjaxOptions.kt new file mode 100644 index 00000000..ee4d7153 --- /dev/null +++ b/kvision-modules/kvision-bootstrap-typeahead/src/main/kotlin/pl/treksoft/kvision/form/text/TaAjaxOptions.kt @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2017-present Robert Jaros + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package pl.treksoft.kvision.form.text + +import pl.treksoft.jquery.JQueryXHR + +/** + * HTTP protocol type for the AJAX call. + */ +enum class HttpType(internal val type: String) { + GET("GET"), + POST("POST") +} + +/** + * Data type for the AJAX call. + */ +enum class DataType(internal val type: String) { + JSON("json"), + JSONP("jsonp"), + XML("xml"), + TEXT("text"), + SCRIPT("script") +} + +/** + * Data class for Typeahead AJAX options. + * + * @constructor + * @param url the url address + * @param preprocessQuery a function to process query string for sending + * @param preprocessData a function to process received data + * @param beforeSend + * [JQuery ajax.beforeSend](http://api.jquery.com/jquery.ajax/#jQuery-ajax-settings) option + * @param httpType + * [JQuery ajax.type](http://api.jquery.com/jquery.ajax/#jQuery-ajax-settings) option + * @param dataType + * [JQuery ajax.dataType](http://api.jquery.com/jquery.ajax/#jQuery-ajax-settings) option + * @param processData + * [JQuery ajax.processData](http://api.jquery.com/jquery.ajax/#jQuery-ajax-settings) option + */ +data class TaAjaxOptions( + val url: String, + val preprocessQuery: ((String) -> dynamic)? = null, + val preprocessData: ((dynamic) -> dynamic)? = null, + val beforeSend: ((JQueryXHR, dynamic) -> dynamic)? = null, + val httpType: HttpType = HttpType.GET, + val dataType: DataType = DataType.JSON, + val processData: Boolean = true +) diff --git a/kvision-modules/kvision-bootstrap-typeahead/src/main/kotlin/pl/treksoft/kvision/form/text/Typeahead.kt b/kvision-modules/kvision-bootstrap-typeahead/src/main/kotlin/pl/treksoft/kvision/form/text/Typeahead.kt new file mode 100644 index 00000000..d0a73794 --- /dev/null +++ b/kvision-modules/kvision-bootstrap-typeahead/src/main/kotlin/pl/treksoft/kvision/form/text/Typeahead.kt @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2017-present Robert Jaros + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package pl.treksoft.kvision.form.text + +import pl.treksoft.kvision.core.Container + +/** + * Form field typeahead component. + * + * @constructor + * @param options a static list of options + * @param taAjaxOptions AJAX options for remote data source + * @param items the max number of items to display in the dropdown + * @param minLength the minimum character length needed before triggering dropdown + * @param delay a delay between lookups + * @param type text input type (default "text") + * @param value text input value + * @param name the name attribute of the generated HTML input element + * @param label label text bound to the input element + * @param rich determines if [label] can contain HTML code + */ +open class Typeahead( + options: List<String>? = null, taAjaxOptions: TaAjaxOptions? = null, + items: Int? = 8, minLength: Int = 1, delay: Int = 0, + type: TextInputType = TextInputType.TEXT, value: String? = null, name: String? = null, + label: String? = null, rich: Boolean = false +) : AbstractText(label, rich) { + + /** + * A static list of options for a typeahead control + */ + var options + get() = input.options + set(value) { + input.options = value + } + + /** + * AJAX options for remote data source + */ + var ajaxOptions + get() = input.ajaxOptions + set(value) { + input.ajaxOptions = value + } + + /** + * The max number of items to display in the dropdown + */ + var items + get() = input.items + set(value) { + input.items = value + } + + /** + * The minimum character length needed before triggering dropdown + */ + var minLength + get() = input.minLength + set(value) { + input.minLength = value + } + + /** + * Determines if hints should be shown as soon as the input gets focus. + */ + var showHintOnFocus + get() = input.showHintOnFocus + set(value) { + input.showHintOnFocus = value + } + + /** + * Determines if the first suggestion is selected automatically. + */ + var autoSelect + get() = input.autoSelect + set(value) { + input.autoSelect = value + } + + /** + * A delay between lookups. + */ + var delay + get() = input.ajaxOptions + set(value) { + input.ajaxOptions = value + } + + /** + * Determines if the menu is the same size as the input it is attached to. + */ + var fitToElement + get() = input.fitToElement + set(value) { + input.fitToElement = value + } + + /** + * Text input type. + */ + var type + get() = input.type + set(value) { + input.type = value + } + /** + * Determines if autocomplete is enabled for the input element. + */ + var autocomplete + get() = input.autocomplete + set(value) { + input.autocomplete = value + } + + final override val input: TypeaheadInput = + TypeaheadInput(options, taAjaxOptions, items, minLength, delay, type, value).apply { + this.id = idc + this.name = name + } + + init { + @Suppress("LeakingThis") + input.eventTarget = this + this.addInternal(input) + this.addInternal(invalidFeedback) + } +} + +/** + * DSL builder extension function. + * + * It takes the same parameters as the constructor of the built component. + */ +fun Container.typeahead( + options: List<String>? = null, taAjaxOptions: TaAjaxOptions? = null, + items: Int? = 8, minLength: Int = 1, delay: Int = 0, + type: TextInputType = TextInputType.TEXT, value: String? = null, name: String? = null, + label: String? = null, rich: Boolean = false, init: (Typeahead.() -> Unit)? = null +): Typeahead { + val typeahead = Typeahead(options, taAjaxOptions, items, minLength, delay, type, value, name, label, rich).apply { + init?.invoke(this) + } + this.add(typeahead) + return typeahead +} diff --git a/kvision-modules/kvision-bootstrap-typeahead/src/main/kotlin/pl/treksoft/kvision/form/text/TypeaheadInput.kt b/kvision-modules/kvision-bootstrap-typeahead/src/main/kotlin/pl/treksoft/kvision/form/text/TypeaheadInput.kt new file mode 100644 index 00000000..0cbfb1c2 --- /dev/null +++ b/kvision-modules/kvision-bootstrap-typeahead/src/main/kotlin/pl/treksoft/kvision/form/text/TypeaheadInput.kt @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2017-present Robert Jaros + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package pl.treksoft.kvision.form.text + +import com.github.snabbdom.VNode +import pl.treksoft.jquery.JQueryXHR +import pl.treksoft.jquery.jQuery +import pl.treksoft.kvision.core.Container +import pl.treksoft.kvision.utils.obj + +enum class ShowHintOnFocus { + NO, + YES, + ALL +} + +/** + * The basic component for typeahead control. + * + * @constructor + * @param options a static list of options + * @param taAjaxOptions AJAX options for remote data source + * @param items the max number of items to display in the dropdown + * @param minLength the minimum character length needed before triggering dropdown + * @param delay a delay between lookups + * @param type text input type (default "text") + * @param value text input value + * @param classes a set of CSS class names + */ +@Suppress("TooManyFunctions") +open class TypeaheadInput( + options: List<String>? = null, taAjaxOptions: TaAjaxOptions? = null, + items: Int? = 8, minLength: Int = 1, delay: Int = 0, + type: TextInputType = TextInputType.TEXT, value: String? = null, classes: Set<String> = setOf() +) : TextInput(type, value, classes) { + + /** + * A static list of options for a typeahead control + */ + var options by refreshOnUpdate(options) { refreshTypeahead() } + + /** + * AJAX options for remote data source + */ + var ajaxOptions by refreshOnUpdate(taAjaxOptions) { refreshTypeahead() } + + /** + * The max number of items to display in the dropdown + */ + var items by refreshOnUpdate(items) { refreshTypeahead() } + + /** + * The minimum character length needed before triggering dropdown + */ + var minLength by refreshOnUpdate(minLength) { refreshTypeahead() } + + /** + * Determines if hints should be shown as soon as the input gets focus. + */ + var showHintOnFocus by refreshOnUpdate(ShowHintOnFocus.NO) { refreshTypeahead() } + + /** + * Determines if the first suggestion is selected automatically. + */ + var autoSelect by refreshOnUpdate(true) { refreshTypeahead() } + + /** + * A delay between lookups. + */ + var delay by refreshOnUpdate(delay) { refreshTypeahead() } + + /** + * Determines if the menu is the same size as the input it is attached to. + */ + var fitToElement by refreshOnUpdate(false) { refreshTypeahead() } + + init { + autocomplete = false + } + + @Suppress("UnsafeCastFromDynamic") + override fun afterInsert(node: VNode) { + getElementJQueryD()?.typeahead(getSettingsObj()) + } + + override fun afterDestroy() { + getElementJQueryD()?.typeahead("destroy") + } + + @Suppress("UnsafeCastFromDynamic") + private fun getSettingsObj(): dynamic { + val sourceOpt = when { + options != null -> { + options?.toTypedArray()?.asDynamic() + } + ajaxOptions != null -> { query: String, callback: (Array<String>) -> Unit -> + ajaxOptions?.let { ajaxOptions -> + val data = ajaxOptions.preprocessQuery?.invoke(query) ?: obj { + this.query = query + } + jQuery.ajax(ajaxOptions.url, obj { + this.contentType = "application/json" + this.data = data + this.method = ajaxOptions.httpType.type + this.beforeSend = ajaxOptions.beforeSend + this.dataType = ajaxOptions.dataType.type + this.processData = ajaxOptions.processData + this.success = + { data: dynamic, _: Any, _: Any -> + val processedData = ajaxOptions.preprocessData?.invoke(data) ?: data + callback(processedData) + } + this.error = + { xhr: JQueryXHR, _: String, errorText: String -> + val message = if (xhr.responseJSON != null && xhr.responseJSON != undefined) { + JSON.stringify(xhr.responseJSON) + } else if (xhr.responseText != undefined) { + xhr.responseText + } else { + errorText + } + console.log(message) + callback(emptyArray()) + } + }) + } + } + else -> { + emptyArray<String>().asDynamic() + } + } + val showHint = when (showHintOnFocus) { + ShowHintOnFocus.NO -> false + ShowHintOnFocus.YES -> true + ShowHintOnFocus.ALL -> "all" + } + return obj { + this.source = sourceOpt + this.items = items + this.minLength = minLength + this.showHintOnFocus = showHint + this.autoSelect = autoSelect + this.delay = delay + this.fitToElement = fitToElement + } + } + + protected fun refreshTypeahead() { + getElementJQueryD()?.typeahead("destroy") + getElementJQueryD()?.typeahead(getSettingsObj()) + } + +} + +/** + * DSL builder extension function. + * + * It takes the same parameters as the constructor of the built component. + */ +fun Container.typeaheadInput( + options: List<String>? = null, taAjaxOptions: TaAjaxOptions? = null, + items: Int? = 8, minLength: Int = 1, delay: Int = 0, + type: TextInputType = TextInputType.TEXT, value: String? = null, classes: Set<String> = setOf(), + init: (TypeaheadInput.() -> Unit)? = null +): TypeaheadInput { + val typeaheadInput = TypeaheadInput(options, taAjaxOptions, items, minLength, delay, type, value, classes).apply { + init?.invoke( + this + ) + } + this.add(typeaheadInput) + return typeaheadInput +} diff --git a/kvision-modules/kvision-bootstrap-typeahead/src/test/kotlin/test/pl/treksoft/kvision/TestUtil.kt b/kvision-modules/kvision-bootstrap-typeahead/src/test/kotlin/test/pl/treksoft/kvision/TestUtil.kt new file mode 100644 index 00000000..9da4dfb4 --- /dev/null +++ b/kvision-modules/kvision-bootstrap-typeahead/src/test/kotlin/test/pl/treksoft/kvision/TestUtil.kt @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2017-present Robert Jaros + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package test.pl.treksoft.kvision + +import org.w3c.dom.Element +import pl.treksoft.jquery.jQuery +import pl.treksoft.kvision.core.Widget +import pl.treksoft.kvision.panel.Root +import kotlin.browser.document +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +interface TestSpec { + fun beforeTest() + + fun afterTest() + + fun run(code: () -> Unit) { + beforeTest() + code() + afterTest() + } +} + +interface SimpleSpec : TestSpec { + + override fun beforeTest() { + } + + override fun afterTest() { + } + +} + +interface DomSpec : TestSpec { + + override fun beforeTest() { + val fixture = "<div style=\"display: none\" id=\"pretest\">" + + "<div id=\"test\"></div></div>" + document.body?.insertAdjacentHTML("afterbegin", fixture) + } + + override fun afterTest() { + val div = document.getElementById("pretest") + div?.let { jQuery(it).remove() } + jQuery(".modal-backdrop").remove() + Root.disposeAllRoots() + } + + fun assertEqualsHtml(expected: String?, actual: String?, message: String?) { + if (expected != null && actual != null) { + val exp = jQuery(expected) + val act = jQuery(actual) + val result = exp[0]?.isEqualNode(act[0]) + if (result == true) { + assertTrue(result == true, message) + } else { + assertEquals(expected, actual, message) + } + } else { + assertEquals(expected, actual, message) + } + } +} + +interface WSpec : DomSpec { + + fun runW(code: (widget: Widget, element: Element?) -> Unit) { + run { + val root = Root("test", fixed = true) + val widget = Widget() + widget.id = "test_id" + root.add(widget) + val element = document.getElementById("test_id") + code(widget, element) + } + } + +} + +external fun require(name: String): dynamic diff --git a/kvision-modules/kvision-bootstrap-typeahead/src/test/kotlin/test/pl/treksoft/kvision/form/text/TypeaheadInputSpec.kt b/kvision-modules/kvision-bootstrap-typeahead/src/test/kotlin/test/pl/treksoft/kvision/form/text/TypeaheadInputSpec.kt new file mode 100644 index 00000000..5690747a --- /dev/null +++ b/kvision-modules/kvision-bootstrap-typeahead/src/test/kotlin/test/pl/treksoft/kvision/form/text/TypeaheadInputSpec.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2017-present Robert Jaros + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package test.pl.treksoft.kvision.form.text + +import pl.treksoft.kvision.form.text.TypeaheadInput +import pl.treksoft.kvision.panel.Root +import test.pl.treksoft.kvision.DomSpec +import kotlin.browser.document +import kotlin.test.Test +import kotlin.test.assertEquals + +class TypeaheadInputSpec : DomSpec { + + @Test + fun render() { + run { + val root = Root("test", fixed = true) + val si = TypeaheadInput(listOf("test1", "test2"), value = "test").apply { + placeholder = "place" + id = "idti" + } + root.add(si) + val value = si.getElementJQuery()?.`val`() + assertEquals("test", value, "Should render typeahead input with correct value") + val element = document.getElementById("test") + assertEqualsHtml( + "<input class=\"form-control\" id=\"idti\" placeholder=\"place\" type=\"text\" value=\"test\" autocomplete=\"off\">", + element?.innerHTML, + "Should render correct input control" + ) + } + } +} diff --git a/kvision-modules/kvision-bootstrap-typeahead/src/test/kotlin/test/pl/treksoft/kvision/form/text/TypeaheadSpec.kt b/kvision-modules/kvision-bootstrap-typeahead/src/test/kotlin/test/pl/treksoft/kvision/form/text/TypeaheadSpec.kt new file mode 100644 index 00000000..2fc9cad5 --- /dev/null +++ b/kvision-modules/kvision-bootstrap-typeahead/src/test/kotlin/test/pl/treksoft/kvision/form/text/TypeaheadSpec.kt @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2017-present Robert Jaros + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package test.pl.treksoft.kvision.form.text + +import pl.treksoft.kvision.form.text.Typeahead +import pl.treksoft.kvision.panel.Root +import test.pl.treksoft.kvision.DomSpec +import kotlin.browser.document +import kotlin.test.Test + +class TypeaheadSpec : DomSpec { + + @Test + fun render() { + run { + val root = Root("test", fixed = true) + val ti = Typeahead(listOf("test1", "test2"), value = "test", label = "Label").apply { + placeholder = "place" + name = "name" + disabled = true + } + root.add(ti) + val element = document.getElementById("test") + val id = ti.input.id + assertEqualsHtml( + "<div class=\"form-group\"><label class=\"control-label\" for=\"$id\">Label</label><input class=\"form-control\" id=\"$id\" placeholder=\"place\" name=\"name\" disabled=\"disabled\" type=\"text\" value=\"test\" autocomplete=\"off\"></div>", + element?.innerHTML, + "Should render correct typeahead input form control" + ) + ti.validatorError = "Validation Error" + assertEqualsHtml( + "<div class=\"form-group text-danger\"><label class=\"control-label\" for=\"$id\">Label</label><input class=\"form-control is-invalid\" id=\"$id\" placeholder=\"place\" name=\"name\" disabled=\"disabled\" type=\"text\" value=\"test\" autocomplete=\"off\"><div class=\"invalid-feedback\">Validation Error</div></div>", + element?.innerHTML, + "Should render correct typeahead input form control with validation error" + ) + } + } +} diff --git a/kvision-modules/kvision-bootstrap-typeahead/webpack.config.d/css.js b/kvision-modules/kvision-bootstrap-typeahead/webpack.config.d/css.js new file mode 100644 index 00000000..5d710d35 --- /dev/null +++ b/kvision-modules/kvision-bootstrap-typeahead/webpack.config.d/css.js @@ -0,0 +1,2 @@ +config.module.rules.push({ test: /\.css$/, loader: "style-loader!css-loader" }); + diff --git a/kvision-modules/kvision-bootstrap-typeahead/webpack.config.d/jquery.js b/kvision-modules/kvision-bootstrap-typeahead/webpack.config.d/jquery.js new file mode 100644 index 00000000..bf5a1a20 --- /dev/null +++ b/kvision-modules/kvision-bootstrap-typeahead/webpack.config.d/jquery.js @@ -0,0 +1,5 @@ +config.plugins.push(new webpack.ProvidePlugin({ + $: "jquery", + jQuery: "jquery", + "window.jQuery": "jquery" +})); diff --git a/settings.gradle b/settings.gradle index 2e1586e4..8b3b6cc0 100644 --- a/settings.gradle +++ b/settings.gradle @@ -12,6 +12,7 @@ include 'kvision-modules:kvision-base', 'kvision-modules:kvision-bootstrap-select', 'kvision-modules:kvision-bootstrap-select-remote', 'kvision-modules:kvision-bootstrap-dialog', + 'kvision-modules:kvision-bootstrap-typeahead', 'kvision-modules:kvision-richtext', 'kvision-modules:kvision-handlebars', 'kvision-modules:kvision-i18n', diff --git a/src/main/resources/css/style.css b/src/main/resources/css/style.css index 5fa4d72c..cae50162 100644 --- a/src/main/resources/css/style.css +++ b/src/main/resources/css/style.css @@ -438,3 +438,8 @@ form[class~="form-horizontal"] fieldset.kv_fieldset { form[class~="form-horizontal"] div.form-group { align-items: center; } + +ul.typeahead > li.active > a { + text-decoration: none; + background-color: #f8f9fa; +} |