mirror of
https://github.com/NoRedInk/noredink-ui.git
synced 2024-12-18 19:21:29 +03:00
04ca511a1a
Improve premium radio buttons locked clicks
410 lines
12 KiB
Elm
410 lines
12 KiB
Elm
module Examples.RadioButton exposing
|
|
( example
|
|
, State, Msg
|
|
)
|
|
|
|
{-|
|
|
|
|
@docs example
|
|
@docs State, Msg
|
|
|
|
-}
|
|
|
|
import Accessibility.Styled.Key as Key
|
|
import Browser.Dom as Dom
|
|
import Category exposing (Category(..))
|
|
import CommonControls exposing (premiumDisplay)
|
|
import Css
|
|
import Debug.Control as Control exposing (Control)
|
|
import Debug.Control.Extra as ControlExtra
|
|
import Example exposing (Example)
|
|
import Html.Styled as Html exposing (..)
|
|
import Html.Styled.Attributes exposing (css)
|
|
import KeyboardSupport exposing (Direction(..), Key(..))
|
|
import Nri.Ui.Button.V10 as Button
|
|
import Nri.Ui.Colors.V1 as Colors
|
|
import Nri.Ui.Data.PremiumDisplay as PremiumDisplay
|
|
import Nri.Ui.Modal.V11 as Modal
|
|
import Nri.Ui.RadioButton.V4 as RadioButton
|
|
import Nri.Ui.Text.V6 as Text
|
|
import Task
|
|
|
|
|
|
{-| -}
|
|
example : Example State Msg
|
|
example =
|
|
{ name = "RadioButton"
|
|
, version = 4
|
|
, state = init
|
|
, update = update
|
|
, subscriptions = subscriptions
|
|
, preview = preview
|
|
, view = view
|
|
, categories = [ Inputs ]
|
|
, keyboardSupport =
|
|
[ { keys = [ Arrow Left ]
|
|
, result = "Move the focus & select the radio button to the left"
|
|
}
|
|
, { keys = [ Arrow Right ]
|
|
, result = "Move the focus & select the radio button to the right"
|
|
}
|
|
, { keys = [ Space ]
|
|
, result = "Select the current radio button"
|
|
}
|
|
]
|
|
}
|
|
|
|
|
|
preview : List (Html Never)
|
|
preview =
|
|
let
|
|
selectedValue =
|
|
Just "Selected"
|
|
in
|
|
[ RadioButton.view
|
|
{ label = "Unselected"
|
|
, name = "preview-radio-inputs"
|
|
, value = "Unselected"
|
|
, selectedValue = selectedValue
|
|
, valueToString = identity
|
|
}
|
|
[ RadioButton.custom [ Key.tabbable False ] ]
|
|
, RadioButton.view
|
|
{ label = "Selected"
|
|
, name = "preview-radio-inputs"
|
|
, value = "Selected"
|
|
, selectedValue = selectedValue
|
|
, valueToString = identity
|
|
}
|
|
[ RadioButton.custom [ Key.tabbable False ] ]
|
|
, RadioButton.view
|
|
{ label = "Premium"
|
|
, name = "preview-radio-inputs"
|
|
, value = "Premium"
|
|
, selectedValue = selectedValue
|
|
, valueToString = identity
|
|
}
|
|
[ RadioButton.custom [ Key.tabbable False ]
|
|
, RadioButton.premium PremiumDisplay.PremiumLocked
|
|
]
|
|
]
|
|
|
|
|
|
{-| -}
|
|
view : State -> List (Html Msg)
|
|
view state =
|
|
let
|
|
selectionSettings =
|
|
Control.currentValue state.selectionSettings
|
|
in
|
|
[ div
|
|
[ css [ Css.displayFlex, Css.justifyContent Css.spaceBetween ] ]
|
|
[ Control.view SetSelectionSettings state.selectionSettings |> fromUnstyled
|
|
, viewExamples selectionSettings state.selectedValue
|
|
, viewExamplesCode selectionSettings state.selectedValue
|
|
]
|
|
, Modal.view
|
|
{ title = "Go Premium!"
|
|
, wrapMsg = ModalMsg
|
|
, content = [ Text.mediumBody [ Text.plaintext "Often, we'll launch a modal showing the benefits of premium when a Premium pennant is clicked." ] ]
|
|
, footer =
|
|
[ Button.button "Okay"
|
|
[ Button.modal
|
|
, Button.onClick CloseModal
|
|
, Button.id "close-premium-modal"
|
|
]
|
|
]
|
|
, focusTrap =
|
|
{ focus = Focus
|
|
, firstId = Modal.closeButtonId
|
|
, lastId = "close-premium-modal"
|
|
}
|
|
}
|
|
[ Modal.closeButton ]
|
|
state.modal
|
|
]
|
|
|
|
|
|
viewExamplesCode : SelectionSettings -> Maybe Selection -> Html Msg
|
|
viewExamplesCode selectionSettings selectedValue =
|
|
let
|
|
selectedValueString =
|
|
case selectedValue of
|
|
Just value ->
|
|
"Just " ++ selectionToString value
|
|
|
|
Nothing ->
|
|
"Nothing"
|
|
|
|
toExampleCode ( kind, settings ) =
|
|
"RadioButton.view"
|
|
++ ("\n\t{ label = " ++ selectionToString kind)
|
|
++ "\n\t, name = \"pets\""
|
|
++ ("\n\t, value = " ++ selectionToString kind)
|
|
++ ("\n\t, selectedValue = " ++ selectedValueString)
|
|
++ "\n\t, valueToString = toString"
|
|
++ "\n\t}\n\t[ "
|
|
++ String.join "\n\t, " (List.map Tuple.first settings)
|
|
++ "\n\t] "
|
|
in
|
|
Html.code
|
|
[ css
|
|
[ Css.display Css.block
|
|
, Css.marginLeft (Css.px 20)
|
|
, Css.flexBasis (Css.px 300)
|
|
, Css.flexGrow Css.zero
|
|
]
|
|
]
|
|
[ Html.pre [ css [ Css.whiteSpace Css.preWrap ] ]
|
|
[ text
|
|
(" " ++ String.join "\n, " (List.map toExampleCode (examples selectionSettings)))
|
|
]
|
|
]
|
|
|
|
|
|
viewExamples : SelectionSettings -> Maybe Selection -> Html Msg
|
|
viewExamples selectionSettings selectedValue =
|
|
let
|
|
viewExample_ ( kind, settings ) =
|
|
RadioButton.view
|
|
{ label = selectionToString kind
|
|
, name = "pets"
|
|
, value = kind
|
|
, selectedValue = selectedValue
|
|
, valueToString = selectionToString
|
|
}
|
|
(RadioButton.onSelect Select :: List.map Tuple.second settings)
|
|
in
|
|
div [ css [ Css.flexBasis (Css.px 300) ] ]
|
|
(List.map viewExample_ (examples selectionSettings))
|
|
|
|
|
|
examples :
|
|
SelectionSettings
|
|
-> List ( Selection, List ( String, RadioButton.Attribute Selection Msg ) )
|
|
examples selectionSettings =
|
|
[ ( Dogs, selectionSettings.dogs )
|
|
, ( Cats, selectionSettings.cats )
|
|
]
|
|
|
|
|
|
type Selection
|
|
= Dogs
|
|
| Cats
|
|
|
|
|
|
selectionToString : Selection -> String
|
|
selectionToString selection =
|
|
case selection of
|
|
Dogs ->
|
|
"Dogs"
|
|
|
|
Cats ->
|
|
"Cats"
|
|
|
|
|
|
{-| -}
|
|
type alias State =
|
|
{ selectedValue : Maybe Selection
|
|
, modal : Modal.Model
|
|
, selectionSettings : Control SelectionSettings
|
|
}
|
|
|
|
|
|
{-| -}
|
|
init : State
|
|
init =
|
|
{ selectedValue = Nothing
|
|
, modal = Modal.init
|
|
, selectionSettings = initSelectionSettings
|
|
}
|
|
|
|
|
|
type alias SelectionSettings =
|
|
{ dogs : List ( String, RadioButton.Attribute Selection Msg )
|
|
, cats : List ( String, RadioButton.Attribute Selection Msg )
|
|
}
|
|
|
|
|
|
initSelectionSettings : Control SelectionSettings
|
|
initSelectionSettings =
|
|
Control.record SelectionSettings
|
|
|> Control.field "Dogs" controlAttributes
|
|
|> Control.field "Cats" controlAttributes
|
|
|
|
|
|
controlAttributes : Control (List ( String, RadioButton.Attribute Selection Msg ))
|
|
controlAttributes =
|
|
ControlExtra.list
|
|
|> ControlExtra.optionalListItem "visibility" labelVisibility
|
|
|> ControlExtra.optionalListItem "status" disabledOrEnabled
|
|
|> ControlExtra.optionalListItem "onLockedClick" onLockedClick
|
|
|> ControlExtra.optionalListItem "premium"
|
|
-- TODO: allow the teacher premium level to vary as well:
|
|
(Control.map
|
|
(\( premiumDisplay, pDisplay ) ->
|
|
( "RadioButton.premium " ++ premiumDisplay, RadioButton.premium pDisplay )
|
|
)
|
|
premiumDisplay
|
|
)
|
|
|> ControlExtra.optionalListItem "containerCss"
|
|
(Control.choice
|
|
[ ( "100% width"
|
|
, Control.value
|
|
( "RadioButton.containerCss [ Css.width (Css.pct 100) ]"
|
|
, RadioButton.containerCss [ Css.width (Css.pct 100) ]
|
|
)
|
|
)
|
|
, ( "10px right margin"
|
|
, Control.value
|
|
( "RadioButton.containerCss [ Css.marginRight (Css.px 10) ]"
|
|
, RadioButton.containerCss [ Css.marginRight (Css.px 10) ]
|
|
)
|
|
)
|
|
]
|
|
)
|
|
|> ControlExtra.optionalListItem "labelCss"
|
|
(Control.choice
|
|
[ ( "backgroundColor highlightMagenta"
|
|
, Control.value
|
|
( "RadioButton.labelCss [ Css.backgroundColor Colors.highlightMagenta ]"
|
|
, RadioButton.labelCss [ Css.backgroundColor Colors.highlightMagenta ]
|
|
)
|
|
)
|
|
, ( "1px ochreDark border"
|
|
, Control.value
|
|
( "RadioButton.labelCss [ Css.border3 (Css.px 1) Css.solid Colors.ochreDark ]"
|
|
, RadioButton.labelCss [ Css.border3 (Css.px 1) Css.solid Colors.ochreDark ]
|
|
)
|
|
)
|
|
]
|
|
)
|
|
|> ControlExtra.optionalListItem "disclosure" controlDisclosure
|
|
|> ControlExtra.optionalListItem "errorIf"
|
|
(Control.map
|
|
(\inError ->
|
|
( "RadioButton.errorIf " ++ Debug.toString inError
|
|
, RadioButton.errorIf inError
|
|
)
|
|
)
|
|
<|
|
|
Control.bool True
|
|
)
|
|
|> ControlExtra.optionalListItem "errorMessage"
|
|
(Control.map
|
|
(\message ->
|
|
( "RadioButton.errorMessage (Just \"" ++ message ++ "\")"
|
|
, RadioButton.errorMessage (Just message)
|
|
)
|
|
)
|
|
<|
|
|
Control.string "The statement must be true."
|
|
)
|
|
|> ControlExtra.optionalListItem "guidance"
|
|
(Control.map
|
|
(\content ->
|
|
( "RadioButton.guidance \"" ++ content ++ "\""
|
|
, RadioButton.guidance content
|
|
)
|
|
)
|
|
<|
|
|
Control.string "The statement must be true."
|
|
)
|
|
|
|
|
|
labelVisibility : Control ( String, RadioButton.Attribute Selection Msg )
|
|
labelVisibility =
|
|
Control.choice
|
|
[ ( "hiddenLabel", Control.value ( "RadioButton.hiddenLabel", RadioButton.hiddenLabel ) )
|
|
, ( "visibleLabel", Control.value ( "RadioButton.visibleLabel", RadioButton.visibleLabel ) )
|
|
]
|
|
|
|
|
|
disabledOrEnabled : Control ( String, RadioButton.Attribute Selection Msg )
|
|
disabledOrEnabled =
|
|
Control.choice
|
|
[ ( "disabled", Control.value ( "RadioButton.disabled", RadioButton.disabled ) )
|
|
, ( "enabled", Control.value ( "RadioButton.enabled", RadioButton.enabled ) )
|
|
]
|
|
|
|
|
|
onLockedClick : Control ( String, RadioButton.Attribute Selection Msg )
|
|
onLockedClick =
|
|
Control.value
|
|
( "RadioButton.onLockedClick OpenPremiumModal"
|
|
, RadioButton.onLockedClick (OpenModal "dogs")
|
|
)
|
|
|
|
|
|
controlDisclosure : Control ( String, RadioButton.Attribute Selection Msg )
|
|
controlDisclosure =
|
|
Control.map
|
|
(\content ->
|
|
( "RadioButton.disclosure [ Text.smallBody [ Text.plaintext \""
|
|
++ content
|
|
++ "\" ]"
|
|
, RadioButton.disclosure [ Text.smallBody [ Text.plaintext content ] ]
|
|
)
|
|
)
|
|
(Control.string "These pets occupy themselves.")
|
|
|
|
|
|
type Msg
|
|
= OpenModal String
|
|
| ModalMsg Modal.Msg
|
|
| CloseModal
|
|
| Select Selection
|
|
| SetSelectionSettings (Control SelectionSettings)
|
|
| Focus String
|
|
| Focused (Result Dom.Error ())
|
|
|
|
|
|
{-| -}
|
|
update : Msg -> State -> ( State, Cmd Msg )
|
|
update msg model =
|
|
case msg of
|
|
OpenModal returnFocusTo ->
|
|
let
|
|
( modal, cmd ) =
|
|
Modal.open
|
|
{ startFocusOn = Modal.closeButtonId
|
|
, returnFocusTo = returnFocusTo
|
|
}
|
|
in
|
|
( { model | modal = modal }, Cmd.map ModalMsg cmd )
|
|
|
|
ModalMsg modalMsg ->
|
|
let
|
|
( modal, cmd ) =
|
|
Modal.update { dismissOnEscAndOverlayClick = True }
|
|
modalMsg
|
|
model.modal
|
|
in
|
|
( { model | modal = modal }, Cmd.map ModalMsg cmd )
|
|
|
|
CloseModal ->
|
|
let
|
|
( modal, cmd ) =
|
|
Modal.close model.modal
|
|
in
|
|
( { model | modal = modal }, Cmd.map ModalMsg cmd )
|
|
|
|
Select value ->
|
|
( { model | selectedValue = Just value }, Cmd.none )
|
|
|
|
SetSelectionSettings selectionSettings ->
|
|
( { model | selectionSettings = selectionSettings }
|
|
, Cmd.none
|
|
)
|
|
|
|
Focus focus ->
|
|
( model, Task.attempt Focused (Dom.focus focus) )
|
|
|
|
Focused _ ->
|
|
( model, Cmd.none )
|
|
|
|
|
|
subscriptions : State -> Sub Msg
|
|
subscriptions { modal } =
|
|
Sub.map ModalMsg (Modal.subscriptions modal)
|