Add option to disable opening menu on focus

This commit is contained in:
Tom Nunn 2023-04-12 20:19:50 +01:00
parent 3288004a71
commit 4aaa691f29
9 changed files with 100 additions and 63 deletions

View File

@ -1,44 +1,40 @@
{
"type": "application",
"source-directories": [
"./src",
"../src"
],
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"elm/browser": "1.0.1",
"elm/core": "1.0.2",
"elm/html": "1.0.0",
"elm/http": "2.0.0",
"elm/json": "1.1.3",
"mdgriffith/elm-ui": "1.1.5",
"rtfeldman/elm-css": "18.0.0",
"supermario/elm-countries": "1.1.1"
},
"indirect": {
"elm/bytes": "1.0.8",
"elm/file": "1.0.5",
"elm/time": "1.0.0",
"elm/url": "1.0.0",
"elm/virtual-dom": "1.0.2",
"robinheghan/murmur3": "1.0.0",
"rtfeldman/elm-hex": "1.0.0"
}
"type": "application",
"source-directories": ["./src", "../src"],
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"elm/browser": "1.0.2",
"elm/core": "1.0.5",
"elm/html": "1.0.0",
"elm/http": "2.0.0",
"elm/json": "1.1.3",
"mdgriffith/elm-ui": "1.1.8",
"rtfeldman/elm-css": "18.0.0",
"supermario/elm-countries": "1.1.1"
},
"test-dependencies": {
"direct": {
"avh4/elm-program-test": "3.6.3",
"elm-explorations/test": "1.2.2"
},
"indirect": {
"avh4/elm-fifo": "1.0.4",
"elm/parser": "1.1.0",
"elm/random": "1.0.0",
"elm-community/list-extra": "8.6.0",
"hecrj/html-parser": "2.4.0",
"mgold/elm-nonempty-list": "4.2.0",
"rtfeldman/elm-iso8601-date-strings": "1.1.4"
}
"indirect": {
"elm/bytes": "1.0.8",
"elm/file": "1.0.5",
"elm/time": "1.0.0",
"elm/url": "1.0.0",
"elm/virtual-dom": "1.0.3",
"robinheghan/murmur3": "1.0.0",
"rtfeldman/elm-hex": "1.0.0"
}
},
"test-dependencies": {
"direct": {
"avh4/elm-program-test": "4.0.0",
"elm-explorations/test": "2.0.0"
},
"indirect": {
"avh4/elm-fifo": "1.0.4",
"elm/parser": "1.1.0",
"elm/random": "1.0.0",
"elm-community/list-extra": "8.7.0",
"hecrj/html-parser": "2.4.0",
"mgold/elm-nonempty-list": "4.2.0"
}
}
}

View File

@ -1,15 +1,13 @@
module ElmCssExampleTest exposing (exampleProgramTest)
import Countries exposing (Country)
import Element
import Element.Input as Input
import ElmCssEffectExample as App
import Expect
import Html
import Html.Styled
import ProgramTest exposing (ProgramTest, SimulatedEffect)
import Select exposing (Select)
import Select.Effect
import Select.ElmCss as Select exposing (Select)
import SimulateInput
import SimulatedEffect.Cmd as SimulatedCmd
import SimulatedEffect.Process as SimulatedProcess
@ -100,6 +98,16 @@ exampleProgramTest =
|> Select.Effect.simulateClickOption simulateInputConfig "country-select" "🇬🇧 United Kingdom of Great Britain and Northern Ireland"
|> ProgramTest.simulateDomEvent (Query.find [ Selector.id (Select.toInputElementId countrySelect) ]) Test.Html.Event.focus
|> ProgramTest.expectViewHas [ Selector.text "🇦🇩 Andorra" ]
, Test.test "Setting open on focus to false does not open the menu when the input is focused" <|
\() ->
programTestWith (Select.withOpenMenuOnFocus False)
|> focusInput
|> ProgramTest.expectModel (.countrySelect >> Select.isMenuOpen >> Expect.equal False)
, Test.test "Setting open on focus to true does open the menu when the input is focused" <|
\() ->
programTestWith (Select.withOpenMenuOnFocus True)
|> focusInput
|> ProgramTest.expectModel (.countrySelect >> Select.isMenuOpen >> Expect.equal True)
]
@ -121,17 +129,19 @@ programTestWith f =
, update = App.update
, view =
\m ->
Element.layout [] <|
(Select.view
|> f
|> Select.toElement []
{ select = m.countrySelect
, onChange = App.CountrySelectMsg
, label = Input.labelAbove [] (Element.text "Choose a country")
, placeholder = Just (Input.placeholder [] (Element.text "Type to search"))
, itemToString = \c -> c.flag ++ " " ++ c.name
}
)
Html.div []
[ Html.label []
[ Html.text "Choose a country"
, Select.view
|> f
|> Select.toStyled []
{ select = m.countrySelect
, onChange = App.CountrySelectMsg
, itemToString = \c -> c.flag ++ " " ++ c.name
}
|> Html.Styled.toUnstyled
]
]
}
|> ProgramTest.withSimulatedEffects simulateEffect
|> ProgramTest.start ()

