Merge pull request #1657 from NoRedInk/kra-1441-add-caption-option-to-nriuimenu-groups

Add Menu.captionedGroup, text+menu css helpers, example
This commit is contained in:
Erik 2024-03-20 10:47:17 -04:00 committed by GitHub
commit ebd5f64952
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 172 additions and 28 deletions

View File

@ -658,7 +658,7 @@ You will need to pass in the first and last focusable element in the dialog cont
}
, Table.custom
{ header = text "Example"
, view = \{ name, menuType, entries } -> forcedOpenExample name menuType entries
, view = \{ name, attributes, entries } -> forcedOpenExample name attributes entries
, width = Css.pct 20
, cellStyles =
\{ name } ->
@ -707,7 +707,7 @@ You will need to pass in the first and last focusable element in the dialog cont
}
]
[ { name = "List of entries"
, menuType = Menu.navMenuList
, attributes = [ Menu.navMenuList ]
, entries =
[ Menu.entry "list-of-entries-clickable-text-entry" <|
\attributes ->
@ -733,7 +733,7 @@ You will need to pass in the first and last focusable element in the dialog cont
, about = "Pass any interactive elements in using `Menu.entry`."
}
, { name = "Grouped entries"
, menuType = Menu.navMenuList
, attributes = [ Menu.navMenuList ]
, entries =
[ List.range 1 2
|> List.map
@ -771,7 +771,7 @@ The structures are recursive and flexible:
"""
}
, { name = "Mix of singular entries and grouped entries"
, menuType = Menu.disclosure { lastId = droppedStudentsId }
, attributes = [ Menu.disclosure { lastId = droppedStudentsId } ]
, entries =
[ Menu.entry "grades-and-perf" <|
\attributes ->
@ -795,26 +795,104 @@ Because `group` and `entry` both result in the same type, individual entries and
Please note that depending on the interactive component you select, our composability may not work correctly yet.
In this realistic example, we can't actually pass the correct attributes to RadioButton or Switch because it will cause the events for those components to be swallowed, rendering them inoperable.
"""
}
, { name = "Custom CSS"
, attributes =
[ Menu.disclosure { lastId = "which-should-i-choose" }
, Menu.menuWidth 350
, Menu.menuCss
[ Css.padding (Css.px 20)
]
, Menu.groupContainerCss
[ Css.displayFlex
, Css.property "gap" "10px"
]
, Menu.groupTitleCss
[ Css.fontSize (Css.px 16)
, Css.fontWeight (Css.int 700)
, Css.color Colors.gray20
]
, Menu.groupCaptionCss
[ Css.fontSize (Css.px 13)
]
]
, entries =
[ Menu.captionedGroup "Quick Write"
"Students write independently, without lessons or tips."
[ Menu.entry "preview-quick-write" <|
\attributes ->
ClickableSvg.link "Preview"
UiIcon.preview
[ ClickableSvg.linkExternal "about:blank"
, ClickableSvg.secondary
, ClickableSvg.withBorder
, ClickableSvg.custom attributes
]
, Menu.entry "assign-quick-write" <|
\attributes ->
ClickableSvg.link "Assign"
UiIcon.arrowPointingRightThick
[ ClickableSvg.linkExternal "about:blank"
, ClickableSvg.primary
, ClickableSvg.withBorder
, ClickableSvg.custom attributes
]
]
, Menu.captionedGroup "Guided Draft"
"Students draft with the support of tutorials, models, and targeted tips."
[ Menu.entry "preview-guided-draft" <|
\attributes ->
ClickableSvg.link "Preview"
UiIcon.preview
[ ClickableSvg.linkExternal "about:blank"
, ClickableSvg.secondary
, ClickableSvg.withBorder
, ClickableSvg.custom attributes
]
, Menu.entry "assign-guided-draft" <|
\attributes ->
ClickableSvg.link "Assign"
UiIcon.arrowPointingRightThick
[ ClickableSvg.linkExternal "about:blank"
, ClickableSvg.primary
, ClickableSvg.withBorder
, ClickableSvg.custom attributes
]
]
, Menu.entry "which-should-i-choose" <|
\attributes ->
ClickableText.link "Which should I choose?"
[ ClickableText.linkExternal "about:blank"
, ClickableText.icon UiIcon.help
, ClickableText.custom attributes
]
]
, about = """
Each container can be styled with CSS to achieve the desired layout.
In this example, we use `Menu.menuCss` to reduce the menu padding, `Menu.groupContainerCss` to layout horizontally, and `Menu.groupTitleCss` and `Menu.groupCaptionCss` to style typography.
"""
}
]
]
forcedOpenExample : String -> Menu.Attribute Msg -> List (Menu.Entry Msg) -> Html Msg
forcedOpenExample name type_ =
forcedOpenExample : String -> List (Menu.Attribute Msg) -> List (Menu.Entry Msg) -> Html Msg
forcedOpenExample name attributes =
Menu.view (FocusAndToggle name)
[ Menu.clickableSvgWithoutIndicator (name ++ " example")
([ Menu.clickableSvgWithoutIndicator (name ++ " example")
UiIcon.arrowDown
[ ClickableSvg.exactSize 15
, ClickableSvg.css [ Css.marginLeft (Css.px 17) ]
]
, Menu.isOpen True
, Menu.buttonId (forcedOpenExampleButtonId name)
, Menu.menuId (safeIdWithPrefix name "menuId")
, Menu.alignLeft
, type_
]
, Menu.isOpen True
, Menu.buttonId (forcedOpenExampleButtonId name)
, Menu.menuId (safeIdWithPrefix name "menuId")
, Menu.alignLeft
]
++ attributes
)
forcedOpenExampleButtonId : String -> String

View File

@ -8,8 +8,8 @@ module Nri.Ui.Menu.V4 exposing
, navMenuList, disclosure, dialog
, menuWidth, menuId, menuZIndex
, alignLeft, alignRight
, containerCss, groupContainerCss, entryContainerCss
, Entry, group, entry
, containerCss, menuCss, groupContainerCss, entryContainerCss, groupTitleCss, groupCaptionCss
, Entry, group, captionedGroup, entry
)
{-| Patch changes:
@ -19,7 +19,9 @@ module Nri.Ui.Menu.V4 exposing
- Adjust disabled styles
- when the Menu is a dialog or disclosure, _don't_ add role menuitem to the entries
- Use ClickableText.medium as the default size when the trigger is `Menu.clickableText`
- Adds containerCss, groupContainerCss, and entryContainerCss to customize the style of the respective containers
- Adds containerCss, menuCss, groupContainerCss, and entryContainerCss to customize the style of the respective containers
- Adds captionedGroup to create a group with a caption
- Adds groupTitleCss and groupCaptionCss to customize the style of the group title and caption
Changes from V3:
@ -53,12 +55,12 @@ A togglable menu view and related buttons.
@docs navMenuList, disclosure, dialog
@docs menuWidth, menuId, menuZIndex
@docs alignLeft, alignRight
@docs containerCss, groupContainerCss, entryContainerCss
@docs containerCss, menuCss, groupContainerCss, entryContainerCss, groupTitleCss, groupCaptionCss
## Menu content
@docs Entry, group, entry
@docs Entry, group, captionedGroup, entry
-}
@ -70,15 +72,18 @@ import Html.Styled as Html exposing (..)
import Html.Styled.Attributes as Attributes exposing (class, classList, css)
import Html.Styled.Events as Events
import Json.Decode
import Maybe.Extra as Maybe
import Nri.Ui.AnimatedIcon.V1 as AnimatedIcon
import Nri.Ui.Button.V10 as Button
import Nri.Ui.ClickableSvg.V2 as ClickableSvg
import Nri.Ui.ClickableText.V4 as ClickableText
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Fonts.V1
import Nri.Ui.Html.Attributes.V2 as AttributesExtra
import Nri.Ui.Html.Attributes.V2 as AttributesExtra exposing (safeId)
import Nri.Ui.Html.V3 exposing (viewJust)
import Nri.Ui.Shadows.V1 as Shadows
import Nri.Ui.Svg.V1 as Svg
import Nri.Ui.Text.V6 as Text
import Nri.Ui.Tooltip.V3 as Tooltip
import Nri.Ui.WhenFocusLeaves.V2 as WhenFocusLeaves
@ -101,8 +106,11 @@ type alias MenuConfig msg =
, purpose : Purpose
, tooltipAttributes : List (Tooltip.Attribute msg)
, containerCss : List Style
, menuCss : List Style
, groupContainerCss : List Style
, entryContainerCss : List Style
, groupTitleCss : List Style
, groupCaptionCss : List Style
}
@ -120,8 +128,11 @@ defaultConfig =
, purpose = NavMenu
, tooltipAttributes = []
, containerCss = []
, menuCss = []
, groupContainerCss = []
, entryContainerCss = []
, groupTitleCss = []
, groupCaptionCss = []
}
@ -170,6 +181,13 @@ containerCss styles =
Attribute <| \config -> { config | containerCss = config.containerCss ++ styles }
{-| Adds CSS to the content of the menu. This will style the menu itself.
-}
menuCss : List Css.Style -> Attribute msg
menuCss styles =
Attribute <| \config -> { config | menuCss = config.menuCss ++ styles }
{-| Adds CSS to the element containing the group. This will style items created via Menu.group
-}
groupContainerCss : List Css.Style -> Attribute msg
@ -184,6 +202,20 @@ entryContainerCss styles =
Attribute <| \config -> { config | entryContainerCss = config.entryContainerCss ++ styles }
{-| Adds CSS to the element containing the group title. This will style the title of items created via Menu.group
-}
groupTitleCss : List Css.Style -> Attribute msg
groupTitleCss styles =
Attribute <| \config -> { config | groupTitleCss = config.groupTitleCss ++ styles }
{-| Adds CSS to the element containing the group caption. This will style the caption of items created via Menu.captionedGroup
-}
groupCaptionCss : List Css.Style -> Attribute msg
groupCaptionCss styles =
Attribute <| \config -> { config | groupCaptionCss = config.groupCaptionCss ++ styles }
{-| Whether the menu is open
-}
isOpen : Bool -> Attribute msg
@ -299,14 +331,21 @@ view focusAndToggle attributes entries =
-}
type Entry msg
= Single String (List (Html.Attribute msg) -> Html msg)
| Batch String (List (Entry msg))
| Batch String (Maybe String) (List (Entry msg))
{-| Represents a group of entries with a named legend.
-}
group : String -> List (Entry msg) -> Entry msg
group legendName entries =
Batch legendName entries
Batch legendName Nothing entries
{-| Represents a group of entries with a named legend and text caption.
-}
captionedGroup : String -> String -> List (Entry msg) -> Entry msg
captionedGroup legendName caption entries =
Batch legendName (Just caption) entries
{-| Represents a single **focusable** entry.
@ -678,7 +717,7 @@ getFirstIds entries =
Single idString _ ->
Just idString
Batch _ es ->
Batch _ _ es ->
Maybe.andThen getIdString (List.head es)
in
List.filterMap getIdString entries
@ -692,7 +731,7 @@ getLastIds entries =
Single idString _ ->
Just idString
Batch _ es ->
Batch _ _ es ->
Maybe.andThen getIdString (List.head (List.reverse es))
in
List.filterMap getIdString (List.reverse entries)
@ -790,15 +829,35 @@ viewEntry config focusAndToggle { upId, downId, entry_ } =
]
]
Batch title childList ->
Batch title caption childList ->
let
captionId =
safeId (title ++ "--caption")
in
case childList of
[] ->
Html.text ""
_ ->
fieldset (styleGroupContainer config.groupContainerCss) <|
legend styleGroupTitle
[ span (styleGroupTitleText config) [ Html.text title ] ]
div [ css [ flexGrow (int 1) ] ]
[ legend styleGroupTitle
[ span
(styleGroupTitleText config
|> Maybe.cons (Maybe.map (always (Aria.describedBy [ captionId ])) caption)
)
[ Html.text title ]
]
, viewJust
(\c ->
Text.caption
[ Text.plaintext c
, Text.id captionId
, Text.css <| styleGroupCaption config
]
)
caption
]
:: viewEntries config
{ focusAndToggle = focusAndToggle
, previousId = upId
@ -845,14 +904,20 @@ styleGroupTitle =
styleGroupTitleText : MenuConfig msg -> List (Html.Attribute msg)
styleGroupTitleText config =
[ class "GroupTitleText"
, css
, css <|
[ backgroundColor Colors.white
, zIndex (int <| config.zIndex + 1)
, position relative
]
++ config.groupTitleCss
]
styleGroupCaption : MenuConfig msg -> List Style
styleGroupCaption config =
paddingTop (px 2) :: config.groupCaptionCss
styleGroupContainer : List Style -> List (Html.Attribute msg)
styleGroupContainer styles =
[ class "GroupContainer"
@ -885,7 +950,7 @@ styleOuterContent _ config =
styleContent : Bool -> MenuConfig msg -> Html.Attribute msg
styleContent contentVisible config =
css
css <|
[ padding (px 25)
, margin zero
, border3 (px 1) solid Colors.gray85
@ -927,6 +992,7 @@ styleContent contentVisible config =
else
display Css.none
]
++ config.menuCss
styleContainer : List Style -> List (Html.Attribute msg)