nunntom / elm-ui-select / Select.Effect

Update the Select by returning Effects instead of Cmds. This module is designed to help testing with elm-program-test, allowing you to simulate the effects produced by the select and simulate input. If you are not doing this kind of testing, you don't need this module.

Type


type alias Effect effect msg =
Internal.Effect.Effect effect msg

The Effect type

Update Effect

update : (Internal.Msg.Msg a -> msg) -> Internal.Msg.Msg a -> Select a -> ( Select a, Effect Basics.Never msg )

Update the Select

type MyEffect
    = SelectEffect (Select.Effect Never Msg)

update : Msg -> Model -> ( Model, MyEffect )
update msg model =
    case msg of
        SelectMsg subMsg ->
            Select.Effect.update SelectMsg subMsg model.select
                |> Tuple.mapFirst (\select -> { model | select = select })
                |> Tuple.mapSecond SelectEffect

performEffect : MyEffect -> Cmd Msg
performEffect effect =
    case effect of
        SelectEffect selectEffect ->
            Select.Effect.perform selectEffect

Update Options


type alias UpdateOption err effect a msg =
Internal.UpdateOptions.UpdateOption err effect a msg

Options for use with updateWith.

updateWith : List (UpdateOption err effect a msg) -> (Internal.Msg.Msg a -> msg) -> Internal.Msg.Msg a -> Select a -> ( Select a, Effect effect msg )

Update with options.

update : Msg -> Model -> ( Model, MyEffect )
update msg model =
    case msg of
        SelectMsg subMsg ->
            Select.Effect.updateWith [ Select.Effect.onSelectedChanged ThingSelected ] SelectMsg subMsg model.select
                |> Tuple.mapFirst (\select -> { model | select = select })
                |> Tuple.mapSecond SelectEffect

        ThingSelected maybeThing ->
            Debug.todo "Do something when the thing is selected/deselected"

request : (String -> (Result err (List a) -> msg) -> effect) -> UpdateOption err effect a msg

Update with an HTTP request to retrieve matching remote results. Note that in order to avoid an elm/http dependency in this package, you will need to provide the request Effect yourself.

