Merge pull request #751 from NoRedInk/dansby/container-v2

Container v2
This commit is contained in:
Ben Dansby 2021-10-22 10:59:05 -07:00 committed by GitHub
commit 8fdb6867ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 490 additions and 46 deletions

View File

@ -1,5 +1,6 @@
Nri.Ui.Accordion.V1,upgrade to V3
Nri.Ui.Button.V8,upgrade to V10
Nri.Ui.Container.V1,upgrade to V2
Nri.Ui.Menu.V1,upgrade to V3
Nri.Ui.Menu.V2,upgrade to V3
Nri.Ui.Modal.V10,upgrade to V11

1 Nri.Ui.Accordion.V1 upgrade to V3
2 Nri.Ui.Button.V8 upgrade to V10
3 Nri.Ui.Container.V1 upgrade to V2
4 Nri.Ui.Menu.V1 upgrade to V3
5 Nri.Ui.Menu.V2 upgrade to V3
6 Nri.Ui.Modal.V10 upgrade to V11

View File

@ -17,6 +17,7 @@
"Nri.Ui.ClickableSvg.V2",
"Nri.Ui.ClickableText.V3",
"Nri.Ui.Container.V1",
"Nri.Ui.Container.V2",
"Nri.Ui.Colors.Extra",
"Nri.Ui.Colors.V1",
"Nri.Ui.Confetti.V2",

View File

@ -50,6 +50,9 @@ usages = ['styleguide-app/../src/Nri/Ui/SlideModal/V2.elm']
hint = 'upgrade to V2'
usages = ['styleguide-app/Examples/Tooltip.elm']
[forbidden."Nri.Ui.Container.V1"]
hint = 'upgrade to V2'
[forbidden."Nri.Ui.Icon.V3"]
hint = 'upgrade to V5'
usages = ['styleguide-app/../src/Nri/Ui/Modal/V3.elm']

301
src/Nri/Ui/Container/V2.elm Normal file
View File

