Merge pull request #74 from NoRedInk/ink__update-checkbox

Ink  update checkbox
This commit is contained in:
Tessa 2018-06-22 10:59:29 -07:00 committed by GitHub
commit 3d44b3f1be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 1067 additions and 34 deletions

View File

@ -16,6 +16,7 @@
"Nri.Ui.Colors.Extra", "Nri.Ui.Colors.Extra",
"Nri.Ui.Checkbox.V1", "Nri.Ui.Checkbox.V1",
"Nri.Ui.Checkbox.V2", "Nri.Ui.Checkbox.V2",
"Nri.Ui.Checkbox.V3",
"Nri.Ui.Divider.V1", "Nri.Ui.Divider.V1",
"Nri.Ui.Dropdown.V1", "Nri.Ui.Dropdown.V1",
"Nri.Ui.Effects.V1", "Nri.Ui.Effects.V1",

295
src/Nri/Ui/Checkbox/V3.elm Normal file
View File

@ -0,0 +1,295 @@
module Nri.Ui.Checkbox.V3
exposing
( IsSelected(..)
, Model
, Theme(..)
, view
, viewWithLabel
)
{-|
@docs Model, Theme, IsSelected
@docs view, viewWithLabel
-}
import Accessibility.Styled as Html
import Accessibility.Styled.Aria as Aria
import Accessibility.Styled.Style
import Accessibility.Styled.Widget as Widget
import Css exposing (..)
import Css.Foreign exposing (Snippet, children, descendants, everything, selector)
import Html.Events exposing (defaultOptions)
import Html.Styled
import Html.Styled.Attributes as Attributes exposing (css)
import Html.Styled.Events as Events
import Json.Decode
import Nri.Ui.AssetPath exposing (Asset(..))
import Nri.Ui.AssetPath.Css
import Nri.Ui.Fonts.V1 as Fonts
import Nri.Ui.Html.Attributes.V2 as ExtraAttributes
import Nri.Ui.Html.V2 as HtmlExtra
{-| -}
type alias Model msg =
{ identifier : String
, label : String
, setterMsg : Bool -> msg
, selected : IsSelected
, disabled : Bool
, theme : Theme
, noOpMsg : msg
}
{-|
= Selected -- Checked (rendered with a checkmark)
| NotSelected -- Not Checked (rendered blank)
| PartiallySelected -- Indeterminate (rendered dash)
-}
type IsSelected
= Selected
| NotSelected
| PartiallySelected
{-| -}
type Theme
= Square
| LockOnInside
selectedToMaybe : IsSelected -> Maybe Bool
selectedToMaybe selected =
case selected of
Selected ->
Just True
NotSelected ->
Just False
PartiallySelected ->
Nothing
{-| Shows a checkbox (the label is only used for accessibility hints)
-}
view : Assets a -> Model msg -> Html.Html msg
view assets model =
buildCheckbox assets [] model <|
Html.span [ Accessibility.Styled.Style.invisible ]
[ Html.text model.label ]
{-| Shows a checkbox and its label text
-}
viewWithLabel : Assets a -> Model msg -> Html.Html msg
viewWithLabel assets model =
buildCheckbox assets [] model <|
Html.span [] [ Html.text model.label ]
buildCheckbox : Assets a -> List String -> Model msg -> Html.Html msg -> Html.Html msg
buildCheckbox assets modifierClasses model labelContent =
let
toClassList =
List.map (\a -> ( "checkbox-" ++ a, True )) >> Attributes.classList
in
viewCheckbox model <|
case model.theme of
Square ->
{ containerClasses = toClassList (modifierClasses ++ [ "SquareClass" ])
, labelStyles =
squareLabelStyles model <|
case model.selected of
Selected ->
assets.checkboxChecked_svg
NotSelected ->
assets.checkboxUnchecked_svg
PartiallySelected ->
assets.checkboxCheckedPartially_svg
, labelClasses = labelClass model.selected
, labelContent = labelContent
}
LockOnInside ->
{ containerClasses = toClassList (modifierClasses ++ [ "LockOnInsideClass" ])
, labelStyles = lockLabelStyles model assets.checkboxLockOnInside_svg
, labelClasses = labelClass model.selected
, labelContent = labelContent
}
squareLabelStyles : { b | disabled : Bool } -> Asset -> Html.Styled.Attribute msg
squareLabelStyles model image =
let
baseStyles =
[ positioning
, textStyle
, outline none
, addIcon image
]
in
css
(if model.disabled then
[ cursor auto, opacity (num 0.4) ] ++ baseStyles
else
[ cursor pointer ] ++ baseStyles
)
lockLabelStyles : { b | disabled : Bool } -> Asset -> Html.Styled.Attribute msg
lockLabelStyles model image =
let
baseStyles =
[ positioning
, textStyle
, outline none
, addIcon image
]
in
css
(if model.disabled then
[ cursor auto, opacity (num 0.4) ] ++ baseStyles
else
[ cursor pointer ] ++ baseStyles
)
positioning : Style
positioning =
batch
[ display inlineBlock
, padding4 (px 13) zero (px 13) (px 35)
, verticalAlign middle
]
textStyle : Style
textStyle =
batch
[ Fonts.baseFont
, fontSize (px 16)
]
addIcon : Asset -> Style
addIcon icon =
batch
[ backgroundImage icon
, backgroundRepeat noRepeat
, backgroundSize (px 24)
, property "background-position" "left center"
]
labelClass : IsSelected -> Html.Styled.Attribute msg
labelClass isSelected =
case isSelected of
Selected ->
Attributes.classList
[ ( "checkbox-Label", True )
, ( "checkbox-Checked", True )
]
NotSelected ->
Attributes.classList
[ ( "checkbox-Label", True )
, ( "checkbox-Unchecked", True )
]
PartiallySelected ->
Attributes.classList
[ ( "checkbox-Label", True )
, ( "checkbox-Indeterminate", True )
]
viewCheckbox :
Model msg
->
{ containerClasses : Html.Attribute msg
, labelStyles : Html.Attribute msg
, labelClasses : Html.Attribute msg
, labelContent : Html.Html msg
}
-> Html.Html msg
viewCheckbox model config =
Html.Styled.span
[ css
[ display block
, height inherit
, descendants [ Css.Foreign.input [ display none ] ]
]
, config.containerClasses
, Attributes.id <| model.identifier ++ "-container"
, -- This is necessary to prevent event propagation.
-- See https://github.com/elm-lang/html/issues/96
Attributes.map (always model.noOpMsg) <|
Events.onWithOptions "click"
{ defaultOptions | stopPropagation = True }
(Json.Decode.succeed "stop click propagation")
]
[ Html.checkbox model.identifier
(selectedToMaybe model.selected)
[ Widget.label model.label
, Events.onCheck model.setterMsg
, Attributes.id model.identifier
, Attributes.disabled model.disabled
]
, viewLabel model config.labelContent config.labelClasses config.labelStyles
]
viewLabel : Model msg -> Html.Html msg -> Html.Attribute msg -> Html.Attribute msg -> Html.Html msg
viewLabel model content class theme =
Html.Styled.label
[ Attributes.for model.identifier
, Aria.controls model.identifier
, Widget.disabled model.disabled
, Widget.checked (selectedToMaybe model.selected)
, if not model.disabled then
Attributes.tabindex 0
else
ExtraAttributes.none
, if not model.disabled then
--TODO: the accessibility keyboard module might make this a tad more readable.
HtmlExtra.onKeyUp
{ defaultOptions | preventDefault = True }
(\keyCode ->
-- 32 is the space bar, 13 is enter
if (keyCode == 32 || keyCode == 13) && not model.disabled then
Just <| model.setterMsg (Maybe.map not (selectedToMaybe model.selected) |> Maybe.withDefault True)
else
Nothing
)
else
ExtraAttributes.none
, class
, theme
]
[ content ]
{-| The assets used in this module.
-}
type alias Assets r =
{ r
| checkboxUnchecked_svg : Asset
, checkboxChecked_svg : Asset
, checkboxCheckedPartially_svg : Asset
, checkboxLockOnInside_svg : Asset
}
backgroundImage : Asset -> Style
backgroundImage =
Nri.Ui.AssetPath.Css.url
>> property "background-image"