Provide an effect (your app's own Effect type) that uses the input value and a msg tagger that can perform an HTTP request. Update will use this Effect when the user types into the input.

When the effect is performed you must use Select.Effect.performWithRequest instead of Select.Effect.perform.

type MyEffect
    = SelectEffect (Select.Effect MyEffect Msg)
    | FetchThings String (Result Http.Error (List Thing) -> Msg)

update : Msg -> Model -> ( Model, MyEffect )
update msg model =
    case msg of
        SelectMsg subMsg ->
            Select.Effect.updateWith [ Select.Effect.request FetchThings ] SelectMsg subMsg model.select
                |> Tuple.mapFirst (\select -> { model | select = select })
                |> Tuple.mapSecond SelectEffect

performEffect : MyEffect -> Cmd Msg
performEffect effect =
    case effect of
        SelectEffect selectEffect ->
            Select.Effect.performWithRequest performEffect selectEffect

        FetchThings query tagger ->
            Http.get
                { url = "https://awesome-thing.api/things?search=" ++ query
                , expect = Http.expectJson tagger (Decode.list thingDecoder)
                }

requestMinInputLength : Basics.Int -> UpdateOption err effect a msg

How many characters does a user need to type before a request is sent? If this is too low you may get an unmanagable number of results! Default is 3 characters.

Select.Effect.updateWith
    [ Select.Effect.request FetchThings
    , Select.Effect.requestMinInputLength 4
    ]
    SelectMsg
    subMsg
    model.select

requestDebounceDelay : Basics.Float -> UpdateOption err effect a msg

Configure debouncing for the request. How long should we wait in milliseconds after the user stops typing to send the request? Default is 300.

Select.Effect.updateWith
    [ Select.Effect.request FetchThings
    , Select.Effect.requestDebounceDelay 500
    ]
    SelectMsg
    subMsg
    model.select

onSelectedChange : (Maybe a -> msg) -> UpdateOption err effect a msg

If provided this msg will be sent whenever the selected item changes.

Select.Effect.updateWith [ Select.Effect.onSelectedChange ThingSelected ] SelectMsg subMsg model.select

onInput : (String -> msg) -> UpdateOption err effect a msg

If provided this msg will be sent whenever the input value changes.

Select.Effect.updateWith [ Select.Effect.onInput InputChanged ] SelectMsg subMsg model.select

onFocus : msg -> UpdateOption err effect a msg

If provided this msg will be sent whenever the input gets focus.

Select.Effect.updateWith [ Select.Effect.onFocus InputFocused ] SelectMsg subMsg model.select

onLoseFocus : msg -> UpdateOption err effect a msg

If provided this msg will be sent whenever the input loses focus.

Select.Effect.updateWith [ Select.Effect.onLoseFocus InputBlurred ] SelectMsg subMsg model.select

onKeyDown : (String -> msg) -> UpdateOption err effect a msg

If provided this will be sent whenever there is a keydown event in the input. The name of the key is provided.

Select.Effect.updateWith [ Select.Effect.onKeyDown InputKeyDown ] SelectMsg subMsg model.select

Send Request

sendRequest : (Internal.Msg.Msg a -> msg) -> (String -> (Result err (List a) -> msg) -> effect) -> Maybe (a -> Basics.Bool) -> Select a -> ( Select a, Effect effect msg )

Send a request to populate the menu items. This is useful for initialising the select with items from an api. Provide a function that takes the current input value and a msg tagger and returns an effect which can be used to perform an HTTP request.

init : ( Model, Effect )
init =
    let
        ( select, effect ) =
            Select.Effect.init "thing-select"
                |> Select.Effect.sendRequest SelectMsg FetchThings Nothing
    in
    ( { select = select }
    , effect
    )

type MyEffect
    = SelectEffect (Select.Effect MyEffect Msg)
    | FetchThings String (Result Http.Error (List Thing) -> Msg)

Optionally provide a function to select one the items when the response returns:

init : ThingId -> ( Model, Effect )
init thingId =
    let
        ( select, effect ) =
            Select.Effect. "thing-select"
                |> Select.Effect.sendRequest SelectMsg fetchThings (Just (\{ id } -> id == thingId))
    in
    ( { select = select }
    , effect
    )

Perform Effect

perform : Effect Basics.Never msg -> Platform.Cmd.Cmd msg

Turn an Effect into a Cmd

performEffect : MyEffect -> Cmd Msg
performEffect effect =
    case effect of
        SelectEffect selectEffect ->
            Select.Effect.perform selectEffect

performWithRequest : (effect -> Platform.Cmd.Cmd msg) -> Effect effect msg -> Platform.Cmd.Cmd msg

Perform the Effect with a request. You need to provide your own perform function to perform the provided request effect.

performEffect : MyEffect -> Cmd Msg
performEffect effect =
    case effect of
        SelectEffect selectEffect ->
            Select.Effect.performWithRequest performEffect selectEffect

        FetchThings query ->
            fetchThings (Select.gotRequestResponse >> SelectMsg) query

Simulating Effects

simulate : { perform : (() -> msg) -> simulatedTask -> simulatedEffect, batch : List simulatedEffect -> simulatedEffect, sleep : Basics.Float -> simulatedTask } -> Effect Basics.Never msg -> simulatedEffect

Simulate the select effects. This is designed to work with elm-program-test, but since this package doesn't have it as a dependency, you need to provide some of the functions to help with the simulation.

simulateEffect : MyEffect -> SimulatedEffect Msg
simulateEffect effect =
    case effect of
        SelectEffect selectEffect ->
            Select.Effect.simulate
                Example.SelectMsg
                { perform = SimulatedTask.perform
                , batch = SimulatedCmd.batch
                , sleep = SimulatedProcess.sleep
                }
                selectEffect

simulateWithRequest : { perform : (() -> msg) -> simulatedTask -> simulatedEffect, batch : List simulatedEffect -> simulatedEffect, sleep : Basics.Float -> simulatedTask } -> (effect -> simulatedEffect) -> Effect effect msg -> simulatedEffect

Simulate the select effects with a request. This is designed to work with elm-program-test, but since this package doesn't have it as a dependency, you need to provide some of the functions to help with the simulation.

simulateEffect : MyEffect -> SimulatedEffect Msg
simulateEffect effect =
    case effect of
        SelectEffect selectEffect ->
            Select.Effect.simulateWithRequest
                Example.SelectMsg
                { perform = SimulatedTask.perform
                , batch = SimulatedCmd.batch
                , sleep = SimulatedProcess.sleep
                }
                simulateEffect
                selectEffect

        FetchThings query ->
            SimulateHttp.get
                { url = "https://awesome-thing.api/things?search=" ++ query
                , expect = SimulateHttp.expectJson tagger (Decode.list thingDecoder)
                }

Simulating Input

simulateClickOption : SimulateInputConfig single selector programTest -> String -> String -> programTest -> programTest

Simulate clicking an option by the text label of the option. This is designed to help simulate input with elm-program-test. Since this package doesn't have elm-test or elm-program-test as dependencies, you need to provide some of the functions from those packages here.

simulateConfig : Select.Effect.SimulateInputConfig (Single msg) Selector (ProgramTest model msg effect)
simulateConfig =
    { simulateDomEvent = ProgramTest.simulateDomEvent
    , find = Query.find
    , attribute = Selector.attribute
    }

selectTest : Test
selectTest =
    Test.test "Typing United and clicking United Kingdom option selects United Kingdom" <|
        \() ->
            ProgramTest.createElement
                { init = App.init
                , update = App.update
                , view = App.view
                }
                |> ProgramTest.withSimulatedEffects simulateEffect
                |> ProgramTest.start ()
                -- Note for testing you need to focus the input before you can type into it
                |> ProgramTest.simulateDomEvent (Query.find [ Selector.id "country-select-input" ]) Test.Html.Event.focus
                |> ProgramTest.fillIn "" "Choose a country" "United"
                |> Select.Effect.simulateClickOption simulateConfig "country-select" "United Kingdom"
                |> ProgramTest.expectViewHas [ Selector.text "You chose United Kingdom" ]


type alias SimulateInputConfig single selector programTest =
{ simulateDomEvent : (single -> single) -> ( String
, Json.Encode.Value ) -> programTest -> programTest
, find : List selector -> single -> single
, attribute : Html.Attribute Basics.Never -> selector 
}

Config type alias used by simulateClickOption

Mapping

map : (msg -> msg2) -> Effect effect msg -> Effect effect msg2

Map Effect from one msg type to another

mapEffect : (effect -> effect2) -> Effect effect msg -> Effect effect2 msg

Map Effect from one effect type to another