Merge remote-tracking branch 'origin/master' into bat/overlapping-highlighter

This commit is contained in:
Tessa Kelly 2023-03-13 15:43:55 -06:00
commit 61f1389bc7
11 changed files with 640 additions and 99 deletions

View File

@ -6,6 +6,7 @@ module Examples.HighlighterToolbar exposing (Msg, State, example)
-}
import Browser.Dom as Dom
import Category exposing (Category(..))
import Code
import Css exposing (Color)
@ -13,13 +14,15 @@ import Debug.Control as Control exposing (Control)
import Debug.Control.View as ControlView
import Example exposing (Example)
import Html.Styled exposing (..)
import Html.Styled.Attributes exposing (css)
import Html.Styled.Attributes exposing (css, id)
import KeyboardSupport exposing (Key(..))
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Heading.V3 as Heading
import Nri.Ui.HighlighterToolbar.V1 as HighlighterToolbar
import Nri.Ui.HighlighterToolbar.V2 as HighlighterToolbar
import Nri.Ui.Svg.V1 as Svg
import Nri.Ui.Text.V6 as Text
import Nri.Ui.UiIcon.V1 as UiIcon
import Task
moduleName : String
@ -29,7 +32,7 @@ moduleName =
version : Int
version =
1
2
{-| -}
@ -61,17 +64,25 @@ example =
}
, Heading.h2 [ Heading.plaintext "Example" ]
, HighlighterToolbar.view
{ onSetEraser = SetTool Nothing
, onChangeTag = SetTool << Just
{ focusAndSelect = FocusAndSelectTag
, getColor = getColor
, getName = getName
, highlighterId = "highlighter"
}
{ currentTool = state.currentTool
, tags = tags
}
, div [ id "highlighter" ] []
]
, categories = [ Instructional ]
, keyboardSupport = []
, keyboardSupport =
[ { keys = [ Arrow KeyboardSupport.Left ]
, result = "Select the tool to the left of the currently-selected tool. If the first tool is selected, select the last tool."
}
, { keys = [ Arrow KeyboardSupport.Right ]
, result = "Select the tool to the right of the currently-selected tool. If the last tool is selected, select the first tool."
}
]
}
@ -174,7 +185,8 @@ controlSettings =
{-| -}
type Msg
= UpdateControls (Control Settings)
| SetTool (Maybe Tag)
| FocusAndSelectTag { select : Maybe Tag, focus : Maybe String }
| Focused (Result Dom.Error ())
{-| -}
@ -184,5 +196,12 @@ update msg state =
UpdateControls settings ->
( { state | settings = settings }, Cmd.none )
SetTool tag ->
( { state | currentTool = tag }, Cmd.none )
FocusAndSelectTag { select, focus } ->
( { state | currentTool = select }
, focus
|> Maybe.map (Dom.focus >> Task.attempt Focused)
|> Maybe.withDefault Cmd.none
)
Focused error ->
( state, Cmd.none )

View File

