mirror of
https://github.com/NoRedInk/noredink-ui.git
synced 2024-12-25 06:33:09 +03:00
Merge pull request #918 from NoRedInk/bat/mobile-friendly-tooltip
Tooltip.V3
This commit is contained in:
commit
b3e32ffed8
@ -10,4 +10,5 @@ Nri.Ui.SideNav.V2,upgrade to V3
|
||||
Nri.Ui.Switch.V1,upgrade to V2
|
||||
Nri.Ui.Table.V4,upgrade to V5
|
||||
Nri.Ui.Tabs.V6,upgrade to V7
|
||||
Nri.Ui.Tooltip.V1,upgrade to V2
|
||||
Nri.Ui.Tooltip.V1,upgrade to V3
|
||||
Nri.Ui.Tooltip.V2,upgrade to V3
|
||||
|
|
2
elm.json
2
elm.json
@ -25,6 +25,7 @@
|
||||
"Nri.Ui.DisclosureIndicator.V2",
|
||||
"Nri.Ui.Divider.V2",
|
||||
"Nri.Ui.Effects.V1",
|
||||
"Nri.Ui.WhenFocusLeaves.V1",
|
||||
"Nri.Ui.FocusTrap.V1",
|
||||
"Nri.Ui.Fonts.V1",
|
||||
"Nri.Ui.Heading.V2",
|
||||
@ -72,6 +73,7 @@
|
||||
"Nri.Ui.TextInput.V7",
|
||||
"Nri.Ui.Tooltip.V1",
|
||||
"Nri.Ui.Tooltip.V2",
|
||||
"Nri.Ui.Tooltip.V3",
|
||||
"Nri.Ui.UiIcon.V1"
|
||||
],
|
||||
"elm-version": "0.19.0 <= v < 0.20.0",
|
||||
|
@ -149,5 +149,8 @@ hint = 'upgrade to V6'
|
||||
hint = 'upgrade to V7'
|
||||
|
||||
[forbidden."Nri.Ui.Tooltip.V1"]
|
||||
hint = 'upgrade to V2'
|
||||
hint = 'upgrade to V3'
|
||||
usages = ['styleguide/../src/Nri/Ui/Menu/V1.elm']
|
||||
|
||||
[forbidden."Nri.Ui.Tooltip.V2"]
|
||||
hint = 'upgrade to V3'
|
||||
|
@ -7,8 +7,7 @@ module Nri.Ui.FocusTrap.V1 exposing (FocusTrap, toAttribute)
|
||||
-}
|
||||
|
||||
import Accessibility.Styled as Html
|
||||
import Html.Styled.Events as Events
|
||||
import Json.Decode as Decode exposing (Decoder)
|
||||
import Nri.Ui.WhenFocusLeaves.V1 as WhenFocusLeaves
|
||||
|
||||
|
||||
{-| Defines how focus will wrap in reponse to tab keypresses in a part of the UI.
|
||||
@ -28,53 +27,13 @@ type alias FocusTrap msg =
|
||||
-}
|
||||
toAttribute : FocusTrap msg -> Html.Attribute msg
|
||||
toAttribute { firstId, lastId, focus } =
|
||||
onTab <|
|
||||
\elementId shiftKey ->
|
||||
-- if the user tabs back while on the first id,
|
||||
-- we want to wrap around to the last id.
|
||||
if elementId == firstId && shiftKey then
|
||||
Decode.succeed
|
||||
{ message = focus lastId
|
||||
, preventDefault = True
|
||||
, stopPropagation = False
|
||||
}
|
||||
|
||||
else if elementId == lastId && not shiftKey then
|
||||
-- if the user tabs forward while on the last id,
|
||||
-- we want to wrap around to the first id.
|
||||
Decode.succeed
|
||||
{ message = focus firstId
|
||||
, preventDefault = True
|
||||
, stopPropagation = False
|
||||
}
|
||||
|
||||
else
|
||||
Decode.fail "No need to intercept the key press"
|
||||
|
||||
|
||||
onTab :
|
||||
(String
|
||||
-> Bool
|
||||
-> Decoder { message : msg, preventDefault : Bool, stopPropagation : Bool }
|
||||
)
|
||||
-> Html.Attribute msg
|
||||
onTab do =
|
||||
Events.custom "keydown"
|
||||
(Decode.andThen
|
||||
(\( id, keyCode, shiftKey ) ->
|
||||
if keyCode == 9 then
|
||||
do id shiftKey
|
||||
|
||||
else
|
||||
Decode.fail "No need to intercept the key press"
|
||||
)
|
||||
decodeKeydown
|
||||
)
|
||||
|
||||
|
||||
decodeKeydown : Decoder ( String, Int, Bool )
|
||||
decodeKeydown =
|
||||
Decode.map3 (\id keyCode shiftKey -> ( id, keyCode, shiftKey ))
|
||||
(Decode.at [ "target", "id" ] Decode.string)
|
||||
(Decode.field "keyCode" Decode.int)
|
||||
(Decode.field "shiftKey" Decode.bool)
|
||||
WhenFocusLeaves.toAttribute
|
||||
{ firstId = firstId
|
||||
, lastId = lastId
|
||||
, -- if the user tabs back while on the first id,
|
||||
-- we want to wrap around to the last id.
|
||||
tabBackAction = focus lastId
|
||||
, -- if the user tabs forward while on the last id,
|
||||
-- we want to wrap around to the first id.
|
||||
tabForwardAction = focus firstId
|
||||
}
|
||||
|
@ -4,7 +4,11 @@ module Nri.Ui.SegmentedControl.V14 exposing
|
||||
, Positioning(..), Width(..)
|
||||
)
|
||||
|
||||
{-| Changes from V13:
|
||||
{-| Patch changes:
|
||||
|
||||
- use Tooltip.V3 instead of Tooltip.V2
|
||||
|
||||
Changes from V13:
|
||||
|
||||
- Adds tooltip support to `viewRadioGroup`
|
||||
|
||||
@ -26,7 +30,7 @@ import Nri.Ui.Colors.Extra exposing (withAlpha)
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.Fonts.V1 as Fonts
|
||||
import Nri.Ui.Svg.V1 as Svg exposing (Svg)
|
||||
import Nri.Ui.Tooltip.V2 as Tooltip
|
||||
import Nri.Ui.Tooltip.V3 as Tooltip
|
||||
import Nri.Ui.Util exposing (dashify)
|
||||
import TabsInternal.V2 as TabsInternal
|
||||
|
||||
|
@ -7,7 +7,11 @@ module Nri.Ui.Tabs.V7 exposing
|
||||
, spaHref
|
||||
)
|
||||
|
||||
{-| Changes from V6:
|
||||
{-| Patch changes:
|
||||
|
||||
- use Tooltip.V3 instead of Tooltip.V2
|
||||
|
||||
Changes from V6:
|
||||
|
||||
- Changes Tab construction to follow attributes-based approach
|
||||
- Adds tooltip support
|
||||
@ -29,7 +33,7 @@ import Nri.Ui
|
||||
import Nri.Ui.Colors.Extra exposing (withAlpha)
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.Fonts.V1 as Fonts
|
||||
import Nri.Ui.Tooltip.V2 as Tooltip
|
||||
import Nri.Ui.Tooltip.V3 as Tooltip
|
||||
import TabsInternal.V2 as TabsInternal
|
||||
|
||||
|
||||
|
@ -5,7 +5,7 @@ module Nri.Ui.Tooltip.V1 exposing
|
||||
, Padding(..), withPadding
|
||||
, withTooltipStyleOverrides
|
||||
, Trigger(..)
|
||||
, primaryLabel, auxillaryDescription, toggleTip
|
||||
, primaryLabel, auxiliaryDescription, toggleTip
|
||||
)
|
||||
|
||||
{-| A tooltip component!
|
||||
@ -51,7 +51,7 @@ Example usage:
|
||||
|
||||
## View Functions
|
||||
|
||||
@docs primaryLabel, auxillaryDescription, toggleTip
|
||||
@docs primaryLabel, auxiliaryDescription, toggleTip
|
||||
|
||||
-}
|
||||
|
||||
@ -215,7 +215,7 @@ primaryLabel =
|
||||
|
||||
{-| Used when the content of the tooltip provides an "auxillary description" for its content.
|
||||
-}
|
||||
auxillaryDescription :
|
||||
auxiliaryDescription :
|
||||
{ trigger : Trigger
|
||||
, triggerHtml : Html msg
|
||||
, extraButtonAttrs : List (Attribute msg)
|
||||
@ -225,7 +225,7 @@ auxillaryDescription :
|
||||
}
|
||||
-> Tooltip msg
|
||||
-> Html msg
|
||||
auxillaryDescription =
|
||||
auxiliaryDescription =
|
||||
viewTooltip_ AuxillaryDescription
|
||||
|
||||
|
||||
|
@ -7,18 +7,18 @@ module Nri.Ui.Tooltip.V2 exposing
|
||||
, alignStart, alignMiddle, alignEnd
|
||||
, exactWidth, fitToContent
|
||||
, smallPadding, normalPadding, customPadding
|
||||
, onClick, onHover
|
||||
, onClick, onToggle
|
||||
, open
|
||||
, css, containerCss
|
||||
, custom, customTriggerAttributes
|
||||
, nriDescription, testId
|
||||
, primaryLabel, auxillaryDescription
|
||||
, primaryLabel, auxiliaryDescription
|
||||
)
|
||||
|
||||
{-| Known issues:
|
||||
|
||||
- tooltips with focusable content (e.g., a link) will not handle focus correctly for
|
||||
keyboard-only users when using the onHover attribute
|
||||
keyboard-only users when using the onToggle attribute
|
||||
|
||||
Post-release patches:
|
||||
|
||||
@ -40,7 +40,7 @@ Changes from V1:
|
||||
- adds plaintext, html helpers for setting the content
|
||||
- pass a list of attributes rather than requiring a pipeline to set up the tooltip
|
||||
- move Trigger into the attributes
|
||||
- change primaryLabel and auxillaryDescription to attributes, adding view
|
||||
- change primaryLabel and auxiliaryDescription to attributes, adding view
|
||||
- move the onTrigger event to the attributes
|
||||
- extraButtonAttrs becomes attribute `customTriggerAttributes`
|
||||
- isOpen field becomes the `open` attribute
|
||||
@ -72,12 +72,12 @@ Example usage:
|
||||
@docs alignStart, alignMiddle, alignEnd
|
||||
@docs exactWidth, fitToContent
|
||||
@docs smallPadding, normalPadding, customPadding
|
||||
@docs onClick, onHover
|
||||
@docs onClick, onToggle
|
||||
@docs open
|
||||
@docs css, containerCss
|
||||
@docs custom, customTriggerAttributes
|
||||
@docs nriDescription, testId
|
||||
@docs primaryLabel, auxillaryDescription
|
||||
@docs primaryLabel, auxiliaryDescription
|
||||
|
||||
-}
|
||||
|
||||
@ -419,8 +419,8 @@ type Trigger msg
|
||||
|
||||
{-| The tooltip opens when hovering over the trigger element, and closes when the hover stops.
|
||||
-}
|
||||
onHover : (Bool -> msg) -> Attribute msg
|
||||
onHover msg =
|
||||
onToggle : (Bool -> msg) -> Attribute msg
|
||||
onToggle msg =
|
||||
Attribute (\config -> { config | trigger = Just (OnHover msg) })
|
||||
|
||||
|
||||
@ -450,8 +450,8 @@ primaryLabel =
|
||||
|
||||
{-| Used when the content of the tooltip provides an "auxillary description" for its content.
|
||||
-}
|
||||
auxillaryDescription : Attribute msg
|
||||
auxillaryDescription =
|
||||
auxiliaryDescription : Attribute msg
|
||||
auxiliaryDescription =
|
||||
Attribute (\config -> { config | purpose = AuxillaryDescription })
|
||||
|
||||
|
||||
|
1103
src/Nri/Ui/Tooltip/V3.elm
Normal file
1103
src/Nri/Ui/Tooltip/V3.elm
Normal file
File diff suppressed because it is too large
Load Diff
78
src/Nri/Ui/WhenFocusLeaves/V1.elm
Normal file
78
src/Nri/Ui/WhenFocusLeaves/V1.elm
Normal file
@ -0,0 +1,78 @@
|
||||
module Nri.Ui.WhenFocusLeaves.V1 exposing (toAttribute)
|
||||
|
||||
{-| Listen for when the focus leaves the area, and then do an action.
|
||||
|
||||
@docs toAttribute
|
||||
|
||||
-}
|
||||
|
||||
import Accessibility.Styled as Html
|
||||
import Html.Styled.Events as Events
|
||||
import Json.Decode as Decode exposing (Decoder)
|
||||
|
||||
|
||||
{-| Attach this attribute to add a focus watcher to an HTML element and define
|
||||
what to do in reponse to tab keypresses in a part of the UI.
|
||||
|
||||
The ids referenced here are expected to correspond to elements in the container
|
||||
we are adding the attribute to.
|
||||
|
||||
-}
|
||||
toAttribute :
|
||||
{ firstId : String
|
||||
, lastId : String
|
||||
, tabBackAction : msg
|
||||
, tabForwardAction : msg
|
||||
}
|
||||
-> Html.Attribute msg
|
||||
toAttribute { firstId, lastId, tabBackAction, tabForwardAction } =
|
||||
onTab <|
|
||||
\elementId shiftKey ->
|
||||
-- if the user tabs back while on the first id,
|
||||
-- we execute the action
|
||||
if elementId == firstId && shiftKey then
|
||||
Decode.succeed
|
||||
{ message = tabBackAction
|
||||
, preventDefault = False
|
||||
, stopPropagation = False
|
||||
}
|
||||
|
||||
else if elementId == lastId && not shiftKey then
|
||||
-- if the user tabs forward while on the last id,
|
||||
-- we want to wrap around to the first id.
|
||||
Decode.succeed
|
||||
{ message = tabForwardAction
|
||||
, preventDefault = False
|
||||
, stopPropagation = False
|
||||
}
|
||||
|
||||
else
|
||||
Decode.fail "No need to intercept the key press"
|
||||
|
||||
|
||||
onTab :
|
||||
(String
|
||||
-> Bool
|
||||
-> Decoder { message : msg, preventDefault : Bool, stopPropagation : Bool }
|
||||
)
|
||||
-> Html.Attribute msg
|
||||
onTab do =
|
||||
Events.custom "keydown"
|
||||
(Decode.andThen
|
||||
(\( id, keyCode, shiftKey ) ->
|
||||
if keyCode == 9 then
|
||||
do id shiftKey
|
||||
|
||||
else
|
||||
Decode.fail "No need to intercept the key press"
|
||||
)
|
||||
decodeKeydown
|
||||
)
|
||||
|
||||
|
||||
decodeKeydown : Decoder ( String, Int, Bool )
|
||||
decodeKeydown =
|
||||
Decode.map3 (\id keyCode shiftKey -> ( id, keyCode, shiftKey ))
|
||||
(Decode.at [ "target", "id" ] Decode.string)
|
||||
(Decode.field "keyCode" Decode.int)
|
||||
(Decode.field "shiftKey" Decode.bool)
|
@ -21,7 +21,7 @@ import Html.Styled.Events as Events
|
||||
import Html.Styled.Keyed as Keyed
|
||||
import Json.Decode
|
||||
import Nri.Ui.Html.Attributes.V2 as AttributesExtra
|
||||
import Nri.Ui.Tooltip.V2 as Tooltip
|
||||
import Nri.Ui.Tooltip.V3 as Tooltip
|
||||
import Nri.Ui.Util exposing (dashify)
|
||||
|
||||
|
||||
|
@ -20,7 +20,7 @@ import Html.Styled.Attributes as Attributes
|
||||
import Nri.Ui.ClickableSvg.V2 as ClickableSvg
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.Svg.V1 exposing (Svg)
|
||||
import Nri.Ui.Tooltip.V2 as Tooltip
|
||||
import Nri.Ui.Tooltip.V3 as Tooltip
|
||||
import Nri.Ui.UiIcon.V1 as UiIcon
|
||||
|
||||
|
||||
@ -96,13 +96,13 @@ Tooltip.view
|
||||
ClickableSvg.button "Preview"
|
||||
UiIcon.preview
|
||||
[ ClickableSvg.custom attrs,
|
||||
, ClickableSvg.custom [ EventExtras.onClickStopPropagation (ShowItWorked "You clicked the preview button!") ]
|
||||
, ClickableSvg.onClick (ShowItWorked "You clicked the preview button!") ]
|
||||
]
|
||||
, id = "preview-tooltip"
|
||||
}
|
||||
[ Tooltip.plaintext "Preview"
|
||||
, Tooltip.primaryLabel
|
||||
, Tooltip.onHover SetPreviewTooltip
|
||||
, Tooltip.onToggle SetPreviewTooltip
|
||||
, Tooltip.open state.tooltipPreview
|
||||
, Tooltip.smallPadding
|
||||
, Tooltip.fitToContent
|
||||
@ -121,7 +121,7 @@ Tooltip.view
|
||||
}
|
||||
[ Tooltip.plaintext "Preview"
|
||||
, Tooltip.primaryLabel
|
||||
, Tooltip.onHover SetPreviewTooltip
|
||||
, Tooltip.onToggle SetPreviewTooltip
|
||||
, Tooltip.open state.tooltipPreview
|
||||
, Tooltip.smallPadding
|
||||
, Tooltip.fitToContent
|
||||
|
@ -26,7 +26,7 @@ import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.Fonts.V1 as Fonts
|
||||
import Nri.Ui.SegmentedControl.V14 as SegmentedControl
|
||||
import Nri.Ui.Svg.V1 as Svg exposing (Svg)
|
||||
import Nri.Ui.Tooltip.V2 as Tooltip
|
||||
import Nri.Ui.Tooltip.V3 as Tooltip
|
||||
import Nri.Ui.UiIcon.V1 as UiIcon
|
||||
import String exposing (toLower)
|
||||
import Task
|
||||
@ -206,7 +206,7 @@ buildOptions { content, longContent, tooltips } openTooltip =
|
||||
, ", tabTooltip = "
|
||||
++ (if tooltips then
|
||||
("\n\t\t[ Tooltip.plaintext " ++ valueStr)
|
||||
++ ("\n\t\t, Tooltip.onHover (OpenTooltip " ++ valueStr ++ ")")
|
||||
++ ("\n\t\t, Tooltip.onToggle (OpenTooltip " ++ valueStr ++ ")")
|
||||
++ ("\n\t\t, Tooltip.open (openTooltip == Just " ++ valueStr ++ ")")
|
||||
++ "\n\t\t]"
|
||||
|
||||
@ -225,7 +225,7 @@ buildOptions { content, longContent, tooltips } openTooltip =
|
||||
, tabTooltip =
|
||||
if tooltips then
|
||||
[ Tooltip.plaintext (Debug.toString value)
|
||||
, Tooltip.onHover (PageTooltip value)
|
||||
, Tooltip.onToggle (PageTooltip value)
|
||||
, Tooltip.open (openTooltip == Just value)
|
||||
]
|
||||
|
||||
@ -279,7 +279,7 @@ buildRadioOptions options currentlyHovered content =
|
||||
, ", tooltip = "
|
||||
++ (if options.tooltips then
|
||||
("\n\t\t[ Tooltip.plaintext " ++ String.fromInt value)
|
||||
++ ("\n\t\t, Tooltip.onHover (OpenTooltip " ++ String.fromInt value ++ ")")
|
||||
++ ("\n\t\t, Tooltip.onToggle (OpenTooltip " ++ String.fromInt value ++ ")")
|
||||
++ ("\n\t\t, Tooltip.open (openTooltip == Just " ++ String.fromInt value ++ ")")
|
||||
++ "\n\t\t]"
|
||||
|
||||
@ -299,7 +299,7 @@ buildRadioOptions options currentlyHovered content =
|
||||
[ Tooltip.plaintext text
|
||||
, Tooltip.open (currentlyHovered == Just value)
|
||||
, Tooltip.fitToContent
|
||||
, Tooltip.onHover
|
||||
, Tooltip.onToggle
|
||||
(\hovered ->
|
||||
HoverRadio
|
||||
(if hovered then
|
||||
@ -309,6 +309,7 @@ buildRadioOptions options currentlyHovered content =
|
||||
Nothing
|
||||
)
|
||||
)
|
||||
, Tooltip.auxiliaryDescription
|
||||
]
|
||||
|
||||
else
|
||||
|
@ -22,7 +22,7 @@ import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.Svg.V1 as Svg
|
||||
import Nri.Ui.Tabs.V7 as Tabs exposing (Alignment(..), Tab)
|
||||
import Nri.Ui.Text.V6 as Text
|
||||
import Nri.Ui.Tooltip.V2 as Tooltip
|
||||
import Nri.Ui.Tooltip.V3 as Tooltip
|
||||
import Nri.Ui.UiIcon.V1 as UiIcon
|
||||
import Routes
|
||||
import Task
|
||||
@ -221,7 +221,7 @@ allTabs openTooltipId labelledBy =
|
||||
, Tabs.tabString "1"
|
||||
, Tabs.withTooltip
|
||||
[ Tooltip.plaintext "Link Example"
|
||||
, Tooltip.onHover (ToggleTooltip First)
|
||||
, Tooltip.onToggle (ToggleTooltip First)
|
||||
, Tooltip.alignStart (Css.px 75)
|
||||
, Tooltip.primaryLabel
|
||||
, Tooltip.open (openTooltipId == Just First)
|
||||
@ -245,7 +245,7 @@ allTabs openTooltipId labelledBy =
|
||||
[ Tabs.tabHtml bulbIcon
|
||||
, Tabs.withTooltip
|
||||
[ Tooltip.plaintext "The Electrifying Third Tab"
|
||||
, Tooltip.onHover (ToggleTooltip Third)
|
||||
, Tooltip.onToggle (ToggleTooltip Third)
|
||||
, Tooltip.primaryLabel
|
||||
, Tooltip.open (openTooltipId == Just Third)
|
||||
]
|
||||
|
@ -16,18 +16,25 @@ import Debug.Control.Extra as ControlExtra
|
||||
import Debug.Control.View as ControlView
|
||||
import EllieLink
|
||||
import Example exposing (Example)
|
||||
import Html.Styled.Attributes exposing (css, href)
|
||||
import Html.Styled.Attributes exposing (css, href, id)
|
||||
import KeyboardSupport exposing (Key(..))
|
||||
import Markdown
|
||||
import Nri.Ui.ClickableSvg.V2 as ClickableSvg
|
||||
import Nri.Ui.ClickableText.V3 as ClickableText
|
||||
import Nri.Ui.Heading.V2 as Heading
|
||||
import Nri.Ui.Text.V6 as Text
|
||||
import Nri.Ui.Tooltip.V2 as Tooltip
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.Svg.V1 as Svg
|
||||
import Nri.Ui.Table.V5 as Table
|
||||
import Nri.Ui.Tooltip.V3 as Tooltip
|
||||
import Nri.Ui.UiIcon.V1 as UiIcon
|
||||
|
||||
|
||||
version : Int
|
||||
version =
|
||||
2
|
||||
3
|
||||
|
||||
|
||||
moduleName : String
|
||||
moduleName =
|
||||
"Tooltip"
|
||||
|
||||
|
||||
example : Example State Msg
|
||||
@ -35,7 +42,17 @@ example =
|
||||
{ name = moduleName
|
||||
, version = version
|
||||
, categories = [ Messaging ]
|
||||
, keyboardSupport = []
|
||||
, keyboardSupport =
|
||||
[ { keys = [ Esc ]
|
||||
, result = "Hitting escape while focusing a tooltip trigger closes all tooltips. Note that hovered-but-not-focused tooltips can't be closed this way."
|
||||
}
|
||||
, { keys = [ Space ]
|
||||
, result = "While focusing a tooltip trigger, opens/closes the tooltip. May trigger the underlying action too."
|
||||
}
|
||||
, { keys = [ Enter ]
|
||||
, result = "While focusing a tooltip trigger, opens/closes the tooltip. May trigger the underlying action too."
|
||||
}
|
||||
]
|
||||
, state = init
|
||||
, update = update
|
||||
, subscriptions = \_ -> Sub.none
|
||||
@ -69,13 +86,8 @@ example =
|
||||
}
|
||||
|
||||
|
||||
moduleName : String
|
||||
moduleName =
|
||||
"Tooltip"
|
||||
|
||||
|
||||
type alias State =
|
||||
{ openTooltip : Maybe TooltipType
|
||||
{ openTooltip : Maybe TooltipId
|
||||
, staticExampleSettings : Control (List ( String, Tooltip.Attribute Never ))
|
||||
}
|
||||
|
||||
@ -87,15 +99,17 @@ init =
|
||||
}
|
||||
|
||||
|
||||
type TooltipType
|
||||
type TooltipId
|
||||
= PrimaryLabel
|
||||
| AuxillaryDescription
|
||||
| LearnMore
|
||||
| Disclosure
|
||||
|
||||
|
||||
type Msg
|
||||
= ToggleTooltip TooltipType Bool
|
||||
= ToggleTooltip TooltipId Bool
|
||||
| SetControl (Control (List ( String, Tooltip.Attribute Never )))
|
||||
| Log String
|
||||
|
||||
|
||||
update : Msg -> State -> ( State, Cmd Msg )
|
||||
@ -111,93 +125,185 @@ update msg model =
|
||||
SetControl settings ->
|
||||
( { model | staticExampleSettings = settings }, Cmd.none )
|
||||
|
||||
Log message ->
|
||||
( Debug.log "Tooltip Log:" |> always model, Cmd.none )
|
||||
|
||||
|
||||
view : EllieLink.Config -> State -> List (Html Msg)
|
||||
view ellieLinkConfig model =
|
||||
[ Heading.h2 [ Heading.style Heading.Subhead ] [ Html.text "Using the Tooltip module" ]
|
||||
, Text.mediumBody
|
||||
[ Text.html
|
||||
[ Html.text "Label the Tooltip as either being the "
|
||||
, viewPrimaryLabelTooltip model.openTooltip
|
||||
, Html.text " or the "
|
||||
, viewAuxillaryDescriptionToolip model.openTooltip
|
||||
, Html.text " for the trigger content."
|
||||
, viewToggleTip model.openTooltip
|
||||
]
|
||||
[ viewCustomizableExample ellieLinkConfig model.staticExampleSettings
|
||||
, Table.view
|
||||
[ Table.string
|
||||
{ header = "Attribute"
|
||||
, value = .name
|
||||
, width = Css.pct 15
|
||||
, cellStyles = always []
|
||||
}
|
||||
, Table.custom
|
||||
{ header = Html.text "About"
|
||||
, view = .description >> Markdown.toHtml Nothing >> List.map Html.fromUnstyled >> Html.span []
|
||||
, width = Css.px 200
|
||||
, cellStyles = always []
|
||||
}
|
||||
, Table.custom
|
||||
{ header = Html.text "view"
|
||||
, view = .view
|
||||
, width = Css.px 50
|
||||
, cellStyles = always [ Css.textAlign Css.center ]
|
||||
}
|
||||
]
|
||||
[ { name = "Tooltip.primaryLabel"
|
||||
, description =
|
||||
"""
|
||||
Used when the content of the tooltip is identical to the accessible name.
|
||||
|
||||
For example, when using the Tooltip component with the ClickableSvg component, the Tooltip is providing
|
||||
extra information to sighted users that screenreader users already have.
|
||||
|
||||
This is the default.
|
||||
"""
|
||||
, view = viewPrimaryLabelTooltip model.openTooltip
|
||||
, tooltipId = PrimaryLabel
|
||||
}
|
||||
, { name = "Tooltip.auxiliaryDescription"
|
||||
, description =
|
||||
"""
|
||||
Used when the content of the tooltip provides an "auxiliary description" for its content.
|
||||
|
||||
An auxiliary description is used when the tooltip content provides supplementary information about its trigger content.
|
||||
"""
|
||||
, view = viewAuxillaryDescriptionToolip model.openTooltip
|
||||
, tooltipId = AuxillaryDescription
|
||||
}
|
||||
, { name = "Tooltip.disclosure"
|
||||
, description =
|
||||
"""
|
||||
Sometimes a "tooltip" only _looks_ like a tooltip, but is really more about hiding and showing extra information when the user asks for it.
|
||||
|
||||
If clicking the "tooltip trigger" only ever shows you more info (and especially if this info is rich or interactable), use this attribute.
|
||||
|
||||
For more information, please read [Sarah Higley's "Tooltips in the time of WCAG 2.1" post](https://sarahmhigley.com/writing/tooltips-in-wcag-21).
|
||||
"""
|
||||
, view = viewDisclosureToolip model.openTooltip
|
||||
, tooltipId = Disclosure
|
||||
}
|
||||
, { name = "Tooltip.viewToggleTip"
|
||||
, description =
|
||||
"""
|
||||
Supplementary information triggered by a "?" icon.
|
||||
|
||||
This is a helper for setting up a commonly-used `disclosure` tooltip. Please see the documentation for `disclosure` to learn more.
|
||||
"""
|
||||
, view = viewToggleTip model.openTooltip
|
||||
, tooltipId = LearnMore
|
||||
}
|
||||
]
|
||||
, viewCustomizableExample ellieLinkConfig model.staticExampleSettings
|
||||
]
|
||||
|
||||
|
||||
viewPrimaryLabelTooltip : Maybe TooltipType -> Html Msg
|
||||
viewPrimaryLabelTooltip : Maybe TooltipId -> Html Msg
|
||||
viewPrimaryLabelTooltip openTooltip =
|
||||
Tooltip.view
|
||||
{ id = "tooltip__primaryLabel"
|
||||
, trigger =
|
||||
\eventHandlers ->
|
||||
ClickableText.button "primaryLabel"
|
||||
[ ClickableText.custom eventHandlers
|
||||
ClickableSvg.button "Download"
|
||||
UiIcon.download
|
||||
[ ClickableSvg.custom eventHandlers
|
||||
, ClickableSvg.onClick (Log "Fake content totally downloaded!")
|
||||
]
|
||||
}
|
||||
[ Tooltip.html
|
||||
[ Html.text "A primary label is used when the tooltip content serves as the main label for its trigger content"
|
||||
, Html.br []
|
||||
, Html.text "e.g. when the trigger content is an icon with no text."
|
||||
]
|
||||
, Tooltip.auxillaryDescription
|
||||
, Tooltip.onHover (ToggleTooltip PrimaryLabel)
|
||||
, Tooltip.open (openTooltip == Just PrimaryLabel)
|
||||
, Tooltip.onBottom
|
||||
]
|
||||
|
||||
|
||||
viewAuxillaryDescriptionToolip : Maybe TooltipType -> Html Msg
|
||||
viewAuxillaryDescriptionToolip openTooltip =
|
||||
Tooltip.view
|
||||
{ id = "tooltip__auxillaryDescription"
|
||||
, trigger =
|
||||
\eventHandlers ->
|
||||
ClickableText.button "auxillaryDescription"
|
||||
[ ClickableText.custom eventHandlers
|
||||
]
|
||||
}
|
||||
[ Tooltip.html
|
||||
[ Html.text "An auxillary description is used when the tooltip content provides supplementary information about its trigger content"
|
||||
, Html.br []
|
||||
, Html.text "e.g. when the trigger content is a word in the middle of a body of text that requires additional explanation."
|
||||
]
|
||||
, Tooltip.auxillaryDescription
|
||||
, Tooltip.onHover (ToggleTooltip AuxillaryDescription)
|
||||
, Tooltip.open (openTooltip == Just AuxillaryDescription)
|
||||
, Tooltip.onBottom
|
||||
]
|
||||
|
||||
|
||||
viewToggleTip : Maybe TooltipType -> Html Msg
|
||||
viewToggleTip openTooltip =
|
||||
Tooltip.toggleTip { label = "tooltip__learn-more" }
|
||||
[ Tooltip.html
|
||||
[ Html.a
|
||||
[ href "https://inclusive-components.design/tooltips-toggletips" ]
|
||||
[ Html.text "Learn more" ]
|
||||
]
|
||||
[ Tooltip.plaintext "Download"
|
||||
, Tooltip.primaryLabel
|
||||
, Tooltip.onHover (ToggleTooltip LearnMore)
|
||||
, Tooltip.open (openTooltip == Just LearnMore)
|
||||
, Tooltip.onToggle (ToggleTooltip PrimaryLabel)
|
||||
, Tooltip.open (openTooltip == Just PrimaryLabel)
|
||||
, Tooltip.smallPadding
|
||||
, Tooltip.fitToContent
|
||||
]
|
||||
|
||||
|
||||
viewAuxillaryDescriptionToolip : Maybe TooltipId -> Html Msg
|
||||
viewAuxillaryDescriptionToolip openTooltip =
|
||||
Tooltip.view
|
||||
{ id = "tooltip__auxiliaryDescription"
|
||||
, trigger =
|
||||
\eventHandlers ->
|
||||
ClickableSvg.button "Period 1"
|
||||
UiIcon.class
|
||||
[ ClickableSvg.custom eventHandlers
|
||||
, ClickableSvg.onClick (Log "You totally started managing Periud 1.")
|
||||
]
|
||||
}
|
||||
[ Tooltip.plaintext "Manage class and students"
|
||||
, Tooltip.auxiliaryDescription
|
||||
, Tooltip.onToggle (ToggleTooltip AuxillaryDescription)
|
||||
, Tooltip.open (openTooltip == Just AuxillaryDescription)
|
||||
, Tooltip.smallPadding
|
||||
, Tooltip.fitToContent
|
||||
, Tooltip.onLeftForMobile
|
||||
]
|
||||
|
||||
|
||||
viewDisclosureToolip : Maybe TooltipId -> Html Msg
|
||||
viewDisclosureToolip openTooltip =
|
||||
let
|
||||
triggerId =
|
||||
"tooltip__disclosure-trigger"
|
||||
|
||||
lastId =
|
||||
"tooltip__disclosure-what-is-mastery"
|
||||
in
|
||||
Tooltip.view
|
||||
{ id = "tooltip__disclosure"
|
||||
, trigger =
|
||||
\eventHandlers ->
|
||||
ClickableSvg.button "Previously mastered"
|
||||
(Svg.withColor Colors.green UiIcon.starFilled)
|
||||
[ ClickableSvg.custom eventHandlers
|
||||
, ClickableSvg.id triggerId
|
||||
]
|
||||
}
|
||||
[ Tooltip.html
|
||||
[ Html.text "You mastered this skill in a previous year! Way to go! "
|
||||
, Html.a
|
||||
[ id lastId
|
||||
, href "https://noredink.zendesk.com/hc/en-us/articles/203022319-What-is-mastery-"
|
||||
]
|
||||
[ Html.text "Learn more about NoRedInk Mastery" ]
|
||||
]
|
||||
, Tooltip.disclosure { triggerId = triggerId, lastId = Just lastId }
|
||||
, Tooltip.onToggle (ToggleTooltip Disclosure)
|
||||
, Tooltip.open (openTooltip == Just Disclosure)
|
||||
, Tooltip.smallPadding
|
||||
, Tooltip.alignEndForMobile (Css.px 148)
|
||||
]
|
||||
|
||||
|
||||
viewToggleTip : Maybe TooltipId -> Html Msg
|
||||
viewToggleTip openTooltip =
|
||||
Tooltip.viewToggleTip { label = "What is mastery?", lastId = Nothing }
|
||||
[ Tooltip.plaintext "Students master topics by correctly answering a series of questions of varying difficulty and scope."
|
||||
, Tooltip.onToggle (ToggleTooltip LearnMore)
|
||||
, Tooltip.open (openTooltip == Just LearnMore)
|
||||
, Tooltip.alignEndForMobile (Css.px 144)
|
||||
]
|
||||
|
||||
|
||||
initStaticExampleSettings : Control (List ( String, Tooltip.Attribute Never ))
|
||||
initStaticExampleSettings =
|
||||
ControlExtra.list
|
||||
|> ControlExtra.listItem "content" controlContent
|
||||
|> ControlExtra.optionalListItem "direction" controlDirection
|
||||
|> ControlExtra.optionalListItem "direction -- mobile" controlDirectionForMobile
|
||||
|> ControlExtra.optionalListItem "alignment" controlAlignment
|
||||
|> ControlExtra.optionalListItem "alignment -- mobile" controlAlignmentForMobile
|
||||
|> ControlExtra.optionalBoolListItem "withoutTail" ( "Tooltip.withoutTail", Tooltip.withoutTail )
|
||||
|> ControlExtra.optionalListItem "width" controlWidth
|
||||
|> ControlExtra.optionalListItem "padding" controlPadding
|
||||
|> CommonControls.css { moduleName = moduleName, use = Tooltip.css }
|
||||
|> CommonControls.mobileCss { moduleName = moduleName, use = Tooltip.mobileCss }
|
||||
|> CommonControls.quizEngineMobileCss { moduleName = moduleName, use = Tooltip.quizEngineMobileCss }
|
||||
|> CommonControls.notMobileCss { moduleName = moduleName, use = Tooltip.notMobileCss }
|
||||
|
||||
|
||||
controlContent : Control ( String, Tooltip.Attribute Never )
|
||||
@ -221,6 +327,16 @@ controlDirection =
|
||||
]
|
||||
|
||||
|
||||
controlDirectionForMobile : Control ( String, Tooltip.Attribute Never )
|
||||
controlDirectionForMobile =
|
||||
CommonControls.choice "Tooltip"
|
||||
[ ( "onTopForMobile", Tooltip.onTopForMobile )
|
||||
, ( "onBottomForMobile", Tooltip.onBottomForMobile )
|
||||
, ( "onLeftForMobile", Tooltip.onLeftForMobile )
|
||||
, ( "onRightForMobile", Tooltip.onRightForMobile )
|
||||
]
|
||||
|
||||
|
||||
controlAlignment : Control ( String, Tooltip.Attribute Never )
|
||||
controlAlignment =
|
||||
Control.choice
|
||||
@ -246,6 +362,31 @@ controlAlignment =
|
||||
]
|
||||
|
||||
|
||||
controlAlignmentForMobile : Control ( String, Tooltip.Attribute Never )
|
||||
controlAlignmentForMobile =
|
||||
Control.choice
|
||||
[ ( "alignMiddleForMobile (default)", Control.value ( "Tooltip.alignMiddleForMobile", Tooltip.alignMiddleForMobile ) )
|
||||
, ( "alignStartForMobile"
|
||||
, Control.map
|
||||
(\float ->
|
||||
( "Tooltip.alignStartForMobile (Css.px " ++ String.fromFloat float ++ ")"
|
||||
, Tooltip.alignStartForMobile (Css.px float)
|
||||
)
|
||||
)
|
||||
(ControlExtra.float 0)
|
||||
)
|
||||
, ( "alignEndForMobile"
|
||||
, Control.map
|
||||
(\float ->
|
||||
( "Tooltip.alignEndForMobile (Css.px " ++ String.fromFloat float ++ ")"
|
||||
, Tooltip.alignEndForMobile (Css.px float)
|
||||
)
|
||||
)
|
||||
(ControlExtra.float 0)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
controlWidth : Control ( String, Tooltip.Attribute Never )
|
||||
controlWidth =
|
||||
Control.choice
|
||||
|
@ -1,21 +1,20 @@
|
||||
module Spec.Nri.Ui.Tooltip exposing (spec)
|
||||
|
||||
import Accessibility.Aria as Aria
|
||||
import Accessibility.Widget as Widget
|
||||
import Html.Attributes as Attributes
|
||||
import Html.Styled as HtmlStyled
|
||||
import Nri.Ui.Tooltip.V2 as Tooltip
|
||||
import ProgramTest exposing (ProgramTest, clickButton, ensureViewHas, ensureViewHasNot)
|
||||
import Nri.Ui.Tooltip.V3 as Tooltip
|
||||
import ProgramTest exposing (ProgramTest, ensureViewHas, ensureViewHasNot)
|
||||
import Test exposing (..)
|
||||
import Test.Html.Event as Event
|
||||
import Test.Html.Query as Query
|
||||
import Test.Html.Selector as Selector exposing (id, tag, text)
|
||||
import Test.Html.Selector as Selector exposing (id, text)
|
||||
|
||||
|
||||
spec : Test
|
||||
spec =
|
||||
describe "Nri.Ui.Tooltip.V2"
|
||||
[ test "Tooltip.toggleTip with onHover trigger" <|
|
||||
describe "Nri.Ui.Tooltip.V3"
|
||||
[ test "Tooltip.viewToggleTip" <|
|
||||
\() ->
|
||||
let
|
||||
tooltipContent =
|
||||
@ -24,27 +23,24 @@ spec =
|
||||
label =
|
||||
"More info"
|
||||
in
|
||||
program (Tooltip.toggleTip { label = label })
|
||||
program (Tooltip.viewToggleTip { label = label, lastId = Nothing })
|
||||
[ Tooltip.plaintext tooltipContent
|
||||
, Tooltip.onHover identity
|
||||
, Tooltip.onToggle identity
|
||||
]
|
||||
-- Tooltip opens on mouse enter
|
||||
|> mouseEnter [ nriDescription "Nri-Ui-Tooltip-V2" ]
|
||||
|> ensureViewHas [ text tooltipContent ]
|
||||
-- Tooltip stays open on trigger-html click
|
||||
|> ensureViewHas (tooltipContentSelector tooltipContent)
|
||||
-- Tooltip closes on trigger-html click
|
||||
|> clickButtonByLabel label
|
||||
|> ensureViewHas [ text tooltipContent ]
|
||||
|> ensureViewHasNot (tooltipContentSelector tooltipContent)
|
||||
-- Tooltip reopens on trigger-html click
|
||||
|> clickButtonByLabel label
|
||||
|> ensureViewHas (tooltipContentSelector tooltipContent)
|
||||
-- Tooltip closes on mouse leave
|
||||
|> mouseLeave [ nriDescription "Nri-Ui-Tooltip-V2" ]
|
||||
|> ensureViewHasNot [ text tooltipContent ]
|
||||
-- Tooltip opens on focus
|
||||
|> focus [ tag "button", Selector.attribute (Widget.label label) ]
|
||||
|> ensureViewHas [ text tooltipContent ]
|
||||
-- Tooltip closes on blur
|
||||
|> blur [ tag "button", Selector.attribute (Widget.label label) ]
|
||||
|> ensureViewHasNot [ text tooltipContent ]
|
||||
|> ensureViewHasNot (tooltipContentSelector tooltipContent)
|
||||
|> ProgramTest.done
|
||||
, test "Tooltip.view with onClick trigger" <|
|
||||
, test "Tooltip.view" <|
|
||||
\() ->
|
||||
let
|
||||
tooltipContent =
|
||||
@ -64,72 +60,26 @@ spec =
|
||||
)
|
||||
[ Tooltip.plaintext tooltipContent
|
||||
, Tooltip.primaryLabel
|
||||
, Tooltip.onClick identity
|
||||
]
|
||||
-- Tooltip opens on click
|
||||
|> clickButton triggerContent
|
||||
|> ensureViewHas
|
||||
[ tag "button"
|
||||
, Selector.attribute (Aria.labeledBy tooltipId)
|
||||
]
|
||||
|> ensureViewHas [ id tooltipId, text tooltipContent ]
|
||||
-- Tooltip closes on another click
|
||||
|> clickButton triggerContent
|
||||
|> ensureViewHasNot [ id tooltipId, text tooltipContent ]
|
||||
|> ProgramTest.done
|
||||
, test "Tooltip.view with onHover trigger" <|
|
||||
\() ->
|
||||
let
|
||||
tooltipContent =
|
||||
"This will be the primary label"
|
||||
|
||||
triggerContent =
|
||||
"label-less icon"
|
||||
|
||||
tooltipId =
|
||||
"primary-label"
|
||||
in
|
||||
program
|
||||
(Tooltip.view
|
||||
{ trigger = \events -> HtmlStyled.button events [ HtmlStyled.text triggerContent ]
|
||||
, id = tooltipId
|
||||
}
|
||||
)
|
||||
[ Tooltip.plaintext tooltipContent
|
||||
, Tooltip.primaryLabel
|
||||
, Tooltip.onHover identity
|
||||
, Tooltip.onToggle identity
|
||||
]
|
||||
-- Tooltip opens on mouse enter
|
||||
|> mouseEnter [ nriDescription "Nri-Ui-Tooltip-V2" ]
|
||||
|> ensureViewHas [ text tooltipContent ]
|
||||
-- Tooltip stays open on trigger-html click
|
||||
|> clickButton triggerContent
|
||||
|> ensureViewHas [ text tooltipContent ]
|
||||
|> ensureViewHas (tooltipContentSelector tooltipContent)
|
||||
-- Tooltip closes on mouse leave
|
||||
|> mouseLeave [ nriDescription "Nri-Ui-Tooltip-V2" ]
|
||||
|> ensureViewHasNot [ text tooltipContent ]
|
||||
|> ensureViewHasNot (tooltipContentSelector tooltipContent)
|
||||
-- Tooltip opens on focus
|
||||
|> focus
|
||||
[ Selector.tag "button"
|
||||
, Selector.containing [ Selector.text triggerContent ]
|
||||
]
|
||||
|> ProgramTest.ensureViewHas
|
||||
[ tag "button"
|
||||
, Selector.attribute (Aria.labeledBy tooltipId)
|
||||
]
|
||||
|> ProgramTest.ensureViewHas
|
||||
[ id tooltipId
|
||||
, Selector.text tooltipContent
|
||||
]
|
||||
|> ProgramTest.ensureViewHas (tooltipContentSelector tooltipContent)
|
||||
-- Tooltip closes on blur
|
||||
|> blur
|
||||
[ Selector.tag "button"
|
||||
, Selector.containing [ Selector.text triggerContent ]
|
||||
]
|
||||
|> ProgramTest.ensureViewHasNot
|
||||
[ id tooltipId
|
||||
, Selector.text tooltipContent
|
||||
]
|
||||
|> ProgramTest.ensureViewHasNot (id tooltipId :: tooltipContentSelector tooltipContent)
|
||||
|> ProgramTest.done
|
||||
]
|
||||
|
||||
@ -149,6 +99,13 @@ program view attributes =
|
||||
|> ProgramTest.start ()
|
||||
|
||||
|
||||
tooltipContentSelector : String -> List Selector.Selector
|
||||
tooltipContentSelector tooltipContent =
|
||||
[ Selector.attribute (Attributes.attribute "data-tooltip-visible" "true")
|
||||
, Selector.containing [ text tooltipContent ]
|
||||
]
|
||||
|
||||
|
||||
nriDescription : String -> Selector.Selector
|
||||
nriDescription desc =
|
||||
Selector.attribute (Attributes.attribute "data-nri-description" desc)
|
||||
|
@ -21,6 +21,7 @@
|
||||
"Nri.Ui.DisclosureIndicator.V2",
|
||||
"Nri.Ui.Divider.V2",
|
||||
"Nri.Ui.Effects.V1",
|
||||
"Nri.Ui.WhenFocusLeaves.V1",
|
||||
"Nri.Ui.FocusTrap.V1",
|
||||
"Nri.Ui.Fonts.V1",
|
||||
"Nri.Ui.Heading.V2",
|
||||
@ -68,6 +69,7 @@
|
||||
"Nri.Ui.TextInput.V7",
|
||||
"Nri.Ui.Tooltip.V1",
|
||||
"Nri.Ui.Tooltip.V2",
|
||||
"Nri.Ui.Tooltip.V3",
|
||||
"Nri.Ui.UiIcon.V1"
|
||||
]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user