Merge pull request #1514 from NoRedInk/tessa/outline

🌟 Extracting Outline from the monolith
This commit is contained in:
Tessa 2023-09-12 14:40:52 -06:00 committed by GitHub
commit 2dbbb04512
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 1211 additions and 0 deletions

View File

@ -28,6 +28,7 @@ import Examples.Logo as Logo
import Examples.Menu as Menu
import Examples.Message as Message
import Examples.Modal as Modal
import Examples.Outline as Outline
import Examples.Page as Page
import Examples.Pagination as Pagination
import Examples.Panel as Panel
@ -567,6 +568,25 @@ all =
ModalState childState ->
Just childState
_ ->
Nothing
)
, Outline.example
|> Example.wrapMsg OutlineMsg
(\msg ->
case msg of
OutlineMsg childMsg ->
Just childMsg
_ ->
Nothing
)
|> Example.wrapState OutlineState
(\msg ->
case msg of
OutlineState childState ->
Just childState
_ ->
Nothing
)
@ -1076,6 +1096,7 @@ type State
| MenuState Menu.State
| MessageState Message.State
| ModalState Modal.State
| OutlineState Outline.State
| PageState Page.State
| PaginationState Pagination.State
| PanelState Panel.State
@ -1132,6 +1153,7 @@ type Msg
| TabsMinimalMsg TabsMinimal.Msg
| MessageMsg Message.Msg
| ModalMsg Modal.Msg
| OutlineMsg Outline.Msg
| PageMsg Page.Msg
| PaginationMsg Pagination.Msg
| PanelMsg Panel.Msg

View File

