Add ability to set custom CSS on checkboxes

This commit is contained in:
charbelrami 2022-07-26 17:21:38 -03:00
parent e372bcbc13
commit 9f65f823f9
9 changed files with 468 additions and 6 deletions

View File

@ -1,4 +1,5 @@
Nri.Ui.Accordion.V1,upgrade to V3
Nri.Ui.Checkbox.V5,upgrade to V6
Nri.Ui.Menu.V1,upgrade to V3
Nri.Ui.SortableTable.V2,upgrade to V3
Nri.Ui.Tabs.V6,upgrade to V7

1 Nri.Ui.Accordion.V1 upgrade to V3
2 Nri.Ui.Checkbox.V5 upgrade to V6
3 Nri.Ui.Menu.V1 upgrade to V3
4 Nri.Ui.SortableTable.V2 upgrade to V3
5 Nri.Ui.Tabs.V6 upgrade to V7

View File

@ -15,6 +15,7 @@
"Nri.Ui.Button.V10",
"Nri.Ui.Carousel.V1",
"Nri.Ui.Checkbox.V5",
"Nri.Ui.Checkbox.V6",
"Nri.Ui.ClickableSvg.V2",
"Nri.Ui.ClickableText.V3",
"Nri.Ui.Container.V2",

View File

@ -42,6 +42,9 @@ hint = 'upgrade to V3'
hint = 'upgrade to V10'
usages = ['styleguide-app/../src/Nri/Ui/SlideModal/V2.elm']
[forbidden."Nri.Ui.Checkbox.V5"]
hint = 'upgrade to V6'
[forbidden."Nri.Ui.ClickableSvg.V1"]
hint = 'upgrade to V2'
usages = ['styleguide-app/Examples/Tooltip.elm']

333
src/Nri/Ui/Checkbox/V6.elm Normal file
View File