View File

@ -120,6 +120,16 @@ exampleProgramTest =
]
>> Query.has [ Selector.attribute (Html.Attributes.value "🇦🇶 Antarctica") ]
)
, Test.test "Setting open on focus to false does not open the menu when the input is focused" <|
\() ->
programTestWith (Select.withOpenMenuOnFocus False)
|> focusInput
|> ProgramTest.expectModel (.countrySelect >> Select.isMenuOpen >> Expect.equal False)
, Test.test "Setting open on focus to true does open the menu when the input is focused" <|
\() ->
programTestWith (Select.withOpenMenuOnFocus True)
|> focusInput
|> ProgramTest.expectModel (.countrySelect >> Select.isMenuOpen >> Expect.equal True)
]

View File

@ -7,7 +7,7 @@ import Internal.Option exposing (Option)
type Msg a
= InputChanged String (List (Option a))
| OptionClicked (Option a)
| InputFocused String (Maybe ( List a, List (Option a) ))
| InputFocused Bool String (Maybe ( List a, List (Option a) ))
| GotNewFilteredOptions ( List a, List (Option a) )
| InputClicked
| InputLostFocus

View File

@ -74,7 +74,7 @@ update_ { request, requestMinInputLength, debounceRequest, onFocus, onLoseFocus,
, Effect.none
)
InputFocused inputValue maybeOptions ->
InputFocused openMenu inputValue maybeOptions ->
(case maybeOptions of
Just ( items, options ) ->
Model.setItems items model
@ -84,7 +84,12 @@ update_ { request, requestMinInputLength, debounceRequest, onFocus, onLoseFocus,
model
)
|> Model.setInputValue inputValue
|> onFocusMenu tagger (request /= Nothing)
|> (if openMenu then
onFocusMenu tagger (request /= Nothing)
else
\m -> ( Model.setFocused True m, Effect.none )
)
|> withEffect (\_ -> Effect.emitJust onFocus)
InputClicked ->

View File

@ -46,10 +46,10 @@ onFocus : (Msg a -> msg) -> (a -> String) -> Model a -> ViewConfigInternal a att
onFocus onChange itemToString model viewConfig filteredOptions =
Html.Events.on "focus" <|
if Model.requiresNewFilteredOptions model then
Decode.lazy (\_ -> Decode.succeed (onChange <| InputFocused (Model.toInputText itemToString model) (Just <| optionsUpdate itemToString model viewConfig filteredOptions)))
Decode.lazy (\_ -> Decode.succeed (onChange <| InputFocused viewConfig.openOnFocus (Model.toInputText itemToString model) (Just <| optionsUpdate itemToString model viewConfig filteredOptions)))
else
InputFocused (Model.toInputText itemToString model) Nothing
InputFocused viewConfig.openOnFocus (Model.toInputText itemToString model) Nothing
|> onChange
|> Decode.succeed

View File

@ -22,6 +22,7 @@ type alias ViewConfigInternal a attribute view =
, selectExactMatchOnBlur : Bool
, selectOnTab : Bool
, minInputLength : Maybe Int
, openOnFocus : Bool
}
@ -40,6 +41,7 @@ init =
, selectExactMatchOnBlur = False
, selectOnTab = True
, minInputLength = Nothing
, openOnFocus = True
}

View File