@ -0,0 +1,490 @@
module Examples.Outline exposing (example, State, Msg)
{-|
@docs example, State, Msg
-}
import Category exposing (Category(..))
import Code
import CommonControls
import Css exposing (Color)
import Css.Media exposing (withMedia)
import Debug.Control as Control exposing (Control)
import Debug.Control.View as ControlView
import Example exposing (Example)
import Html.Styled exposing (..)
import Html.Styled.Attributes exposing (css)
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Heading.V3 as Heading
import Nri.Ui.MediaQuery.V1 exposing (mobile)
import Nri.Ui.Outline.V1 as Outline exposing (KeyedOutline, Outline, RowTheme)
import Nri.Ui.Spacing.V1 as Spacing
import Svg.Styled as Svg
import Svg.Styled.Attributes as SvgAttrs
moduleName : String
moduleName =
"Outline"
version : Int
version =
1
{-| -}
example : Example State Msg
example =
{ name = moduleName
, version = version
, categories = [ Layout, Instructional ]
, keyboardSupport = []
, state = init
, update = update
, subscriptions = \_ -> Sub.none
, preview = [ preview ]
, about = []
, view =
\ellieLinkConfig state ->
let
settings =
Control.currentValue state.control
in
[ ControlView.view
{ ellieLinkConfig = ellieLinkConfig
, name = moduleName
, version = version
, update = UpdateControl
, settings = state.control
, mainType = Just "RootHtml.Html msg"
, extraCode = []
, renderExample = Code.unstyledView
, toExampleCode =
\_ ->
[ { sectionName = "Customizable Example"
, code =
case settings.type_ of
Plain ->
Code.fromModule moduleName "view"
++ Code.listMultiline
[ Code.fromModule moduleName "row "
++ Code.recordMultiline
[ ( "title", Code.maybe (Maybe.map Code.string settings.title) )
, ( "content", "text " ++ Code.string settings.content )
, ( "palette", Tuple.first settings.palette )
, ( "rows", Code.listMultiline [ "-- " ] 3 )
]
2
]
1
Keyed ->
Code.fromModule moduleName "viewKeyed"
++ Code.listMultiline
[ Code.fromModule moduleName "keyedRow "
++ Code.string "base-node"
++ Code.recordMultiline
[ ( "title", Code.maybe (Maybe.map Code.string settings.title) )
, ( "content", "text " ++ Code.string settings.content )
, ( "palette", Tuple.first settings.palette )
, ( "rows", Code.listMultiline [ "-- " ] 3 )
]
2
]
1
KeyedWithExtraContent extraContent ->
Code.fromModule moduleName "viewKeyed"
++ Code.listMultiline
[ Code.fromModule moduleName "keyedRowWithExtraContent "
++ Code.string "base-node"
++ Code.recordMultiline
[ ( "title", Code.maybe (Maybe.map Code.string settings.title) )
, ( "content", "text " ++ Code.string settings.content )
, ( "palette", Tuple.first settings.palette )
, ( "rows", Code.listMultiline [ "-- " ] 3 )
, ( "extraContent"
, "text " ++ Code.string "Extra content"
)
]
2
]
1
}
]
}
, Heading.h2
[ Heading.plaintext "Customizable Example"
, Heading.css [ Css.marginTop Spacing.verticalSpacerPx ]
]
, case settings.type_ of
Plain ->
Outline.view
[ Outline.row
{ title = settings.title
, content = text settings.content
, palette = Tuple.second settings.palette
, rows = plainRows
}
]
Keyed ->
Outline.viewKeyed
[ Outline.keyedRow "base-node"
{ title = settings.title
, content = text settings.content
, palette = Tuple.second settings.palette
, rows = keyedRows
}
]
KeyedWithExtraContent extraContent ->
Outline.viewKeyed
[ Outline.keyedRowWithExtraContent "base-node"
{ title = settings.title
, content = text settings.content
, palette = Tuple.second settings.palette
, rows = keyedRows
, extraContent =
pre [ css [ Css.margin Css.zero ] ]
[ text extraContent ]
}
]
, Heading.h2
[ Heading.plaintext "Row Themes"
, Heading.css [ Css.margin2 Spacing.verticalSpacerPx Css.zero ]
]
, div
[ css
[ Css.displayFlex
, Css.property "gap" "20px"
, withMedia [ mobile ] [ Css.flexWrap Css.wrap ]
]
]
[ Outline.view
[ Outline.row
{ title = Just "Outline.view"
, content = text "Regular outlines support custom row themes (like this node's theme) as well as predefined themes."
, palette =
{ border = Colors.azure
, borderStyle = Css.batch []
, background = Colors.gray96
}
, rows =
List.map
(\( themeName, theme ) ->
Outline.row
{ title = Just themeName
, content = text ""
, palette = theme
, rows = []
}
)
allRowThemes
}
]
, Outline.viewKeyed
[ Outline.keyedRow "base"
{ title = Just "Outline.viewKeyed"
, content = text "Keyed outlines support custom row themes (like this node's theme) as well as predefined themes."
, palette =
{ border = Colors.azure
, borderStyle = Css.batch []
, background = Colors.gray96
}
, rows =
List.map
(\( themeName, theme ) ->
Outline.keyedRow ("row-" ++ themeName)
{ title = Just themeName
, content = text ""
, palette = theme
, rows = []
}
)
allRowThemes
}
]
]
]
}
plainRows : List (Outline msg)
plainRows =
[ Outline.row
{ title = Just "Node 2"
, content = text ""
, palette = Outline.cornflower
, rows = []
}
, Outline.row
{ title = Just "Node 3"
, content = text ""
, palette = Outline.cornflower
, rows = []
}
]
keyedRows : List (KeyedOutline msg)
keyedRows =
[ Outline.keyedRow "node-2"
{ title = Just "Node 2"
, content = text ""
, palette = Outline.cornflower
, rows = []
}
, Outline.keyedRow "node-3"
{ title = Just "Node 3"
, content = text ""
, palette = Outline.cornflower
, rows = []
}
]
preview : Html msg
preview =
Svg.svg
[ SvgAttrs.viewBox "5 0 90 90"
, SvgAttrs.width "100%"
, SvgAttrs.height "100%"
]
[ -- Connecting lines
Svg.path
[ SvgAttrs.d "M14 41, 14 58 Q14 60 16 60 L 20 60"
, SvgAttrs.stroke Colors.purple.value
, SvgAttrs.fill "none"
]
[]
, Svg.path
[ SvgAttrs.d "M14 29, 14 40 Q14 42 16 42 L 20 42"
, SvgAttrs.stroke Colors.greenDarkest.value
, SvgAttrs.fill "none"
]
[]
-- Azure node
, -- white box
Svg.rect
[ SvgAttrs.x "10"
, SvgAttrs.y "13"
, SvgAttrs.width "80"
, SvgAttrs.height "16"
, SvgAttrs.rx "2"
, SvgAttrs.fill Colors.white.value
, SvgAttrs.stroke Colors.azure.value
, SvgAttrs.strokeWidth "0.5"
]
[]
, -- azure label
Svg.rect
[ SvgAttrs.x "5"
, SvgAttrs.y "10"
, SvgAttrs.width "16"
, SvgAttrs.height "6"
, SvgAttrs.rx "4"
, SvgAttrs.fill Colors.azure.value
]
[]
-- Green node
, -- white box
Svg.rect
[ SvgAttrs.x "20"
, SvgAttrs.y "35"
, SvgAttrs.width "70"
, SvgAttrs.height "12"
, SvgAttrs.rx "2"
, SvgAttrs.fill Colors.white.value
, SvgAttrs.stroke Colors.greenDarkest.value
, SvgAttrs.strokeWidth "0.5"
]
[]
, -- green label
Svg.rect
[ SvgAttrs.x "16"
, SvgAttrs.y "32"
, SvgAttrs.width "14"
, SvgAttrs.height "6"
, SvgAttrs.rx "4"
, SvgAttrs.fill Colors.greenDarkest.value
]
[]
-- Purple node
, -- white box
Svg.rect
[ SvgAttrs.x "20"
, SvgAttrs.y "54"
, SvgAttrs.width "70"
, SvgAttrs.height "12"
, SvgAttrs.rx "2"
, SvgAttrs.fill Colors.white.value
, SvgAttrs.stroke Colors.purple.value
, SvgAttrs.strokeWidth "0.5"
]
[]
, -- purple label
Svg.rect
[ SvgAttrs.x "16"
, SvgAttrs.y "50"
, SvgAttrs.width "17"
, SvgAttrs.height "6"
, SvgAttrs.rx "4"
, SvgAttrs.fill Colors.purple.value
]
[]
]
allRowThemes : List ( String, RowTheme )
allRowThemes =
[ ( "purpleBordered", Outline.purpleBordered )
, ( "greenBordered", Outline.greenBordered )
, ( "blueDashBordered", Outline.blueDashBordered )
, ( "red", Outline.red )
, ( "green", Outline.green )
, ( "aqua", Outline.aqua )
, ( "turquoise", Outline.turquoise )
, ( "cornflower", Outline.cornflower )
, ( "blue", Outline.blue )
, ( "darkBlue", Outline.darkBlue )
, ( "purple", Outline.purple )
, ( "darkGray", Outline.darkGray )
, ( "gray", Outline.gray )
, ( "white", Outline.white )
]
borderColorList : List ( String, Color )
borderColorList =
[ ( "azure", Colors.azure )
, ( "cornflower", Colors.cornflower )
, ( "gray45", Colors.gray45 )
, ( "gray75", Colors.gray75 )
, ( "green", Colors.green )
, ( "navy", Colors.navy )
, ( "purple", Colors.purple )
, ( "red", Colors.red )
, ( "turquoise", Colors.turquoise )
]
backgroundColorList : List ( String, Color )
backgroundColorList =
[ ( "gray96", Colors.gray96 )
, ( "aquaLight", Colors.aquaLight )
, ( "cornflowerLight", Colors.cornflowerLight )
, ( "frost", Colors.frost )
, ( "greenLightest", Colors.greenLightest )
, ( "purpleLight", Colors.purpleLight )
, ( "redLight", Colors.redLight )
, ( "turquoiseLight", Colors.turquoiseLight )
, ( "white", Colors.white )
]
{-| -}
type alias State =
{ control : Control Settings
}
type alias Settings =
{ title : Maybe String
, content : String
, palette : ( String, RowTheme )
, type_ : RowType
}
init : State
init =
{ control =
Control.record Settings
|> Control.field "title" (Control.maybe True (Control.string "Title"))
|> Control.field "content" (Control.string "")
|> Control.field "palette"
(Control.choice
(List.map
(\( name, value ) ->
( name, Control.value ( Code.fromModule moduleName "." ++ name, value ) )
)
allRowThemes
++ [ ( "custom", customRowTheme ) ]
)
)
|> Control.field "type"
(Control.choice
[ ( "plain", Control.value Plain )
, ( "keyed", Control.value Keyed )
, ( "keyed with extra content"
, [ "Extra content!"
, "This content requires the height of the connecting arrow to increase. Do NOT use vertical margin on this element."
, "Extra content is used for selecting which drafts to compare on the results views for Topic Sentence Peer Review results."
, "Check it out!"
]
|> String.join "\n\n"
|> Control.stringTextarea
|> Control.map KeyedWithExtraContent
)
]
)
}
type RowType
= Plain
| Keyed
| KeyedWithExtraContent String
customRowTheme : Control ( String, RowTheme )
customRowTheme =
Control.record
(\( a1, a2 ) ( b1, b2 ) ( c1, c2 ) ->
( Code.recordMultiline
[ ( "border", a1 )
, ( "borderStyle", b1 )
, ( "background", c1 )
]
3
, RowTheme a2 b2 c2
)
)
|> Control.field "border" (CommonControls.choice "Colors" borderColorList)
|> Control.field "borderStyle"
(Control.choice
[ ( "none", Control.value ( "Css.batch []", Css.batch [] ) )
, ( "1px solid"
, Control.value
( "Css.batch [ Css.borderWidth (Css.px 1), Css.borderStyle Css.solid ]"
, Css.batch [ Css.borderWidth (Css.px 1), Css.borderStyle Css.solid ]
)
)
, ( "1px dashed"
, Control.value
( "Css.batch [ Css.borderWidth (Css.px 1), Css.borderStyle Css.dashed ]"
, Css.batch [ Css.borderWidth (Css.px 1), Css.borderStyle Css.dashed ]
)
)
]
)
|> Control.field "background" (CommonControls.choice "Colors" backgroundColorList)
{-| -}
type Msg
= UpdateControl (Control Settings)
update : Msg -> State -> ( State, Cmd Msg )
update msg state =
case msg of
UpdateControl settings ->
( { state | control = settings }, Cmd.none )

