diff --git a/elm.json b/elm.json index ba0ed1b3..0db934a6 100644 --- a/elm.json +++ b/elm.json @@ -81,6 +81,7 @@ "Nri.Ui.TextArea.V4", "Nri.Ui.TextInput.V3", "Nri.Ui.TextInput.V4", + "Nri.Ui.TextInput.V5", "Nri.Ui" ], "elm-version": "0.19.0 <= v < 0.20.0", diff --git a/src/Nri/Ui/TextInput/V5.elm b/src/Nri/Ui/TextInput/V5.elm new file mode 100644 index 00000000..08cafb9f --- /dev/null +++ b/src/Nri/Ui/TextInput/V5.elm @@ -0,0 +1,170 @@ +module Nri.Ui.TextInput.V5 exposing + ( Model + , view, writing + , generateId + , number, text, password + ) + +{-| + + +# Changes from V4 + + - adds a password input + +@docs Model +@docs view, writing +@docs generateId + + +## Input types + +@docs number, text, password + +-} + +import Accessibility.Styled.Style as Accessibility +import Css exposing (batch, center, position, px, relative, textAlign) +import Css.Global +import Html.Styled as Html exposing (..) +import Html.Styled.Attributes as Attributes exposing (..) +import Html.Styled.Events as Events exposing (onInput) +import Nri.Ui.Html.Attributes.V2 as Extra +import Nri.Ui.InputStyles.V2 as InputStyles exposing (Theme) +import Nri.Ui.Util exposing (dashify) + + +{-| -} +type alias Model value msg = + { label : String + , isInError : Bool + , onInput : value -> msg + , onBlur : Maybe msg + , placeholder : String + , value : value + , autofocus : Bool + , showLabel : Bool + , type_ : InputType value + } + + +{-| -} +type InputType value + = InputType + { toString : value -> String + , fromString : String -> value + , fieldType : String + } + + +{-| An input that allows text entry +-} +text : InputType String +text = + InputType + { toString = identity + , fromString = identity + , fieldType = "text" + } + + +{-| An input that allows number entry +-} +number : InputType (Maybe Int) +number = + InputType + { toString = Maybe.map String.fromInt >> Maybe.withDefault "" + , fromString = String.toInt + , fieldType = "number" + } + + +{-| An input that allows password entry +-} +password : InputType String +password = + InputType + { toString = identity + , fromString = identity + , fieldType = "password" + } + + +{-| -} +view : Model value msg -> Html msg +view model = + view_ InputStyles.Standard model + + +{-| -} +writing : Model value msg -> Html msg +writing model = + view_ InputStyles.Writing model + + +view_ : Theme -> Model value msg -> Html msg +view_ theme model = + let + idValue = + generateId model.label + + (InputType inputType) = + model.type_ + in + div + [ Attributes.css [ position relative ] + ] + [ input + [ Attributes.id idValue + , css + [ InputStyles.input theme model.isInError + , if theme == InputStyles.Writing then + Css.Global.withClass "override-sass-styles" + [ textAlign center + , Css.height Css.auto + ] + + else + Css.Global.withClass "override-sass-styles" + [ Css.height (px 45) + ] + ] + , placeholder model.placeholder + , value (inputType.toString model.value) + , onInput (inputType.fromString >> model.onInput) + , Maybe.withDefault Extra.none (Maybe.map Events.onBlur model.onBlur) + , autofocus model.autofocus + , type_ inputType.fieldType + , class "override-sass-styles" + , Attributes.attribute "aria-invalid" <| + if model.isInError then + "true" + + else + "false" + ] + [] + , if model.showLabel then + Html.label + [ for idValue + , css [ InputStyles.label theme model.isInError ] + ] + [ Html.text model.label ] + + else + Html.label + ([ for idValue + , css [ InputStyles.label theme model.isInError ] + ] + ++ Accessibility.invisible + ) + [ Html.text model.label ] + ] + + +{-| Gives you the DOM element id that will be used by a `TextInput.view` with the given label. +This is for use when you need the DOM element id for use in javascript (such as trigger an event to focus a particular text input) +-} +generateId : String -> String +generateId labelText = + "Nri-Ui-TextInput-" ++ dashify labelText diff --git a/styleguide-app/Examples/TextInput.elm b/styleguide-app/Examples/TextInput.elm index 1bc9b7a4..1af1cfbb 100644 --- a/styleguide-app/Examples/TextInput.elm +++ b/styleguide-app/Examples/TextInput.elm @@ -1,26 +1,30 @@ module Examples.TextInput exposing (Msg, State, example, init, update) -{- \ - @docs Msg, State, example, init, update, +{-| + +@docs Msg, State, example, init, update + -} import Dict exposing (Dict) import Html.Styled as Html import ModuleExample as ModuleExample exposing (Category(..), ModuleExample) import Nri.Ui.Heading.V2 as Heading -import Nri.Ui.TextInput.V4 as TextInput +import Nri.Ui.TextInput.V5 as TextInput {-| -} type Msg = SetTextInput Id String | SetNumberInput (Maybe Int) + | SetPassword String {-| -} type alias State = { numberInputValue : Maybe Int , textInputValues : Dict Id String + , passwordInputValue : String } @@ -56,6 +60,18 @@ example parentMessage state = , showLabel = True } , Html.br [] [] + , TextInput.view + { label = "Password" + , isInError = False + , placeholder = "" + , value = state.passwordInputValue + , onInput = SetPassword + , onBlur = Nothing + , autofocus = False + , type_ = TextInput.password + , showLabel = True + } + , Html.br [] [] , TextInput.view { label = "Error" , isInError = True @@ -149,6 +165,7 @@ init : State init = { numberInputValue = Nothing , textInputValues = Dict.empty + , passwordInputValue = "" } @@ -162,6 +179,9 @@ update msg state = SetNumberInput numberInputValue -> ( { state | numberInputValue = numberInputValue }, Cmd.none ) + SetPassword password -> + ( { state | passwordInputValue = password }, Cmd.none ) + -- INTERNAL