@ -6,7 +6,7 @@ module Select exposing
, isMenuOpen, isLoading, isRequestFailed, isFocused
, Msg, update, updateWith, sendRequest
, UpdateOption, request, requestMinInputLength, requestDebounceDelay, onSelectedChange, onInput, onFocus, onLoseFocus
, ViewConfig, view, withMenuAttributes, MenuPlacement(..), withMenuMaxHeight, withMenuMaxWidth, withNoMatchElement, withOptionElement, defaultOptionElement, OptionState, withClearButton, ClearButton, clearButton, withFilter, withMenuAlwaysAbove, withMenuAlwaysBelow, withMenuPlacementAuto, withMenuPositionFixed, withClearInputValueOnBlur, withSelectExactMatchOnBlur, withSelectOnTab, withMinInputLength
, ViewConfig, view, withMenuAttributes, MenuPlacement(..), withMenuMaxHeight, withMenuMaxWidth, withNoMatchElement, withOptionElement, defaultOptionElement, OptionState, withClearButton, ClearButton, clearButton, withFilter, withMenuAlwaysAbove, withMenuAlwaysBelow, withMenuPlacementAuto, withMenuPositionFixed, withClearInputValueOnBlur, withSelectExactMatchOnBlur, withSelectOnTab, withMinInputLength, withOpenMenuOnFocus
, toElement
, Effect
)
@ -51,7 +51,7 @@ module Select exposing
# Configure View
@docs ViewConfig, view, withMenuAttributes, MenuPlacement, withMenuMaxHeight, withMenuMaxWidth, withNoMatchElement, withOptionElement, defaultOptionElement, OptionState, withClearButton, ClearButton, clearButton, withFilter, withMenuAlwaysAbove, withMenuAlwaysBelow, withMenuPlacementAuto, withMenuPositionFixed, withClearInputValueOnBlur, withSelectExactMatchOnBlur, withSelectOnTab, withMinInputLength
@docs ViewConfig, view, withMenuAttributes, MenuPlacement, withMenuMaxHeight, withMenuMaxWidth, withNoMatchElement, withOptionElement, defaultOptionElement, OptionState, withClearButton, ClearButton, clearButton, withFilter, withMenuAlwaysAbove, withMenuAlwaysBelow, withMenuPlacementAuto, withMenuPositionFixed, withClearInputValueOnBlur, withSelectExactMatchOnBlur, withSelectOnTab, withMinInputLength, withOpenMenuOnFocus
# Element
@ -637,6 +637,13 @@ withMinInputLength v (ViewConfig config) =
ViewConfig { config | minInputLength = v }
{-| Should the menu be opened when the input gets focus?
-}
withOpenMenuOnFocus : Bool -> ViewConfig a msg -> ViewConfig a msg
withOpenMenuOnFocus v (ViewConfig config) =
ViewConfig { config | openOnFocus = v }
{-| Turn the ViewConfig into an Element.
-}
toElement :

View File

@ -6,7 +6,7 @@ module Select.ElmCss exposing
, isMenuOpen, isLoading, isRequestFailed, isFocused
, Msg, update, updateWith, sendRequest
, UpdateOption, request, requestMinInputLength, requestDebounceDelay, onSelectedChange, onInput, onFocus, onLoseFocus
, ViewConfig, view, withMenuAttributes, MenuPlacement(..), withMenuMaxHeight, withMenuMaxWidth, withNoMatchElement, withOptionElement, defaultOptionElement, OptionState, withClearButton, ClearButton, clearButton, withFilter, withMenuAlwaysAbove, withMenuAlwaysBelow, withMenuPlacementAuto, withMenuPositionFixed, withClearInputValueOnBlur, withSelectExactMatchOnBlur, withSelectOnTab, withMinInputLength
, ViewConfig, view, withMenuAttributes, MenuPlacement(..), withMenuMaxHeight, withMenuMaxWidth, withNoMatchElement, withOptionElement, defaultOptionElement, OptionState, withClearButton, ClearButton, clearButton, withFilter, withMenuAlwaysAbove, withMenuAlwaysBelow, withMenuPlacementAuto, withMenuPositionFixed, withClearInputValueOnBlur, withSelectExactMatchOnBlur, withSelectOnTab, withMinInputLength, withOpenMenuOnFocus
, toStyled
, Effect
)
@ -51,7 +51,7 @@ module Select.ElmCss exposing
# Configure View
@docs ViewConfig, view, withMenuAttributes, MenuPlacement, withMenuMaxHeight, withMenuMaxWidth, withNoMatchElement, withOptionElement, defaultOptionElement, OptionState, withClearButton, ClearButton, clearButton, withFilter, withMenuAlwaysAbove, withMenuAlwaysBelow, withMenuPlacementAuto, withMenuPositionFixed, withClearInputValueOnBlur, withSelectExactMatchOnBlur, withSelectOnTab, withMinInputLength
@docs ViewConfig, view, withMenuAttributes, MenuPlacement, withMenuMaxHeight, withMenuMaxWidth, withNoMatchElement, withOptionElement, defaultOptionElement, OptionState, withClearButton, ClearButton, clearButton, withFilter, withMenuAlwaysAbove, withMenuAlwaysBelow, withMenuPlacementAuto, withMenuPositionFixed, withClearInputValueOnBlur, withSelectExactMatchOnBlur, withSelectOnTab, withMinInputLength, withOpenMenuOnFocus
# Element
@ -637,6 +637,13 @@ withMinInputLength v (ViewConfig config) =
ViewConfig { config | minInputLength = v }
{-| Should the menu be opened when the input gets focus?
-}
withOpenMenuOnFocus : Bool -> ViewConfig a msg -> ViewConfig a msg
withOpenMenuOnFocus v (ViewConfig config) =
ViewConfig { config | openOnFocus = v }
{-| Turn the ViewConfig into an Element.
-}
toStyled :