JetBrains / kotlin-wrappers

Kotlin wrappers for popular JavaScript libraries
Apache License 2.0
1.34k stars 164 forks source link

import external react component #115

Closed steelxt closed 5 years ago

steelxt commented 5 years ago

Hello, I cannot find an easy way to import this toast library https://www.npmjs.com/package/react-toastify to bind to kotlin class, can you point me in to the right direction.

thanks in advance

lawikayoub commented 5 years ago

I have used this library too but I didn't need/implement all of its features (I only make use of the default success and error toast messages), but this should still point you in the right direction. I assume you are using the kotlin-frontend plugin and have already added the react-toastify library as a dependency.

Create 2 files, one will contain the external declarations and the other will contain the RBuilder extension functions which allow you to add react components from the library as children. I call these two files Toast and ToastDSL respectively.

// Toast.kt
@file:JsModule("react-toastify")

package nl.lawik.prototype.react.components.external.toast

import react.*

@JsName("ToastContainer")
external class ToastContainerComponent: Component<RProps, RState> {
    override fun render(): ReactElement?
}

@JsName("toast")
external val toast: dynamic

import react.RBuilder

fun RBuilder.toastContainer() = child(ToastContainerComponent::class) {}

Here we create a new RBuilder function for the ToastContainerComponent class component which we defined in Toast.kt, like we normally would.

Now this allows you to add the ToastContainerComponent to your application (for example in the root as is recommended in the react-toastify docs)

render(rootDiv) { div { h1 { +"Hello World!" } toastContainer() } }

And you can create toast messages anywhere from your application by using the toast value defined in Toast.kt:

toast.success("success toast") toast.error("error toast") toast.warn("warn toast") toast.info("info toast")


These all work because you have defined toast as a dynamic value.

This should allow you to make basic use of the react-toastify library. 

If you want to apply the configurations that react-toastify offers you will also have to write external declarations for the library's props and toast function's options (external interfaces). I recommend you have a look at the [react-router wrapper](https://github.com/JetBrains/kotlin-wrappers/tree/master/kotlin-react-router-dom/src/main/kotlin/react/router/dom) for a more detailed example as this also an "external react component".
steelxt commented 5 years ago

Thanks @lawik123 that was very helpful, Before I generated Interface bindings using ts2kt tool and now with your help I'm able to call toast, and because I'm using interfaces created by ts2kt I'm able to call like this:

` toast.error("error message", object:ToastOptions{}.apply {

                hideProgressBar=true
                autoClose=2000
            })`

again, thanks a lot.

PD how did you include ReactToastify.css file?

lawikayoub commented 5 years ago

Regarding the css.

Add the following dev dependencies

devDependency "css-loader", "^0.28.11"
devDependency "node-sass", "^4.8.3"
devDependency "sass-loader", "^6.0.7"
devDependency "style-loader", "^0.20.3"

In the root of your project create a new folder named webpack.config.d. In this folder create a new JavaScript file (name it sass-loader or something like that), add the following code to this file (this code will be appended to the end of the webpack.config.js file which the kotlin-frontend plugin generates)

config.module.rules.push({
    test: /\.scss$/,
    use: [
        "style-loader", // creates style nodes from JS strings
        "css-loader", // translates CSS into CommonJS
        "sass-loader" // compiles Sass to CSS, using Node Sass by default
    ]
});

Finally, require react-toastify's scss file in your main function. You can do this by using kotlinext.js.require function: require("react-toastify/scss/main.scss") or by using the js function: js("require('react-toastify/scss/main.scss')")