View File

@ -59,6 +59,7 @@
"Nri.Ui.Message.V4",
"Nri.Ui.Modal.V11",
"Nri.Ui.Modal.V12",
"Nri.Ui.Outline.V1",
"Nri.Ui.Page.V3",
"Nri.Ui.Pagination.V1",
"Nri.Ui.Palette.V1",

View File

@ -232,6 +232,7 @@ describe("UI tests", function () {
const skippedRules = {
// Loading's color contrast check seems to change behavior depending on whether Percy snapshots are taken or not
Loading: ["color-contrast"],
Outline: ["color-contrast"],
RadioButton: ["duplicate-id"],
};

633
src/Nri/Ui/Outline/V1.elm Normal file
View File

@ -0,0 +1,633 @@
module Nri.Ui.Outline.V1 exposing
( Outline, view, row
, KeyedOutline, viewKeyed, keyedRow, keyedRowWithExtraContent
, RowTheme
, white, gray, darkGray, blue, darkBlue, purple, turquoise, green, red, aqua, cornflower
, blueDashBordered
, purpleBordered, greenBordered
)
{-| A nestable layout that can be themed.
@docs Outline, view, row
When you're adding or removing elements, use KeyedOutline and corresponding helpers:
@docs KeyedOutline, viewKeyed, keyedRow, keyedRowWithExtraContent
## Predefined color palettes for use with Outlines and KeyedOutlines.
@docs RowTheme
@docs white, gray, darkGray, blue, darkBlue, purple, turquoise, green, red, aqua, cornflower
@docs blueDashBordered
@docs purpleBordered, greenBordered
-}
import Css exposing (..)
import Html.Styled exposing (..)
import Html.Styled.Attributes exposing (css)
import Html.Styled.Keyed
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Fonts.V1 as Fonts
import Nri.Ui.Html.Attributes.V2 as Attributes
import Nri.Ui.Html.V3 exposing (viewJust)
{-| -}
type Outline msg
= Outline
{ rows : List (Outline msg)
, title : Maybe String
, content : Html msg
, palette : RowTheme
}
{-|
import Html.Styled exposing (..)
import Nri.Ui.Outline.V1 as Outline
main : Html msg
main =
Outline.view []
-}
view : List (Outline msg) -> Html msg
view rows =
case rows of
[] ->
text ""
_ ->
view_ rows
view_ : List (Outline msg) -> Html msg
view_ rows =
Html.Styled.ul
[ Html.Styled.Attributes.css
[ Css.listStyle Css.none
, Css.margin4 (Css.px 10) Css.zero Css.zero Css.zero
, Css.padding Css.zero
]
]
(viewRows Root rows)
viewRows : Hierarchy -> List (Outline msg) -> List (Html msg)
viewRows hierarchy rows =
let
orderedNodeColors =
(List.map (\(Outline { palette }) -> Just palette.border) rows ++ [ Nothing ])
|> List.drop 1
in
List.map2 (viewRow hierarchy) orderedNodeColors rows
viewRow : Hierarchy -> Maybe Css.Color -> Outline msg -> Html msg
viewRow hierarchy nextNodeColor (Outline config) =
utilViewRow hierarchy
nextNodeColor
{ rows = config.rows
, title = config.title
, content = config.content
, palette = config.palette
, extraContent = Nothing
}
(\(Outline { palette }) -> palette.border)
(Html.Styled.ul
[ css
[ Css.marginLeft (Css.px 25)
, Css.paddingLeft (Css.px 25)
, Css.paddingTop (Css.px 25)
, Css.listStyleType Css.none
]
]
(viewRows Child config.rows)
)
{-|
import Html.Styled exposing (..)
import Nri.Ui.Outline.V1 as Outline exposing (Outline)
myRow : Outline msg
myRow =
Outline.row
{ title = Just "My node"
, content = text "This is my content"
, palette = RowTheme.red
, rows = []
}
-}
row :
{ title : Maybe String
, content : Html msg
, palette : RowTheme
, rows : List (Outline msg)
}
-> Outline msg
row config =
Outline config
-- KEYED OUTLINE
{-| Aliased strictly for exporting
-}
type KeyedOutline msg
= KeyedOutline
String
{ extraContent : Maybe (Html msg)
, rows : List (KeyedOutline msg)
, title : Maybe String
, content : Html msg
, palette : RowTheme
}
{-| The row view.
import Html.Styled exposing (..)
import Nri.Ui.Outline.V1 as Outline
main : Html msg
main =
Outline.viewKeyed [{- Rows go here -}]
-}
viewKeyed : List (KeyedOutline msg) -> Html msg
viewKeyed rows =
case rows of
[] ->
text ""
_ ->
viewKeyed_ rows
viewKeyed_ : List (KeyedOutline msg) -> Html msg
viewKeyed_ rows =
Html.Styled.Keyed.node "ul"
[ Html.Styled.Attributes.css
[ Css.listStyle Css.none
, Css.margin4 (Css.px 10) Css.zero Css.zero Css.zero
, Css.padding Css.zero
]
]
(viewKeyedRows Root rows)
viewKeyedRows : Hierarchy -> List (KeyedOutline msg) -> List ( String, Html msg )
viewKeyedRows hierarchy rows =
let
orderedNodeColors =
(List.map (\(KeyedOutline _ { palette }) -> Just palette.border) rows ++ [ Nothing ])
|> List.drop 1
in
List.map2 (viewKeyedRow hierarchy) orderedNodeColors rows
viewKeyedRow : Hierarchy -> Maybe Css.Color -> KeyedOutline msg -> ( String, Html msg )
viewKeyedRow hierarchy nextNodeColor (KeyedOutline key config) =
( key
, utilViewRow hierarchy
nextNodeColor
config
(\(KeyedOutline _ { palette }) -> palette.border)
(Html.Styled.Keyed.node "ul"
[ css
[ Css.marginLeft (Css.px 25)
, Css.paddingLeft (Css.px 25)
, Css.paddingTop (Css.px 25)
, Css.listStyleType Css.none
]
]
(viewKeyedRows Child config.rows)
)
)
{-| Render an unstyled row with only the outline styles.
import Html.Styled exposing (..)
import Nri.Ui.Outline.V1 as Outline
main : Html msg
main =
Outline.viewKeyed []
[ Outline.keyedRow someKey
{ title = Just "My outline node"
, content = text "This is my content"
, palette = RowTheme.red
, rows = []
}
]
-}
keyedRow :
String
->
{ title : Maybe String
, content : Html msg
, palette : RowTheme
, rows : List (KeyedOutline msg)
}
-> KeyedOutline msg
keyedRow key config =
KeyedOutline key
{ title = config.title
, content = config.content
, palette = config.palette
, rows = config.rows
, extraContent = Nothing
}
{-| Render a row with extra content. This row cannot have child rows.
import Html.Styled exposing (..)
import Nri.Ui.Outline.V1 as Outline
main : Html msg
main =
Outline.view
[ Outline.keyedRowWithExtraContent someKey
{ title = Just "My outline node"
, content = text "This is my content"
, palette = RowTheme.red
, extraContent = text "My extra content"
, rows = []
}
]
-}
keyedRowWithExtraContent :
String
->
{ rows : List (KeyedOutline msg)
, extraContent : Html msg
, title : Maybe String
, content : Html msg
, palette : RowTheme
}
-> KeyedOutline msg
keyedRowWithExtraContent key config =
KeyedOutline key
{ title = config.title
, content = config.content
, palette = config.palette
, rows = config.rows
, extraContent = Just config.extraContent
}
-- THEMES
{-| -}
type alias RowTheme =
{ border : Color
, borderStyle : Style
, background : Color
}
{-| Aqua palette
-}
aqua : RowTheme
aqua =
{ border = Colors.aquaDark
, borderStyle = Css.batch []
, background = Colors.aquaLight
}
{-| Dark Gray palette
-}
darkGray : RowTheme
darkGray =
{ border = Colors.gray45
, borderStyle = Css.batch []
, background = Colors.gray96
}
{-| -}
gray : RowTheme
gray =
{ border = Colors.gray45
, borderStyle = Css.batch []
, background = Colors.white
}
{-| Blue palette
-}
blue : RowTheme
blue =
{ border = Colors.azure
, borderStyle = Css.batch []
, background = Colors.frost
}
{-| Blue palette
-}
blueDashBordered : RowTheme
blueDashBordered =
{ border = Colors.azure
, borderStyle = Css.batch [ Css.borderWidth (Css.px 1), Css.borderStyle Css.dashed ]
, background = Colors.frost
}
{-| Dark blue palette
-}
darkBlue : RowTheme
darkBlue =
{ border = Colors.navy
, borderStyle = Css.batch []
, background = Colors.frost
}
{-| Purple palette with a purple border instead of a purple background color
-}
purple : RowTheme
purple =
{ border = Colors.purple
, borderStyle = Css.batch []
, background = Colors.purpleLight
}
{-| Purple palette with a purple border instead of a purple background color
-}
purpleBordered : RowTheme
purpleBordered =
{ border = Colors.purple
, borderStyle = Css.batch [ Css.borderWidth (Css.px 1), Css.borderStyle Css.solid ]
, background = Colors.white
}
{-| Turquoise palette
-}
turquoise : RowTheme
turquoise =
{ border = Colors.turquoiseDark
, borderStyle = Css.batch []
, background = Colors.turquoiseLight
}
{-| Green palette
-}
green : RowTheme
green =
{ border = Colors.greenDarkest
, borderStyle = Css.batch []
, background = Colors.greenLightest
}
{-| Green palette with a green border instead of a green background color
-}
greenBordered : RowTheme
greenBordered =
{ border = Colors.greenDarkest
, borderStyle = Css.batch [ Css.borderWidth (Css.px 1), Css.borderStyle Css.solid ]
, background = Colors.white
}
{-| Red palette
-}
red : RowTheme
red =
{ border = Colors.red
, borderStyle = Css.batch []
, background = Colors.redLight
}
{-| White palette (borders are blue)
-}
white : RowTheme
white =
{ border = Colors.navy
, borderStyle = Css.batch []
, background = Colors.white
}
{-| Cornflower palette
-}
cornflower : RowTheme
cornflower =
{ border = Colors.cornflowerDark
, borderStyle = Css.batch []
, background = Colors.cornflowerLight
}
-- UTILS
wrapViewWithTitleBubble :
{ title : String
, content : Html msg
, palette : RowTheme
}
-> Html msg
wrapViewWithTitleBubble config =
let
kebabTitle =
String.replace " " "-" (String.toLower config.title)
in
Html.Styled.div []
[ Html.Styled.div
[ Html.Styled.Attributes.attribute "data-nri-description" "outline-title"
, css
[ Fonts.baseFont
, borderRadius (px 18)
, color Colors.white
, display inlineBlock
, fontSize (px 15)
, fontWeight bold
, height (px 35)
, lineHeight (px 35)
, left (px -10)
, padding2 zero (px 15)
, position absolute
, top (px -15)
, backgroundColor config.palette.border
]
]
[ Html.Styled.text config.title ]
, Html.Styled.div
[ Attributes.testId (kebabTitle ++ "-text")
, css
[ borderRadius (px 8)
, color Colors.gray20
, fontSize (px 18)
, Fonts.quizFont
, padding3 (px 30) (px 15) (px 15)
, lineHeight (px 30)
, backgroundColor config.palette.background
, config.palette.borderStyle
, borderColor config.palette.border
, after [ borderColor config.palette.border ]
]
]
[ config.content ]
]
{-| -}
utilViewRow :
Hierarchy
-> Maybe Color
->
{ title : Maybe String
, content : Html msg
, extraContent : Maybe (Html msg)
, palette : RowTheme
, rows : List outline
}
-> (outline -> Color)
-> Html msg
-> Html msg
utilViewRow hierarchy nextNodeColor config getOutlineBorder children =
let
rowAttrs =
[ Html.Styled.Attributes.attribute "data-nri-description" "outline-row"
, css
[ paddingBottom (px 25)
, position relative
, lastChild [ paddingBottom zero ]
, case hierarchy of
Child ->
verticalChildConnector config.palette.border nextNodeColor
Root ->
Css.batch []
]
]
in
Html.Styled.li
rowAttrs
[ Html.Styled.div
[ css
[ position relative
, case hierarchy of
Child ->
horizontalChildConnector config.palette.border
Root ->
Css.batch []
]
]
[ case config.title of
Just title ->
wrapViewWithTitleBubble
{ title = title
, content = config.content
, palette = config.palette
}
Nothing ->
config.content
, viewJust (viewExtraContent (Maybe.map getOutlineBorder (List.head config.rows))) config.extraContent
]
, if List.isEmpty config.rows then
text ""
else
children
]
verticalChildConnector : Color -> Maybe Color -> Style
verticalChildConnector paletteBorder nextNodeColor =
Css.batch
[ after
[ property "content" "''"
, position absolute
, top (px -25)
, left (px -18)
, width (px 18)
, borderLeft3 (px 1) solid Colors.gray75
, property "height" "calc(16px)"
, borderColor paletteBorder
]
, before
(case nextNodeColor of
Just border ->
[ property "content" "''"
, position absolute
, left (px -18)
, width (px 18)
, borderLeft3 (px 1) solid Colors.gray75
, property "height" "calc(100%)"
, borderColor border
]
Nothing ->
[]
)
, lastChild
[ after [ borderLeftWidth zero ]
, before [ borderLeftWidth zero ]
]
]
horizontalChildConnector : Color -> Style
horizontalChildConnector paletteBorder =
after
[ property "content" "''"
, height (pct 80)
, width (px 18)
, borderBottom3 (px 1) solid Colors.gray75
, borderLeft3 (px 1) solid Colors.gray75
, left (px -18)
, borderRadius4 zero zero zero (px 4)
, top (px -25)
, position absolute
, maxHeight (px 60)
, borderColor paletteBorder
]
viewExtraContent : Maybe Color -> Html msg -> Html msg
viewExtraContent border content =
div
[ css
[ marginLeft (px 32)
, Maybe.map (borderLeft3 (px 1) solid) border
|> Maybe.withDefault (Css.batch [])
, paddingLeft (px 15)
]
]
[ content ]
-- Types
{-| -}
type Hierarchy
= Root
| Child

View File

@ -0,0 +1,63 @@
module Spec.Nri.Ui.Outline exposing (spec)
import Expect exposing (Expectation)
import Html.Styled as Html exposing (Html, toUnstyled)
import Nri.Ui.Outline.V1 as Outline
import Test exposing (..)
import Test.Html.Query as Query
import Test.Html.Selector exposing (..)
spec : Test
spec =
describe "Nri.Ui.Outline"
[ test "view without rows does not render anything" <|
\() ->
Outline.view []
|> hasNoUl
, test "viewKeyed without rows does not render anything" <|
\() ->
Outline.viewKeyed []
|> hasNoUl
, test "view with rows renders ul with lis" <|
\() ->
Outline.view
[ Outline.row
{ title = Nothing
, content = Html.text ""
, palette = Outline.gray
, rows = []
}
]
|> hasOneUlWithOneLi
, test "viewKeyed with rows renders ul with lis" <|
\() ->
Outline.viewKeyed
[ Outline.keyedRow "key"
{ title = Nothing
, content = Html.text ""
, palette = Outline.gray
, rows = []
}
]
|> hasOneUlWithOneLi
]
hasNoUl : Html msg -> Expectation
hasNoUl content =
Html.div [] [ content ]
|> toUnstyled
|> Query.fromHtml
|> Query.hasNot [ tag "ul" ]
hasOneUlWithOneLi : Html msg -> Expectation
hasOneUlWithOneLi content =
Html.div [] [ content ]
|> toUnstyled
|> Query.fromHtml
|> Expect.all
[ Query.count (Expect.equal 1) << Query.findAll [ tag "ul" ]
, Query.count (Expect.equal 1) << Query.findAll [ tag "li" ]
]

View File

@ -55,6 +55,7 @@
"Nri.Ui.Message.V4",
"Nri.Ui.Modal.V11",
"Nri.Ui.Modal.V12",
"Nri.Ui.Outline.V1",
"Nri.Ui.Page.V3",
"Nri.Ui.Pagination.V1",
"Nri.Ui.Palette.V1",