View File

@ -0,0 +1,40 @@
module Nri.Ui.Html.Attributes.V2 exposing (includeIf, none)
{-| Extras for working with Html.Attributes.
This is the new version of Nri.Ui.Html.Attributes.Extra.
@docs none, includeIf
-}
import Html.Styled exposing (Attribute)
import Html.Styled.Attributes as Attributes
import Json.Encode as Encode
{-| Represents an attribute with no semantic meaning, useful for conditionals.
This is implemented such that whenever Html.Attributes.Extra.none is encountered
by VirtualDom it will set a meaningless property on the element object itself to
null:
domNode['Html.Attributes.Extra.none'] = null
It's totally safe and lets us clean up conditional and maybe attributes
-}
none : Attribute msg
none =
Attributes.property "Html.Attributes.Extra.none" Encode.null
{-| conditionally include an attribute. Useful for CSS classes generated with
`UniqueClass`!
-}
includeIf : Bool -> Attribute msg -> Attribute msg
includeIf cond attr =
if cond then
attr
else
none

143
src/Nri/Ui/Html/V2.elm Normal file
View File

@ -0,0 +1,143 @@
module Nri.Ui.Html.V2 exposing (..)
{-| For all utils involving HTML. New version of Nri.Ui.Html.Extra.
@docs role, noOpHref, noOpHrefUrl
@docs onEsc, onEnter, onKeyUp, onEnterAndSpace
@docs textFromList, oxfordifyWithHtml, nbsp
-}
import Char
import Html.Styled as Html exposing (Attribute, Html, span, text)
import Html.Styled.Attributes exposing (..)
import Html.Styled.Events exposing (..)
import Json.Decode
{-| Convenience for defining role attributes, e.g. <div role="tabpanel">
-}
role : String -> Attribute msg
role =
attribute "role"
{-| -}
noOpHref : Attribute a
noOpHref =
href noOpHrefUrl
{-| This is a better choice for a no-op than "#" because "#" changes your
location bar. See <http://stackoverflow.com/a/20676911> for more details.
-}
noOpHrefUrl : String
noOpHrefUrl =
"javascript:void(0)"
{-| -}
onEsc : a -> a -> Attribute a
onEsc onEscAction onOtherKey =
on "keyup"
(Json.Decode.map
(\keyCode ->
if keyCode == 27 then
onEscAction
else
onOtherKey
)
keyCode
)
{-| -}
onEnter : a -> Attribute a
onEnter onEnterAction =
onKeyUp defaultOptions
(\keyCode ->
if keyCode == 13 then
Just onEnterAction
else
Nothing
)
{-| "Buttons" should trigger on Enter and on Space.
-}
onEnterAndSpace : msg -> Attribute msg
onEnterAndSpace msg =
onKeyUp defaultOptions
(\keyCode ->
if keyCode == 13 || keyCode == 32 then
Just msg
else
Nothing
)
{-| Convert a keycode into a message on keyup
-}
onKeyUp : Options -> (Int -> Maybe a) -> Attribute a
onKeyUp options toMaybeMsg =
onWithOptions "keyup" options <|
Json.Decode.andThen
(\keyCode ->
keyCode
|> toMaybeMsg
|> Maybe.map Json.Decode.succeed
|> Maybe.withDefault (Json.Decode.fail (toString keyCode))
)
keyCode
{-| Takes a list of strings, joins them with a space and returns it as a Html.text.
textFromList ["Hello", "World"] == text [ String.join " " ["Hello", "World" ] ]
-}
textFromList : List String -> Html msg
textFromList =
String.join " " >> text
{-| -}
oxfordifyWithHtml : String -> String -> List (Html msg) -> List (Html msg)
oxfordifyWithHtml pre post items =
let
textSpan string =
span [] [ text string ]
final centrals =
[ textSpan pre ] ++ centrals ++ [ textSpan post ]
in
case items of
[] ->
[]
[ single ] ->
final [ single ]
[ first, second ] ->
final [ first, textSpan " and ", second ]
many ->
let
beforeAnd =
List.take (List.length many - 1) many
afterAnd =
List.drop (List.length many - 1) many
|> List.head
|> Maybe.withDefault (textSpan "")
in
final (List.intersperse (textSpan ", ") beforeAnd ++ [ textSpan ", and ", afterAnd ])
{-| Workaround for `Html.text "&nbsp;"` not working in elm.
-}
nbsp : Html msg
nbsp =
Char.fromCode 160
|> String.fromChar
|> Html.text