@ -17,8 +17,13 @@ import Example exposing (Example)
import Html.Styled exposing (..)
import Html.Styled.Attributes exposing (css)
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Container.V2 as Container
import Nri.Ui.Fonts.V1 as Fonts
import Nri.Ui.Heading.V3 as Heading
import Nri.Ui.Spacing.V1 as Spacing
import Nri.Ui.Table.V6 as Table
import Svg.Styled
import Svg.Styled.Attributes
moduleName : String
@ -41,11 +46,163 @@ example =
, state = init
, update = update
, subscriptions = \_ -> Sub.none
, preview = []
, preview = preview
, view = view
}
preview : List (Html msg)
preview =
[ Svg.Styled.svg
[ Svg.Styled.Attributes.viewBox "0 0 100 100"
]
[ Svg.Styled.rect
[ Svg.Styled.Attributes.width "100"
, Svg.Styled.Attributes.height "100"
, Svg.Styled.Attributes.fill Colors.white.value
]
[]
, Svg.Styled.rect
[ Svg.Styled.Attributes.x "15"
, Svg.Styled.Attributes.y "30"
, Svg.Styled.Attributes.width "70"
, Svg.Styled.Attributes.height "20"
, Svg.Styled.Attributes.fill Colors.gray96.value
]
[]
, Svg.Styled.text_
[ Svg.Styled.Attributes.fill Colors.gray20.value
, Svg.Styled.Attributes.css [ Fonts.baseFont, Css.fontSize (Css.px 8) ]
, Svg.Styled.Attributes.x "20"
, Svg.Styled.Attributes.y "43"
]
[ Svg.Styled.text "Content" ]
, -- Top red line indicator
Svg.Styled.g []
[ Svg.Styled.line
[ Svg.Styled.Attributes.x1 "50"
, Svg.Styled.Attributes.x2 "50"
, Svg.Styled.Attributes.y1 "1"
, Svg.Styled.Attributes.y2 "29"
, Svg.Styled.Attributes.stroke Colors.red.value
]
[]
, Svg.Styled.line
[ Svg.Styled.Attributes.x1 "45"
, Svg.Styled.Attributes.x2 "55"
, Svg.Styled.Attributes.y1 "1"
, Svg.Styled.Attributes.y2 "1"
, Svg.Styled.Attributes.stroke Colors.red.value
]
[]
, Svg.Styled.line
[ Svg.Styled.Attributes.x1 "45"
, Svg.Styled.Attributes.x2 "55"
, Svg.Styled.Attributes.y1 "29"
, Svg.Styled.Attributes.y2 "29"
, Svg.Styled.Attributes.stroke Colors.red.value
]
[]
, Svg.Styled.text_
[ Svg.Styled.Attributes.fill Colors.red.value
, Svg.Styled.Attributes.css [ Fonts.baseFont, Css.fontSize (Css.px 8) ]
, Svg.Styled.Attributes.x "53"
, Svg.Styled.Attributes.y "20"
]
[ Svg.Styled.text "30px" ]
]
, -- Bottom red line indicator
Svg.Styled.g []
[ Svg.Styled.line
[ Svg.Styled.Attributes.x1 "50"
, Svg.Styled.Attributes.x2 "50"
, Svg.Styled.Attributes.y1 "51"
, Svg.Styled.Attributes.y2 "99"
, Svg.Styled.Attributes.stroke Colors.red.value
]
[]
, Svg.Styled.line
[ Svg.Styled.Attributes.x1 "45"
, Svg.Styled.Attributes.x2 "55"
, Svg.Styled.Attributes.y1 "51"
, Svg.Styled.Attributes.y2 "51"
, Svg.Styled.Attributes.stroke Colors.red.value
]
[]
, Svg.Styled.line
[ Svg.Styled.Attributes.x1 "45"
, Svg.Styled.Attributes.x2 "55"
, Svg.Styled.Attributes.y1 "99"
, Svg.Styled.Attributes.y2 "99"
, Svg.Styled.Attributes.stroke Colors.red.value
]
[]
, Svg.Styled.text_
[ Svg.Styled.Attributes.fill Colors.red.value
, Svg.Styled.Attributes.css [ Fonts.baseFont, Css.fontSize (Css.px 8) ]
, Svg.Styled.Attributes.x "53"
, Svg.Styled.Attributes.y "75"
]
[ Svg.Styled.text "50px" ]
]
, -- Right red line indicator
Svg.Styled.g []
[ Svg.Styled.line
[ Svg.Styled.Attributes.x1 "86"
, Svg.Styled.Attributes.x2 "99"
, Svg.Styled.Attributes.y1 "40"
, Svg.Styled.Attributes.y2 "40"
, Svg.Styled.Attributes.stroke Colors.red.value
]
[]
, Svg.Styled.line
[ Svg.Styled.Attributes.x1 "86"
, Svg.Styled.Attributes.x2 "86"
, Svg.Styled.Attributes.y1 "38"
, Svg.Styled.Attributes.y2 "42"
, Svg.Styled.Attributes.stroke Colors.red.value
]
[]
, Svg.Styled.line
[ Svg.Styled.Attributes.x1 "99"
, Svg.Styled.Attributes.x2 "99"
, Svg.Styled.Attributes.y1 "38"
, Svg.Styled.Attributes.y2 "42"
, Svg.Styled.Attributes.stroke Colors.red.value
]
[]
]
, -- Left red line indicator
Svg.Styled.g []
[ Svg.Styled.line
[ Svg.Styled.Attributes.x1 "1"
, Svg.Styled.Attributes.x2 "14"
, Svg.Styled.Attributes.y1 "40"
, Svg.Styled.Attributes.y2 "40"
, Svg.Styled.Attributes.stroke Colors.red.value
]
[]
, Svg.Styled.line
[ Svg.Styled.Attributes.x1 "1"
, Svg.Styled.Attributes.x2 "1"
, Svg.Styled.Attributes.y1 "38"
, Svg.Styled.Attributes.y2 "42"
, Svg.Styled.Attributes.stroke Colors.red.value
]
[]
, Svg.Styled.line
[ Svg.Styled.Attributes.x1 "14"
, Svg.Styled.Attributes.x2 "14"
, Svg.Styled.Attributes.y1 "38"
, Svg.Styled.Attributes.y2 "42"
, Svg.Styled.Attributes.stroke Colors.red.value
]
[]
]
]
]
view : EllieLink.Config -> State -> List (Html Msg)
view ellieLinkConfig state =
let
@ -58,25 +215,8 @@ view ellieLinkConfig state =
[ settings.topContainerStyle
, settings.horizontalContainerStyle
, settings.bottomContainerStyle
, if settings.childVerticalSpace then
Just
( "Css.property \"row-gap\" (.value Spacing.verticalSpacerPx)"
, Css.property "row-gap" (.value Spacing.verticalSpacerPx)
)
else
Nothing
, if settings.childHorizontalSpace then
Just
( "Css.property \"column-gap\" (.value Spacing.horizontalSpacerPx"
, Css.property "column-gap" (.value Spacing.horizontalSpacerPx)
)
else
Nothing
]
)
(List.repeat settings.childCount child)
in
[ ControlView.view
{ ellieLinkConfig = ellieLinkConfig
@ -94,8 +234,67 @@ view ellieLinkConfig state =
}
]
}
, Heading.h2 [ Heading.plaintext "Example" ]
, Heading.h2 [ Heading.plaintext "Example", Heading.css [ Css.marginTop Spacing.verticalSpacerPx ] ]
, fakePage [ exampleView ]
, Heading.h2 [ Heading.plaintext "Content alignment", Heading.css [ Css.marginTop Spacing.verticalSpacerPx ] ]
, Table.view
[ Table.string
{ header = "Name"
, value = .name
, width = Css.pct 10
, cellStyles = always [ Css.padding2 (Css.px 14) (Css.px 7), Css.verticalAlign Css.middle ]
, sort = Nothing
}
, Table.string
{ header = "Content alignment"
, value = .alignment
, width = Css.pct 10
, cellStyles = always [ Css.padding2 (Css.px 14) (Css.px 7), Css.verticalAlign Css.middle ]
, sort = Nothing
}
, Table.string
{ header = "Content max-width"
, value = .maxWidth
, width = Css.pct 10
, cellStyles = always [ Css.padding2 (Css.px 14) (Css.px 7), Css.verticalAlign Css.middle ]
, sort = Nothing
}
, Table.string
{ header = "Side padding"
, value = .sidePadding
, width = Css.pct 10
, cellStyles = always [ Css.padding2 (Css.px 14) (Css.px 7), Css.verticalAlign Css.middle ]
, sort = Nothing
}
]
[ { name = "centeredContentWithSidePadding", alignment = "Centered", maxWidth = "1000px", sidePadding = "when viewport <= 970px" }
, { name = "centeredContent", alignment = "Centered", maxWidth = "1000px", sidePadding = "0px" }
, { name = "centeredQuizEngineContentWithSidePadding", alignment = "Centered", maxWidth = "750px", sidePadding = "when viewport <= 720px" }
, { name = "centeredQuizEngineContent", alignment = "Centered", maxWidth = "750px", sidePadding = "0px" }
, { name = "centeredContentWithSidePaddingAndCustomWidth", alignment = "Centered", maxWidth = "(customizable)", sidePadding = "when viewport <= (custom breakpoint value - 30)" }
, { name = "centeredContentWithCustomWidth", alignment = "Centered", maxWidth = "(customizable)", sidePadding = "0px" }
]
, Heading.h2 [ Heading.plaintext "Constants", Heading.css [ Css.marginTop Spacing.verticalSpacerPx ] ]
, Table.view
[ Table.string
{ header = "Name"
, value = .name
, width = Css.pct 10
, cellStyles = always [ Css.padding2 (Css.px 14) (Css.px 7), Css.verticalAlign Css.middle ]
, sort = Nothing
}
, Table.string
{ header = "Value"
, value = .value
, width = Css.pct 10
, cellStyles = always [ Css.padding2 (Css.px 14) (Css.px 7), Css.verticalAlign Css.middle ]
, sort = Nothing
}
]
[ { name = "pageTopWhitespacePx", value = "30px" }
, { name = "pageBottomWhitespacePx", value = "50px" }
, { name = "pageSideWhitespacePx", value = "15px" }
]
]
@ -109,53 +308,16 @@ fakePage =
]
container : List ( String, Css.Style ) -> List ( String, Html msg ) -> ( String, Html msg )
container styles children =
( [ "div"
, "[ css"
, " [ Css.border3 (Css.px 2) Css.dashed Colors.greenDarkest"
, " , Css.backgroundColor Colors.greenLightest"
, " , Css.property " ++ Code.string "display" ++ " " ++ Code.string "grid"
, " , Css.property " ++ Code.string "grid-template-columns" ++ " " ++ Code.string "1fr 1fr 1fr 1fr 1fr"
, " , Css.batch " ++ Code.listMultiline (List.map Tuple.first styles) 3
container : List ( String, Css.Style ) -> ( String, Html msg )
container styles =
( [ "div [ css " ++ Code.listMultiline (List.map Tuple.first styles) 2
, "]"
, "]"
, Code.list (List.map Tuple.first children)
, "[ Container.view [ Container.paragraph \"Content...\" ]"
]
|> String.join (Code.newlineWithIndent 1)
, div
[ css
[ Css.border3 (Css.px 2) Css.dashed Colors.greenDarkest
, Css.backgroundColor Colors.greenLightest
, Css.property "display" "grid"
, Css.property "grid-template-columns" "1fr 1fr 1fr 1fr 1fr"
, Css.batch (List.map Tuple.second styles)
, div [ css (List.map Tuple.second styles) ]
[ Container.view [ Container.paragraph "Content..." ]
]
]
(List.map Tuple.second children)
)
child : ( String, Html msg )
child =
( [ "div"
, "[ css"
, " [ Css.border3 (Css.px 1) Css.solid Colors.ochreDark"
, " , Css.backgroundColor Colors.sunshine"
, " , Css.height (Css.px 150)"
, " ]"
, "]"
, "[]"
]
|> String.join (Code.newlineWithIndent 2)
, div
[ css
[ Css.border3 (Css.px 1) Css.solid Colors.ochreDark
, Css.backgroundColor Colors.sunshine
, Css.height (Css.px 150)
]
]
[]
)
@ -173,9 +335,6 @@ type alias Settings =
{ topContainerStyle : Maybe ( String, Style )
, horizontalContainerStyle : Maybe ( String, Style )
, bottomContainerStyle : Maybe ( String, Style )
, childCount : Int
, childVerticalSpace : Bool
, childHorizontalSpace : Bool
}
@ -236,9 +395,6 @@ controlSettings =
)
)
)
|> Control.field "Child count" (ControlExtra.int 10)
|> Control.field "Separate children vertically" (Control.bool True)
|> Control.field "Separate children horizontally" (Control.bool True)
asChoice : ( String, Style ) -> ( String, Control ( String, Style ) )