@ -0,0 +1,301 @@
module Nri.Ui.Container.V2 exposing
( view, Attribute
, paddingPx, custom, css, testId, id
, plaintext, markdown, html
, gray, default, disabled, invalid, pillow, buttony
)
{-| Common NoRedInk Containers
# Changelog
## Changes from V1
- removes fullHeight
- changes the API from providing many themed functions that create HTML (`alternate`, `general`, etc.) to having a single `view` helper that takes a list of attributes (including themed attributes and content attributes)
- adds `custom`, `testId`, `id`
- adds `plaintext` helper and `markdown` helper
- renames themes from:
- `alternate` -> `gray`
- `general` -> `default`
- `interactable` -> `pillow`
- removes `interactableWithLabel` theme
- adds `buttony` theme
# Documentation
## View
@docs view, Attribute
@docs paddingPx, custom, css, testId, id
## Content
@docs plaintext, markdown, html
## Themes
@docs gray, default, disabled, invalid, pillow, buttony
-}
import Css exposing (..)
import Css.Media exposing (withMedia)
import Html.Styled as Html exposing (..)
import Html.Styled.Attributes
import Markdown
import Nri.Ui
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Html.Attributes.V2 as ExtraAttributes
import Nri.Ui.MediaQuery.V1 exposing (mobile)
import Nri.Ui.Text.V5 as Text
{-| -}
type Attribute msg
= Attribute (Settings msg -> Settings msg)
{-| PRIVATE
-}
type alias Settings msg =
{ containerType : String
, padding : Float
, css : List Css.Style
, content : List (Html msg)
, attributes : List (Html.Attribute msg)
}
{-| Changes the padding inside the container border around the content.
-}
paddingPx : Float -> Attribute msg
paddingPx padding =
Attribute <| \config -> { config | padding = padding }
{-| 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 styles change.
Instead, please use the `css` helper.
-}
custom : List (Html.Attribute msg) -> Attribute msg
custom attributes =
Attribute <|
\config ->
{ config
| attributes =
config.attributes ++ attributes
}
{-| -}
testId : String -> Attribute msg
testId id_ =
custom [ ExtraAttributes.testId id_ ]
{-| -}
id : String -> Attribute msg
id id_ =
custom [ Html.Styled.Attributes.id id_ ]
{-| -}
css : List Css.Style -> Attribute msg
css css_ =
Attribute <| \config -> { config | css = config.css ++ css_ }
{-| -}
view : List (Attribute msg) -> Html msg
view attributes =
let
settings : Settings msg
settings =
List.foldl (\(Attribute set) -> set)
defaultSettings
attributes
in
Nri.Ui.styled div
settings.containerType
(padding (px settings.padding) :: settings.css)
settings.attributes
settings.content
{-| Used for the default container case.
-}
default : Attribute msg
default =
Attribute identity
defaultSettings : Settings msg
defaultSettings =
{ containerType = "default-container"
, padding = 20
, css = defaultStyles
, content = []
, attributes = []
}
defaultStyles : List Css.Style
defaultStyles =
[ borderRadius (px 8)
, border3 (px 1) solid Colors.gray92
, boxShadow5 zero (px 1) (px 1) zero (rgba 0 0 0 0.25)
, backgroundColor Colors.white
]
{-| Used when there are a lot of containers.
-}
gray : Attribute msg
gray =
Attribute <|
\config ->
{ config
| containerType = "gray-container"
, padding = 20
, css = grayStyles
}
grayStyles : List Css.Style
grayStyles =
[ borderRadius (px 8)
, backgroundColor Colors.gray96
]
{-| -}
disabled : Attribute msg
disabled =
Attribute <|
\config ->
{ config
| containerType = "disabled-container"
, padding = 20
, css = disabledStyles
}
disabledStyles : List Css.Style
disabledStyles =
[ borderRadius (px 8)
, border3 (px 1) solid Colors.gray92
, backgroundColor Colors.white
, color Colors.gray45
]
{-| -}
invalid : Attribute msg
invalid =
Attribute <|
\config ->
{ config
| containerType = "invalid-container"
, padding = 20
, css = invalidStyles
}
invalidStyles : List Css.Style
invalidStyles =
[ borderRadius (px 8)
, border3 (px 1) solid Colors.purpleLight
, boxShadow5 zero (px 1) (px 1) zero Colors.purple
, backgroundColor Colors.purpleLight
]
{-| Used for containers of interactive elements.
-}
pillow : Attribute msg
pillow =
Attribute <|
\config ->
{ config
| containerType = "pillow-container"
, padding = 40
, css = pillowStyles
}
pillowStyles : List Style
pillowStyles =
[ borderRadius (px 20)
, border3 (px 1) solid Colors.gray92
, boxShadow5 zero (px 2) (px 4) zero (rgba 0 0 0 0.25)
, backgroundColor Colors.white
, withMedia [ mobile ]
[ borderRadius (px 8)
, padding (px 20)
]
]
{-| Used for clickable cards
-}
buttony : Attribute msg
buttony =
Attribute <|
\config ->
{ config
| containerType = "buttony-container"
, css = buttonyStyles
}
buttonyStyles : List Style
buttonyStyles =
[ borderRadius (px 20)
, border3 (px 1) solid Colors.gray85
, borderBottom3 (px 4) solid Colors.gray85
, backgroundColor Colors.white
, withMedia [ mobile ]
[ borderRadius (px 8)
]
]
{-| Provide a list of custom HTML.
-}
html : List (Html msg) -> Attribute msg
html content =
Attribute <| \config -> { config | content = content }
{-| Provide a plain-text string.
-}
plaintext : String -> Attribute msg
plaintext content =
Attribute <| \config -> { config | content = [ text content ] }
{-| Provide a string that will be rendered as markdown.
Note that you may need to remove extra margin added by default
to `p` tags by user agents.
-}
markdown : String -> Attribute msg
markdown content =
Attribute <|
\config ->
{ config
| content =
Markdown.toHtml Nothing content
|> List.map fromUnstyled
}

View File