View File

@ -0,0 +1,114 @@
module Nri.Ui.PremiumCheckbox.V1 exposing (PremiumConfig, premium)
{-|
@docs PremiumConfig, premium
-}
import Accessibility.Styled as Html
import Css exposing (..)
import Html.Styled.Attributes as Attributes exposing (css)
import Nri.Ui.AssetPath exposing (Asset(..))
import Nri.Ui.AssetPath.Css
import Nri.Ui.Checkbox.V3 as Checkbox
import Nri.Ui.Data.PremiumLevel as PremiumLevel exposing (PremiumLevel(..))
{-|
- `onChange`: A message for when the user toggles the checkbox
- `onLockedClick`: A message for when the user clicks a checkbox they don't have PremiumLevel for.
If you get this message, you should show an `Nri.Ui.Premium.Model.view`
-}
type alias PremiumConfig msg =
{ label : String
, id : String
, selected : Checkbox.IsSelected
, disabled : Bool
, teacherPremiumLevel : PremiumLevel
, contentPremiumLevel : PremiumLevel
, showFlagWhenLocked : Bool
, onChange : Bool -> msg
, onLockedClick : msg
, noOpMsg : msg
}
{-| A checkbox that should be used for premium content
This checkbox is locked when the premium level of the content is greater than the premium level of the teacher
-}
premium : Assets a -> PremiumConfig msg -> Html.Html msg
premium assets config =
let
isLocked =
not <|
PremiumLevel.allowedFor
config.contentPremiumLevel
config.teacherPremiumLevel
in
Html.div
[ css
[ displayFlex
, alignItems center
]
]
[ Checkbox.viewWithLabel assets
{ identifier = config.id
, label = config.label
, setterMsg =
if isLocked then
\_ -> config.onLockedClick
else
config.onChange
, selected = config.selected
, disabled = config.disabled
, theme =
if isLocked then
Checkbox.LockOnInside
else
Checkbox.Square
, noOpMsg = config.noOpMsg
}
, if
(isLocked && config.showFlagWhenLocked)
|| (not isLocked && config.contentPremiumLevel /= Free)
then
Html.div
[ Attributes.class "checkbox-PremiumClass"
, css
[ property "content" "''"
, display inlineBlock
, width (px 26)
, height (px 24)
, marginLeft (px 8)
, backgroundImage assets.iconPremiumFlag_svg
, backgroundRepeat noRepeat
, backgroundPosition center
]
]
[]
else
Html.text ""
]
{-| The assets used in this module.
-}
type alias Assets r =
{ r
| checkboxUnchecked_svg : Asset
, checkboxChecked_svg : Asset
, checkboxCheckedPartially_svg : Asset
, checkboxLockOnInside_svg : Asset
, iconPremiumFlag_svg : Asset
}
backgroundImage : Asset -> Style
backgroundImage =
Nri.Ui.AssetPath.Css.url
>> property "background-image"

View File