View File

@ -1,6 +1,7 @@
Nri.Ui.Block.V3,upgrade to V4
Nri.Ui.Highlightable.V1,upgrade to V2
Nri.Ui.Highlighter.V2,upgrade to V3
Nri.Ui.HighlighterToolbar.V1,upgrade to V2
Nri.Ui.QuestionBox.V2,upgrade to V4
Nri.Ui.QuestionBox.V3,upgrade to V4
Nri.Ui.Select.V8,upgrade to V9

1 Nri.Ui.Block.V3 upgrade to V4
2 Nri.Ui.Highlightable.V1 upgrade to V2
3 Nri.Ui.Highlighter.V2 upgrade to V3
4 Nri.Ui.HighlighterToolbar.V1 upgrade to V2
5 Nri.Ui.QuestionBox.V2 upgrade to V4
6 Nri.Ui.QuestionBox.V3 upgrade to V4
7 Nri.Ui.Select.V8 upgrade to V9

View File

@ -40,6 +40,7 @@
"Nri.Ui.Highlighter.V3",
"Nri.Ui.HighlighterTool.V1",
"Nri.Ui.HighlighterToolbar.V1",
"Nri.Ui.HighlighterToolbar.V2",
"Nri.Ui.Html.Attributes.V2",
"Nri.Ui.Html.V3",
"Nri.Ui.InputStyles.V4",

