mirror of
https://github.com/NoRedInk/noredink-ui.git
synced 2024-11-27 13:02:42 +03:00
Merge branch 'master' into growth/menu-containsForm
This commit is contained in:
commit
ed06e5a929
@ -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
elm.json
1
elm.json
@ -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",
|
||||
|
@ -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'
|
||||
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
||||
|
@ -333,7 +333,7 @@ viewCodeDetails errorMessageForEngineers =
|
||||
, code
|
||||
[ Attributes.css
|
||||
[ display block
|
||||
, whiteSpace normal
|
||||
, whiteSpace preWrap
|
||||
, overflowWrap breakWord
|
||||
, color Colors.gray45
|
||||
, backgroundColor Colors.gray96
|
||||
|
386
src/Nri/Ui/SortableTable/V3.elm
Normal file
386
src/Nri/Ui/SortableTable/V3.elm
Normal 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
|
@ -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" )
|
||||
]
|
||||
|
@ -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 = \_ -> []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
Loading…
Reference in New Issue
Block a user