@ -9,6 +9,10 @@ type alias Assets =
, attention_svg : Asset , attention_svg : Asset
, bulb : String , bulb : String
, calendar : String , calendar : String
, checkboxCheckedPartially_svg : Asset
, checkboxChecked_svg : Asset
, checkboxLockOnInside_svg : Asset
, checkboxUnchecked_svg : Asset
, checkmark : String , checkmark : String
, class : String , class : String
, clever : String , clever : String
@ -32,6 +36,7 @@ type alias Assets =
, iconCalendar_svg : Asset , iconCalendar_svg : Asset
, iconCheck_png : Asset , iconCheck_png : Asset
, iconFlag_png : Asset , iconFlag_png : Asset
, iconPremiumFlag_svg : Asset
, icons_arrowDownBlue_svg : Asset , icons_arrowDownBlue_svg : Asset
, icons_arrowRightBlue_svg : Asset , icons_arrowRightBlue_svg : Asset
, icons_clockRed_svg : Asset , icons_clockRed_svg : Asset
@ -60,6 +65,8 @@ type alias Assets =
, premiumLock_svg : Asset , premiumLock_svg : Asset
, preview : String , preview : String
, quiz : String , quiz : String
, rating : String
, revising : String
, seemore : String , seemore : String
, share : String , share : String
, skip : String , skip : String
@ -69,6 +76,7 @@ type alias Assets =
, speedometer : String , speedometer : String
, squiggly_png : Asset , squiggly_png : Asset
, startingOffBadge_png : Asset , startingOffBadge_png : Asset
, submitting : String
, teach_assignments_copyWhite_svg : Asset , teach_assignments_copyWhite_svg : Asset
, twitterBlue_svg : Asset , twitterBlue_svg : Asset
, unarchiveBlue2x_png : Asset , unarchiveBlue2x_png : Asset
@ -76,9 +84,6 @@ type alias Assets =
, writingcycle : String , writingcycle : String
, x : String , x : String
, xWhite_svg : Asset , xWhite_svg : Asset
, submitting : String
, rating : String
, revising : String
} }
@ -89,6 +94,10 @@ assets =
, attention_svg = Asset "assets/images/attention.svg" , attention_svg = Asset "assets/images/attention.svg"
, bulb = "icon-bulb" , bulb = "icon-bulb"
, calendar = "icon-calendar" , calendar = "icon-calendar"
, checkboxCheckedPartially_svg = Asset "assets/images/checkbox_checkedPartially.svg"
, checkboxChecked_svg = Asset "assets/images/checkbox_checked.svg"
, checkboxLockOnInside_svg = Asset "assets/images/checkbox_lock_on_inside.svg"
, checkboxUnchecked_svg = Asset "assets/images/checkbox_unchecked.svg"
, checkmark = "icon-checkmark" , checkmark = "icon-checkmark"
, class = "icon-class" , class = "icon-class"
, clever = "icon-clever" , clever = "icon-clever"
@ -112,6 +121,7 @@ assets =
, iconCalendar_svg = Asset "assets/images/icon-calendar.svg" , iconCalendar_svg = Asset "assets/images/icon-calendar.svg"
, iconCheck_png = Asset "assets/images/icon-check.png" , iconCheck_png = Asset "assets/images/icon-check.png"
, iconFlag_png = Asset "assets/images/icon-flag.png" , iconFlag_png = Asset "assets/images/icon-flag.png"
, iconPremiumFlag_svg = Asset "assets/images/icon_premium_flag.svg"
, icons_arrowDownBlue_svg = Asset "assets/images/arrow-down-blue.svg" , icons_arrowDownBlue_svg = Asset "assets/images/arrow-down-blue.svg"
, icons_arrowRightBlue_svg = Asset "assets/images/arrow-right-blue.svg" , icons_arrowRightBlue_svg = Asset "assets/images/arrow-right-blue.svg"
, icons_clockRed_svg = Asset "assets/images/clock-red.svg" , icons_clockRed_svg = Asset "assets/images/clock-red.svg"
@ -139,6 +149,8 @@ assets =
, premiumLock_svg = Asset "assets/images/premium-lock.svg" , premiumLock_svg = Asset "assets/images/premium-lock.svg"
, preview = "icon-preview" , preview = "icon-preview"
, quiz = "icon-quiz" , quiz = "icon-quiz"
, rating = "icon-rating"
, revising = "icon-revising"
, seemore = "icon-seemore" , seemore = "icon-seemore"
, share = "icon-share" , share = "icon-share"
, skip = "icon-skip" , skip = "icon-skip"
@ -148,6 +160,7 @@ assets =
, speedometer = "icon-speedometer" , speedometer = "icon-speedometer"
, squiggly_png = Asset "assets/images/squiggly.png" , squiggly_png = Asset "assets/images/squiggly.png"
, startingOffBadge_png = Asset "assets/images/starting-off-badge.png" , startingOffBadge_png = Asset "assets/images/starting-off-badge.png"
, submitting = "icon-submitting"
, teach_assignments_copyWhite_svg = Asset "assets/images/copy-white.svg" , teach_assignments_copyWhite_svg = Asset "assets/images/copy-white.svg"
, twitterBlue_svg = Asset "assets/images/twitter-blue.svg" , twitterBlue_svg = Asset "assets/images/twitter-blue.svg"
, unarchiveBlue2x_png = Asset "assets/images/unarchive-blue_2x.png" , unarchiveBlue2x_png = Asset "assets/images/unarchive-blue_2x.png"
@ -155,7 +168,4 @@ assets =
, writingcycle = "icon-writingcycle" , writingcycle = "icon-writingcycle"
, x = "icon-x" , x = "icon-x"
, xWhite_svg = Asset "assets/images/x-white.svg" , xWhite_svg = Asset "assets/images/x-white.svg"
, submitting = "icon-submitting"
, rating = "icon-rating"
, revising = "icon-revising"
} }

View File

@ -0,0 +1,199 @@
module Examples.Checkbox exposing (Msg, State, example, init, update)
{-|
@docs Msg, State, example, init, update,
-}
import Assets exposing (assets)
import Debug.Control as Control exposing (Control)
import Html.Styled as Html exposing (..)
import ModuleExample as ModuleExample exposing (Category(..), ModuleExample)
import Nri.Ui.Checkbox.V3 as Checkbox
import Nri.Ui.Data.PremiumLevel as PremiumLevel exposing (PremiumLevel(..))
import Nri.Ui.PremiumCheckbox.V1 as PremiumCheckbox
import Set exposing (Set)
{-| -}
type Msg
= ToggleCheck Id Bool
| SetPremiumControl (Control PremiumExampleConfig)
| NoOp
{-| -}
type alias State =
{ isChecked : Set String
, premiumControl : Control PremiumExampleConfig
}
{-| -}
example : (Msg -> msg) -> State -> ModuleExample msg
example parentMessage state =
{ filename = "Nri/Checkbox.elm"
, category = Inputs
, content =
[ viewInteractableCheckbox "styleguide-checkbox-interactable" state
, viewIndeterminateCheckbox "styleguide-checkbox-indeterminate" state
, viewLockedOnInsideCheckbox "styleguide-locked-on-inside-checkbox" state
, viewDisabledCheckbox "styleguide-checkbox-disabled" state
, h3 [] [ text "Premium Checkboxes" ]
, Control.view SetPremiumControl state.premiumControl
|> Html.fromUnstyled
, viewPremiumCheckboxes state
]
|> List.map (Html.toUnstyled << Html.map parentMessage)
}
{-| -}
init : State
init =
{ isChecked = Set.empty
, premiumControl =
Control.record PremiumExampleConfig
|> Control.field "disabled" (Control.bool False)
|> Control.field "teacherPremiumLevel"
(Control.choice
[ ( "Free", Control.value PremiumLevel.Free )
, ( "Premium", Control.value PremiumLevel.Premium )
, ( "Premium (with writing)", Control.value PremiumLevel.PremiumWithWriting )
]
)
|> Control.field "showFlagWhenLocked" (Control.bool True)
}
{-| -}
update : Msg -> State -> ( State, Cmd Msg )
update msg state =
case msg of
ToggleCheck id checked ->
let
isChecked =
if checked then
Set.insert id state.isChecked
else
Set.remove id state.isChecked
in
( { state | isChecked = isChecked }, Cmd.none )
SetPremiumControl premiumControl ->
( { state | premiumControl = premiumControl }, Cmd.none )
NoOp ->
( state, Cmd.none )
-- INTERNAL
type alias PremiumExampleConfig =
{ disabled : Bool
, teacherPremiumLevel : PremiumLevel
, showFlagWhenLocked : Bool
}
viewInteractableCheckbox : Id -> State -> Html Msg
viewInteractableCheckbox id state =
Checkbox.viewWithLabel
assets
{ identifier = id
, label = "This is an interactable checkbox!"
, setterMsg = ToggleCheck id
, selected = isSelected id state
, disabled = False
, theme = Checkbox.Square
, noOpMsg = NoOp
}
viewIndeterminateCheckbox : Id -> State -> Html Msg
viewIndeterminateCheckbox id state =
Checkbox.viewWithLabel
assets
{ identifier = id
, label = "This Checkbox is set in an indeterminate state"
, setterMsg = ToggleCheck id
, selected = Checkbox.PartiallySelected
, disabled = True
, theme = Checkbox.Square
, noOpMsg = NoOp
}
viewLockedOnInsideCheckbox : Id -> State -> Html Msg
viewLockedOnInsideCheckbox id state =
Checkbox.viewWithLabel
assets
{ identifier = id
, label = "I'm a locked Checkbox on the inside"
, setterMsg = ToggleCheck id
, selected = Checkbox.NotSelected
, disabled = True
, theme = Checkbox.LockOnInside
, noOpMsg = NoOp
}
viewDisabledCheckbox : Id -> State -> Html Msg
viewDisabledCheckbox id state =
Checkbox.viewWithLabel
assets
{ identifier = id
, label = "Disabled theme"
, setterMsg = ToggleCheck id
, selected = isSelected id state
, disabled = True
, theme = Checkbox.Square
, noOpMsg = NoOp
}
viewPremiumCheckboxes : State -> Html Msg
viewPremiumCheckboxes state =
let
config =
Control.currentValue state.premiumControl
checkbox label premiumLevel =
PremiumCheckbox.premium
assets
{ label = label
, id = "premium-checkbox-" ++ label
, selected =
if Set.member label state.isChecked then
Checkbox.Selected
else
Checkbox.NotSelected
, disabled = config.disabled
, teacherPremiumLevel = config.teacherPremiumLevel
, contentPremiumLevel = premiumLevel
, showFlagWhenLocked = config.showFlagWhenLocked
, onChange = ToggleCheck label
, onLockedClick = NoOp
, noOpMsg = NoOp
}
in
Html.div []
[ checkbox "Identify Adjectives 1 (Free)" PremiumLevel.Free
, checkbox "Identify Adjectives 2 (Premium)" PremiumLevel.Premium
, checkbox "Revising Wordy Phrases 1 (Writing)" PremiumLevel.PremiumWithWriting
]
type alias Id =
String
isSelected : Id -> State -> Checkbox.IsSelected
isSelected id state =
if Set.member id state.isChecked then
Checkbox.Selected
else
Checkbox.NotSelected