View File

@ -87,6 +87,9 @@ hint = 'upgrade to V2'
[forbidden."Nri.Ui.Highlighter.V2"]
hint = 'upgrade to V3'
[forbidden."Nri.Ui.HighlighterToolbar.V1"]
hint = 'upgrade to V2'
[forbidden."Nri.Ui.Icon.V3"]
hint = 'upgrade to V5'
usages = ['component-catalog-app/../src/Nri/Ui/Modal/V3.elm']

View File

@ -7,6 +7,7 @@ module Nri.Ui.HighlighterToolbar.V1 exposing (view)
-}
import Accessibility.Styled.Aria as Aria
import Accessibility.Styled.Role as Role
import Css exposing (Color)
import EventExtras exposing (onClickPreventDefaultAndStopPropagation)
import Html.Styled exposing (..)
@ -19,10 +20,13 @@ import Nri.Ui.Svg.V1 as Svg
import Nri.Ui.UiIcon.V1 as UiIcon
toolbar : List (Html msg) -> Html msg
toolbar =
toolbar : String -> List (Html msg) -> Html msg
toolbar highlighterId =
ul
[ nriDescription "tools"
, Role.toolBar
, Aria.label "Highlighter options"
, Aria.controls [ highlighterId ]
, css
[ Css.displayFlex
, Css.listStyle Css.none
@ -46,6 +50,7 @@ view :
, onChangeTag : tag -> msg
, getColor : tag -> { extras | colorSolid : Color, colorLight : Color }
, getName : tag -> String
, highlighterId : String
}
-> { model | currentTool : Maybe tag, tags : List tag }
-> Html msg
@ -59,7 +64,7 @@ view config model =
eraserSelected =
model.currentTool == Nothing
in
toolbar
toolbar config.highlighterId
(List.map viewTagWithConfig model.tags
++ [ viewEraser config.onSetEraser eraserSelected ]
)
@ -70,6 +75,7 @@ viewTag :
, onChangeTag : tag -> msg
, getColor : tag -> { extras | colorSolid : Color, colorLight : Color }
, getName : tag -> String
, highlighterId : String
}
-> Bool
-> tag

