From 8b7e3924ab4d28ff69ea0f8f0b9bc314d2e32b6f Mon Sep 17 00:00:00 2001 From: Tessa Kelly Date: Thu, 2 Dec 2021 17:34:55 -0800 Subject: [PATCH 01/23] Extract SideNav from AL, with some light modifications --- src/Nri/Ui/SideNav/V1.elm | 225 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 src/Nri/Ui/SideNav/V1.elm diff --git a/src/Nri/Ui/SideNav/V1.elm b/src/Nri/Ui/SideNav/V1.elm new file mode 100644 index 00000000..4eafeee4 --- /dev/null +++ b/src/Nri/Ui/SideNav/V1.elm @@ -0,0 +1,225 @@ +module Nri.Ui.SideNav.V1 exposing (view, Config) + +{-| + +@docs view, Config + +-} + +import Accessibility.Styled exposing (..) +import Css exposing (..) +import Css.Media as Media +import Html.Styled +import Html.Styled.Attributes as Attributes exposing (css) +import Html.Styled.Events as Events +import Nri.Ui.Colors.V1 as Colors +import Nri.Ui.Data.PremiumLevel as PremiumLevel exposing (PremiumLevel) +import Nri.Ui.Fonts.V1 as Fonts +import Nri.Ui.Svg.V1 as Svg exposing (Svg) +import Nri.Ui.UiIcon.V1 as UiIcon +import String exposing (toLower) +import String.Extra exposing (dasherize) + + +{-| Use `link` or `externalLink` to create a sidebar link. +-} +type SidebarEntry route msg + = Link (LinkConfig route msg) + | ExternalLink ExternalLinkConfig + + +{-| -} +type alias LinkConfig route msg = + { title : String + , route : route + , attributes : List (Html.Styled.Attribute msg) + , children : List (SidebarEntry route msg) + , premiumLevel : PremiumLevel + } + + +{-| -} +link : LinkConfig route msg -> SidebarEntry route msg +link = + Link + + +{-| -} +type alias ExternalLinkConfig = + { icon : Svg + , plaintext : String + , url : String + } + + +{-| -} +externalLink : ExternalLinkConfig -> SidebarEntry route msg +externalLink = + ExternalLink + + +{-| -} +type alias Config route = + { userPremiumLevel : PremiumLevel + , isCurrentRoute : route -> Bool + } + + +{-| -} +view : Config route -> List (SidebarEntry route msg) -> Html msg +view config entries = + styled nav + [ width (px 300) + , borderRadius (px 8) + , backgroundColor Colors.gray96 + , padding (px 20) + , marginRight (px 20) + , position sticky + , property "position" "-webkit-sticky" + , top (px 20) + , overflowY auto + , minWidth (px 250) + , Media.withMedia + [ Media.all [ Media.maxWidth (px 600) ] ] + -- custom, smaller breakpoint since the sidebar nav allows access to subcategories + -- in the assignment library + [ width (pct 100) + , margin2 (px 20) zero + ] + ] + [] + (List.map (viewSidebarEntry config []) entries) + + +viewSidebarEntry : Config route -> List Css.Style -> SidebarEntry route msg -> Html msg +viewSidebarEntry config extraStyles sidebarEntry = + case sidebarEntry of + ExternalLink { plaintext, icon, url } -> + styled a + (sharedEntryStyles + ++ extraStyles + ++ [ backgroundColor Colors.white + , boxShadow3 zero (px 2) Colors.gray75 + , border3 (px 1) solid Colors.gray75 + , marginBottom (px 10) + ] + ) + [ Attributes.href url ] + [ icon + |> Svg.withWidth (px 20) + |> Svg.withHeight (px 20) + |> Svg.withCss [ marginRight (px 5) ] + |> Svg.toHtml + , text plaintext + ] + + Link entry -> + if PremiumLevel.allowedFor entry.premiumLevel config.userPremiumLevel then + if anyLinkDescendants (.route >> config.isCurrentRoute) entry then + div [ css extraStyles ] + (styled span + (sharedEntryStyles + ++ [ backgroundColor Colors.gray92 + , marginBottom (px 10) + , color Colors.navy + , fontWeight bold + , cursor default + ] + ) + [] + [ text entry.title ] + :: List.map (viewSidebarEntry config [ marginLeft (px 20) ]) + entry.children + ) + + else + viewSidebarLeaf config entry + + else + viewLockedEntry entry.title + + +anyLinkDescendants : (LinkConfig route msg -> Bool) -> LinkConfig route msg -> Bool +anyLinkDescendants f { children } = + List.any + (\child -> + case child of + Link entry -> + f entry || anyLinkDescendants f entry + + ExternalLink _ -> + False + ) + children + + +viewSidebarLeaf : + Config route + -> LinkConfig route msg + -> Html msg +viewSidebarLeaf config { title, route, attributes } = + styled Html.Styled.a + (sharedEntryStyles + ++ (if config.isCurrentRoute route then + [ backgroundColor Colors.glacier + , color Colors.navy + , fontWeight bold + , visited [ color Colors.navy ] + ] + + else + [] + ) + ) + attributes + [ text title ] + + +viewLockedEntry : String -> Html msg +viewLockedEntry title = + let + lockedEntryId = + -- TODO: pass in ids + "browse-and-assign-locked-entry__" ++ dasherize (toLower title) + in + styled Html.Styled.button + [ paddingLeft (px 20) + , paddingRight (px 20) + , displayFlex + , alignItems center + , Fonts.baseFont + , fontSize (px 15) + , textDecoration none + , color Colors.gray45 + , height (px 45) + , borderStyle none + , backgroundColor Colors.gray96 + , textAlign left + ] + [ -- TODO: reimplement lock click behavior! + --Events.onClick (launchPremiumModal lockedEntryId) , + Attributes.id lockedEntryId + ] + [ UiIcon.premiumLock + |> Svg.withWidth (px 17) + |> Svg.withHeight (px 25) + |> Svg.withCss [ marginRight (px 10), minWidth (px 17) ] + |> Svg.toHtml + , text title + ] + + +sharedEntryStyles : List Css.Style +sharedEntryStyles = + [ paddingLeft (px 20) + , paddingRight (px 20) + , height (px 45) + , displayFlex + , borderRadius (px 8) + , alignItems center + , Fonts.baseFont + , color Colors.navy + , textDecoration none + , fontSize (px 15) + , fontWeight (int 600) + ] From b1d9432a9b5d8f6ac02c117682bcc4c2e1429573 Mon Sep 17 00:00:00 2001 From: Tessa Kelly Date: Thu, 2 Dec 2021 17:42:23 -0800 Subject: [PATCH 02/23] Begin using the sidenav for the styleguide --- src/Nri/Ui/SideNav/V1.elm | 8 ++- styleguide-app/Main.elm | 108 +++++++------------------------------- 2 files changed, 27 insertions(+), 89 deletions(-) diff --git a/src/Nri/Ui/SideNav/V1.elm b/src/Nri/Ui/SideNav/V1.elm index 4eafeee4..83bd2402 100644 --- a/src/Nri/Ui/SideNav/V1.elm +++ b/src/Nri/Ui/SideNav/V1.elm @@ -1,8 +1,14 @@ -module Nri.Ui.SideNav.V1 exposing (view, Config) +module Nri.Ui.SideNav.V1 exposing + ( view, Config + , link, LinkConfig + , externalLink, ExternalLinkConfig + ) {-| @docs view, Config +@docs link, LinkConfig +@docs externalLink, ExternalLinkConfig -} diff --git a/styleguide-app/Main.elm b/styleguide-app/Main.elm index 17065f63..c5485e6f 100644 --- a/styleguide-app/Main.elm +++ b/styleguide-app/Main.elm @@ -4,7 +4,7 @@ import Accessibility.Styled as Html exposing (Html) import Browser exposing (Document, UrlRequest(..)) import Browser.Dom import Browser.Navigation exposing (Key) -import Category +import Category exposing (Category) import Css exposing (..) import Css.Media exposing (withMedia) import Dict exposing (Dict) @@ -16,10 +16,12 @@ import Html.Styled.Events as Events import Nri.Ui.ClickableText.V3 as ClickableText import Nri.Ui.Colors.V1 as Colors import Nri.Ui.CssVendorPrefix.V1 as VendorPrefixed +import Nri.Ui.Data.PremiumLevel as PremiumLevel import Nri.Ui.Fonts.V1 as Fonts import Nri.Ui.Heading.V2 as Heading import Nri.Ui.MediaQuery.V1 exposing (mobile, notMobile) import Nri.Ui.Page.V3 as Page +import Nri.Ui.SideNav.V1 as SideNav import Routes as Routes exposing (Route(..)) import Sort.Set as Set exposing (Set) import Task @@ -221,92 +223,22 @@ viewPreviews containerId examples = navigation : Route -> Html Msg -navigation route = +navigation currentRoute = + -- TODO: add the skip link back in + -- TODO: add the "All" section back in let - isActive category = - case route of - Routes.Category routeCategory -> - category == routeCategory - - _ -> - False - - link active hash displayName = - ClickableText.link displayName - [ ClickableText.small - , ClickableText.css - [ Css.color Colors.navy - , Css.display Css.block - , Css.padding (Css.px 8) - , Css.borderRadius (Css.px 8) - , if active then - Css.backgroundColor Colors.glacier - - else - Css.batch [] - ] - , ClickableText.href hash - ] - - navLink category = - link (isActive category) - (Routes.toString (Routes.Category category)) - (Category.forDisplay category) - - toNavLi element = - Html.li - [ css - [ margin zero - , listStyle none - , textDecoration none - ] - ] - [ element ] + toNavLinkConfig : Category -> SideNav.LinkConfig Route Msg + toNavLinkConfig category = + { title = Category.forDisplay category + , route = Routes.Category category + , attributes = [ href (Routes.toString (Routes.Category category)) ] + , children = [] + , premiumLevel = PremiumLevel.Free + } in - Html.nav - [ css - [ backgroundColor Colors.gray96 - , withMedia [ notMobile ] - [ VendorPrefixed.value "position" "sticky" - , top (px 55) - , flexShrink zero - , borderRadius (px 8) - , marginRight (px 40) - , padding (px 20) - , flexBasis (px 200) - ] - ] - , attribute "aria-label" "Main Navigation" - ] - [ Html.button - [ css - [ backgroundColor transparent - , borderStyle none - , textDecoration none - , color Colors.azure - , Fonts.baseFont - , Css.marginBottom (px 20) - , Css.pseudoClass "not(:focus)" - [ Css.property "clip" "rect(1px, 1px, 1px, 1px)" - , Css.position Css.absolute - , Css.height (Css.px 1) - , Css.width (Css.px 1) - , Css.overflow Css.hidden - , Css.margin (Css.px -1) - , Css.padding Css.zero - , Css.border Css.zero - ] - ] - , Events.onClick SkipToMainContent - , id "skip" - ] - [ Html.text "Skip to main content" ] - , (link (route == Routes.All) "#/" "All" - :: List.map navLink Category.all - ) - |> List.map toNavLi - |> Html.ul - [ css [ margin zero, padding zero ] - , id "categories" - ] - ] + Category.all + |> List.map (toNavLinkConfig >> SideNav.link) + |> SideNav.view + { userPremiumLevel = PremiumLevel.Free + , isCurrentRoute = (==) currentRoute + } From ae1b769d27ef09341fcbf67848f032c2e138a850 Mon Sep 17 00:00:00 2001 From: Tessa Kelly Date: Fri, 3 Dec 2021 08:32:21 -0800 Subject: [PATCH 03/23] Adds the skip link back in --- src/Nri/Ui/SideNav/V1.elm | 39 ++++++++++++++++++++++++++++++++++----- styleguide-app/Main.elm | 2 +- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/Nri/Ui/SideNav/V1.elm b/src/Nri/Ui/SideNav/V1.elm index 83bd2402..1c89d725 100644 --- a/src/Nri/Ui/SideNav/V1.elm +++ b/src/Nri/Ui/SideNav/V1.elm @@ -65,14 +65,15 @@ externalLink = {-| -} -type alias Config route = +type alias Config route msg = { userPremiumLevel : PremiumLevel , isCurrentRoute : route -> Bool + , onSkipNav : msg } {-| -} -view : Config route -> List (SidebarEntry route msg) -> Html msg +view : Config route msg -> List (SidebarEntry route msg) -> Html msg view config entries = styled nav [ width (px 300) @@ -94,10 +95,38 @@ view config entries = ] ] [] - (List.map (viewSidebarEntry config []) entries) + (viewSkipLink config.onSkipNav + :: List.map (viewSidebarEntry config []) entries + ) -viewSidebarEntry : Config route -> List Css.Style -> SidebarEntry route msg -> Html msg +viewSkipLink : msg -> Html msg +viewSkipLink onSkip = + button + [ css + [ backgroundColor transparent + , borderStyle none + , textDecoration none + , color Colors.azure + , Fonts.baseFont + , Css.marginBottom (px 20) + , Css.pseudoClass "not(:focus)" + [ Css.property "clip" "rect(1px, 1px, 1px, 1px)" + , Css.position Css.absolute + , Css.height (Css.px 1) + , Css.width (Css.px 1) + , Css.overflow Css.hidden + , Css.margin (Css.px -1) + , Css.padding Css.zero + , Css.border Css.zero + ] + ] + , Events.onClick onSkip + ] + [ text "Skip to main content" ] + + +viewSidebarEntry : Config route msg -> List Css.Style -> SidebarEntry route msg -> Html msg viewSidebarEntry config extraStyles sidebarEntry = case sidebarEntry of ExternalLink { plaintext, icon, url } -> @@ -160,7 +189,7 @@ anyLinkDescendants f { children } = viewSidebarLeaf : - Config route + Config route msg -> LinkConfig route msg -> Html msg viewSidebarLeaf config { title, route, attributes } = diff --git a/styleguide-app/Main.elm b/styleguide-app/Main.elm index c5485e6f..1236fe5d 100644 --- a/styleguide-app/Main.elm +++ b/styleguide-app/Main.elm @@ -224,7 +224,6 @@ viewPreviews containerId examples = navigation : Route -> Html Msg navigation currentRoute = - -- TODO: add the skip link back in -- TODO: add the "All" section back in let toNavLinkConfig : Category -> SideNav.LinkConfig Route Msg @@ -241,4 +240,5 @@ navigation currentRoute = |> SideNav.view { userPremiumLevel = PremiumLevel.Free , isCurrentRoute = (==) currentRoute + , onSkipNav = SkipToMainContent } From 24ab7a8fac3d3f09e1e82b887820151f27c36fcf Mon Sep 17 00:00:00 2001 From: Tessa Kelly Date: Fri, 3 Dec 2021 08:38:10 -0800 Subject: [PATCH 04/23] Use clickable text for the skip link --- src/Nri/Ui/SideNav/V1.elm | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/Nri/Ui/SideNav/V1.elm b/src/Nri/Ui/SideNav/V1.elm index 1c89d725..d25198b0 100644 --- a/src/Nri/Ui/SideNav/V1.elm +++ b/src/Nri/Ui/SideNav/V1.elm @@ -18,6 +18,7 @@ import Css.Media as Media import Html.Styled import Html.Styled.Attributes as Attributes exposing (css) import Html.Styled.Events as Events +import Nri.Ui.ClickableText.V3 as ClickableText import Nri.Ui.Colors.V1 as Colors import Nri.Ui.Data.PremiumLevel as PremiumLevel exposing (PremiumLevel) import Nri.Ui.Fonts.V1 as Fonts @@ -102,15 +103,14 @@ view config entries = viewSkipLink : msg -> Html msg viewSkipLink onSkip = - button - [ css - [ backgroundColor transparent - , borderStyle none - , textDecoration none - , color Colors.azure - , Fonts.baseFont - , Css.marginBottom (px 20) - , Css.pseudoClass "not(:focus)" + ClickableText.button "Skip to main content" + [ ClickableText.icon UiIcon.arrowPointingRight + , ClickableText.small + , ClickableText.css + [ Css.pseudoClass "not(:focus)" + -- TODO: use Accessibility.Styled.Style.invisibleStyle + -- when we're on a higher version of tesk9/accessible-html-with-css + -- than 2.2.1 [ Css.property "clip" "rect(1px, 1px, 1px, 1px)" , Css.position Css.absolute , Css.height (Css.px 1) @@ -121,9 +121,8 @@ viewSkipLink onSkip = , Css.border Css.zero ] ] - , Events.onClick onSkip + , ClickableText.onClick onSkip ] - [ text "Skip to main content" ] viewSidebarEntry : Config route msg -> List Css.Style -> SidebarEntry route msg -> Html msg From ce44a68f70670578840e83cce3f3170273d91962 Mon Sep 17 00:00:00 2001 From: Tessa Kelly Date: Fri, 3 Dec 2021 08:45:06 -0800 Subject: [PATCH 05/23] Fix left alignment of sub-nav links --- src/Nri/Ui/SideNav/V1.elm | 13 ++++++++----- styleguide-app/Main.elm | 18 ++++++++++++------ 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/Nri/Ui/SideNav/V1.elm b/src/Nri/Ui/SideNav/V1.elm index d25198b0..d2f990e4 100644 --- a/src/Nri/Ui/SideNav/V1.elm +++ b/src/Nri/Ui/SideNav/V1.elm @@ -167,10 +167,10 @@ viewSidebarEntry config extraStyles sidebarEntry = ) else - viewSidebarLeaf config entry + viewSidebarLeaf config extraStyles entry else - viewLockedEntry entry.title + viewLockedEntry entry.title extraStyles anyLinkDescendants : (LinkConfig route msg -> Bool) -> LinkConfig route msg -> Bool @@ -189,11 +189,13 @@ anyLinkDescendants f { children } = viewSidebarLeaf : Config route msg + -> List Style -> LinkConfig route msg -> Html msg -viewSidebarLeaf config { title, route, attributes } = +viewSidebarLeaf config extraStyles { title, route, attributes } = styled Html.Styled.a (sharedEntryStyles + ++ extraStyles ++ (if config.isCurrentRoute route then [ backgroundColor Colors.glacier , color Colors.navy @@ -209,8 +211,8 @@ viewSidebarLeaf config { title, route, attributes } = [ text title ] -viewLockedEntry : String -> Html msg -viewLockedEntry title = +viewLockedEntry : String -> List Style -> Html msg +viewLockedEntry title extraStyles = let lockedEntryId = -- TODO: pass in ids @@ -229,6 +231,7 @@ viewLockedEntry title = , borderStyle none , backgroundColor Colors.gray96 , textAlign left + , batch extraStyles ] [ -- TODO: reimplement lock click behavior! --Events.onClick (launchPremiumModal lockedEntryId) , diff --git a/styleguide-app/Main.elm b/styleguide-app/Main.elm index 1236fe5d..8859d422 100644 --- a/styleguide-app/Main.elm +++ b/styleguide-app/Main.elm @@ -235,10 +235,16 @@ navigation currentRoute = , premiumLevel = PremiumLevel.Free } in - Category.all - |> List.map (toNavLinkConfig >> SideNav.link) - |> SideNav.view - { userPremiumLevel = PremiumLevel.Free - , isCurrentRoute = (==) currentRoute - , onSkipNav = SkipToMainContent + SideNav.view + { userPremiumLevel = PremiumLevel.Free + , isCurrentRoute = (==) currentRoute + , onSkipNav = SkipToMainContent + } + [ SideNav.link + { title = "All" + , route = Routes.All + , attributes = [ href (Routes.toString Routes.All) ] + , children = List.map (toNavLinkConfig >> SideNav.link) Category.all + , premiumLevel = PremiumLevel.Free } + ] From caa28bd23c459182e60d405a77a9da73a70f19da Mon Sep 17 00:00:00 2001 From: Tessa Kelly Date: Fri, 3 Dec 2021 09:12:32 -0800 Subject: [PATCH 06/23] Get closer to the existing styleguide styles --- src/Nri/Ui/SideNav/V1.elm | 17 ++++------------- styleguide-app/Main.elm | 6 ++++++ 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/Nri/Ui/SideNav/V1.elm b/src/Nri/Ui/SideNav/V1.elm index d2f990e4..e4d3da22 100644 --- a/src/Nri/Ui/SideNav/V1.elm +++ b/src/Nri/Ui/SideNav/V1.elm @@ -70,6 +70,7 @@ type alias Config route msg = { userPremiumLevel : PremiumLevel , isCurrentRoute : route -> Bool , onSkipNav : msg + , css : List Style } @@ -77,23 +78,13 @@ type alias Config route msg = view : Config route msg -> List (SidebarEntry route msg) -> Html msg view config entries = styled nav - [ width (px 300) + [ flexBasis (px 250) + , flexShrink (num 0) , borderRadius (px 8) , backgroundColor Colors.gray96 , padding (px 20) , marginRight (px 20) - , position sticky - , property "position" "-webkit-sticky" - , top (px 20) - , overflowY auto - , minWidth (px 250) - , Media.withMedia - [ Media.all [ Media.maxWidth (px 600) ] ] - -- custom, smaller breakpoint since the sidebar nav allows access to subcategories - -- in the assignment library - [ width (pct 100) - , margin2 (px 20) zero - ] + , batch config.css ] [] (viewSkipLink config.onSkipNav diff --git a/styleguide-app/Main.elm b/styleguide-app/Main.elm index 8859d422..be347410 100644 --- a/styleguide-app/Main.elm +++ b/styleguide-app/Main.elm @@ -239,6 +239,12 @@ navigation currentRoute = { userPremiumLevel = PremiumLevel.Free , isCurrentRoute = (==) currentRoute , onSkipNav = SkipToMainContent + , css = + [ withMedia [ notMobile ] + [ VendorPrefixed.value "position" "sticky" + , top (px 55) + ] + ] } [ SideNav.link { title = "All" From e012dc1f913238ee98e4fd856a769600b7121150 Mon Sep 17 00:00:00 2001 From: Tessa Kelly Date: Fri, 3 Dec 2021 09:22:25 -0800 Subject: [PATCH 07/23] Adds the All section back in the way it should be --- src/Nri/Ui/SideNav/V1.elm | 4 ++-- styleguide-app/Main.elm | 21 ++++++++++++--------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/Nri/Ui/SideNav/V1.elm b/src/Nri/Ui/SideNav/V1.elm index e4d3da22..6ce4bef4 100644 --- a/src/Nri/Ui/SideNav/V1.elm +++ b/src/Nri/Ui/SideNav/V1.elm @@ -1,12 +1,12 @@ module Nri.Ui.SideNav.V1 exposing - ( view, Config + ( view, Config, SidebarEntry , link, LinkConfig , externalLink, ExternalLinkConfig ) {-| -@docs view, Config +@docs view, Config, SidebarEntry @docs link, LinkConfig @docs externalLink, ExternalLinkConfig diff --git a/styleguide-app/Main.elm b/styleguide-app/Main.elm index be347410..818b80b7 100644 --- a/styleguide-app/Main.elm +++ b/styleguide-app/Main.elm @@ -224,7 +224,6 @@ viewPreviews containerId examples = navigation : Route -> Html Msg navigation currentRoute = - -- TODO: add the "All" section back in let toNavLinkConfig : Category -> SideNav.LinkConfig Route Msg toNavLinkConfig category = @@ -234,6 +233,17 @@ navigation currentRoute = , children = [] , premiumLevel = PremiumLevel.Free } + + navLinks : List (SideNav.SidebarEntry Route Msg) + navLinks = + SideNav.link + { title = "All" + , route = Routes.All + , attributes = [ href (Routes.toString Routes.All) ] + , children = [] + , premiumLevel = PremiumLevel.Free + } + :: List.map (toNavLinkConfig >> SideNav.link) Category.all in SideNav.view { userPremiumLevel = PremiumLevel.Free @@ -246,11 +256,4 @@ navigation currentRoute = ] ] } - [ SideNav.link - { title = "All" - , route = Routes.All - , attributes = [ href (Routes.toString Routes.All) ] - , children = List.map (toNavLinkConfig >> SideNav.link) Category.all - , premiumLevel = PremiumLevel.Free - } - ] + navLinks From 925c4ea64a8af7ab429399b3e263aa420130c6f5 Mon Sep 17 00:00:00 2001 From: Tessa Kelly Date: Fri, 3 Dec 2021 09:40:45 -0800 Subject: [PATCH 08/23] Remove external link --- src/Nri/Ui/SideNav/V1.elm | 96 ++++++++++++++------------------------- styleguide-app/Main.elm | 36 ++++++++++++++- 2 files changed, 67 insertions(+), 65 deletions(-) diff --git a/src/Nri/Ui/SideNav/V1.elm b/src/Nri/Ui/SideNav/V1.elm index 6ce4bef4..63c13ae4 100644 --- a/src/Nri/Ui/SideNav/V1.elm +++ b/src/Nri/Ui/SideNav/V1.elm @@ -1,14 +1,13 @@ module Nri.Ui.SideNav.V1 exposing ( view, Config, SidebarEntry , link, LinkConfig - , externalLink, ExternalLinkConfig + , withBorderStyles ) {-| @docs view, Config, SidebarEntry @docs link, LinkConfig -@docs externalLink, ExternalLinkConfig -} @@ -22,22 +21,23 @@ import Nri.Ui.ClickableText.V3 as ClickableText import Nri.Ui.Colors.V1 as Colors import Nri.Ui.Data.PremiumLevel as PremiumLevel exposing (PremiumLevel) import Nri.Ui.Fonts.V1 as Fonts +import Nri.Ui.Html.V3 exposing (viewJust) import Nri.Ui.Svg.V1 as Svg exposing (Svg) import Nri.Ui.UiIcon.V1 as UiIcon import String exposing (toLower) import String.Extra exposing (dasherize) -{-| Use `link` or `externalLink` to create a sidebar link. +{-| Use `link` to create a sidebar link. -} type SidebarEntry route msg = Link (LinkConfig route msg) - | ExternalLink ExternalLinkConfig {-| -} type alias LinkConfig route msg = - { title : String + { icon : Maybe Svg + , title : String , route : route , attributes : List (Html.Styled.Attribute msg) , children : List (SidebarEntry route msg) @@ -51,20 +51,6 @@ link = Link -{-| -} -type alias ExternalLinkConfig = - { icon : Svg - , plaintext : String - , url : String - } - - -{-| -} -externalLink : ExternalLinkConfig -> SidebarEntry route msg -externalLink = - ExternalLink - - {-| -} type alias Config route msg = { userPremiumLevel : PremiumLevel @@ -119,25 +105,6 @@ viewSkipLink onSkip = viewSidebarEntry : Config route msg -> List Css.Style -> SidebarEntry route msg -> Html msg viewSidebarEntry config extraStyles sidebarEntry = case sidebarEntry of - ExternalLink { plaintext, icon, url } -> - styled a - (sharedEntryStyles - ++ extraStyles - ++ [ backgroundColor Colors.white - , boxShadow3 zero (px 2) Colors.gray75 - , border3 (px 1) solid Colors.gray75 - , marginBottom (px 10) - ] - ) - [ Attributes.href url ] - [ icon - |> Svg.withWidth (px 20) - |> Svg.withHeight (px 20) - |> Svg.withCss [ marginRight (px 5) ] - |> Svg.toHtml - , text plaintext - ] - Link entry -> if PremiumLevel.allowedFor entry.premiumLevel config.userPremiumLevel then if anyLinkDescendants (.route >> config.isCurrentRoute) entry then @@ -166,16 +133,7 @@ viewSidebarEntry config extraStyles sidebarEntry = anyLinkDescendants : (LinkConfig route msg -> Bool) -> LinkConfig route msg -> Bool anyLinkDescendants f { children } = - List.any - (\child -> - case child of - Link entry -> - f entry || anyLinkDescendants f entry - - ExternalLink _ -> - False - ) - children + List.any (\(Link entry) -> f entry || anyLinkDescendants f entry) children viewSidebarLeaf : @@ -183,7 +141,7 @@ viewSidebarLeaf : -> List Style -> LinkConfig route msg -> Html msg -viewSidebarLeaf config extraStyles { title, route, attributes } = +viewSidebarLeaf config extraStyles { icon, title, route, attributes } = styled Html.Styled.a (sharedEntryStyles ++ extraStyles @@ -199,7 +157,17 @@ viewSidebarLeaf config extraStyles { title, route, attributes } = ) ) attributes - [ text title ] + [ viewJust + (\icon_ -> + icon_ + |> Svg.withWidth (px 20) + |> Svg.withHeight (px 20) + |> Svg.withCss [ marginRight (px 5) ] + |> Svg.toHtml + ) + icon + , text title + ] viewLockedEntry : String -> List Style -> Html msg @@ -210,18 +178,9 @@ viewLockedEntry title extraStyles = "browse-and-assign-locked-entry__" ++ dasherize (toLower title) in styled Html.Styled.button - [ paddingLeft (px 20) - , paddingRight (px 20) - , displayFlex - , alignItems center - , Fonts.baseFont - , fontSize (px 15) - , textDecoration none - , color Colors.gray45 - , height (px 45) - , borderStyle none - , backgroundColor Colors.gray96 - , textAlign left + [ batch sharedEntryStyles + , important (color Colors.gray45) + , borderWidth zero , batch extraStyles ] [ -- TODO: reimplement lock click behavior! @@ -237,7 +196,7 @@ viewLockedEntry title extraStyles = ] -sharedEntryStyles : List Css.Style +sharedEntryStyles : List Style sharedEntryStyles = [ paddingLeft (px 20) , paddingRight (px 20) @@ -247,7 +206,18 @@ sharedEntryStyles = , alignItems center , Fonts.baseFont , color Colors.navy + , backgroundColor transparent , textDecoration none , fontSize (px 15) , fontWeight (int 600) + , marginBottom (px 10) + ] + + +withBorderStyles : List Style +withBorderStyles = + -- TODO: add a convenient way to use these styels + [ backgroundColor Colors.white + , boxShadow3 zero (px 2) Colors.gray75 + , border3 (px 1) solid Colors.gray75 ] diff --git a/styleguide-app/Main.elm b/styleguide-app/Main.elm index 818b80b7..9c397de3 100644 --- a/styleguide-app/Main.elm +++ b/styleguide-app/Main.elm @@ -22,6 +22,7 @@ import Nri.Ui.Heading.V2 as Heading import Nri.Ui.MediaQuery.V1 exposing (mobile, notMobile) import Nri.Ui.Page.V3 as Page import Nri.Ui.SideNav.V1 as SideNav +import Nri.Ui.UiIcon.V1 as UiIcon import Routes as Routes exposing (Route(..)) import Sort.Set as Set exposing (Set) import Task @@ -227,7 +228,8 @@ navigation currentRoute = let toNavLinkConfig : Category -> SideNav.LinkConfig Route Msg toNavLinkConfig category = - { title = Category.forDisplay category + { icon = Nothing + , title = Category.forDisplay category , route = Routes.Category category , attributes = [ href (Routes.toString (Routes.Category category)) ] , children = [] @@ -237,13 +239,43 @@ navigation currentRoute = navLinks : List (SideNav.SidebarEntry Route Msg) navLinks = SideNav.link - { title = "All" + { icon = Nothing + , title = "All" , route = Routes.All , attributes = [ href (Routes.toString Routes.All) ] , children = [] , premiumLevel = PremiumLevel.Free } :: List.map (toNavLinkConfig >> SideNav.link) Category.all + ++ [ SideNav.link + { icon = Nothing + , title = "Special Examples" + , route = Routes.All + , attributes = [] + , children = + [ SideNav.link + { icon = Nothing + , title = "Example of Locked Premium content" + , route = Routes.All + , attributes = [ href (Routes.toString Routes.All) ] + , children = [] + , premiumLevel = PremiumLevel.PremiumWithWriting + } + , SideNav.link + { icon = Just UiIcon.gear + , title = "Create your own" + , route = Routes.All + , attributes = + [ href (Routes.toString Routes.All) + , css SideNav.withBorderStyles + ] + , children = [] + , premiumLevel = PremiumLevel.Free + } + ] + , premiumLevel = PremiumLevel.Free + } + ] in SideNav.view { userPremiumLevel = PremiumLevel.Free From 5d1dbdb367097e288c6ef37f0a3d3018f84252e6 Mon Sep 17 00:00:00 2001 From: Tessa Kelly Date: Fri, 3 Dec 2021 09:59:55 -0800 Subject: [PATCH 09/23] link -> entry --- src/Nri/Ui/SideNav/V1.elm | 80 +++++++++++++++++++-------------------- styleguide-app/Main.elm | 45 +++++++++------------- 2 files changed, 57 insertions(+), 68 deletions(-) diff --git a/src/Nri/Ui/SideNav/V1.elm b/src/Nri/Ui/SideNav/V1.elm index 63c13ae4..5b1d61f1 100644 --- a/src/Nri/Ui/SideNav/V1.elm +++ b/src/Nri/Ui/SideNav/V1.elm @@ -1,13 +1,13 @@ module Nri.Ui.SideNav.V1 exposing - ( view, Config, SidebarEntry - , link, LinkConfig + ( view, Config, Entry + , entry, EntryConfig , withBorderStyles ) {-| -@docs view, Config, SidebarEntry -@docs link, LinkConfig +@docs view, Config, Entry +@docs entry, EntryConfig -} @@ -28,27 +28,27 @@ import String exposing (toLower) import String.Extra exposing (dasherize) -{-| Use `link` to create a sidebar link. +{-| Use `entry` to create a sidebar entry. -} -type SidebarEntry route msg - = Link (LinkConfig route msg) +type Entry route msg + = Entry (EntryConfig route msg) {-| -} -type alias LinkConfig route msg = +type alias EntryConfig route msg = { icon : Maybe Svg , title : String , route : route , attributes : List (Html.Styled.Attribute msg) - , children : List (SidebarEntry route msg) + , children : List (Entry route msg) , premiumLevel : PremiumLevel } {-| -} -link : LinkConfig route msg -> SidebarEntry route msg -link = - Link +entry : EntryConfig route msg -> Entry route msg +entry = + Entry {-| -} @@ -61,7 +61,7 @@ type alias Config route msg = {-| -} -view : Config route msg -> List (SidebarEntry route msg) -> Html msg +view : Config route msg -> List (Entry route msg) -> Html msg view config entries = styled nav [ flexBasis (px 250) @@ -102,44 +102,42 @@ viewSkipLink onSkip = ] -viewSidebarEntry : Config route msg -> List Css.Style -> SidebarEntry route msg -> Html msg -viewSidebarEntry config extraStyles sidebarEntry = - case sidebarEntry of - Link entry -> - if PremiumLevel.allowedFor entry.premiumLevel config.userPremiumLevel then - if anyLinkDescendants (.route >> config.isCurrentRoute) entry then - div [ css extraStyles ] - (styled span - (sharedEntryStyles - ++ [ backgroundColor Colors.gray92 - , marginBottom (px 10) - , color Colors.navy - , fontWeight bold - , cursor default - ] - ) - [] - [ text entry.title ] - :: List.map (viewSidebarEntry config [ marginLeft (px 20) ]) - entry.children - ) +viewSidebarEntry : Config route msg -> List Css.Style -> Entry route msg -> Html msg +viewSidebarEntry config extraStyles (Entry entry_) = + if PremiumLevel.allowedFor entry_.premiumLevel config.userPremiumLevel then + if anyLinkDescendants (.route >> config.isCurrentRoute) entry_ then + div [ css extraStyles ] + (styled span + (sharedEntryStyles + ++ [ backgroundColor Colors.gray92 + , marginBottom (px 10) + , color Colors.navy + , fontWeight bold + , cursor default + ] + ) + [] + [ text entry_.title ] + :: List.map (viewSidebarEntry config [ marginLeft (px 20) ]) + entry_.children + ) - else - viewSidebarLeaf config extraStyles entry + else + viewSidebarLeaf config extraStyles entry_ - else - viewLockedEntry entry.title extraStyles + else + viewLockedEntry entry_.title extraStyles -anyLinkDescendants : (LinkConfig route msg -> Bool) -> LinkConfig route msg -> Bool +anyLinkDescendants : (EntryConfig route msg -> Bool) -> EntryConfig route msg -> Bool anyLinkDescendants f { children } = - List.any (\(Link entry) -> f entry || anyLinkDescendants f entry) children + List.any (\(Entry entry_) -> f entry_ || anyLinkDescendants f entry_) children viewSidebarLeaf : Config route msg -> List Style - -> LinkConfig route msg + -> EntryConfig route msg -> Html msg viewSidebarLeaf config extraStyles { icon, title, route, attributes } = styled Html.Styled.a diff --git a/styleguide-app/Main.elm b/styleguide-app/Main.elm index 9c397de3..959a09cd 100644 --- a/styleguide-app/Main.elm +++ b/styleguide-app/Main.elm @@ -226,7 +226,7 @@ viewPreviews containerId examples = navigation : Route -> Html Msg navigation currentRoute = let - toNavLinkConfig : Category -> SideNav.LinkConfig Route Msg + toNavLinkConfig : Category -> SideNav.EntryConfig Route Msg toNavLinkConfig category = { icon = Nothing , title = Category.forDisplay category @@ -236,9 +236,9 @@ navigation currentRoute = , premiumLevel = PremiumLevel.Free } - navLinks : List (SideNav.SidebarEntry Route Msg) + navLinks : List (SideNav.Entry Route Msg) navLinks = - SideNav.link + SideNav.entry { icon = Nothing , title = "All" , route = Routes.All @@ -246,33 +246,24 @@ navigation currentRoute = , children = [] , premiumLevel = PremiumLevel.Free } - :: List.map (toNavLinkConfig >> SideNav.link) Category.all - ++ [ SideNav.link + :: List.map (toNavLinkConfig >> SideNav.entry) Category.all + ++ [ SideNav.entry { icon = Nothing - , title = "Special Examples" + , title = "Example of Locked Premium content" , route = Routes.All - , attributes = [] - , children = - [ SideNav.link - { icon = Nothing - , title = "Example of Locked Premium content" - , route = Routes.All - , attributes = [ href (Routes.toString Routes.All) ] - , children = [] - , premiumLevel = PremiumLevel.PremiumWithWriting - } - , SideNav.link - { icon = Just UiIcon.gear - , title = "Create your own" - , route = Routes.All - , attributes = - [ href (Routes.toString Routes.All) - , css SideNav.withBorderStyles - ] - , children = [] - , premiumLevel = PremiumLevel.Free - } + , attributes = [ href (Routes.toString Routes.All) ] + , children = [] + , premiumLevel = PremiumLevel.PremiumWithWriting + } + , SideNav.entry + { icon = Just UiIcon.gear + , title = "Create your own" + , route = Routes.All + , attributes = + [ href (Routes.toString Routes.All) + , css SideNav.withBorderStyles ] + , children = [] , premiumLevel = PremiumLevel.Free } ] From de8903eeeede8f7d10af448471c13b4c5e401c0a Mon Sep 17 00:00:00 2001 From: Tessa Kelly Date: Fri, 3 Dec 2021 10:47:40 -0800 Subject: [PATCH 10/23] Use ClickableAttribute helpers --- src/Nri/Ui/SideNav/V1.elm | 254 +++++++++++++++++++++++++++++++++----- styleguide-app/Main.elm | 60 ++++----- 2 files changed, 245 insertions(+), 69 deletions(-) diff --git a/src/Nri/Ui/SideNav/V1.elm b/src/Nri/Ui/SideNav/V1.elm index 5b1d61f1..dddb7520 100644 --- a/src/Nri/Ui/SideNav/V1.elm +++ b/src/Nri/Ui/SideNav/V1.elm @@ -1,26 +1,50 @@ module Nri.Ui.SideNav.V1 exposing - ( view, Config, Entry - , entry, EntryConfig - , withBorderStyles + ( view, Config + , entry, Entry + , icon, custom, css, nriDescription, testId, id + , onClick + , href, linkSpa, linkExternal, linkWithMethod, linkWithTracking, linkExternalWithTracking + , primary, secondary + , premiumLevel ) {-| -@docs view, Config, Entry -@docs entry, EntryConfig +@docs view, Config +@docs entry, Entry +@docs icon, custom, css, nriDescription, testId, id + + +## Behavior + +@docs onClick +@docs href, linkSpa, linkExternal, linkWithMethod, linkWithTracking, linkExternalWithTracking + + +## Change the color scheme + +@docs primary, secondary + + +## Change the state + +@docs premiumLevel -} import Accessibility.Styled exposing (..) +import ClickableAttributes exposing (ClickableAttributes) import Css exposing (..) import Css.Media as Media import Html.Styled import Html.Styled.Attributes as Attributes exposing (css) import Html.Styled.Events as Events +import Nri.Ui import Nri.Ui.ClickableText.V3 as ClickableText import Nri.Ui.Colors.V1 as Colors import Nri.Ui.Data.PremiumLevel as PremiumLevel exposing (PremiumLevel) import Nri.Ui.Fonts.V1 as Fonts +import Nri.Ui.Html.Attributes.V2 as ExtraAttributes import Nri.Ui.Html.V3 exposing (viewJust) import Nri.Ui.Svg.V1 as Svg exposing (Svg) import Nri.Ui.UiIcon.V1 as UiIcon @@ -35,20 +59,11 @@ type Entry route msg {-| -} -type alias EntryConfig route msg = - { icon : Maybe Svg - , title : String - , route : route - , attributes : List (Html.Styled.Attribute msg) - , children : List (Entry route msg) - , premiumLevel : PremiumLevel - } - - -{-| -} -entry : EntryConfig route msg -> Entry route msg -entry = - Entry +entry : String -> route -> List (Attribute route msg) -> Entry route msg +entry title route attributes = + attributes + |> List.foldl (\(Attribute attribute) b -> attribute b) (build title route) + |> Entry {-| -} @@ -106,7 +121,7 @@ viewSidebarEntry : Config route msg -> List Css.Style -> Entry route msg -> Html viewSidebarEntry config extraStyles (Entry entry_) = if PremiumLevel.allowedFor entry_.premiumLevel config.userPremiumLevel then if anyLinkDescendants (.route >> config.isCurrentRoute) entry_ then - div [ css extraStyles ] + div [ Attributes.css extraStyles ] (styled span (sharedEntryStyles ++ [ backgroundColor Colors.gray92 @@ -139,11 +154,16 @@ viewSidebarLeaf : -> List Style -> EntryConfig route msg -> Html msg -viewSidebarLeaf config extraStyles { icon, title, route, attributes } = - styled Html.Styled.a +viewSidebarLeaf config extraStyles entryConfig = + let + ( linkFunctionName, attributes ) = + ClickableAttributes.toLinkAttributes entryConfig.clickableAttributes + in + Nri.Ui.styled Html.Styled.a + ("Nri-Ui-SideNav-" ++ linkFunctionName) (sharedEntryStyles ++ extraStyles - ++ (if config.isCurrentRoute route then + ++ (if config.isCurrentRoute entryConfig.route then [ backgroundColor Colors.glacier , color Colors.navy , fontWeight bold @@ -153,8 +173,9 @@ viewSidebarLeaf config extraStyles { icon, title, route, attributes } = else [] ) + ++ entryConfig.customStyles ) - attributes + (attributes ++ entryConfig.customAttributes) [ viewJust (\icon_ -> icon_ @@ -163,8 +184,8 @@ viewSidebarLeaf config extraStyles { icon, title, route, attributes } = |> Svg.withCss [ marginRight (px 5) ] |> Svg.toHtml ) - icon - , text title + entryConfig.icon + , text entryConfig.title ] @@ -212,10 +233,177 @@ sharedEntryStyles = ] -withBorderStyles : List Style -withBorderStyles = - -- TODO: add a convenient way to use these styels - [ backgroundColor Colors.white - , boxShadow3 zero (px 2) Colors.gray75 - , border3 (px 1) solid Colors.gray75 - ] + +-- Entry Customization helpers + + +{-| -} +type alias EntryConfig route msg = + { icon : Maybe Svg + , title : String + , route : route + , clickableAttributes : ClickableAttributes msg + , customAttributes : List (Html.Styled.Attribute msg) + , customStyles : List Style + , children : List (Entry route msg) + , premiumLevel : PremiumLevel + } + + +build : String -> route -> EntryConfig route msg +build title route = + { icon = Nothing + , title = title + , route = route + , clickableAttributes = ClickableAttributes.init + , customAttributes = [] + , customStyles = [] + , children = [] + , premiumLevel = PremiumLevel.Free + } + + +type Attribute route msg + = Attribute (EntryConfig route msg -> EntryConfig route msg) + + +{-| -} +icon : Svg -> Attribute route msg +icon icon_ = + Attribute (\attributes -> { attributes | icon = Just icon_ }) + + +{-| -} +premiumLevel : PremiumLevel -> msg -> Attribute route msg +premiumLevel level ifLocked = + -- TODO: adds the lock click behavior + Attribute (\attributes -> { attributes | premiumLevel = level }) + + +{-| Use this helper to add custom attributes. + +Do NOT use this helper to add css styles, as they may not be applied the way +you want/expect if underlying Button styles change. +Instead, please use the `css` helper. + +-} +custom : List (Html.Styled.Attribute msg) -> Attribute route msg +custom attributes = + Attribute + (\config -> + { config + | customAttributes = List.append config.customAttributes attributes + } + ) + + +{-| -} +nriDescription : String -> Attribute route msg +nriDescription description = + custom [ ExtraAttributes.nriDescription description ] + + +{-| -} +testId : String -> Attribute route msg +testId id_ = + custom [ ExtraAttributes.testId id_ ] + + +{-| -} +id : String -> Attribute route msg +id id_ = + custom [ Attributes.id id_ ] + + +{-| -} +css : List Style -> Attribute route msg +css styles = + Attribute + (\config -> + { config + | customStyles = List.append config.customStyles styles + } + ) + + +{-| -} +primary : Attribute route msg +primary = + Attribute (\attributes -> { attributes | customStyles = [] }) + + +{-| -} +secondary : Attribute route msg +secondary = + Attribute + (\attributes -> + { attributes + | customStyles = + [ backgroundColor Colors.white + , boxShadow3 zero (px 2) Colors.gray75 + , border3 (px 1) solid Colors.gray75 + ] + } + ) + + + +-- LINKING, CLICKING, and TRACKING BEHAVIOR + + +setClickableAttributes : + (ClickableAttributes msg -> ClickableAttributes msg) + -> Attribute route msg +setClickableAttributes apply = + Attribute + (\attributes -> + { attributes | clickableAttributes = apply attributes.clickableAttributes } + ) + + +{-| -} +onClick : msg -> Attribute route msg +onClick msg = + setClickableAttributes (ClickableAttributes.onClick msg) + + +{-| -} +href : String -> Attribute route msg +href url = + setClickableAttributes (ClickableAttributes.href url) + + +{-| Use this link for routing within a single page app. + +This will make a normal tag, but change the Events.onClick behavior to avoid reloading the page. + +See for details on this implementation. + +-} +linkSpa : String -> Attribute route msg +linkSpa url = + setClickableAttributes (ClickableAttributes.linkSpa url) + + +{-| -} +linkWithMethod : { method : String, url : String } -> Attribute route msg +linkWithMethod config = + setClickableAttributes (ClickableAttributes.linkWithMethod config) + + +{-| -} +linkWithTracking : { track : msg, url : String } -> Attribute route msg +linkWithTracking config = + setClickableAttributes (ClickableAttributes.linkWithTracking config) + + +{-| -} +linkExternal : String -> Attribute route msg +linkExternal url = + setClickableAttributes (ClickableAttributes.linkExternal url) + + +{-| -} +linkExternalWithTracking : { track : msg, url : String } -> Attribute route msg +linkExternalWithTracking config = + setClickableAttributes (ClickableAttributes.linkExternalWithTracking config) diff --git a/styleguide-app/Main.elm b/styleguide-app/Main.elm index 959a09cd..4660d318 100644 --- a/styleguide-app/Main.elm +++ b/styleguide-app/Main.elm @@ -226,46 +226,34 @@ viewPreviews containerId examples = navigation : Route -> Html Msg navigation currentRoute = let - toNavLinkConfig : Category -> SideNav.EntryConfig Route Msg + toNavLinkConfig : Category -> SideNav.Entry Route Msg toNavLinkConfig category = - { icon = Nothing - , title = Category.forDisplay category - , route = Routes.Category category - , attributes = [ href (Routes.toString (Routes.Category category)) ] - , children = [] - , premiumLevel = PremiumLevel.Free - } + SideNav.entry + (Category.forDisplay category) + (Routes.Category category) + [ -- TODO: we shouldn't require manually adding the href + SideNav.href (Routes.toString (Routes.Category category)) + ] navLinks : List (SideNav.Entry Route Msg) navLinks = - SideNav.entry - { icon = Nothing - , title = "All" - , route = Routes.All - , attributes = [ href (Routes.toString Routes.All) ] - , children = [] - , premiumLevel = PremiumLevel.Free - } - :: List.map (toNavLinkConfig >> SideNav.entry) Category.all - ++ [ SideNav.entry - { icon = Nothing - , title = "Example of Locked Premium content" - , route = Routes.All - , attributes = [ href (Routes.toString Routes.All) ] - , children = [] - , premiumLevel = PremiumLevel.PremiumWithWriting - } - , SideNav.entry - { icon = Just UiIcon.gear - , title = "Create your own" - , route = Routes.All - , attributes = - [ href (Routes.toString Routes.All) - , css SideNav.withBorderStyles - ] - , children = [] - , premiumLevel = PremiumLevel.Free - } + SideNav.entry "All" + Routes.All + [ SideNav.href (Routes.toString Routes.All) + ] + :: List.map toNavLinkConfig Category.all + ++ [ SideNav.entry "Example of Locked Premium content" + Routes.All + [ SideNav.premiumLevel PremiumLevel.PremiumWithWriting + NoOp + ] + , SideNav.entry "Create your own" + -- TODO: support _no_ route + Routes.All + [ SideNav.icon UiIcon.gear + , SideNav.secondary + , SideNav.linkExternal "external-link" + ] ] in SideNav.view From 8876c5be328dfb3378fa584e2554775a8c9bcd2d Mon Sep 17 00:00:00 2001 From: Tessa Kelly Date: Fri, 3 Dec 2021 11:10:15 -0800 Subject: [PATCH 11/23] Generalize ClickableAttributes to improve the routing links --- src/ClickableAttributes.elm | 63 ++++++++++++++++++--------------- src/Nri/Ui/Button/V10.elm | 6 ++-- src/Nri/Ui/ClickableSvg/V2.elm | 6 ++-- src/Nri/Ui/ClickableText/V3.elm | 6 ++-- src/Nri/Ui/SideNav/V1.elm | 20 ++++++----- styleguide-app/Main.elm | 8 +++-- 6 files changed, 59 insertions(+), 50 deletions(-) diff --git a/src/ClickableAttributes.elm b/src/ClickableAttributes.elm index f472e903..3cc22907 100644 --- a/src/ClickableAttributes.elm +++ b/src/ClickableAttributes.elm @@ -23,9 +23,9 @@ import Nri.Ui.Html.Attributes.V2 as AttributeExtras exposing (targetBlank) {-| -} -type alias ClickableAttributes msg = +type alias ClickableAttributes route msg = { linkType : Link - , url : String + , url : Maybe route , onClick : Maybe msg } @@ -40,58 +40,58 @@ type Link {-| -} -init : ClickableAttributes msg +init : ClickableAttributes route msg init = { linkType = Default - , url = "#" + , url = Nothing , onClick = Nothing } {-| -} -onClick : msg -> ClickableAttributes msg -> ClickableAttributes msg +onClick : msg -> ClickableAttributes route msg -> ClickableAttributes route msg onClick msg clickableAttributes = { clickableAttributes | onClick = Just msg } {-| -} -href : String -> ClickableAttributes msg -> ClickableAttributes msg +href : route -> ClickableAttributes route msg -> ClickableAttributes route msg href url clickableAttributes = - { clickableAttributes | url = url } + { clickableAttributes | url = Just url } {-| -} -linkSpa : String -> ClickableAttributes msg -> ClickableAttributes msg +linkSpa : route -> ClickableAttributes route msg -> ClickableAttributes route msg linkSpa url clickableAttributes = - { clickableAttributes | linkType = SinglePageApp, url = url } + { clickableAttributes | linkType = SinglePageApp, url = Just url } {-| -} -linkWithMethod : { method : String, url : String } -> ClickableAttributes msg -> ClickableAttributes msg +linkWithMethod : { method : String, url : route } -> ClickableAttributes route msg -> ClickableAttributes route msg linkWithMethod { method, url } clickableAttributes = - { clickableAttributes | linkType = WithMethod method, url = url } + { clickableAttributes | linkType = WithMethod method, url = Just url } {-| -} -linkWithTracking : { track : msg, url : String } -> ClickableAttributes msg -> ClickableAttributes msg +linkWithTracking : { track : msg, url : route } -> ClickableAttributes route msg -> ClickableAttributes route msg linkWithTracking { track, url } _ = - { linkType = WithTracking, url = url, onClick = Just track } + { linkType = WithTracking, url = Just url, onClick = Just track } {-| -} -linkExternal : String -> ClickableAttributes msg -> ClickableAttributes msg +linkExternal : route -> ClickableAttributes route msg -> ClickableAttributes route msg linkExternal url clickableAttributes = - { clickableAttributes | linkType = External, url = url } + { clickableAttributes | linkType = External, url = Just url } {-| -} -linkExternalWithTracking : { track : msg, url : String } -> ClickableAttributes msg -> ClickableAttributes msg +linkExternalWithTracking : { track : msg, url : route } -> ClickableAttributes route msg -> ClickableAttributes route msg linkExternalWithTracking { track, url } _ = - { linkType = ExternalWithTracking, url = url, onClick = Just track } + { linkType = ExternalWithTracking, url = Just url, onClick = Just track } {-| -} -toButtonAttributes : ClickableAttributes msg -> List (Attribute msg) +toButtonAttributes : ClickableAttributes route msg -> List (Attribute msg) toButtonAttributes clickableAttributes = case clickableAttributes.onClick of Just handler -> @@ -102,12 +102,17 @@ toButtonAttributes clickableAttributes = {-| -} -toLinkAttributes : ClickableAttributes msg -> ( String, List (Attribute msg) ) -toLinkAttributes clickableAttributes = +toLinkAttributes : (route -> String) -> ClickableAttributes route msg -> ( String, List (Attribute msg) ) +toLinkAttributes routeToString clickableAttributes = + let + stringUrl = + Maybe.map routeToString clickableAttributes.url + |> Maybe.withDefault "#" + in case clickableAttributes.linkType of Default -> ( "link" - , [ Attributes.href clickableAttributes.url + , [ Attributes.href stringUrl , Attributes.target "_self" ] ) @@ -116,17 +121,17 @@ toLinkAttributes clickableAttributes = ( "linkSpa" , case clickableAttributes.onClick of Just handler -> - [ Attributes.href clickableAttributes.url + [ Attributes.href stringUrl , EventExtras.onClickPreventDefaultForLinkWithHref handler ] Nothing -> - [ Attributes.href clickableAttributes.url ] + [ Attributes.href stringUrl ] ) WithMethod method -> ( "linkWithMethod" - , [ Attributes.href clickableAttributes.url + , [ Attributes.href stringUrl , Attributes.attribute "data-method" method ] ) @@ -135,18 +140,18 @@ toLinkAttributes clickableAttributes = ( "linkWithTracking" , case clickableAttributes.onClick of Just track -> - [ Attributes.href clickableAttributes.url + [ Attributes.href stringUrl , Events.preventDefaultOn "click" (Json.Decode.succeed ( track, True )) ] Nothing -> - [ Attributes.href clickableAttributes.url ] + [ Attributes.href stringUrl ] ) External -> ( "linkExternal" - , Attributes.href clickableAttributes.url + , Attributes.href stringUrl :: targetBlank ) @@ -154,13 +159,13 @@ toLinkAttributes clickableAttributes = ( "linkExternalWithTracking" , case clickableAttributes.onClick of Just handler -> - [ Attributes.href clickableAttributes.url + [ Attributes.href stringUrl , Events.onClick handler , Events.on "auxclick" (Json.Decode.succeed handler) ] ++ targetBlank Nothing -> - Attributes.href clickableAttributes.url + Attributes.href stringUrl :: targetBlank ) diff --git a/src/Nri/Ui/Button/V10.elm b/src/Nri/Ui/Button/V10.elm index d499f850..24dff7d5 100644 --- a/src/Nri/Ui/Button/V10.elm +++ b/src/Nri/Ui/Button/V10.elm @@ -187,7 +187,7 @@ css styles = setClickableAttributes : - (ClickableAttributes msg -> ClickableAttributes msg) + (ClickableAttributes String msg -> ClickableAttributes String msg) -> Attribute msg setClickableAttributes apply = set @@ -467,7 +467,7 @@ type ButtonOrLink msg type alias ButtonOrLinkAttributes msg = - { clickableAttributes : ClickableAttributes msg + { clickableAttributes : ClickableAttributes String msg , size : ButtonSize , style : ColorPalette , width : ButtonWidth @@ -523,7 +523,7 @@ renderLink ((ButtonOrLink config) as link_) = getColorPalette link_ ( linkFunctionName, attributes ) = - ClickableAttributes.toLinkAttributes config.clickableAttributes + ClickableAttributes.toLinkAttributes identity config.clickableAttributes in Nri.Ui.styled Styled.a (styledName linkFunctionName) diff --git a/src/Nri/Ui/ClickableSvg/V2.elm b/src/Nri/Ui/ClickableSvg/V2.elm index fd76d617..e58f8322 100644 --- a/src/Nri/Ui/ClickableSvg/V2.elm +++ b/src/Nri/Ui/ClickableSvg/V2.elm @@ -87,7 +87,7 @@ link name icon attributes = setClickableAttributes : - (ClickableAttributes msg -> ClickableAttributes msg) + (ClickableAttributes String msg -> ClickableAttributes String msg) -> Attribute msg setClickableAttributes apply = set @@ -384,7 +384,7 @@ type ButtonOrLink msg type alias ButtonOrLinkAttributes msg = - { clickableAttributes : ClickableAttributes msg + { clickableAttributes : ClickableAttributes String msg , label : String , icon : Svg , disabled : Bool @@ -435,7 +435,7 @@ renderLink : ButtonOrLink msg -> Html msg renderLink ((ButtonOrLink config) as link_) = let ( linkFunctionName, extraAttrs ) = - ClickableAttributes.toLinkAttributes config.clickableAttributes + ClickableAttributes.toLinkAttributes identity config.clickableAttributes theme = if config.disabled then diff --git a/src/Nri/Ui/ClickableText/V3.elm b/src/Nri/Ui/ClickableText/V3.elm index 2dcb23e0..620662a9 100644 --- a/src/Nri/Ui/ClickableText/V3.elm +++ b/src/Nri/Ui/ClickableText/V3.elm @@ -178,7 +178,7 @@ css styles = setClickableAttributes : - (ClickableAttributes msg -> ClickableAttributes msg) + (ClickableAttributes String msg -> ClickableAttributes String msg) -> Attribute msg setClickableAttributes apply = set @@ -269,7 +269,7 @@ link label_ attributes = |> List.foldl (\(Attribute attribute) l -> attribute l) defaults ( name, clickableAttributes ) = - ClickableAttributes.toLinkAttributes config.clickableAttributes + ClickableAttributes.toLinkAttributes identity config.clickableAttributes in Nri.Ui.styled Html.a (dataDescriptor name) @@ -366,7 +366,7 @@ dataDescriptor descriptor = type alias ClickableTextAttributes msg = - { clickableAttributes : ClickableAttributes msg + { clickableAttributes : ClickableAttributes String msg , label : String , size : Size , icon : Maybe Svg diff --git a/src/Nri/Ui/SideNav/V1.elm b/src/Nri/Ui/SideNav/V1.elm index dddb7520..ce88a456 100644 --- a/src/Nri/Ui/SideNav/V1.elm +++ b/src/Nri/Ui/SideNav/V1.elm @@ -70,6 +70,7 @@ entry title route attributes = type alias Config route msg = { userPremiumLevel : PremiumLevel , isCurrentRoute : route -> Bool + , routeToString : route -> String , onSkipNav : msg , css : List Style } @@ -157,7 +158,8 @@ viewSidebarLeaf : viewSidebarLeaf config extraStyles entryConfig = let ( linkFunctionName, attributes ) = - ClickableAttributes.toLinkAttributes entryConfig.clickableAttributes + ClickableAttributes.toLinkAttributes config.routeToString + entryConfig.clickableAttributes in Nri.Ui.styled Html.Styled.a ("Nri-Ui-SideNav-" ++ linkFunctionName) @@ -242,7 +244,7 @@ type alias EntryConfig route msg = { icon : Maybe Svg , title : String , route : route - , clickableAttributes : ClickableAttributes msg + , clickableAttributes : ClickableAttributes route msg , customAttributes : List (Html.Styled.Attribute msg) , customStyles : List Style , children : List (Entry route msg) @@ -352,7 +354,7 @@ secondary = setClickableAttributes : - (ClickableAttributes msg -> ClickableAttributes msg) + (ClickableAttributes route msg -> ClickableAttributes route msg) -> Attribute route msg setClickableAttributes apply = Attribute @@ -368,7 +370,7 @@ onClick msg = {-| -} -href : String -> Attribute route msg +href : route -> Attribute route msg href url = setClickableAttributes (ClickableAttributes.href url) @@ -380,30 +382,30 @@ This will make a normal tag, but change the Events.onClick behavior to avoid See for details on this implementation. -} -linkSpa : String -> Attribute route msg +linkSpa : route -> Attribute route msg linkSpa url = setClickableAttributes (ClickableAttributes.linkSpa url) {-| -} -linkWithMethod : { method : String, url : String } -> Attribute route msg +linkWithMethod : { method : String, url : route } -> Attribute route msg linkWithMethod config = setClickableAttributes (ClickableAttributes.linkWithMethod config) {-| -} -linkWithTracking : { track : msg, url : String } -> Attribute route msg +linkWithTracking : { track : msg, url : route } -> Attribute route msg linkWithTracking config = setClickableAttributes (ClickableAttributes.linkWithTracking config) {-| -} -linkExternal : String -> Attribute route msg +linkExternal : route -> Attribute route msg linkExternal url = setClickableAttributes (ClickableAttributes.linkExternal url) {-| -} -linkExternalWithTracking : { track : msg, url : String } -> Attribute route msg +linkExternalWithTracking : { track : msg, url : route } -> Attribute route msg linkExternalWithTracking config = setClickableAttributes (ClickableAttributes.linkExternalWithTracking config) diff --git a/styleguide-app/Main.elm b/styleguide-app/Main.elm index 4660d318..3c11ca28 100644 --- a/styleguide-app/Main.elm +++ b/styleguide-app/Main.elm @@ -232,14 +232,14 @@ navigation currentRoute = (Category.forDisplay category) (Routes.Category category) [ -- TODO: we shouldn't require manually adding the href - SideNav.href (Routes.toString (Routes.Category category)) + SideNav.href (Routes.Category category) ] navLinks : List (SideNav.Entry Route Msg) navLinks = SideNav.entry "All" Routes.All - [ SideNav.href (Routes.toString Routes.All) + [ SideNav.href Routes.All ] :: List.map toNavLinkConfig Category.all ++ [ SideNav.entry "Example of Locked Premium content" @@ -252,13 +252,15 @@ navigation currentRoute = Routes.All [ SideNav.icon UiIcon.gear , SideNav.secondary - , SideNav.linkExternal "external-link" + + --, SideNav.linkExternal "external-link" ] ] in SideNav.view { userPremiumLevel = PremiumLevel.Free , isCurrentRoute = (==) currentRoute + , routeToString = Routes.toString , onSkipNav = SkipToMainContent , css = [ withMedia [ notMobile ] From 57c43588227baa55d1647356e1082c98ffff8f88 Mon Sep 17 00:00:00 2001 From: Tessa Kelly Date: Fri, 3 Dec 2021 11:25:47 -0800 Subject: [PATCH 12/23] Thread the route through once --- src/Nri/Ui/SideNav/V1.elm | 63 ++++++++++++++++++++++++++------------- styleguide-app/Main.elm | 15 ++-------- 2 files changed, 45 insertions(+), 33 deletions(-) diff --git a/src/Nri/Ui/SideNav/V1.elm b/src/Nri/Ui/SideNav/V1.elm index ce88a456..58be5f06 100644 --- a/src/Nri/Ui/SideNav/V1.elm +++ b/src/Nri/Ui/SideNav/V1.elm @@ -59,10 +59,10 @@ type Entry route msg {-| -} -entry : String -> route -> List (Attribute route msg) -> Entry route msg -entry title route attributes = +entry : String -> List (Attribute route msg) -> Entry route msg +entry title attributes = attributes - |> List.foldl (\(Attribute attribute) b -> attribute b) (build title route) + |> List.foldl (\(Attribute attribute) b -> attribute b) (build title) |> Entry @@ -121,7 +121,7 @@ viewSkipLink onSkip = viewSidebarEntry : Config route msg -> List Css.Style -> Entry route msg -> Html msg viewSidebarEntry config extraStyles (Entry entry_) = if PremiumLevel.allowedFor entry_.premiumLevel config.userPremiumLevel then - if anyLinkDescendants (.route >> config.isCurrentRoute) entry_ then + if anyLinkDescendants (isCurrentRoute config) entry_ then div [ Attributes.css extraStyles ] (styled span (sharedEntryStyles @@ -145,6 +145,12 @@ viewSidebarEntry config extraStyles (Entry entry_) = viewLockedEntry entry_.title extraStyles +isCurrentRoute : Config route msg -> EntryConfig route msg -> Bool +isCurrentRoute config { route } = + Maybe.map config.isCurrentRoute route + |> Maybe.withDefault False + + anyLinkDescendants : (EntryConfig route msg -> Bool) -> EntryConfig route msg -> Bool anyLinkDescendants f { children } = List.any (\(Entry entry_) -> f entry_ || anyLinkDescendants f entry_) children @@ -165,7 +171,7 @@ viewSidebarLeaf config extraStyles entryConfig = ("Nri-Ui-SideNav-" ++ linkFunctionName) (sharedEntryStyles ++ extraStyles - ++ (if config.isCurrentRoute entryConfig.route then + ++ (if isCurrentRoute config entryConfig then [ backgroundColor Colors.glacier , color Colors.navy , fontWeight bold @@ -243,7 +249,7 @@ sharedEntryStyles = type alias EntryConfig route msg = { icon : Maybe Svg , title : String - , route : route + , route : Maybe route , clickableAttributes : ClickableAttributes route msg , customAttributes : List (Html.Styled.Attribute msg) , customStyles : List Style @@ -252,11 +258,11 @@ type alias EntryConfig route msg = } -build : String -> route -> EntryConfig route msg -build title route = +build : String -> EntryConfig route msg +build title = { icon = Nothing , title = title - , route = route + , route = Nothing , clickableAttributes = ClickableAttributes.init , customAttributes = [] , customStyles = [] @@ -354,25 +360,35 @@ secondary = setClickableAttributes : - (ClickableAttributes route msg -> ClickableAttributes route msg) + Maybe route + -> (ClickableAttributes route msg -> ClickableAttributes route msg) -> Attribute route msg -setClickableAttributes apply = +setClickableAttributes route apply = Attribute (\attributes -> - { attributes | clickableAttributes = apply attributes.clickableAttributes } + { attributes + | route = + case route of + Just r -> + Just r + + Nothing -> + attributes.route + , clickableAttributes = apply attributes.clickableAttributes + } ) {-| -} onClick : msg -> Attribute route msg onClick msg = - setClickableAttributes (ClickableAttributes.onClick msg) + setClickableAttributes Nothing (ClickableAttributes.onClick msg) {-| -} href : route -> Attribute route msg -href url = - setClickableAttributes (ClickableAttributes.href url) +href route = + setClickableAttributes (Just route) (ClickableAttributes.href route) {-| Use this link for routing within a single page app. @@ -383,29 +399,34 @@ See for details on this implementa -} linkSpa : route -> Attribute route msg -linkSpa url = - setClickableAttributes (ClickableAttributes.linkSpa url) +linkSpa route = + setClickableAttributes (Just route) + (ClickableAttributes.linkSpa route) {-| -} linkWithMethod : { method : String, url : route } -> Attribute route msg linkWithMethod config = - setClickableAttributes (ClickableAttributes.linkWithMethod config) + setClickableAttributes (Just config.url) + (ClickableAttributes.linkWithMethod config) {-| -} linkWithTracking : { track : msg, url : route } -> Attribute route msg linkWithTracking config = - setClickableAttributes (ClickableAttributes.linkWithTracking config) + setClickableAttributes (Just config.url) + (ClickableAttributes.linkWithTracking config) {-| -} linkExternal : route -> Attribute route msg linkExternal url = - setClickableAttributes (ClickableAttributes.linkExternal url) + setClickableAttributes (Just url) + (ClickableAttributes.linkExternal url) {-| -} linkExternalWithTracking : { track : msg, url : route } -> Attribute route msg linkExternalWithTracking config = - setClickableAttributes (ClickableAttributes.linkExternalWithTracking config) + setClickableAttributes (Just config.url) + (ClickableAttributes.linkExternalWithTracking config) diff --git a/styleguide-app/Main.elm b/styleguide-app/Main.elm index 3c11ca28..98abd2e4 100644 --- a/styleguide-app/Main.elm +++ b/styleguide-app/Main.elm @@ -228,28 +228,19 @@ navigation currentRoute = let toNavLinkConfig : Category -> SideNav.Entry Route Msg toNavLinkConfig category = - SideNav.entry - (Category.forDisplay category) - (Routes.Category category) - [ -- TODO: we shouldn't require manually adding the href - SideNav.href (Routes.Category category) + SideNav.entry (Category.forDisplay category) + [ SideNav.href (Routes.Category category) ] navLinks : List (SideNav.Entry Route Msg) navLinks = - SideNav.entry "All" - Routes.All - [ SideNav.href Routes.All - ] + SideNav.entry "All" [ SideNav.href Routes.All ] :: List.map toNavLinkConfig Category.all ++ [ SideNav.entry "Example of Locked Premium content" - Routes.All [ SideNav.premiumLevel PremiumLevel.PremiumWithWriting NoOp ] , SideNav.entry "Create your own" - -- TODO: support _no_ route - Routes.All [ SideNav.icon UiIcon.gear , SideNav.secondary From 5b086b5d9ebd1600645547493f35fef1d55ecafb Mon Sep 17 00:00:00 2001 From: Tessa Kelly Date: Fri, 3 Dec 2021 11:31:29 -0800 Subject: [PATCH 13/23] Support external links --- src/ClickableAttributes.elm | 35 ++++++++++++++++++++++++++--------- src/Nri/Ui/SideNav/V1.elm | 10 ++++------ styleguide-app/Main.elm | 3 +-- 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/src/ClickableAttributes.elm b/src/ClickableAttributes.elm index 3cc22907..988f3342 100644 --- a/src/ClickableAttributes.elm +++ b/src/ClickableAttributes.elm @@ -26,6 +26,7 @@ import Nri.Ui.Html.Attributes.V2 as AttributeExtras exposing (targetBlank) type alias ClickableAttributes route msg = { linkType : Link , url : Maybe route + , urlString : Maybe String , onClick : Maybe msg } @@ -44,6 +45,7 @@ init : ClickableAttributes route msg init = { linkType = Default , url = Nothing + , urlString = Nothing , onClick = Nothing } @@ -74,20 +76,28 @@ linkWithMethod { method, url } clickableAttributes = {-| -} linkWithTracking : { track : msg, url : route } -> ClickableAttributes route msg -> ClickableAttributes route msg -linkWithTracking { track, url } _ = - { linkType = WithTracking, url = Just url, onClick = Just track } +linkWithTracking { track, url } clickableAttributes = + { clickableAttributes + | linkType = WithTracking + , url = Just url + , onClick = Just track + } {-| -} -linkExternal : route -> ClickableAttributes route msg -> ClickableAttributes route msg +linkExternal : String -> ClickableAttributes route msg -> ClickableAttributes route msg linkExternal url clickableAttributes = - { clickableAttributes | linkType = External, url = Just url } + { clickableAttributes | linkType = External, urlString = Just url } {-| -} -linkExternalWithTracking : { track : msg, url : route } -> ClickableAttributes route msg -> ClickableAttributes route msg -linkExternalWithTracking { track, url } _ = - { linkType = ExternalWithTracking, url = Just url, onClick = Just track } +linkExternalWithTracking : { track : msg, url : String } -> ClickableAttributes route msg -> ClickableAttributes route msg +linkExternalWithTracking { track, url } clickableAttributes = + { clickableAttributes + | linkType = ExternalWithTracking + , urlString = Just url + , onClick = Just track + } {-| -} @@ -106,8 +116,15 @@ toLinkAttributes : (route -> String) -> ClickableAttributes route msg -> ( Strin toLinkAttributes routeToString clickableAttributes = let stringUrl = - Maybe.map routeToString clickableAttributes.url - |> Maybe.withDefault "#" + case ( clickableAttributes.urlString, clickableAttributes.url ) of + ( Just url, _ ) -> + url + + ( _, Just route ) -> + routeToString route + + ( Nothing, Nothing ) -> + "#" in case clickableAttributes.linkType of Default -> diff --git a/src/Nri/Ui/SideNav/V1.elm b/src/Nri/Ui/SideNav/V1.elm index 58be5f06..903e4fd9 100644 --- a/src/Nri/Ui/SideNav/V1.elm +++ b/src/Nri/Ui/SideNav/V1.elm @@ -419,14 +419,12 @@ linkWithTracking config = {-| -} -linkExternal : route -> Attribute route msg +linkExternal : String -> Attribute route msg linkExternal url = - setClickableAttributes (Just url) - (ClickableAttributes.linkExternal url) + setClickableAttributes Nothing (ClickableAttributes.linkExternal url) {-| -} -linkExternalWithTracking : { track : msg, url : route } -> Attribute route msg +linkExternalWithTracking : { track : msg, url : String } -> Attribute route msg linkExternalWithTracking config = - setClickableAttributes (Just config.url) - (ClickableAttributes.linkExternalWithTracking config) + setClickableAttributes Nothing (ClickableAttributes.linkExternalWithTracking config) diff --git a/styleguide-app/Main.elm b/styleguide-app/Main.elm index 98abd2e4..cc4f0054 100644 --- a/styleguide-app/Main.elm +++ b/styleguide-app/Main.elm @@ -243,8 +243,7 @@ navigation currentRoute = , SideNav.entry "Create your own" [ SideNav.icon UiIcon.gear , SideNav.secondary - - --, SideNav.linkExternal "external-link" + , SideNav.linkExternal "external-link" ] ] in From a56a4a3b7573aeaed842937763473f0d528b266f Mon Sep 17 00:00:00 2001 From: Tessa Kelly Date: Fri, 3 Dec 2021 11:33:11 -0800 Subject: [PATCH 14/23] fix locked styles --- src/Nri/Ui/SideNav/V1.elm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Nri/Ui/SideNav/V1.elm b/src/Nri/Ui/SideNav/V1.elm index 903e4fd9..c979e579 100644 --- a/src/Nri/Ui/SideNav/V1.elm +++ b/src/Nri/Ui/SideNav/V1.elm @@ -238,6 +238,8 @@ sharedEntryStyles = , fontSize (px 15) , fontWeight (int 600) , marginBottom (px 10) + , textAlign left + , cursor pointer ] From 434ba3cc91afef31efa6d51fd8ddd2e851d15f6c Mon Sep 17 00:00:00 2001 From: Tessa Kelly Date: Fri, 3 Dec 2021 11:37:54 -0800 Subject: [PATCH 15/23] Attach locked event --- src/Nri/Ui/SideNav/V1.elm | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/Nri/Ui/SideNav/V1.elm b/src/Nri/Ui/SideNav/V1.elm index c979e579..eef638e4 100644 --- a/src/Nri/Ui/SideNav/V1.elm +++ b/src/Nri/Ui/SideNav/V1.elm @@ -142,7 +142,7 @@ viewSidebarEntry config extraStyles (Entry entry_) = viewSidebarLeaf config extraStyles entry_ else - viewLockedEntry entry_.title extraStyles + viewLockedEntry extraStyles entry_ isCurrentRoute : Config route msg -> EntryConfig route msg -> Bool @@ -197,29 +197,27 @@ viewSidebarLeaf config extraStyles entryConfig = ] -viewLockedEntry : String -> List Style -> Html msg -viewLockedEntry title extraStyles = - let - lockedEntryId = - -- TODO: pass in ids - "browse-and-assign-locked-entry__" ++ dasherize (toLower title) - in +viewLockedEntry : List Style -> EntryConfig route msg -> Html msg +viewLockedEntry extraStyles entryConfig = styled Html.Styled.button [ batch sharedEntryStyles , important (color Colors.gray45) , borderWidth zero , batch extraStyles ] - [ -- TODO: reimplement lock click behavior! - --Events.onClick (launchPremiumModal lockedEntryId) , - Attributes.id lockedEntryId - ] + (case entryConfig.onLockedContent of + Just event -> + Events.onClick event :: entryConfig.customAttributes + + Nothing -> + entryConfig.customAttributes + ) [ UiIcon.premiumLock |> Svg.withWidth (px 17) |> Svg.withHeight (px 25) |> Svg.withCss [ marginRight (px 10), minWidth (px 17) ] |> Svg.toHtml - , text title + , text entryConfig.title ] @@ -257,6 +255,7 @@ type alias EntryConfig route msg = , customStyles : List Style , children : List (Entry route msg) , premiumLevel : PremiumLevel + , onLockedContent : Maybe msg } @@ -270,6 +269,7 @@ build title = , customStyles = [] , children = [] , premiumLevel = PremiumLevel.Free + , onLockedContent = Nothing } @@ -286,8 +286,13 @@ icon icon_ = {-| -} premiumLevel : PremiumLevel -> msg -> Attribute route msg premiumLevel level ifLocked = - -- TODO: adds the lock click behavior - Attribute (\attributes -> { attributes | premiumLevel = level }) + Attribute + (\attributes -> + { attributes + | premiumLevel = level + , onLockedContent = Just ifLocked + } + ) {-| Use this helper to add custom attributes. From a31da1772e4e90dacafa3da8b2e4d1106bc107ea Mon Sep 17 00:00:00 2001 From: Tessa Kelly Date: Fri, 3 Dec 2021 11:40:57 -0800 Subject: [PATCH 16/23] Restrict page width --- styleguide-app/Main.elm | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/styleguide-app/Main.elm b/styleguide-app/Main.elm index cc4f0054..2e85ee0c 100644 --- a/styleguide-app/Main.elm +++ b/styleguide-app/Main.elm @@ -148,7 +148,12 @@ view_ model = Routes.Doodad doodad -> case List.head (examples (\m -> m.name == doodad)) of Just example -> - Html.main_ [] + Html.main_ + [ css + [ maxWidth (Css.px 1400) + , margin auto + ] + ] [ Example.view model.previousRoute example |> Html.map (UpdateModuleStates example.name) ] @@ -185,6 +190,8 @@ withSideNav currentRoute content = [ displayFlex , withMedia [ mobile ] [ flexDirection column, alignItems stretch ] , alignItems flexStart + , maxWidth (Css.px 1400) + , margin auto ] ] [ navigation currentRoute From c569498f1989610209e6219578d9a776d5ec89ec Mon Sep 17 00:00:00 2001 From: Tessa Kelly Date: Fri, 3 Dec 2021 11:46:22 -0800 Subject: [PATCH 17/23] Expose SideNav --- elm.json | 1 + tests/elm-verify-examples.json | 1 + 2 files changed, 2 insertions(+) diff --git a/elm.json b/elm.json index 02fbbb71..1bfbe3f4 100644 --- a/elm.json +++ b/elm.json @@ -47,6 +47,7 @@ "Nri.Ui.RadioButton.V3", "Nri.Ui.SegmentedControl.V14", "Nri.Ui.Select.V8", + "Nri.Ui.SideNav.V1", "Nri.Ui.Slide.V1", "Nri.Ui.SlideModal.V2", "Nri.Ui.SortableTable.V2", diff --git a/tests/elm-verify-examples.json b/tests/elm-verify-examples.json index 27b74fe3..c4fa36d3 100644 --- a/tests/elm-verify-examples.json +++ b/tests/elm-verify-examples.json @@ -43,6 +43,7 @@ "Nri.Ui.RadioButton.V3", "Nri.Ui.SegmentedControl.V14", "Nri.Ui.Select.V8", + "Nri.Ui.SideNav.V1", "Nri.Ui.Slide.V1", "Nri.Ui.SlideModal.V2", "Nri.Ui.SortableTable.V2", From a88dbae84cc8a4c7efd96792c7ef365a8b9053ae Mon Sep 17 00:00:00 2001 From: Tessa Kelly Date: Fri, 3 Dec 2021 12:03:43 -0800 Subject: [PATCH 18/23] Allow custom html content a future version of the sidebar should probably remove this, but for a first version, i think this will help to get all the views using the same base component --- src/Nri/Ui/SideNav/V1.elm | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/Nri/Ui/SideNav/V1.elm b/src/Nri/Ui/SideNav/V1.elm index eef638e4..bde31bcc 100644 --- a/src/Nri/Ui/SideNav/V1.elm +++ b/src/Nri/Ui/SideNav/V1.elm @@ -1,6 +1,6 @@ module Nri.Ui.SideNav.V1 exposing ( view, Config - , entry, Entry + , entry, html, Entry , icon, custom, css, nriDescription, testId, id , onClick , href, linkSpa, linkExternal, linkWithMethod, linkWithTracking, linkExternalWithTracking @@ -11,7 +11,7 @@ module Nri.Ui.SideNav.V1 exposing {-| @docs view, Config -@docs entry, Entry +@docs entry, html, Entry @docs icon, custom, css, nriDescription, testId, id @@ -56,6 +56,7 @@ import String.Extra exposing (dasherize) -} type Entry route msg = Entry (EntryConfig route msg) + | Html (List (Html msg)) {-| -} @@ -66,6 +67,12 @@ entry title attributes = |> Entry +{-| -} +html : List (Html msg) -> Entry route msg +html = + Html + + {-| -} type alias Config route msg = { userPremiumLevel : PremiumLevel @@ -119,7 +126,17 @@ viewSkipLink onSkip = viewSidebarEntry : Config route msg -> List Css.Style -> Entry route msg -> Html msg -viewSidebarEntry config extraStyles (Entry entry_) = +viewSidebarEntry config extraStyles entry_ = + case entry_ of + Entry entryConfig -> + viewSidebarEntry_ config extraStyles entryConfig + + Html html_ -> + div [ Attributes.css extraStyles ] html_ + + +viewSidebarEntry_ : Config route msg -> List Css.Style -> EntryConfig route msg -> Html msg +viewSidebarEntry_ config extraStyles entry_ = if PremiumLevel.allowedFor entry_.premiumLevel config.userPremiumLevel then if anyLinkDescendants (isCurrentRoute config) entry_ then div [ Attributes.css extraStyles ] @@ -153,7 +170,16 @@ isCurrentRoute config { route } = anyLinkDescendants : (EntryConfig route msg -> Bool) -> EntryConfig route msg -> Bool anyLinkDescendants f { children } = - List.any (\(Entry entry_) -> f entry_ || anyLinkDescendants f entry_) children + List.any + (\entry_ -> + case entry_ of + Entry entryConfig -> + f entryConfig || anyLinkDescendants f entryConfig + + Html _ -> + False + ) + children viewSidebarLeaf : From 66245759b1077b1eaa65298a84581be38d7f28fc Mon Sep 17 00:00:00 2001 From: Tessa Kelly Date: Fri, 3 Dec 2021 12:04:21 -0800 Subject: [PATCH 19/23] :skull: remove locked and create your own content --- styleguide-app/Main.elm | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/styleguide-app/Main.elm b/styleguide-app/Main.elm index 2e85ee0c..e1575bfd 100644 --- a/styleguide-app/Main.elm +++ b/styleguide-app/Main.elm @@ -243,16 +243,6 @@ navigation currentRoute = navLinks = SideNav.entry "All" [ SideNav.href Routes.All ] :: List.map toNavLinkConfig Category.all - ++ [ SideNav.entry "Example of Locked Premium content" - [ SideNav.premiumLevel PremiumLevel.PremiumWithWriting - NoOp - ] - , SideNav.entry "Create your own" - [ SideNav.icon UiIcon.gear - , SideNav.secondary - , SideNav.linkExternal "external-link" - ] - ] in SideNav.view { userPremiumLevel = PremiumLevel.Free From 27261307d301003491bc4e23c58161cf8f382db3 Mon Sep 17 00:00:00 2001 From: Tessa Kelly Date: Fri, 3 Dec 2021 12:10:49 -0800 Subject: [PATCH 20/23] Fix mobile styles --- styleguide-app/Main.elm | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/styleguide-app/Main.elm b/styleguide-app/Main.elm index e1575bfd..beff8a60 100644 --- a/styleguide-app/Main.elm +++ b/styleguide-app/Main.elm @@ -233,16 +233,26 @@ viewPreviews containerId examples = navigation : Route -> Html Msg navigation currentRoute = let - toNavLinkConfig : Category -> SideNav.Entry Route Msg - toNavLinkConfig category = - SideNav.entry (Category.forDisplay category) - [ SideNav.href (Routes.Category category) + toNavLinkConfig : String -> Route -> SideNav.Entry Route Msg + toNavLinkConfig name route = + SideNav.entry name + [ SideNav.href route + , SideNav.css + [ withMedia [ mobile ] + [ Css.marginBottom Css.zero + ] + ] ] navLinks : List (SideNav.Entry Route Msg) navLinks = - SideNav.entry "All" [ SideNav.href Routes.All ] - :: List.map toNavLinkConfig Category.all + toNavLinkConfig "All" Routes.All + :: List.map + (\category -> + toNavLinkConfig (Category.forDisplay category) + (Routes.Category category) + ) + Category.all in SideNav.view { userPremiumLevel = PremiumLevel.Free @@ -254,6 +264,10 @@ navigation currentRoute = [ VendorPrefixed.value "position" "sticky" , top (px 55) ] + , withMedia [ mobile ] + [ Css.padding Css.zero + , Css.margin Css.zero + ] ] } navLinks From 7c10606ee8550c0de82dd16e67d5996030bce05d Mon Sep 17 00:00:00 2001 From: Tessa Kelly Date: Fri, 3 Dec 2021 13:11:02 -0800 Subject: [PATCH 21/23] Style feedback --- src/Nri/Ui/SideNav/V1.elm | 2 -- styleguide-app/Main.elm | 13 +------------ 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/Nri/Ui/SideNav/V1.elm b/src/Nri/Ui/SideNav/V1.elm index bde31bcc..81c7678e 100644 --- a/src/Nri/Ui/SideNav/V1.elm +++ b/src/Nri/Ui/SideNav/V1.elm @@ -143,7 +143,6 @@ viewSidebarEntry_ config extraStyles entry_ = (styled span (sharedEntryStyles ++ [ backgroundColor Colors.gray92 - , marginBottom (px 10) , color Colors.navy , fontWeight bold , cursor default @@ -261,7 +260,6 @@ sharedEntryStyles = , textDecoration none , fontSize (px 15) , fontWeight (int 600) - , marginBottom (px 10) , textAlign left , cursor pointer ] diff --git a/styleguide-app/Main.elm b/styleguide-app/Main.elm index beff8a60..cd7a3e92 100644 --- a/styleguide-app/Main.elm +++ b/styleguide-app/Main.elm @@ -235,14 +235,7 @@ navigation currentRoute = let toNavLinkConfig : String -> Route -> SideNav.Entry Route Msg toNavLinkConfig name route = - SideNav.entry name - [ SideNav.href route - , SideNav.css - [ withMedia [ mobile ] - [ Css.marginBottom Css.zero - ] - ] - ] + SideNav.entry name [ SideNav.href route ] navLinks : List (SideNav.Entry Route Msg) navLinks = @@ -264,10 +257,6 @@ navigation currentRoute = [ VendorPrefixed.value "position" "sticky" , top (px 55) ] - , withMedia [ mobile ] - [ Css.padding Css.zero - , Css.margin Css.zero - ] ] } navLinks From 79957b0c8719e5a8c1a46ba56c7aa209da7f141f Mon Sep 17 00:00:00 2001 From: Tessa Kelly Date: Fri, 3 Dec 2021 13:28:04 -0800 Subject: [PATCH 22/23] Expose a helper for entry with children --- src/Nri/Ui/SideNav/V1.elm | 77 ++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/src/Nri/Ui/SideNav/V1.elm b/src/Nri/Ui/SideNav/V1.elm index 81c7678e..bd7f0884 100644 --- a/src/Nri/Ui/SideNav/V1.elm +++ b/src/Nri/Ui/SideNav/V1.elm @@ -1,6 +1,6 @@ module Nri.Ui.SideNav.V1 exposing ( view, Config - , entry, html, Entry + , entry, entryWithChildren, html, Entry , icon, custom, css, nriDescription, testId, id , onClick , href, linkSpa, linkExternal, linkWithMethod, linkWithTracking, linkExternalWithTracking @@ -11,7 +11,7 @@ module Nri.Ui.SideNav.V1 exposing {-| @docs view, Config -@docs entry, html, Entry +@docs entry, entryWithChildren, html, Entry @docs icon, custom, css, nriDescription, testId, id @@ -55,7 +55,7 @@ import String.Extra exposing (dasherize) {-| Use `entry` to create a sidebar entry. -} type Entry route msg - = Entry (EntryConfig route msg) + = Entry (List (Entry route msg)) (EntryConfig route msg) | Html (List (Html msg)) @@ -64,7 +64,15 @@ entry : String -> List (Attribute route msg) -> Entry route msg entry title attributes = attributes |> List.foldl (\(Attribute attribute) b -> attribute b) (build title) - |> Entry + |> Entry [] + + +{-| -} +entryWithChildren : String -> List (Attribute route msg) -> List (Entry route msg) -> Entry route msg +entryWithChildren title attributes children = + attributes + |> List.foldl (\(Attribute attribute) b -> attribute b) (build title) + |> Entry children {-| -} @@ -128,52 +136,47 @@ viewSkipLink onSkip = viewSidebarEntry : Config route msg -> List Css.Style -> Entry route msg -> Html msg viewSidebarEntry config extraStyles entry_ = case entry_ of - Entry entryConfig -> - viewSidebarEntry_ config extraStyles entryConfig + Entry children entryConfig -> + if PremiumLevel.allowedFor entryConfig.premiumLevel config.userPremiumLevel then + if anyLinkDescendants (isCurrentRoute config) children then + div [ Attributes.css extraStyles ] + (styled span + (sharedEntryStyles + ++ [ backgroundColor Colors.gray92 + , color Colors.navy + , fontWeight bold + , cursor default + , marginBottom (px 10) + ] + ) + [] + [ text entryConfig.title ] + :: List.map (viewSidebarEntry config [ marginLeft (px 20) ]) children + ) + + else + viewSidebarLeaf config extraStyles entryConfig + + else + viewLockedEntry extraStyles entryConfig Html html_ -> div [ Attributes.css extraStyles ] html_ -viewSidebarEntry_ : Config route msg -> List Css.Style -> EntryConfig route msg -> Html msg -viewSidebarEntry_ config extraStyles entry_ = - if PremiumLevel.allowedFor entry_.premiumLevel config.userPremiumLevel then - if anyLinkDescendants (isCurrentRoute config) entry_ then - div [ Attributes.css extraStyles ] - (styled span - (sharedEntryStyles - ++ [ backgroundColor Colors.gray92 - , color Colors.navy - , fontWeight bold - , cursor default - ] - ) - [] - [ text entry_.title ] - :: List.map (viewSidebarEntry config [ marginLeft (px 20) ]) - entry_.children - ) - - else - viewSidebarLeaf config extraStyles entry_ - - else - viewLockedEntry extraStyles entry_ - - isCurrentRoute : Config route msg -> EntryConfig route msg -> Bool isCurrentRoute config { route } = Maybe.map config.isCurrentRoute route |> Maybe.withDefault False -anyLinkDescendants : (EntryConfig route msg -> Bool) -> EntryConfig route msg -> Bool -anyLinkDescendants f { children } = +anyLinkDescendants : (EntryConfig route msg -> Bool) -> List (Entry route msg) -> Bool +anyLinkDescendants f children = List.any (\entry_ -> case entry_ of - Entry entryConfig -> - f entryConfig || anyLinkDescendants f entryConfig + Entry children_ entryConfig -> + f entryConfig || anyLinkDescendants f children_ Html _ -> False @@ -277,7 +280,6 @@ type alias EntryConfig route msg = , clickableAttributes : ClickableAttributes route msg , customAttributes : List (Html.Styled.Attribute msg) , customStyles : List Style - , children : List (Entry route msg) , premiumLevel : PremiumLevel , onLockedContent : Maybe msg } @@ -291,7 +293,6 @@ build title = , clickableAttributes = ClickableAttributes.init , customAttributes = [] , customStyles = [] - , children = [] , premiumLevel = PremiumLevel.Free , onLockedContent = Nothing } From ca55e1da5efeeb79b5bfc1a74f0dc49e45732a84 Mon Sep 17 00:00:00 2001 From: Tessa Kelly Date: Fri, 3 Dec 2021 13:34:32 -0800 Subject: [PATCH 23/23] :art: cleanup nav setup --- styleguide-app/Main.elm | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/styleguide-app/Main.elm b/styleguide-app/Main.elm index cd7a3e92..f5f1a297 100644 --- a/styleguide-app/Main.elm +++ b/styleguide-app/Main.elm @@ -233,19 +233,14 @@ viewPreviews containerId examples = navigation : Route -> Html Msg navigation currentRoute = let - toNavLinkConfig : String -> Route -> SideNav.Entry Route Msg - toNavLinkConfig name route = - SideNav.entry name [ SideNav.href route ] - - navLinks : List (SideNav.Entry Route Msg) - navLinks = - toNavLinkConfig "All" Routes.All - :: List.map - (\category -> - toNavLinkConfig (Category.forDisplay category) - (Routes.Category category) - ) - Category.all + categoryNavLinks : List (SideNav.Entry Route Msg) + categoryNavLinks = + List.map + (\category -> + SideNav.entry (Category.forDisplay category) + [ SideNav.href (Routes.Category category) ] + ) + Category.all in SideNav.view { userPremiumLevel = PremiumLevel.Free @@ -259,4 +254,6 @@ navigation currentRoute = ] ] } - navLinks + (SideNav.entry "All" [ SideNav.href Routes.All ] + :: categoryNavLinks + )