View File

@ -10,7 +10,8 @@ import Dict exposing (Dict)
import Html import Html
import Html.Styled import Html.Styled
import ModuleExample as ModuleExample exposing (Category(..), ModuleExample) import ModuleExample as ModuleExample exposing (Category(..), ModuleExample)
import Nri.Ui.Checkbox.V2 as Checkbox import Nri.Ui.AssetPath exposing (Asset(..))
import Nri.Ui.Checkbox.V3 as Checkbox
import Nri.Ui.Text.V2 as Text import Nri.Ui.Text.V2 as Text
import Nri.Ui.TextArea.V3 as TextArea import Nri.Ui.TextArea.V3 as TextArea
@ -27,9 +28,9 @@ type Msg
{-| -} {-| -}
type alias State = type alias State =
{ textValues : Dict Int String { textValues : Dict Int String
, showLabel : Bool , showLabel : Checkbox.IsSelected
, isInError : Bool , isInError : Checkbox.IsSelected
, autoResize : Bool , autoResize : Checkbox.IsSelected
} }
@ -42,77 +43,80 @@ example parentMessage state =
[ Text.heading [ Html.Styled.text "Textarea controls" ] [ Text.heading [ Html.Styled.text "Textarea controls" ]
|> Html.Styled.toUnstyled |> Html.Styled.toUnstyled
, Html.div [] , Html.div []
[ Checkbox.viewWithLabel [ Checkbox.viewWithLabel assets
{ identifier = "show-textarea-label" { identifier = "show-textarea-label"
, label = "Show Label" , label = "Show Label"
, setterMsg = ToggleLabel , setterMsg = ToggleLabel
, isChecked = Just state.showLabel , selected = state.showLabel
, disabled = False , disabled = False
, theme = Checkbox.Square Checkbox.Default , theme = Checkbox.Square
, noOpMsg = NoOp , noOpMsg = NoOp
} }
, Checkbox.viewWithLabel |> Html.Styled.toUnstyled
, Checkbox.viewWithLabel assets
{ identifier = "textarea-autoresize" { identifier = "textarea-autoresize"
, label = "Autoresize" , label = "Autoresize"
, setterMsg = ToggleAutoResize , setterMsg = ToggleAutoResize
, isChecked = Just state.autoResize , selected = state.autoResize
, disabled = False , disabled = False
, theme = Checkbox.Square Checkbox.Default , theme = Checkbox.Square
, noOpMsg = NoOp , noOpMsg = NoOp
} }
, Checkbox.viewWithLabel |> Html.Styled.toUnstyled
, Checkbox.viewWithLabel assets
{ identifier = "textarea-isInError" { identifier = "textarea-isInError"
, label = "Show Error State" , label = "Show Error State"
, setterMsg = ToggleErrorState , setterMsg = ToggleErrorState
, isChecked = Just state.isInError , selected = state.isInError
, disabled = False , disabled = False
, theme = Checkbox.Square Checkbox.Default , theme = Checkbox.Square
, noOpMsg = NoOp , noOpMsg = NoOp
} }
|> Html.Styled.toUnstyled
] ]
, TextArea.view , TextArea.view
{ value = Maybe.withDefault "" <| Dict.get 1 state.textValues { value = Maybe.withDefault "" <| Dict.get 1 state.textValues
, autofocus = False , autofocus = False
, onInput = InputGiven 1 , onInput = InputGiven 1
, isInError = state.isInError , isInError = state.isInError == Checkbox.Selected
, label = "TextArea.view" , label = "TextArea.view"
, height = , height =
if state.autoResize then if state.autoResize == Checkbox.Selected then
TextArea.AutoResize TextArea.SingleLine TextArea.AutoResize TextArea.SingleLine
else else
TextArea.Fixed TextArea.Fixed
, placeholder = "Placeholder" , placeholder = "Placeholder"
, showLabel = state.showLabel , showLabel = state.showLabel == Checkbox.Selected
} }
|> Html.Styled.toUnstyled |> Html.Styled.toUnstyled
, TextArea.writing , TextArea.writing
{ value = Maybe.withDefault "" <| Dict.get 2 state.textValues { value = Maybe.withDefault "" <| Dict.get 2 state.textValues
, autofocus = False , autofocus = False
, onInput = InputGiven 2 , onInput = InputGiven 2
, isInError = state.isInError , isInError = state.isInError == Checkbox.Selected
, label = "TextArea.writing" , label = "TextArea.writing"
, height = , height =
if state.autoResize then if state.autoResize == Checkbox.Selected then
TextArea.AutoResize TextArea.DefaultHeight TextArea.AutoResize TextArea.DefaultHeight
else else
TextArea.Fixed TextArea.Fixed
, placeholder = "Placeholder" , placeholder = "Placeholder"
, showLabel = state.showLabel , showLabel = state.showLabel == Checkbox.Selected
} }
|> Html.Styled.toUnstyled |> Html.Styled.toUnstyled
, TextArea.contentCreation , TextArea.contentCreation
{ value = Maybe.withDefault "" <| Dict.get 3 state.textValues { value = Maybe.withDefault "" <| Dict.get 3 state.textValues
, autofocus = False , autofocus = False
, onInput = InputGiven 3 , onInput = InputGiven 3
, isInError = state.isInError , isInError = state.isInError == Checkbox.Selected
, label = "TextArea.contentCreation" , label = "TextArea.contentCreation"
, height = , height =
if state.autoResize then if state.autoResize == Checkbox.Selected then
TextArea.AutoResize TextArea.DefaultHeight TextArea.AutoResize TextArea.DefaultHeight
else else
TextArea.Fixed TextArea.Fixed
, placeholder = "Placeholder" , placeholder = "Placeholder"
, showLabel = state.showLabel , showLabel = state.showLabel == Checkbox.Selected
} }
|> Html.Styled.toUnstyled |> Html.Styled.toUnstyled
] ]
@ -124,15 +128,22 @@ example parentMessage state =
init : State init : State
init = init =
{ textValues = Dict.empty { textValues = Dict.empty
, showLabel = True , showLabel = Checkbox.Selected
, isInError = False , isInError = Checkbox.NotSelected
, autoResize = False , autoResize = Checkbox.NotSelected
} }
{-| -} {-| -}
update : Msg -> State -> ( State, Cmd Msg ) update : Msg -> State -> ( State, Cmd Msg )
update msg state = update msg state =
let
toggle bool =
if bool then
Checkbox.Selected
else
Checkbox.NotSelected
in
case msg of case msg of
InputGiven id newValue -> InputGiven id newValue ->
( { state | textValues = Dict.insert id newValue state.textValues } ( { state | textValues = Dict.insert id newValue state.textValues }
@ -140,17 +151,17 @@ update msg state =
) )
ToggleLabel bool -> ToggleLabel bool ->
( { state | showLabel = bool } ( { state | showLabel = toggle bool }
, Cmd.none , Cmd.none
) )
ToggleErrorState bool -> ToggleErrorState bool ->
( { state | isInError = bool } ( { state | isInError = toggle bool }
, Cmd.none , Cmd.none
) )
ToggleAutoResize bool -> ToggleAutoResize bool ->
( { state | autoResize = bool } ( { state | autoResize = toggle bool }
, Cmd.none , Cmd.none
) )
@ -164,3 +175,15 @@ update msg state =
type alias Id = type alias Id =
Int Int
assets =
{ checkboxUnchecked_svg = Asset "checkboxUnchecked_svg"
, checkboxChecked_svg = Asset "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAAb1BMVEX///9DoEc2nDt1tXcoly601bU5nD1nr2o/n0Pw9vAzmzhAn0QxmjYumTMqmDAsmTK92r6UxJacyJ1YqFu52LvC3MOQwpIZlCGLwI3M4s2u0q+q0KueyaCkzKX1+fXq8+rh7uHW6NeDvIVztHVssW4Cx6cUAAACZUlEQVR4nO3Zi1LTYBRF4dOSQkqt4AURL6Di+z+jQWVsadJcen5z9pm1HiDZ3yQzSSZmRERERERERERERERERERERERERERERERElKj7uQeUbrVdzz2hbKuqrlMTV9VikZr4BExN/ANMTHwGpiX+AyYl7gJTEveBCYkvgemIh8BkxDZgKmI7sCEuH+ae5lMXcLF8nHuaT93As7mn+QRQPYDqAVQPoHoA1QOoHkD1AKoHUD2A6gFUD6B6ANUDqB5A9QCqB1A9gOoBVA+gegDVA6geQPUAqgfw/3Rd7MhRgLcXhY4cBHhTLc7LEIMA755mFCFGAhYhBgF+f57hTowGdCcGAX672jm1KzEI8GG7WZQhRgFebvZP70YMAlwvNy8HOBGjAOsDoBMxCPB+U7eNcCAGAa7bgQ7EIECzi/OOIScSwwBLEQMByxBDAUsQgwHNfjoTwwG9iQGBDXHpRwwJ9CQGBZo9OhHDAr2IgYFmZw7E0EAPYnDg6cTwwFOJAsDTiBJAsx+TiSLAhng5jSgDnEoUAk4jSgGnEMWA44lywLFEQaDZxxFESeAYoiiwIb4aRpQFDiUKA80+DCBKA4cQxYFmb3qI8sCGuD1GTAA8TkwBPEKs33U9MbWAZm+7iC3/xiWBR4hZgCOJikCzL1f9MmngCKIq0Ozr6+TAhjjkKioDBxG1gWaf+25UdWAvUR/YQ8wANPvU9aqdBXiEmAXYScwD7PjozQRsJeYCthCzAQ+I+YBm76vkwD1iTuAOMSvQ7LpKDvxLzAz8TcwNNLu5TQ40u5t7ABERERERERERERERERFN7hdTvSHAHrHcMgAAAABJRU5ErkJggg=="
, checkboxCheckedPartially_svg = Asset "checkboxCheckedPartially_svg"
, iconPremiumUnlocked_png = Asset "iconPremiumUnlocked_png"
, iconPremiumLocked_png = Asset "iconPremiumLocked_png"
, checkboxLockOnInside_svg = Asset "checkboxLockOnInside_svg"
, iconPremiumKey_png = Asset "iconPremiumKey_png"
, iconPremiumFlag_svg = Asset "iconPremiumFlag_svg"
}