View File

@ -0,0 +1,276 @@
module Nri.Ui.HighlighterToolbar.V2 exposing (view)
{-| Bar with markers for choosing how text will be highlighted in a highlighter.
@docs view
Changes from V1:
- replaces `onChangeTag` and `onSetEraser` with `focusAndSelect`.
- adds `highlighterId` to config
- adds keyboard navigation
-}
import Accessibility.Styled.Aria as Aria
import Accessibility.Styled.Key as Key
import Accessibility.Styled.Role as Role
import Css exposing (Color)
import EventExtras exposing (onClickPreventDefaultAndStopPropagation)
import Html.Styled exposing (..)
import Html.Styled.Attributes exposing (css, id, tabindex)
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Fonts.V1 as Fonts
import Nri.Ui.Html.Attributes.V2 exposing (nriDescription)
import Nri.Ui.Html.V3 exposing (viewIf)
import Nri.Ui.Svg.V1 as Svg
import Nri.Ui.UiIcon.V1 as UiIcon
toolbar : String -> List (Html msg) -> Html msg
toolbar highlighterId =
div
[ nriDescription "tools"
, Role.toolBar
, Aria.label "Highlighter options"
, Aria.controls [ highlighterId ]
, css
[ Css.displayFlex
, Css.listStyle Css.none
, Css.padding (Css.px 0)
, Css.margin (Css.px 0)
, Css.marginTop (Css.px 10)
, Css.flexWrap Css.wrap
]
]
toolContainer : String -> Html msg -> Html msg
toolContainer toolName tool =
div [ nriDescription toolName ] [ tool ]
{-| View renders each marker and an eraser. This is usually used with a Highlighter.
-}
view :
{ focusAndSelect : { select : Maybe tag, focus : Maybe String } -> msg
, getColor : tag -> { extras | colorSolid : Color, colorLight : Color }
, getName : tag -> String
, highlighterId : String
}
-> { model | currentTool : Maybe tag, tags : List tag }
-> Html msg
view config model =
let
tools =
List.map Just model.tags ++ [ Nothing ]
viewTagWithConfig : tag -> Html msg
viewTagWithConfig tag =
viewTag config (model.currentTool == Just tag) tag tools model.currentTool
eraserSelected : Bool
eraserSelected =
model.currentTool == Nothing
in
toolbar config.highlighterId
(List.map viewTagWithConfig model.tags
++ [ viewEraser config.focusAndSelect eraserSelected tools model.currentTool config.getName ]
)
viewTag :
{ focusAndSelect : { select : Maybe tag, focus : Maybe String } -> msg
, getColor : tag -> { extras | colorSolid : Color, colorLight : Color }
, getName : tag -> String
, highlighterId : String
}
-> Bool
-> tag
-> List (Maybe tag)
-> Maybe tag
-> Html msg
viewTag { focusAndSelect, getColor, getName } selected tag tools currentTool =
toolContainer ("tag-" ++ getName tag)
(viewTool (getName tag) focusAndSelect (getColor tag) selected (Just tag) tools currentTool getName)
viewEraser :
({ select : Maybe tag, focus : Maybe String } -> msg)
-> Bool
-> List (Maybe tag)
-> Maybe tag
-> (tag -> String)
-> Html msg
viewEraser focusAndSelect selected tools currentTool getName =
toolContainer "eraser"
(viewTool "Remove highlight"
focusAndSelect
{ colorLight = Colors.gray75, colorSolid = Colors.white }
selected
Nothing
tools
currentTool
getName
)
viewTool :
String
-> ({ select : Maybe tag, focus : Maybe String } -> msg)
-> { extras | colorSolid : Color, colorLight : Color }
-> Bool
-> Maybe tag
-> List (Maybe tag)
-> Maybe tag
-> (tag -> String)
-> Html msg
viewTool name focusAndSelect theme selected tag tools currentTool getName =
button
[ id ("tag-" ++ name)
, css
[ Css.backgroundColor Css.transparent
, Css.borderRadius (Css.px 0)
, Css.border (Css.px 0)
, Css.active [ Css.outlineStyle Css.none ]
, Css.focus [ Css.outlineStyle Css.none ]
, Css.cursor Css.pointer
]
, onClickPreventDefaultAndStopPropagation (focusAndSelect { select = tag, focus = Nothing })
, Aria.pressed (Just selected)
, tabindex
(if selected then
0
else
-1
)
, Key.onKeyDownPreventDefault (keyEvents focusAndSelect currentTool tools getName)
]
[ toolContent name theme tag
, viewIf (\() -> active theme) selected
]
keyEvents : ({ select : Maybe tag, focus : Maybe String } -> msg) -> Maybe tag -> List (Maybe tag) -> (tag -> String) -> List (Key.Event msg)
keyEvents focusAndSelect tag tools getName =
let
onFocus tag_ =
focusAndSelect
{ select = tag_
, focus =
case tag_ of
Just tag__ ->
Just ("tag-" ++ getName tag__)
Nothing ->
Just "tag-Remove highlight"
}
findAdjacentTag tag_ ( isAdjacentTab, acc ) =
if isAdjacentTab then
( False, Just (onFocus tag_) )
else
( tag_ == tag, acc )
goToNextTag : Maybe msg
goToNextTag =
List.foldl findAdjacentTag
( False
, -- if there is no adjacent tag, default to the first tag
Maybe.map onFocus (List.head tools)
)
tools
|> Tuple.second
goToPreviousTag : Maybe msg
goToPreviousTag =
List.foldr findAdjacentTag
( False
, -- if there is no adjacent tag, default to the last tag
Maybe.map onFocus (List.head (List.reverse tools))
)
tools
|> Tuple.second
in
List.filterMap identity
[ Maybe.map Key.right goToNextTag
, Maybe.map Key.left goToPreviousTag
]
active :
{ extras | colorLight : Color }
-> Html msg
active palette_ =
div
[ nriDescription "active-tool"
, css
[ Css.width (Css.px 38)
, Css.height (Css.px 4)
, Css.backgroundColor palette_.colorLight
]
]
[]
toolContent :
String
-> { extras | colorSolid : Color, colorLight : Color }
-> Maybe tag
-> Html msg
toolContent name palette_ tool =
span
[ nriDescription "tool-content"
, css
[ Css.position Css.relative
, Css.height (Css.pct 100)
, Css.padding (Css.px 0)
, Css.paddingRight (Css.px 15)
, Css.display Css.inlineFlex
, Css.alignItems Css.center
]
]
[ case tool of
Just _ ->
toolIcon
{ background = palette_.colorSolid
, border = palette_.colorSolid
, icon = Svg.withColor Colors.white UiIcon.highlighter
}
Nothing ->
toolIcon
{ background = palette_.colorSolid
, border = Colors.gray75
, icon = Svg.withColor Colors.gray20 UiIcon.eraser
}
, span
[ nriDescription "tool-label"
, css
[ Css.color Colors.navy
, Css.fontSize (Css.px 15)
, Css.marginLeft (Css.px 5)
, Css.fontWeight (Css.int 600)
, Fonts.baseFont
]
]
[ text name ]
]
toolIcon : { background : Color, border : Color, icon : Svg.Svg } -> Html msg
toolIcon config =
span
[ css
[ Css.backgroundColor config.background
, Css.width (Css.px 38)
, Css.height (Css.px 38)
, Css.borderRadius (Css.pct 50)
, Css.padding (Css.px 7)
, Css.border3 (Css.px 1) Css.solid config.border
]
]
[ Svg.toHtml config.icon
]

