Merge branch 'master' into growth/menu-containsForm

This commit is contained in:
Brian J. Cardiff 2022-07-08 15:18:32 -03:00
commit ed06e5a929
11 changed files with 443 additions and 7 deletions

View File

@ -1,4 +1,5 @@
Nri.Ui.Accordion.V1,upgrade to V3
Nri.Ui.Menu.V1,upgrade to V3
Nri.Ui.SortableTable.V2,upgrade to V3
Nri.Ui.Tabs.V6,upgrade to V7
Nri.Ui.Tooltip.V1,upgrade to V3

1 Nri.Ui.Accordion.V1 upgrade to V3
2 Nri.Ui.Menu.V1 upgrade to V3
3 Nri.Ui.SortableTable.V2 upgrade to V3
4 Nri.Ui.Tabs.V6 upgrade to V7
5 Nri.Ui.Tooltip.V1 upgrade to V3

View File

@ -51,6 +51,7 @@
"Nri.Ui.Shadows.V1",
"Nri.Ui.SideNav.V3",
"Nri.Ui.SortableTable.V2",
"Nri.Ui.SortableTable.V3",
"Nri.Ui.Sprite.V1",
"Nri.Ui.Svg.V1",
"Nri.Ui.Switch.V2",

View File

@ -114,6 +114,9 @@ hint = 'upgrade to V3'
[forbidden."Nri.Ui.SideNav.V2"]
hint = 'upgrade to V3'
[forbidden."Nri.Ui.SortableTable.V2"]
hint = 'upgrade to V3'
[forbidden."Nri.Ui.Switch.V1"]
hint = 'upgrade to V2'

View File

@ -5,7 +5,7 @@ module Nri.Ui.Button.V10 exposing
, href, linkSpa, linkExternal, linkWithMethod, linkWithTracking, linkExternalWithTracking
, small, medium, large, modal
, exactWidth, boundedWidth, unboundedWidth, fillContainerWidth
, primary, secondary, danger, premium
, primary, secondary, tertiary, danger, premium
, enabled, unfulfilled, disabled, error, loading, success
, icon, custom, nriDescription, testId, id
, hideIconForMobile, hideIconFor
@ -29,6 +29,7 @@ adding a span around the text could potentially lead to regressions.
- adds `notMobileCss`, `mobileCss`, `quizEngineMobileCss`
- adds `hideIconForMobile` and `hideIconFor`
- support 'disabled' links according to [Scott O'Hara's disabled links](https://www.scottohara.me/blog/2021/05/28/disabled-links.html) article
- adds `tertiary` style
# Changes from V9:
@ -57,7 +58,7 @@ adding a span around the text could potentially lead to regressions.
## Change the color scheme
@docs primary, secondary, danger, premium
@docs primary, secondary, tertiary, danger, premium
## Change the state (buttons only)
@ -417,6 +418,15 @@ secondary =
)
{-| -}
tertiary : Attribute msg
tertiary =
set
(\attributes ->
{ attributes | style = tertiaryColors }
)
{-| -}
danger : Attribute msg
danger =
@ -834,6 +844,16 @@ secondaryColors =
}
tertiaryColors : ColorPalette
tertiaryColors =
{ background = Colors.white
, hover = Colors.frost
, text = Colors.navy
, border = Just <| Colors.gray75
, shadow = Colors.gray75
}
getColorPalette : ButtonOrLink msg -> ColorPalette
getColorPalette (ButtonOrLink config) =
case config.state of

View File

@ -389,7 +389,7 @@ viewArrow { isOpen } =
[ Css.Global.svg [ display block ]
]
, property "transform-origin" "center"
, property "transition" "transform 0.1s"
, property "transition" "transform 0.4s"
, if isOpen then
transform (rotate (deg 180))

View File

