mirror of
https://github.com/NoRedInk/noredink-ui.git
synced 2025-01-04 04:33:59 +03:00
Merge pull request #424 from NoRedInk/tooltip-hovers
Toggle tip triggers "on hover"
This commit is contained in:
commit
d91ad5dc7d
@ -26,6 +26,14 @@ Example usage:
|
||||
}
|
||||
|
||||
|
||||
## Suggested Improvements for V2
|
||||
|
||||
- The toggle tip does not currently manage focus correctly for keyboard users - if a
|
||||
user tries to click on a link in the toggle tip, the tip will disappear as focus moves
|
||||
to the next item in the page. This should be improved in the next release.
|
||||
- Currently, only toggle tip supports links on hover - generalize this to all tooltips
|
||||
|
||||
|
||||
## Tooltip Construction
|
||||
|
||||
@docs Tooltip, tooltip
|
||||
@ -223,7 +231,7 @@ auxillaryDescription =
|
||||
|
||||
{-| Supplementary information triggered by a "?" icon
|
||||
|
||||
A toggle tip is always triggered by a click.
|
||||
A toggle tip is always triggered by a hover (or focus, for keyboard users)
|
||||
|
||||
-}
|
||||
toggleTip :
|
||||
@ -235,37 +243,73 @@ toggleTip :
|
||||
-> Tooltip msg
|
||||
-> Html msg
|
||||
toggleTip { isOpen, onTrigger, extraButtonAttrs, label } tooltip_ =
|
||||
let
|
||||
contentSize =
|
||||
20
|
||||
in
|
||||
Nri.Ui.styled Html.div
|
||||
"Nri-Ui-Tooltip-V1-ToggleTip"
|
||||
tooltipContainerStyles
|
||||
(tooltipContainerStyles
|
||||
++ [ -- Take up enough room within the document flow
|
||||
Css.width (Css.px contentSize)
|
||||
, Css.height (Css.px contentSize)
|
||||
, Css.margin (Css.px 5)
|
||||
]
|
||||
)
|
||||
[]
|
||||
[ Html.button
|
||||
([ Widget.label label
|
||||
, css
|
||||
([ Css.width (Css.px 20)
|
||||
, Css.height (Css.px 20)
|
||||
, Css.color Colors.azure
|
||||
]
|
||||
++ buttonStyleOverrides
|
||||
)
|
||||
, EventExtras.onClickStopPropagation (onTrigger True)
|
||||
, Events.onBlur (onTrigger False)
|
||||
, css buttonStyleOverrides
|
||||
]
|
||||
++ eventsForTrigger OnHover onTrigger
|
||||
++ extraButtonAttrs
|
||||
)
|
||||
[ iconHelp ]
|
||||
, viewIf (\_ -> viewCloseTooltipOverlay (onTrigger False)) isOpen
|
||||
, Html.span
|
||||
[ -- This adds aria-live polite & also aria-live atomic, so our screen readers are alerted when content appears
|
||||
Role.status
|
||||
]
|
||||
[ -- Popout is rendered after the overlay, to allow client code to give it
|
||||
-- priority when clicking by setting its position
|
||||
viewIf (\_ -> viewTooltip Nothing tooltip_) isOpen
|
||||
[ hoverBridge contentSize
|
||||
[ Html.div
|
||||
[ css
|
||||
[ Css.position Css.relative
|
||||
, Css.width (Css.px contentSize)
|
||||
, Css.height (Css.px contentSize)
|
||||
, Css.color Colors.azure
|
||||
]
|
||||
]
|
||||
[ iconHelp
|
||||
, Html.span
|
||||
[ -- This adds aria-live polite & also aria-live atomic, so our screen readers are alerted when content appears
|
||||
Role.status
|
||||
]
|
||||
[ viewIf (\_ -> viewTooltip Nothing OnHover tooltip_) isOpen ]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
{-| Provides a "bridge" for the cursor to move from trigger content to tooltip, so the user can click on links, etc.
|
||||
|
||||
Works by being larger than the trigger content & overlaying it, but is removed from the flow of the page (position: absolute), so that it looks ok visually.
|
||||
|
||||
-}
|
||||
hoverBridge : Float -> List (Html msg) -> Html msg
|
||||
hoverBridge contentSize =
|
||||
let
|
||||
padding =
|
||||
-- enough to cover the empty gap between tooltip and trigger content
|
||||
10
|
||||
in
|
||||
Nri.Ui.styled Html.div
|
||||
"tooltip-hover-bridge"
|
||||
[ Css.boxSizing Css.borderBox
|
||||
, Css.padding (Css.px padding)
|
||||
, Css.width (Css.px <| contentSize + padding * 2)
|
||||
, Css.height (Css.px <| contentSize + padding * 2)
|
||||
, Css.position Css.absolute
|
||||
, Css.top (Css.px <| negate padding)
|
||||
, Css.left (Css.px <| negate padding)
|
||||
]
|
||||
[]
|
||||
|
||||
|
||||
{-| Made with <https://levelteams.com/svg-to-elm>
|
||||
-}
|
||||
iconHelp : Svg msg
|
||||
@ -333,7 +377,7 @@ viewTooltip_ purpose { trigger, triggerHtml, onTrigger, isOpen, id, extraButtonA
|
||||
|
||||
-- Popout is rendered after the overlay, to allow client code to give it
|
||||
-- priority when clicking by setting its position
|
||||
, viewIf (\_ -> viewTooltip (Just id) tooltip_) isOpen
|
||||
, viewIf (\_ -> viewTooltip (Just id) trigger tooltip_) isOpen
|
||||
]
|
||||
|
||||
|
||||
@ -349,9 +393,9 @@ viewIf viewFn condition =
|
||||
Html.text ""
|
||||
|
||||
|
||||
viewTooltip : Maybe String -> Tooltip msg -> Html msg
|
||||
viewTooltip maybeTooltipId (Tooltip config) =
|
||||
Html.div (containerPositioningForArrowPosition config.position)
|
||||
viewTooltip : Maybe String -> Trigger -> Tooltip msg -> Html msg
|
||||
viewTooltip maybeTooltipId trigger (Tooltip config) =
|
||||
Html.div [ css (containerPositioningForArrowPosition config.position) ]
|
||||
[ Html.div
|
||||
([ css
|
||||
([ Css.borderRadius (Css.px 8)
|
||||
@ -426,33 +470,32 @@ tooltipColor =
|
||||
|
||||
{-| This returns an absolute positioning style attribute for the popout container for a given arrow position.
|
||||
-}
|
||||
containerPositioningForArrowPosition : Position -> List (Attribute msg)
|
||||
containerPositioningForArrowPosition : Position -> List Style
|
||||
containerPositioningForArrowPosition arrowPosition =
|
||||
List.map (\( k, v ) -> Attributes.style k v) <|
|
||||
case arrowPosition of
|
||||
OnTop ->
|
||||
[ ( "left", "50%" )
|
||||
, ( "top", "calc(-" ++ String.fromFloat arrowSize ++ "px - 2px)" )
|
||||
, ( "position", "absolute" )
|
||||
]
|
||||
case arrowPosition of
|
||||
OnTop ->
|
||||
[ Css.left (Css.pct 50)
|
||||
, Css.top (Css.calc (Css.px (negate arrowSize)) Css.minus (Css.px 2))
|
||||
, Css.position Css.absolute
|
||||
]
|
||||
|
||||
OnBottom ->
|
||||
[ ( "left", "50%" )
|
||||
, ( "bottom", "calc(-" ++ String.fromFloat arrowSize ++ "px - 2px)" )
|
||||
, ( "position", "absolute" )
|
||||
]
|
||||
OnBottom ->
|
||||
[ Css.left (Css.pct 50)
|
||||
, Css.bottom (Css.calc (Css.px (negate arrowSize)) Css.minus (Css.px 2))
|
||||
, Css.position Css.absolute
|
||||
]
|
||||
|
||||
OnLeft ->
|
||||
[ ( "top", "50%" )
|
||||
, ( "left", "calc(-" ++ String.fromFloat arrowSize ++ "px - 2px)" )
|
||||
, ( "position", "absolute" )
|
||||
]
|
||||
OnLeft ->
|
||||
[ Css.top (Css.pct 50)
|
||||
, Css.left (Css.calc (Css.px (negate arrowSize)) Css.minus (Css.px 2))
|
||||
, Css.position Css.absolute
|
||||
]
|
||||
|
||||
OnRight ->
|
||||
[ ( "top", "50%" )
|
||||
, ( "right", "calc(-" ++ String.fromFloat arrowSize ++ "px - 2px)" )
|
||||
, ( "position", "absolute" )
|
||||
]
|
||||
OnRight ->
|
||||
[ Css.top (Css.pct 50)
|
||||
, Css.right (Css.calc (Css.px (negate arrowSize)) Css.minus (Css.px 2))
|
||||
, Css.position Css.absolute
|
||||
]
|
||||
|
||||
|
||||
pointerBox : Position -> Attribute msg
|
||||
|
@ -8,10 +8,10 @@ module Examples.Tooltip exposing (example, init, update, State, Msg)
|
||||
|
||||
import Accessibility.Styled as Html
|
||||
import Css
|
||||
import Html.Styled.Attributes exposing (css)
|
||||
import Html.Styled.Attributes exposing (css, href)
|
||||
import ModuleExample as ModuleExample exposing (Category(..), ModuleExample)
|
||||
import Nri.Ui.Heading.V2 as Heading
|
||||
import Nri.Ui.Text.V3 as Text
|
||||
import Nri.Ui.Text.V4 as Text
|
||||
import Nri.Ui.Tooltip.V1 as Tooltip
|
||||
|
||||
|
||||
@ -19,7 +19,10 @@ type TooltipType
|
||||
= PrimaryLabelOnClick
|
||||
| PrimaryLabelOnHover
|
||||
| AuxillaryDescription
|
||||
| ToggleTip
|
||||
| ToggleTipTop
|
||||
| ToggleTipRight
|
||||
| ToggleTipBottom
|
||||
| ToggleTipLeft
|
||||
|
||||
|
||||
type alias State =
|
||||
@ -97,12 +100,40 @@ example msg model =
|
||||
, Html.br [ css [ Css.marginBottom (Css.px 20) ] ]
|
||||
, Heading.h3 [] [ Html.text "toggleTip" ]
|
||||
, Text.smallBody [ Html.text "A Toggle Tip is triggered by the \"?\" icon and provides supplemental information for the page." ]
|
||||
, Tooltip.tooltip [ Html.text "Tooltip" ]
|
||||
|> Tooltip.toggleTip
|
||||
{ onTrigger = ToggleTooltip ToggleTip >> msg
|
||||
, isOpen = model.openTooltip == Just ToggleTip
|
||||
, label = "More info"
|
||||
, extraButtonAttrs = []
|
||||
}
|
||||
, Html.div [ css [ Css.displayFlex, Css.alignItems Css.center ] ]
|
||||
[ Tooltip.tooltip
|
||||
[ Html.text "Tooltip On Top! "
|
||||
, Html.a
|
||||
[ href "/" ]
|
||||
[ Html.text "Links work!" ]
|
||||
]
|
||||
|> Tooltip.toggleTip
|
||||
{ onTrigger = ToggleTooltip ToggleTipTop >> msg
|
||||
, isOpen = model.openTooltip == Just ToggleTipTop
|
||||
, label = "More info"
|
||||
, extraButtonAttrs = []
|
||||
}
|
||||
, Text.mediumBody
|
||||
[ Html.text "This toggletip will open on top"
|
||||
]
|
||||
]
|
||||
, Html.div [ css [ Css.displayFlex, Css.alignItems Css.center ] ]
|
||||
[ Tooltip.tooltip
|
||||
[ Html.text "Tooltip On Left! "
|
||||
, Html.a
|
||||
[ href "/" ]
|
||||
[ Html.text "Links work!" ]
|
||||
]
|
||||
|> Tooltip.withPosition Tooltip.OnLeft
|
||||
|> Tooltip.toggleTip
|
||||
{ onTrigger = ToggleTooltip ToggleTipLeft >> msg
|
||||
, isOpen = model.openTooltip == Just ToggleTipLeft
|
||||
, label = "More info"
|
||||
, extraButtonAttrs = []
|
||||
}
|
||||
, Text.mediumBody
|
||||
[ Html.text "This toggletip will open on the left"
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ spec : Test
|
||||
spec =
|
||||
describe "Nri.Ui.Tooltip.V1"
|
||||
[ describe "toggleTip"
|
||||
[ test "Toggletip is available on click and hides on blur" <|
|
||||
[ test "Toggletip is available on hover and hides on blur" <|
|
||||
\() ->
|
||||
ProgramTest.createSandbox
|
||||
{ init = init
|
||||
@ -52,7 +52,7 @@ spec =
|
||||
(Widget.label "More info")
|
||||
]
|
||||
)
|
||||
Event.click
|
||||
Event.mouseEnter
|
||||
|> ProgramTest.ensureViewHas
|
||||
[ Selector.text "Toggly"
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user