View File

@ -23,6 +23,10 @@ module Nri.Ui.Spacing.V1 exposing
@docs pageTopWhitespace, pageTopWhitespacePx
@docs pageSideWhitespace, pageSideWhitespacePx
@docs pageBottomWhitespace, pageBottomWhitespacePx
## Deprecated:
@docs verticalSpacerPx, horizontalSpacerPx
-}
@ -168,20 +172,14 @@ pageTopWhitespacePx =
Css.px 30
{-| Most elements should have 20px of whitespace separating them vertically.
See [the UI Style Guide and Caveats' Spacing section](https://paper.dropbox.com/doc/UI-Style-Guide-and-Caveats--BobQllelpdS56NBITiRcrO6gAg-PvOLxeX3oyujYEzdJx5pu#:uid=905917270049954035442315&h2=:under-construction:-Spacing) for more details.
{-| DEPRECATED -- do not use. A future version of noredink-ui will remove.
-}
verticalSpacerPx : Css.Px
verticalSpacerPx =
Css.px 20
{-| Most elements should have 10px of whitespace separating them horizontally.
See [the UI Style Guide and Caveats' Spacing section](https://paper.dropbox.com/doc/UI-Style-Guide-and-Caveats--BobQllelpdS56NBITiRcrO6gAg-PvOLxeX3oyujYEzdJx5pu#:uid=905917270049954035442315&h2=:under-construction:-Spacing) for more details.
{-| DEPRECATED -- do not use. A future version of noredink-ui will remove.
-}
horizontalSpacerPx : Css.Px
horizontalSpacerPx =

View File

@ -1,13 +1,15 @@
module Spec.Nri.Ui.HighlighterToolbar exposing (..)
import Accessibility.Aria as Aria
import Accessibility.Key as Key
import Css exposing (Color)
import Expect
import Html.Attributes as Attributes
import Html.Styled as Html exposing (..)
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.HighlighterToolbar.V1 as HighlighterToolbar
import Nri.Ui.HighlighterToolbar.V2 as HighlighterToolbar
import ProgramTest exposing (..)
import Spec.KeyboardHelpers as KeyboardHelpers
import Test exposing (..)
import Test.Html.Event as Event
import Test.Html.Query as Query
@ -16,8 +18,9 @@ import Test.Html.Selector as Selector
spec : Test
spec =
describe "Nri.Ui.HighlighterToolbar.V1"
describe "Nri.Ui.HighlighterToolbar.V2"
[ describe "tool selection" selectionTests
, describe "keyboard behavior" keyboardTests
]
@ -43,6 +46,83 @@ selectionTests =
]
keyboardTests : List Test
keyboardTests =
[ test "has a focusable tool" <|
\() ->
program
|> ensureTabbable "Remove highlight"
|> done
, test "has only one tool included in the tab sequence" <|
\() ->
program
|> ensureOnlyOneInTabSequence [ "Claim", "Evidence", "Reasoning", "Remove highlight" ]
|> done
, test "moves focus right on right arrow key. Should wrap focus on last element." <|
\() ->
program
|> ensureTabbable "Remove highlight"
|> rightArrow
|> ensureTabbable "Claim"
|> ensureOnlyOneInTabSequence [ "Claim", "Evidence", "Reasoning", "Remove highlight" ]
|> rightArrow
|> ensureTabbable "Evidence"
|> rightArrow
|> ensureTabbable "Reasoning"
|> rightArrow
|> ensureTabbable "Remove highlight"
|> done
, test "moves focus left on left arrow key. Should wrap focus on first element." <|
\() ->
program
|> ensureTabbable "Remove highlight"
|> leftArrow
|> ensureTabbable "Reasoning"
|> leftArrow
|> ensureTabbable "Evidence"
|> leftArrow
|> ensureTabbable "Claim"
|> leftArrow
|> ensureTabbable "Remove highlight"
|> ensureOnlyOneInTabSequence [ "Claim", "Evidence", "Reasoning", "Remove highlight" ]
|> done
]
ensureTabbable : String -> TestContext -> TestContext
ensureTabbable word testContext =
testContext
|> ensureView
(Query.find [ Selector.attribute (Key.tabbable True) ]
>> Query.has [ Selector.text word ]
)
ensureOnlyOneInTabSequence : List String -> TestContext -> TestContext
ensureOnlyOneInTabSequence words testContext =
testContext
|> ensureView
(Query.findAll [ Selector.attribute (Key.tabbable True) ]
>> Query.count (Expect.equal 1)
)
|> ensureView
(Query.findAll [ Selector.attribute (Key.tabbable False) ]
>> Query.count (Expect.equal (List.length words - 1))
)
rightArrow : TestContext -> TestContext
rightArrow =
KeyboardHelpers.pressRightArrow { targetDetails = [] }
[ Selector.attribute (Key.tabbable True) ]
leftArrow : TestContext -> TestContext
leftArrow =
KeyboardHelpers.pressLeftArrow { targetDetails = [] }
[ Selector.attribute (Key.tabbable True) ]
clickTool : String -> ProgramTest model msg effect -> ProgramTest model msg effect
clickTool label =
ProgramTest.simulateDomEvent
@ -152,24 +232,24 @@ init =
{-| -}
type Msg
= SetTool (Maybe Tag)
= FocusAndSelectTag { select : Maybe Tag, focus : Maybe String }
{-| -}
update : Msg -> State -> State
update msg state =
case msg of
SetTool tag ->
{ state | currentTool = tag }
FocusAndSelectTag { select } ->
{ state | currentTool = select }
view : State -> Html Msg
view model =
HighlighterToolbar.view
{ onSetEraser = SetTool Nothing
, onChangeTag = SetTool << Just
{ focusAndSelect = FocusAndSelectTag
, getColor = getColor
, getName = getName
, highlighterId = "highlighter"
}
{ currentTool = model.currentTool
, tags = tags

View File

@ -91,10 +91,6 @@ keyboardTests =
]
type alias TestContext =
ProgramTest State Msg ()
update : Msg -> State -> State
update msg model =
case msg of
@ -126,6 +122,10 @@ view model =
}
type alias TestContext =
ProgramTest State Msg ()
program : TestContext
program =
ProgramTest.createSandbox

View File

@ -36,6 +36,7 @@
"Nri.Ui.Highlighter.V3",
"Nri.Ui.HighlighterTool.V1",
"Nri.Ui.HighlighterToolbar.V1",
"Nri.Ui.HighlighterToolbar.V2",
"Nri.Ui.Html.Attributes.V2",
"Nri.Ui.Html.V3",
"Nri.Ui.InputStyles.V4",