@ -0,0 +1,43 @@
module Debug.Control.Extra exposing
( float
, list, listItem, optionalListItem
)
{-|
@docs float
@docs list, listItem, optionalListItem
-}
import Debug.Control as Control exposing (Control)
{-| -}
float : Float -> Control Float
float default =
Control.map (String.toFloat >> Maybe.withDefault default)
(Control.string (String.fromFloat default))
{-| Use with `listItem` and `optionalListItem`
-}
list : Control (List a)
list =
Control.record []
{-| -}
listItem : String -> Control a -> Control (List a) -> Control (List a)
listItem name accessor accumulator =
Control.field name
(Control.map List.singleton accessor)
(Control.map (++) accumulator)
{-| -}
optionalListItem : String -> Control a -> Control (List a) -> Control (List a)
optionalListItem name accessor accumulator =
Control.field name
(Control.map (List.singleton >> List.filterMap identity) (Control.maybe False accessor))
(Control.map (++) accumulator)

View File

@ -9,6 +9,7 @@ module Examples.Balloon exposing (example, State, Msg)
import Category exposing (Category(..))
import Css
import Debug.Control as Control exposing (Control)
import Debug.Control.Extra as ControlExtra
import Example exposing (Example)
import Examples.IconExamples as IconExamples
import Html.Styled exposing (Html, div, fromUnstyled, text)
@ -83,12 +84,12 @@ widthOptions =
[ ( "px"
, Control.map
(\w -> ( "Balloon.widthPx " ++ String.fromFloat w, Balloon.widthPx w ))
(controlFloat 50)
(ControlExtra.float 50)
)
, ( "%"
, Control.map
(\w -> ( "Balloon.widthPct " ++ String.fromFloat w, Balloon.widthPct w ))
(controlFloat 50)
(ControlExtra.float 50)
)
]
@ -97,13 +98,7 @@ paddingOptions : Control ( String, Balloon.Attribute )
paddingOptions =
Control.map
(\w -> ( "Balloon.paddingPx " ++ String.fromFloat w, Balloon.paddingPx w ))
(controlFloat 10)
controlFloat : Float -> Control Float
controlFloat default =
Control.map (String.toFloat >> Maybe.withDefault default)
(Control.string (String.fromFloat default))
(ControlExtra.float 10)
{-| -}

View File