@ -333,7 +333,7 @@ viewCodeDetails errorMessageForEngineers =
, code
[ Attributes.css
[ display block
, whiteSpace normal
, whiteSpace preWrap
, overflowWrap breakWord
, color Colors.gray45
, backgroundColor Colors.gray96

View File

@ -0,0 +1,386 @@
module Nri.Ui.SortableTable.V3 exposing
( Column, Config, Sorter, State
, init, initDescending
, custom, string, view, viewLoading
, invariantSort, simpleSort, combineSorters
)
{-| TODO for next major version:
- add the possibility to pass Aria.sortAscending and Aria.sortDescending attributes to the <th> tag
Changes from V2:
- made column non-sortable (e.g. buttons in a column should not be sorted)
- use a button instead of a clickable div in headers
- use Aria.roleDescription instead of Aria.label in sortable columns headers
- use Nri.Ui.UiIcon.V1 sortArrow and Nri.Ui.UiIcon.V1 sortArrowDown icons for the sort indicators
@docs Column, Config, Sorter, State
@docs init, initDescending
@docs custom, string, view, viewLoading
@docs invariantSort, simpleSort, combineSorters
-}
import Accessibility.Styled.Aria as Aria
import Css exposing (..)
import Html.Styled as Html exposing (Html)
import Html.Styled.Attributes exposing (css)
import Html.Styled.Events
import Nri.Ui.Colors.V1
import Nri.Ui.CssVendorPrefix.V1 as CssVendorPrefix
import Nri.Ui.Fonts.V1 as Fonts
import Nri.Ui.Svg.V1
import Nri.Ui.Table.V5
import Nri.Ui.UiIcon.V1
type SortDirection
= Ascending
| Descending
{-| -}
type alias Sorter a =
SortDirection -> a -> a -> Order
{-| -}
type Column id entry msg
= Column
{ id : id
, header : Html msg
, view : entry -> Html msg
, sorter : Maybe (Sorter entry)
, width : Int
, cellStyles : entry -> List Style
}
{-| -}
type alias State id =
{ column : id
, sortDirection : SortDirection
}
{-| -}
type alias Config id entry msg =
{ updateMsg : State id -> msg
, columns : List (Column id entry msg)
}
{-| -}
init : id -> State id
init initialSort =
{ column = initialSort
, sortDirection = Ascending
}
{-| -}
initDescending : id -> State id
initDescending initialSort =
{ column = initialSort
, sortDirection = Descending
}
{-| -}
string :
{ id : id
, header : String
, value : entry -> String
, width : Int
, cellStyles : entry -> List Style
}
-> Column id entry msg
string { id, header, value, width, cellStyles } =
Column
{ id = id
, header = Html.text header
, view = value >> Html.text
, sorter = Just (simpleSort value)
, width = width
, cellStyles = cellStyles
}
{-| -}
custom :
{ id : id
, header : Html msg
, view : entry -> Html msg
, sorter : Maybe (Sorter entry)
, width : Int
, cellStyles : entry -> List Style
}
-> Column id entry msg
custom config =
Column
{ id = config.id
, header = config.header
, view = config.view
, sorter = config.sorter
, width = config.width
, cellStyles = config.cellStyles
}
{-| Create a sorter function that always orders the entries in the same order.
For example, this is useful when we want to resolve ties and sort the tied
entries by name, no matter of the sort direction set on the table.
-}
invariantSort : (entry -> comparable) -> Sorter entry
invariantSort mapper =
\sortDirection elem1 elem2 ->
compare (mapper elem1) (mapper elem2)
{-| Create a simple sorter function that orders entries by mapping a function
over the collection. It will also reverse it when the sort direction is descending.
-}
simpleSort : (entry -> comparable) -> Sorter entry
simpleSort mapper =
\sortDirection elem1 elem2 ->
let
result =
compare (mapper elem1) (mapper elem2)
in
case sortDirection of
Ascending ->
result
Descending ->
flipOrder result
flipOrder : Order -> Order
flipOrder order =
case order of
LT ->
GT
EQ ->
EQ
GT ->
LT
{-| -}
combineSorters : List (Sorter entry) -> Sorter entry
combineSorters sorters =
\sortDirection elem1 elem2 ->
let
folder =
\sorter acc ->
case acc of
EQ ->
sorter sortDirection elem1 elem2
_ ->
acc
in
List.foldl folder EQ sorters
{-| -}
viewLoading : Config id entry msg -> State id -> Html msg
viewLoading config state =
let
tableColumns =
List.map (buildTableColumn config.updateMsg state) config.columns
in
Nri.Ui.Table.V5.viewLoading
tableColumns
{-| -}
view : Config id entry msg -> State id -> List entry -> Html msg
view config state entries =
let
tableColumns =
List.map (buildTableColumn config.updateMsg state) config.columns
sorter =
findSorter config.columns state.column
in
Nri.Ui.Table.V5.view
tableColumns
(List.sortWith (sorter state.sortDirection) entries)
findSorter : List (Column id entry msg) -> id -> Sorter entry
findSorter columns columnId =
columns
|> listExtraFind (\(Column column) -> column.id == columnId)
|> Maybe.andThen (\(Column column) -> column.sorter)
|> Maybe.withDefault identitySorter
{-| Taken from <https://github.com/elm-community/list-extra/blob/8.2.0/src/List/Extra.elm#L556>
-}
listExtraFind : (a -> Bool) -> List a -> Maybe a
listExtraFind predicate list =
case list of
[] ->
Nothing
first :: rest ->
if predicate first then
Just first
else
listExtraFind predicate rest
identitySorter : Sorter a
identitySorter =
\sortDirection item1 item2 ->
EQ
buildTableColumn : (State id -> msg) -> State id -> Column id entry msg -> Nri.Ui.Table.V5.Column entry msg
buildTableColumn updateMsg state (Column column) =
Nri.Ui.Table.V5.custom
{ header = viewSortHeader (column.sorter /= Nothing) column.header updateMsg state column.id
, view = column.view
, width = Css.px (toFloat column.width)
, cellStyles = column.cellStyles
}
viewSortHeader : Bool -> Html msg -> (State id -> msg) -> State id -> id -> Html msg
viewSortHeader isSortable header updateMsg state id =
let
nextState =
nextTableState state id
in
if isSortable then
Html.button
[ css
[ Css.displayFlex
, Css.alignItems Css.center
, Css.justifyContent Css.spaceBetween
, CssVendorPrefix.property "user-select" "none"
, if state.column == id then
fontWeight bold
else
fontWeight normal
, cursor pointer
-- make this look less "buttony"
, Css.border Css.zero
, Css.backgroundColor Css.transparent
, Css.width (Css.pct 100)
, Css.height (Css.pct 100)
, Css.margin Css.zero
, Css.padding Css.zero
, Fonts.baseFont
, Css.fontSize (Css.em 1)
]
, Html.Styled.Events.onClick (updateMsg nextState)
-- screen readers should know what clicking this button will do
, Aria.roleDescription "sort button"
]
[ Html.div [] [ header ]
, viewSortButton updateMsg state id
]
else
Html.div
[ css [ fontWeight normal ]
]
[ header ]
viewSortButton : (State id -> msg) -> State id -> id -> Html msg
viewSortButton updateMsg state id =
let
arrows upHighlighted downHighlighted =
Html.div
[ css
[ Css.displayFlex
, Css.flexDirection Css.column
, Css.alignItems Css.center
, Css.justifyContent Css.center
]
]
[ sortArrow Up upHighlighted
, sortArrow Down downHighlighted
]
buttonContent =
case ( state.column == id, state.sortDirection ) of
( True, Ascending ) ->
arrows True False
( True, Descending ) ->
arrows False True
( False, _ ) ->
arrows False False
in
Html.div [ css [ padding (px 2) ] ] [ buttonContent ]
nextTableState : State id -> id -> State id
nextTableState state id =
if state.column == id then
{ column = id
, sortDirection = flipSortDirection state.sortDirection
}
else
{ column = id
, sortDirection = Ascending
}
flipSortDirection : SortDirection -> SortDirection
flipSortDirection order =
case order of
Ascending ->
Descending
Descending ->
Ascending
type Direction
= Up
| Down
sortArrow : Direction -> Bool -> Html msg
sortArrow direction active =
let
arrow =
case direction of
Up ->
Nri.Ui.UiIcon.V1.sortArrow
Down ->
Nri.Ui.UiIcon.V1.sortArrowDown
color =
if active then
Nri.Ui.Colors.V1.azure
else
Nri.Ui.Colors.V1.gray75
in
arrow
|> Nri.Ui.Svg.V1.withHeight (px 6)
|> Nri.Ui.Svg.V1.withWidth (px 8)
|> Nri.Ui.Svg.V1.withColor color
|> Nri.Ui.Svg.V1.withCss
[ displayFlex
, margin2 (px 1) zero
]
|> Nri.Ui.Svg.V1.toHtml

View File

@ -51,6 +51,14 @@ example =
, Button.custom [ Key.tabbable False ]
, Button.icon UiIcon.link
]
, Button.link "Tertiary"
[ Button.small
, Button.fillContainerWidth
, Button.tertiary
, Button.css [ Css.marginTop (Css.px 8) ]
, Button.custom [ Key.tabbable False ]
, Button.icon UiIcon.link
]
, Button.link "Premium"
[ Button.small
, Button.fillContainerWidth
@ -248,6 +256,7 @@ buttons model =
styles =
[ ( Button.primary, "primary" )
, ( Button.secondary, "secondary" )
, ( Button.tertiary, "tertiary" )
, ( Button.danger, "danger" )
, ( Button.premium, "premium" )
]

View File

@ -11,9 +11,10 @@ import Css exposing (..)
import Example exposing (Example)
import Html.Styled as Html exposing (..)
import Html.Styled.Attributes exposing (css)
import Nri.Ui.Button.V10 as Button
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Heading.V2 as Heading
import Nri.Ui.SortableTable.V2 as SortableTable
import Nri.Ui.SortableTable.V3 as SortableTable
import Nri.Ui.Svg.V1 as Svg exposing (Svg)
import Nri.Ui.Table.V5 as Table
import Nri.Ui.UiIcon.V1 as UiIcon
@ -23,6 +24,7 @@ type Column
= FirstName
| LastName
| Coins
| ViewButton
{-| -}
@ -122,10 +124,23 @@ example =
{ id = Coins
, header = Html.text "Coins"
, view = .coins >> String.fromInt >> Html.text
, sorter = SortableTable.simpleSort .coins
, sorter = Just (SortableTable.simpleSort .coins)
, width = 125
, cellStyles = \_ -> []
}
, SortableTable.custom
{ id = ViewButton
, header = Html.text "View"
, view =
\_ ->
Button.link "View"
[ Button.small
, Button.fillContainerWidth
]
, sorter = Nothing
, width = 25
, cellStyles = \_ -> []
}
]
}

View File

@ -2,7 +2,7 @@ module Spec.Nri.Ui.SortableTable exposing (spec)
import Expect
import Html.Styled
import Nri.Ui.SortableTable.V2 as SortableTable
import Nri.Ui.SortableTable.V3 as SortableTable
import Test exposing (..)
import Test.Html.Query as Query
import Test.Html.Selector as Selector

View File

@ -47,6 +47,7 @@
"Nri.Ui.Shadows.V1",
"Nri.Ui.SideNav.V3",
"Nri.Ui.SortableTable.V2",
"Nri.Ui.SortableTable.V3",
"Nri.Ui.Sprite.V1",
"Nri.Ui.Svg.V1",
"Nri.Ui.Switch.V2",