@ -0,0 +1,333 @@
module Nri.Ui.Checkbox.V6 exposing
( Model, Theme(..), IsSelected(..)
, view, viewWithLabel
, selectedFromBool
, viewIcon, checkboxLockOnInside
)
{-|
# Patch changes
- Use Nri.Ui.Svg.V1 rather than a custom Icon type specific to this module
- Make the filter ids within the svg unique (now the id depends on the checkbox identifier)
- Explicitly box-sizing content-box on the label (<https://github.com/NoRedInk/NoRedInk/pull/30886#issuecomment-737854831>)
# Changes from V5:
- Adds `containerCss`
- Adds `enabledLabelCss`
- Adds `disabledLabelCss`
@docs Model, Theme, IsSelected
@docs view, viewWithLabel
@docs selectedFromBool
@docs viewIcon, checkboxLockOnInside
-}
import Accessibility.Styled as Html
import Accessibility.Styled.Aria as Aria
import Accessibility.Styled.Style
import CheckboxIcons
import Css exposing (..)
import Css.Global
import Html.Styled
import Html.Styled.Attributes as Attributes exposing (css)
import Html.Styled.Events as Events
import Json.Decode
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Fonts.V1 as Fonts
import Nri.Ui.Svg.V1 exposing (Svg)
{-| -}
type alias Model msg =
{ identifier : String
, label : String
, setterMsg : Bool -> msg
, selected : IsSelected
, disabled : Bool
, theme : Theme
, containerCss : List Css.Style
, enabledLabelCss : List Css.Style
, disabledLabelCss : List Css.Style
}
{-|
= Selected -- Checked (rendered with a checkmark)
| NotSelected -- Not Checked (rendered blank)
| PartiallySelected -- Indeterminate (rendered dash)
-}
type IsSelected
= Selected
| NotSelected
| PartiallySelected
{-| -}
type Theme
= Square
| Locked
{-| If your selectedness is always selected or not selected,
you will likely store that state as a `Bool` in your model.
`selectedFromBool` lets you easily convert that into an `IsSelected` value
for use with `Nri.Ui.Checkbox`.
-}
selectedFromBool : Bool -> IsSelected
selectedFromBool isSelected =
case isSelected of
True ->
Selected
False ->
NotSelected
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 : Model msg -> Html.Html msg
view model =
buildCheckbox model
(\label ->
Html.span Accessibility.Styled.Style.invisible
[ Html.text label ]
)
{-| Shows a checkbox and its label text
-}
viewWithLabel : Model msg -> Html.Html msg
viewWithLabel model =
buildCheckbox model <|
\label -> Html.span [] [ Html.text label ]
buildCheckbox : Model msg -> (String -> Html.Html msg) -> Html.Html msg
buildCheckbox model labelView =
checkboxContainer model
[ viewCheckbox model
, case model.theme of
Square ->
let
icon =
case model.selected of
Selected ->
CheckboxIcons.checked model.identifier
NotSelected ->
CheckboxIcons.unchecked model.identifier
PartiallySelected ->
CheckboxIcons.checkedPartially model.identifier
in
if model.disabled then
viewDisabledLabel model labelView icon
else
viewEnabledLabel model labelView icon
Locked ->
if model.disabled then
viewDisabledLabel model labelView (checkboxLockOnInside model.identifier)
else
viewEnabledLabel model labelView (checkboxLockOnInside model.identifier)
]
checkboxContainer : { a | identifier : String, containerCss : List Style } -> List (Html.Html msg) -> Html.Html msg
checkboxContainer model =
Html.Styled.span
[ css
[ display block
, height inherit
, position relative
, marginLeft (px -4)
, pseudoClass "focus-within"
[ Css.Global.descendants
[ Css.Global.class "checkbox-icon-container"
[ borderColor (rgb 0 95 204)
]
]
]
, Css.Global.descendants
[ Css.Global.input [ position absolute, top (calc (pct 50) minus (px 10)), left (px 10) ]
]
, Css.batch model.containerCss
]
, Attributes.id (model.identifier ++ "-container")
, Events.stopPropagationOn "click" (Json.Decode.fail "stop click propagation")
]
viewCheckbox :
{ a
| identifier : String
, setterMsg : Bool -> msg
, selected : IsSelected
, disabled : Bool
}
-> Html.Html msg
viewCheckbox model =
Html.checkbox model.identifier
(selectedToMaybe model.selected)
[ Attributes.id model.identifier
, if model.disabled then
Aria.disabled True
else
Events.onCheck (\_ -> onCheck model)
]
viewEnabledLabel :
{ a
| identifier : String
, setterMsg : Bool -> msg
, selected : IsSelected
, label : String
, enabledLabelCss : List Style
}
-> (String -> Html.Html msg)
-> Svg
-> Html.Html msg
viewEnabledLabel model labelView icon =
Html.Styled.label
[ Attributes.for model.identifier
, labelClass model.selected
, css
[ positioning
, textStyle
, cursor pointer
, Css.batch model.enabledLabelCss
]
]
[ viewIcon [] icon
, labelView model.label
]
onCheck : { a | setterMsg : Bool -> msg, selected : IsSelected } -> msg
onCheck model =
selectedToMaybe model.selected
|> Maybe.withDefault False
|> not
|> model.setterMsg
viewDisabledLabel :
{ a | identifier : String, selected : IsSelected, label : String, disabledLabelCss : List Style }
-> (String -> Html.Html msg)
-> Svg
-> Html.Html msg
viewDisabledLabel model labelView icon =
Html.Styled.label
[ Attributes.for model.identifier
, labelClass model.selected
, css
[ positioning
, textStyle
, outline none
, cursor auto
, Css.batch model.disabledLabelCss
]
]
[ viewIcon [ opacity (num 0.4) ] icon
, labelView model.label
]
labelClass : IsSelected -> Html.Styled.Attribute msg
labelClass isSelected =
case isSelected of
Selected ->
toClassList [ "Label", "Checked" ]
NotSelected ->
toClassList [ "Label", "Unchecked" ]
PartiallySelected ->
toClassList [ "Label", "Indeterminate" ]
toClassList : List String -> Html.Styled.Attribute msg
toClassList =
List.map (\a -> ( "checkbox-V5__" ++ a, True )) >> Attributes.classList
positioning : Style
positioning =
batch
[ display inlineBlock
, padding4 (px 13) zero (px 13) (px 40)
, position relative
]
textStyle : Style
textStyle =
batch
[ Fonts.baseFont
, fontSize (px 15)
, fontWeight (int 600)
, color Colors.navy
]
{-| -}
viewIcon : List Style -> Svg -> Html.Html msg
viewIcon styles icon =
Html.div
[ css
[ position absolute
, left zero
, top (calc (pct 50) minus (px 18))
, border3 (px 2) solid transparent
, padding (px 2)
, borderRadius (px 3)
, height (Css.px 27)
, boxSizing contentBox
]
, Attributes.class "checkbox-icon-container"
]
[ Html.div
[ css
[ display inlineBlock
, backgroundColor Colors.white
, height (Css.px 27)
, borderRadius (px 4)
]
]
[ Nri.Ui.Svg.V1.toHtml (Nri.Ui.Svg.V1.withCss styles icon)
]
]
{-| -}
checkboxLockOnInside : String -> Svg
checkboxLockOnInside =
CheckboxIcons.lockOnInside

View File

@ -5,6 +5,9 @@ module Nri.Ui.PremiumCheckbox.V8 exposing
, Attribute
, disabled, enabled
, id
, setCheckboxContainerCss
, setCheckboxEnabledLabelCss
, setCheckboxDisabledLabelCss
)
{-| Changes from V7:
@ -12,6 +15,7 @@ module Nri.Ui.PremiumCheckbox.V8 exposing
- Use PremiumDisplay instead of PremiumLevel
- Rename showPennant to onLockedClick
- Fix clicking on locked checkbox to send a onLockedClick
- Exposes checkbox custom styling
@docs view
@ -29,13 +33,20 @@ module Nri.Ui.PremiumCheckbox.V8 exposing
@docs disabled, enabled
@docs id
### Custom CSS
@docs setCheckboxContainerCss
@docs setCheckboxEnabledLabelCss
@docs setCheckboxDisabledLabelCss
-}
import Accessibility.Styled as Html exposing (Html)
import Css exposing (..)
import Html.Styled.Attributes as Attributes exposing (class, css)
import Html.Styled.Events as Events
import Nri.Ui.Checkbox.V5 as Checkbox
import Nri.Ui.Checkbox.V6 as Checkbox
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Data.PremiumDisplay as PremiumDisplay exposing (PremiumDisplay)
import Nri.Ui.Fonts.V1 as Fonts
@ -93,6 +104,27 @@ setSelectionStatus status =
Attribute (\config -> { config | selected = status })
{-| Set custom CSS for the checkbox container
-}
setCheckboxContainerCss : List Css.Style -> Attribute msg
setCheckboxContainerCss checkboxContainerCss =
Attribute <| \config -> { config | checkboxContainerCss = checkboxContainerCss }
{-| Set custom CSS for the enabled checkbox label
-}
setCheckboxEnabledLabelCss : List Css.Style -> Attribute msg
setCheckboxEnabledLabelCss checkboxEnabledLabelCss =
Attribute <| \config -> { config | checkboxEnabledLabelCss = checkboxEnabledLabelCss }
{-| Set custom CSS for the disabled checkbox label
-}
setCheckboxDisabledLabelCss : List Css.Style -> Attribute msg
setCheckboxDisabledLabelCss checkboxDisabledLabelCss =
Attribute <| \config -> { config | checkboxDisabledLabelCss = checkboxDisabledLabelCss }
{-| -}
selected : Bool -> Attribute msg
selected isSelected =
@ -125,6 +157,9 @@ type alias Config msg =
, containerCss : List Css.Style
, selected : Checkbox.IsSelected
, onLockedMsg : Maybe msg
, checkboxContainerCss : List Css.Style
, checkboxEnabledLabelCss : List Css.Style
, checkboxDisabledLabelCss : List Css.Style
}
@ -139,6 +174,9 @@ emptyConfig =
]
, selected = Checkbox.NotSelected
, onLockedMsg = Nothing
, checkboxContainerCss = []
, checkboxEnabledLabelCss = []
, checkboxDisabledLabelCss = []
}
@ -203,6 +241,9 @@ view { label, onChange } attributes =
else
Checkbox.Square
, containerCss = config.checkboxContainerCss
, enabledLabelCss = config.checkboxEnabledLabelCss
, disabledLabelCss = config.checkboxDisabledLabelCss
}
]