@ -8,23 +8,27 @@ module Examples.Container exposing (Msg, State, example)
import Category exposing (Category(..))
import Css
import Debug.Control as Control exposing (Control)
import Debug.Control.Extra as ControlExtra
import Example exposing (Example)
import Html.Styled as Html
import Html.Styled as Html exposing (Html)
import Html.Styled.Attributes exposing (css)
import Html.Styled.Events exposing (onClick)
import KeyboardSupport exposing (Direction(..), Key(..))
import Nri.Ui.Button.V10 as Button
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Container.V1 as Container
import Nri.Ui.Container.V2 as Container
import Nri.Ui.Heading.V2 as Heading
import Nri.Ui.Svg.V1 as Svg
import Nri.Ui.Text.V5 as Text
import Nri.Ui.UiIcon.V1 as UiIcon
{-| -}
example : Example State Msg
example =
{ name = "Container"
, version = 1
, version = 2
, categories = [ Layout ]
, keyboardSupport = []
, state = init
@ -32,52 +36,147 @@ example =
, subscriptions = \_ -> Sub.none
, view =
\state ->
[ Heading.h3 [ Heading.css [ Css.marginTop (Css.px 8) ] ]
[ Html.text "General Container" ]
, Html.text "Used for the general container case."
, Container.general [] (Html.text "Content, content...")
, Heading.h3 [ Heading.css [ Css.marginTop (Css.px 8) ] ]
[ Html.text "Alternate Container" ]
, Html.text "Used when there are a lot of containers."
, Container.alternate [] (Html.text "Content, content...")
, Heading.h3 [ Heading.css [ Css.marginTop (Css.px 8) ] ]
[ Html.text "Interactable Container" ]
, Html.text "Usually used for larger containers with many elements inside."
, Container.interactable [] (Html.text "Content, content...")
, Heading.h3 [ Heading.css [ Css.marginTop (Css.px 8) ] ]
[ Html.text "Disabled Container" ]
, Html.text "Used to indicate content is locked/inaccessible"
, Container.disabled [] (Html.text "Content, content...")
, Heading.h3 [ Heading.css [ Css.marginTop (Css.px 8) ] ]
[ Html.text "Invalid Container" ]
, Html.text "Used to indicate content is invalid"
, Container.invalid [] (Html.text "Content, content...")
, Heading.h3 [ Heading.css [ Css.marginTop (Css.px 8) ] ]
[ Html.text "Interactable container with a label" ]
, Html.text "Used for helpful tidbits."
, Container.interactableWithLabel "The label" <|
Html.text "Content, content..."
let
attributes =
Control.currentValue state.control
in
[ Control.view UpdateControl state.control
|> Html.fromUnstyled
, viewExample
{ name = "Default Container"
, description = "Your go-to container."
}
(Container.default :: attributes)
, viewExample
{ name = "Gray Container"
, description = "A container that doesnt draw too much attention to itself."
}
(Container.gray :: attributes)
, viewExample
{ name = "Pillow Container"
, description = "When you want something big and soft."
}
(Container.pillow :: attributes)
, viewExample
{ name = "Buttony Container"
, description = "Used for clickable button card things."
}
(Container.buttony :: attributes)
, viewExample
{ name = "Disabled Container"
, description = "Used to indicate content is locked/inaccessible"
}
(Container.disabled :: attributes)
, viewExample
{ name = "Invalid Container"
, description = "Used to indicate content is invalid"
}
(Container.invalid :: attributes)
]
}
viewExample : { name : String, description : String } -> List (Container.Attribute msg) -> Html msg
viewExample { name, description } attributes =
Html.section
[ css
[ Css.marginTop (Css.px 20)
]
]
[ Heading.h3 [] [ Html.text name ]
, Html.text description
, Container.view attributes
]
{-| -}
type alias State =
{ control : Control (List (Container.Attribute Msg))
}
{-| -}
init : State
init =
{}
{ control =
ControlExtra.list
|> ControlExtra.optionalListItem "paddingPx" controlPaddingPx
|> ControlExtra.optionalListItem "css" controlCss
|> ControlExtra.listItem "content" controlContent
}
controlPaddingPx : Control (Container.Attribute msg)
controlPaddingPx =
Control.map Container.paddingPx (ControlExtra.float 20)
controlCss : Control (Container.Attribute msg)
controlCss =
Control.map Container.css
(Control.value
[ Css.minHeight (Css.px 100)
, Css.hover [ Css.backgroundColor Colors.glacier ]
]
)
controlContent : Control (Container.Attribute msg)
controlContent =
Control.choice
[ ( "plain text (short)"
, Control.string "Content, content..."
|> Control.map Container.plaintext
)
, ( "plain text (long)"
, Control.stringTextarea romeoAndJulietQuotation
|> Control.map Container.plaintext
)
, ( "markdown"
, Control.string romeoAndJulietQuotation
|> Control.map Container.markdown
)
, ( "HTML (short)"
, Control.value
(Container.html
[ UiIcon.footsteps
|> Svg.withHeight (Css.px 200)
|> Svg.withColor Colors.grassland
|> Svg.toHtml
]
)
)
]
romeoAndJulietQuotation : String
romeoAndJulietQuotation =
"""
Two households, both alike in dignity,
In fair Verona, where we lay our scene,
From ancient grudge break to new mutiny,
Where civil blood makes civil hands unclean.
From forth the fatal loins of these two foes
A pair of star-crossd lovers take their life;
Whose misadventured piteous overthrows
Do with their death bury their parents strife.
The fearful passage of their death-markd love,
And the continuance of their parents rage,
Which, but their childrens end, nought could remove,
Is now the two hours traffic of our stage;
The which if you with patient ears attend,
What here shall miss, our toil shall strive to mend.
"""
{-| -}
type alias State =
{}
{-| -}
type alias Msg =
()
type Msg
= UpdateControl (Control (List (Container.Attribute Msg)))
{-| -}
update : Msg -> State -> ( State, Cmd Msg )
update msg state =
( state, Cmd.none )
case msg of
UpdateControl newControl ->
( { state | control = newControl }, Cmd.none )

View File

@ -13,6 +13,7 @@
"Nri.Ui.ClickableSvg.V2",
"Nri.Ui.ClickableText.V3",
"Nri.Ui.Container.V1",
"Nri.Ui.Container.V2",
"Nri.Ui.Colors.Extra",
"Nri.Ui.Colors.V1",
"Nri.Ui.Confetti.V2",