View File

@ -3,6 +3,7 @@ module NriModules exposing (ModuleStates, Msg, init, nriThemedModules, styles, s
import Assets exposing (assets) import Assets exposing (assets)
import DEPRECATED.Css.File exposing (Stylesheet, compile, stylesheet) import DEPRECATED.Css.File exposing (Stylesheet, compile, stylesheet)
import Examples.Button import Examples.Button
import Examples.Checkbox
import Examples.Colors import Examples.Colors
import Examples.Dropdown import Examples.Dropdown
import Examples.Fonts import Examples.Fonts
@ -31,6 +32,7 @@ import String.Extra
type alias ModuleStates = type alias ModuleStates =
{ buttonExampleState : Examples.Button.State { buttonExampleState : Examples.Button.State
, checkboxExampleState : Examples.Checkbox.State
, dropdownState : Examples.Dropdown.State Examples.Dropdown.Value , dropdownState : Examples.Dropdown.State Examples.Dropdown.Value
, segmentedControlState : Examples.SegmentedControl.State , segmentedControlState : Examples.SegmentedControl.State
, selectState : Examples.Select.State Examples.Select.Value , selectState : Examples.Select.State Examples.Select.Value
@ -43,6 +45,7 @@ type alias ModuleStates =
init : ModuleStates init : ModuleStates
init = init =
{ buttonExampleState = Examples.Button.init assets { buttonExampleState = Examples.Button.init assets
, checkboxExampleState = Examples.Checkbox.init
, dropdownState = Examples.Dropdown.init , dropdownState = Examples.Dropdown.init
, segmentedControlState = Examples.SegmentedControl.init , segmentedControlState = Examples.SegmentedControl.init
, selectState = Examples.Select.init , selectState = Examples.Select.init
@ -54,6 +57,7 @@ init =
type Msg type Msg
= ButtonExampleMsg Examples.Button.Msg = ButtonExampleMsg Examples.Button.Msg
| CheckboxExampleMsg Examples.Checkbox.Msg
| DropdownMsg Examples.Dropdown.Msg | DropdownMsg Examples.Dropdown.Msg
| SegmentedControlMsg Examples.SegmentedControl.Msg | SegmentedControlMsg Examples.SegmentedControl.Msg
| SelectMsg Examples.Select.Msg | SelectMsg Examples.Select.Msg
@ -76,6 +80,13 @@ update msg moduleStates =
, Cmd.map ButtonExampleMsg cmd , Cmd.map ButtonExampleMsg cmd
) )
CheckboxExampleMsg msg ->
let
( checkboxExampleState, cmd ) =
Examples.Checkbox.update msg moduleStates.checkboxExampleState
in
( { moduleStates | checkboxExampleState = checkboxExampleState }, Cmd.map CheckboxExampleMsg cmd )
DropdownMsg msg -> DropdownMsg msg ->
let let
( dropdownState, cmd ) = ( dropdownState, cmd ) =
@ -162,6 +173,7 @@ container width children =
nriThemedModules : ModuleStates -> List (ModuleExample Msg) nriThemedModules : ModuleStates -> List (ModuleExample Msg)
nriThemedModules model = nriThemedModules model =
[ Examples.Button.example assets (exampleMessages ButtonExampleMsg) model.buttonExampleState [ Examples.Button.example assets (exampleMessages ButtonExampleMsg) model.buttonExampleState
, Examples.Checkbox.example CheckboxExampleMsg model.checkboxExampleState
, Examples.Dropdown.example DropdownMsg model.dropdownState , Examples.Dropdown.example DropdownMsg model.dropdownState
, Examples.Icon.example , Examples.Icon.example
, Examples.Page.example NoOp , Examples.Page.example NoOp

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:915d0521f5bc23c22b393b71da2cd18503077fed13bec61822ce2900b96b0813
size 2051

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4e5ceec4a07abd78ea907ec309cae33f146360d908c834b75c1b0e5fb28f5838
size 3002

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d45df2ba55c45815fd7df437ec023b49542c46428548d344480c253abf5b2062
size 4341

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2e10d667be9d7f32ef27714c59a1743796872127eb33870075b013458e6c58c8
size 1282

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3bcb4a83ca4e7c4739748a81d5f56fbedf9d9925fe1db8e9fe2e4312c3fa21d2
size 1069

View File

@ -0,0 +1,180 @@
module Spec.Nri.Ui.PremiumCheckbox.V1 exposing (spec)
import Html.Styled
import Nri.Ui.AssetPath exposing (Asset(Asset))
import Nri.Ui.Checkbox.V3 exposing (IsSelected(..))
import Nri.Ui.Data.PremiumLevel exposing (PremiumLevel(..))
import Nri.Ui.PremiumCheckbox.V1 as PremiumCheckbox
import Test exposing (..)
import Test.Html.Query as Query
import Test.Html.Selector as Selector
premiumView config =
PremiumCheckbox.premium assets
{ label = config.label
, id = "id"
, selected = config.selected
, disabled = config.disabled
, teacherPremiumLevel = config.teacherPremiumLevel
, contentPremiumLevel = config.contentPremiumLevel
, showFlagWhenLocked = config.showFlagWhenLocked
, onChange = \_ -> ()
, onLockedClick = ()
, noOpMsg = ()
}
|> Html.Styled.toUnstyled
|> Query.fromHtml
spec : Test
spec =
describe "Nri.Ui.PremiumCheckbox.V1"
[ describe "premium"
[ test "displays the label" <|
\() ->
premiumView
{ label = "i am label"
, selected = Selected
, disabled = False
, teacherPremiumLevel = Free
, contentPremiumLevel = Free
, showFlagWhenLocked = True
}
|> Query.has [ Selector.text "i am label" ]
, test "appears selected when Selected is passed in" <|
\() ->
premiumView
{ label = "i am label"
, selected = Selected
, disabled = False
, teacherPremiumLevel = Free
, contentPremiumLevel = Free
, showFlagWhenLocked = True
}
|> Query.has [ Selector.class "checkbox-Checked" ]
, test "appears unselected when NotSelected is passed in" <|
\() ->
premiumView
{ label = "i am label"
, selected = NotSelected
, disabled = False
, teacherPremiumLevel = Free
, contentPremiumLevel = Free
, showFlagWhenLocked = True
}
|> Query.has [ Selector.class "checkbox-Unchecked" ]
, test "appears partially selected when PartiallySelected is passed in" <|
\() ->
premiumView
{ label = "i am label"
, selected = PartiallySelected
, disabled = False
, teacherPremiumLevel = Free
, contentPremiumLevel = Free
, showFlagWhenLocked = True
}
|> Query.has [ Selector.class "checkbox-Indeterminate" ]
, test "appears locked when teacherPremiumLevel < contentPremiumLevel" <|
\() ->
premiumView
{ label = "i am label"
, selected = PartiallySelected
, disabled = False
, teacherPremiumLevel = Free
, contentPremiumLevel = Premium
, showFlagWhenLocked = True
}
|> Query.has [ Selector.class "checkbox-LockOnInsideClass" ]
, test "appears unlocked when teacherPremiumLevel >= contentPremiumLevel" <|
\() ->
premiumView
{ label = "i am label"
, selected = PartiallySelected
, disabled = False
, teacherPremiumLevel = Premium
, contentPremiumLevel = Premium
, showFlagWhenLocked = True
}
|> Query.hasNot [ Selector.class "checkbox-LockOnInsideClass" ]
, test "appears with P flag when teacherPremiumLevel >= contentPremiumLevel" <|
\() ->
premiumView
{ label = "i am label"
, selected = PartiallySelected
, disabled = False
, teacherPremiumLevel = Premium
, contentPremiumLevel = Premium
, showFlagWhenLocked = False
}
|> Query.has [ Selector.class "checkbox-PremiumClass" ]
, test "does not appear with P flag when teacherPremiumLevel < contentPremiumLevel and showFlagWhenLocked = False" <|
\() ->
premiumView
{ label = "i am label"
, selected = PartiallySelected
, disabled = False
, teacherPremiumLevel = Free
, contentPremiumLevel = Premium
, showFlagWhenLocked = False
}
|> Query.hasNot [ Selector.class "checkbox-PremiumClass" ]
, test "appears with P flag for Premium content when teacherPremiumLevel < contentPremiumLevel and showFlagWhenLocked = True" <|
\() ->
premiumView
{ label = "i am label"
, selected = PartiallySelected
, disabled = False
, teacherPremiumLevel = Free
, contentPremiumLevel = Premium
, showFlagWhenLocked = True
}
|> Query.has [ Selector.class "checkbox-PremiumClass" ]
, test "never shows P flag for nonPremium content" <|
\() ->
premiumView
{ label = "i am label"
, selected = PartiallySelected
, disabled = False
, teacherPremiumLevel = Free
, contentPremiumLevel = Free
, showFlagWhenLocked = True
}
|> Query.hasNot [ Selector.class "checkbox-PremiumClass" ]
, test "is not disabled when disabled = False and the checkbox is unlocked" <|
\() ->
premiumView
{ label = "i am label"
, selected = PartiallySelected
, disabled = False
, teacherPremiumLevel = Free
, contentPremiumLevel = Free
, showFlagWhenLocked = True
}
|> Query.has [ Selector.disabled False ]
, test "is disabled when disabled = True and the checkbox is unlocked" <|
\() ->
premiumView
{ label = "i am label"
, selected = PartiallySelected
, disabled = True
, teacherPremiumLevel = Free
, contentPremiumLevel = Free
, showFlagWhenLocked = True
}
|> Query.has [ Selector.disabled True ]
]
]
assets =
{ checkboxUnchecked_svg = Asset "checkboxUnchecked reference"
, checkboxChecked_svg = Asset "checkboxChecked reference"
, checkboxCheckedPartially_svg = Asset "checkboxCheckedPartially reference"
, iconPremiumUnlocked_png = Asset "iconPremiumUnlocked reference"
, iconCheck_png = Asset "iconCheck reference"
, iconPremiumLocked_png = Asset "iconPremiumLocked reference"
, checkboxLockOnInside_svg = Asset "checkboxLockOnInside reference"
, iconPremiumKey_png = Asset "iconPremiumKey reference"
, iconPremiumFlag_svg = Asset "iconPremiumFlag reference"
}

View File

@ -14,6 +14,7 @@
"elm-community/elm-test": "4.0.0 <= v < 5.0.0", "elm-community/elm-test": "4.0.0 <= v < 5.0.0",
"elm-lang/core": "5.1.1 <= v < 6.0.0", "elm-lang/core": "5.1.1 <= v < 6.0.0",
"elm-lang/html": "2.0.0 <= v < 3.0.0", "elm-lang/html": "2.0.0 <= v < 3.0.0",
"elm-lang/svg": "2.0.0 <= v < 3.0.0",
"pablohirafuji/elm-markdown": "2.0.4 <= v < 3.0.0", "pablohirafuji/elm-markdown": "2.0.4 <= v < 3.0.0",
"rtfeldman/elm-css": "13.1.1 <= v < 14.0.0", "rtfeldman/elm-css": "13.1.1 <= v < 14.0.0",
"rtfeldman/elm-css-helpers": "2.1.0 <= v < 3.0.0", "rtfeldman/elm-css-helpers": "2.1.0 <= v < 3.0.0",