diff --git a/CHANGELOG.md b/CHANGELOG.md index a05f03e..8b76ceb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +# 4.0.1 (not published yet) + +### Enhancements + +- When `requestDebounceDelay` is set to 0, a request is sent immediately only when input length reaches `requestMinInputLength`, but not on further typing beyond the minimum. + # 4.0.0 ### Enhancements diff --git a/examples/src/RequestExample.elm b/examples/src/RequestExample.elm index 3cf6bd8..6d29caa 100644 --- a/examples/src/RequestExample.elm +++ b/examples/src/RequestExample.elm @@ -64,7 +64,10 @@ update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case msg of SelectMsg subMsg -> - Select.updateWith [ Select.request fetchCocktails ] SelectMsg subMsg model.select + Select.updateWith [ Select.request fetchCocktails ] + SelectMsg + subMsg + model.select |> Tuple.mapFirst (\select -> { model | select = select }) diff --git a/examples/tests/RequestTest.elm b/examples/tests/RequestTest.elm index dee4c9e..127a52b 100644 --- a/examples/tests/RequestTest.elm +++ b/examples/tests/RequestTest.elm @@ -1,6 +1,7 @@ module RequestTest exposing (exampleProgramTest) import EffectRequestExample as App exposing (Cocktail) +import Expect import Json.Decode as Decode import Json.Encode as Encode import ProgramTest exposing (ProgramTest, SimulatedEffect) @@ -50,6 +51,38 @@ exampleProgramTest = "https://thecocktaildb.com/api/json/v1/1/search.php?s=Chocolate+Drink" cocktailsResponse |> ProgramTest.expectViewHas [ Selector.text "Melt the bar in a small amount of boiling water. Add milk." ] + , Test.test "Setting requestDebounceDelay to 0 sends a request at exactly requestMinInputLength and no more on further typing" <| + \() -> + ProgramTest.createElement + { init = App.init + , update = + \msg model -> + case msg of + App.SelectMsg subMsg -> + Select.Effect.updateWith + [ Select.Effect.request App.FetchCocktails + , Select.Effect.requestDebounceDelay 0 + , Select.Effect.requestMinInputLength 3 + ] + App.SelectMsg + subMsg + model.select + |> Tuple.mapBoth (\select -> { model | select = select }) App.SelectEffect + , view = App.view + } + |> ProgramTest.withSimulatedEffects simulateEffect + |> ProgramTest.start (Encode.object []) + |> focusInput + |> ProgramTest.fillIn "" "Find a cocktail" "Cho" + |> ProgramTest.advanceTime 0 + |> ProgramTest.ensureHttpRequestWasMade "GET" "https://thecocktaildb.com/api/json/v1/1/search.php?s=Cho" + |> ProgramTest.simulateHttpOk "GET" "https://thecocktaildb.com/api/json/v1/1/search.php?s=Cho" cocktailsResponse + |> ProgramTest.fillIn "" "Find a cocktail" "Choc" + |> ProgramTest.advanceTime 500 + |> ProgramTest.ensureHttpRequests "GET" "https://thecocktaildb.com/api/json/v1/1/search.php?s=Choc" (Expect.equal []) + |> ProgramTest.fillIn "" "Find a cocktail" "Chocolate" + |> ProgramTest.advanceTime 500 + |> ProgramTest.expectHttpRequests "GET" "https://thecocktaildb.com/api/json/v1/1/search.php?s=Chocolate" (Expect.equal []) ] diff --git a/src/Internal/Update.elm b/src/Internal/Update.elm index b8ca5ba..61e6d8a 100644 --- a/src/Internal/Update.elm +++ b/src/Internal/Update.elm @@ -56,7 +56,19 @@ update_ { request, requestMinInputLength, debounceRequest, onFocus, onLoseFocus, , Effect.batch [ case request of Just _ -> - if String.length val >= requestMinInputLength then + if debounceRequest == 0 then + if + String.length val + == requestMinInputLength + && String.length val + > String.length (Model.toInputValue model) + then + Effect.Debounce (InputDebounceReturned >> tagger) 0 val + + else + Effect.none + + else if String.length val >= requestMinInputLength then Effect.Debounce (InputDebounceReturned >> tagger) debounceRequest val else diff --git a/src/Select.elm b/src/Select.elm index 91faf0d..1c3b441 100644 --- a/src/Select.elm +++ b/src/Select.elm @@ -307,11 +307,17 @@ request effect = UpdateOptions.Request effect -{-| Configure debouncing for the request. How long should we wait in milliseconds after the user stops typing to send the request? Default is 300. +{-| Configure debouncing for the request. +How long should we wait in milliseconds after the user stops typing to send the request? Default is 300. + +If set to 0, a request will be immediately whenever the input length reaches requestMinInputLength, +no further requests will be sent on typing beyond that length and beyond that filtering will be done client side. +This can make the input feel more responsive (options appear sooner), +but may result in a larger result payload requiring more filtering on the client side. Select.updateWith [ Select.request fetchThings - , Select.requestDebounceDelay 500 + , Select.requestDebounceDelay 200 ] SelectMsg subMsg diff --git a/src/Select/Effect.elm b/src/Select/Effect.elm index ca11cfe..e005a25 100644 --- a/src/Select/Effect.elm +++ b/src/Select/Effect.elm @@ -164,11 +164,17 @@ request effect = UpdateOptions.Request effect -{-| Configure debouncing for the request. How long should we wait in milliseconds after the user stops typing to send the request? Default is 300. +{-| Configure debouncing for the request. +How long should we wait in milliseconds after the user stops typing to send the request? Default is 300. + +If set to 0, a request will be immediately whenever the input length reaches requestMinInputLength, +no further requests will be sent on typing beyond that length and beyond that filtering will be done client side. +This can make the input feel more responsive (options appear sooner), +but may result in a larger result payload requiring more filtering on the client side. Select.Effect.updateWith [ Select.Effect.request FetchThings - , Select.Effect.requestDebounceDelay 500 + , Select.Effect.requestDebounceDelay 200 ] SelectMsg subMsg