View File

@ -13,7 +13,7 @@ import Example exposing (Example)
import Html.Styled as Html exposing (..)
import Html.Styled.Attributes exposing (css)
import KeyboardSupport exposing (Key(..))
import Nri.Ui.Checkbox.V5 as Checkbox
import Nri.Ui.Checkbox.V6 as Checkbox
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Data.PremiumDisplay as PremiumDisplay
import Nri.Ui.Fonts.V1 as Fonts
@ -39,7 +39,7 @@ type alias State =
example : Example State Msg
example =
{ name = "Checkbox"
, version = 5
, version = 6
, state = init
, update = update
, subscriptions = \_ -> Sub.none
@ -53,6 +53,8 @@ example =
, viewMultilineCheckboxes state
, Heading.h2 [ Heading.style Heading.Subhead ] [ text "Premium Checkboxes" ]
, viewPremiumCheckboxes state
, viewCustomStyledCheckbox state
, viewCustomStyledPremiumCheckboxes state
]
, categories = [ Inputs ]
, keyboardSupport =
@ -128,6 +130,9 @@ viewInteractableCheckbox id state =
, selected = isSelected id state
, disabled = False
, theme = Checkbox.Square
, containerCss = []
, enabledLabelCss = []
, disabledLabelCss = []
}
@ -140,6 +145,9 @@ viewIndeterminateCheckbox id state =
, selected = Checkbox.PartiallySelected
, disabled = True
, theme = Checkbox.Square
, containerCss = []
, enabledLabelCss = []
, disabledLabelCss = []
}
@ -152,6 +160,9 @@ viewLockedOnInsideCheckbox id state =
, selected = Checkbox.NotSelected
, disabled = True
, theme = Checkbox.Locked
, containerCss = []
, enabledLabelCss = []
, disabledLabelCss = []
}
@ -164,6 +175,9 @@ viewDisabledCheckbox id state =
, selected = isSelected id state
, disabled = True
, theme = Checkbox.Square
, containerCss = []
, enabledLabelCss = []
, disabledLabelCss = []
}
@ -183,6 +197,9 @@ viewMultilineCheckboxes state =
, selected = isSelected id state
, disabled = False
, theme = Checkbox.Square
, containerCss = []
, enabledLabelCss = []
, disabledLabelCss = []
}
, Checkbox.viewWithLabel
{ identifier = "fake-partially-selected"
@ -191,6 +208,9 @@ viewMultilineCheckboxes state =
, selected = Checkbox.PartiallySelected
, disabled = True
, theme = Checkbox.Square
, containerCss = []
, enabledLabelCss = []
, disabledLabelCss = []
}
, Checkbox.viewWithLabel
{ identifier = "fake-not-selected-locked"
@ -199,6 +219,9 @@ viewMultilineCheckboxes state =
, selected = Checkbox.NotSelected
, disabled = True
, theme = Checkbox.Locked
, containerCss = []
, enabledLabelCss = []
, disabledLabelCss = []
}
, Checkbox.viewWithLabel
{ identifier = "fake-not-selected-square"
@ -207,6 +230,9 @@ viewMultilineCheckboxes state =
, selected = Checkbox.NotSelected
, disabled = True
, theme = Checkbox.Square
, containerCss = []
, enabledLabelCss = []
, disabledLabelCss = []
}
]
@ -241,6 +267,47 @@ viewPremiumCheckboxes state =
]
viewCustomStyledCheckbox : State -> Html Msg
viewCustomStyledCheckbox state =
Html.section
[ css [ Css.width (Css.px 500) ] ]
[ Heading.h2 [ Heading.style Heading.Subhead ] [ Html.text "Custom-styled Checkboxes" ]
, let
id =
"styleguide-checkbox-custom-style"
in
Checkbox.viewWithLabel
{ identifier = id
, label = "This is a custom-styled Checkbox"
, setterMsg = ToggleCheck id
, selected = isSelected id state
, disabled = False
, theme = Checkbox.Square
, containerCss = [ Css.backgroundColor Colors.navy ]
, enabledLabelCss = [ Css.color Colors.white ]
, disabledLabelCss = []
}
]
viewCustomStyledPremiumCheckboxes : State -> Html Msg
viewCustomStyledPremiumCheckboxes state =
Html.section
[ css [ Css.width (Css.px 500) ] ]
[ Heading.h2 [ Heading.style Heading.Subhead ] [ Html.text "Custom-styled Premium Checkboxes" ]
, PremiumCheckbox.view
{ label = "This is a custom-styled Premium Checkbox"
, onChange = ToggleCheck "premium-custom"
}
[ PremiumCheckbox.premium PremiumDisplay.PremiumUnlocked
, PremiumCheckbox.onLockedClick NoOp
, PremiumCheckbox.selected (Set.member "premium-custom" state.isChecked)
, PremiumCheckbox.setCheckboxContainerCss [ Css.backgroundColor Colors.navy ]
, PremiumCheckbox.setCheckboxEnabledLabelCss [ Css.color Colors.white ]
]
]
type alias Id =
String

View File

@ -21,7 +21,7 @@ import Example exposing (Example)
import Html.Styled as Html exposing (Html)
import Html.Styled.Attributes as Attributes exposing (css)
import Html.Styled.Events as Events
import Nri.Ui.Checkbox.V5 as Checkbox
import Nri.Ui.Checkbox.V6 as Checkbox
import Nri.Ui.Colors.Extra exposing (fromCssColor, toCssColor)
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Heading.V2 as Heading
@ -167,6 +167,9 @@ viewSettings { showIconName } =
, selected = Checkbox.selectedFromBool showIconName
, disabled = False
, theme = Checkbox.Square
, containerCss = []
, enabledLabelCss = []
, disabledLabelCss = []
}
@ -306,6 +309,9 @@ viewSingularExampleSettings groups state =
, selected = Checkbox.selectedFromBool state.showBorder
, disabled = False
, theme = Checkbox.Square
, containerCss = []
, enabledLabelCss = []
, disabledLabelCss = []
}
, Html.label []
[ Html.text "Color: "

View File

@ -12,7 +12,7 @@ import Dict exposing (Dict)
import Example exposing (Example)
import Html.Styled as Html
import Html.Styled.Attributes as Attributes exposing (css)
import Nri.Ui.Checkbox.V5 as Checkbox
import Nri.Ui.Checkbox.V6 as Checkbox
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Heading.V2 as Heading
import Nri.Ui.InputStyles.V3 as InputStyles exposing (Theme(..))
@ -80,6 +80,9 @@ example =
, selected = state.showLabel
, disabled = False
, theme = Checkbox.Square
, containerCss = []
, enabledLabelCss = []
, disabledLabelCss = []
}
, Checkbox.viewWithLabel
{ identifier = "textarea-autoresize"
@ -88,6 +91,9 @@ example =
, selected = state.autoResize
, disabled = False
, theme = Checkbox.Square
, containerCss = []
, enabledLabelCss = []
, disabledLabelCss = []
}
, Checkbox.viewWithLabel
{ identifier = "textarea-isInError"
@ -96,6 +102,9 @@ example =
, selected = state.isInError
, disabled = False
, theme = Checkbox.Square
, containerCss = []
, enabledLabelCss = []
, disabledLabelCss = []
}
]
, TextArea.view

View File

@ -11,6 +11,7 @@
"Nri.Ui.Button.V10",
"Nri.Ui.Carousel.V1",
"Nri.Ui.Checkbox.V5",
"Nri.Ui.Checkbox.V6",
"Nri.Ui.ClickableSvg.V2",
"Nri.Ui.ClickableText.V3",
"Nri.Ui.Container.V2",