diff --git a/component-catalog/src/Examples/Carousel.elm b/component-catalog/src/Examples/Carousel.elm index 5c760472..4877627e 100644 --- a/component-catalog/src/Examples/Carousel.elm +++ b/component-catalog/src/Examples/Carousel.elm @@ -23,6 +23,7 @@ import Html.Styled.Attributes as Attributes import KeyboardSupport exposing (Key(..)) import Nri.Ui.Carousel.V1 as Carousel import Nri.Ui.Colors.V1 as Colors +import Nri.Ui.Html.Attributes.V2 as Attributes import Task @@ -217,11 +218,11 @@ toCarouselItem : Int -> a -> ( String, Carousel.Item Int msg ) toCarouselItem id _ = let idString = - String.fromInt id + Attributes.safeIdWithPrefix "slide" <| String.fromInt id in ( [ "Carousel.buildItem" - , " { id = " ++ idString - , " , idString = \"" ++ idString ++ "-slide\"" + , " { id = " ++ String.fromInt id + , " , idString = \"" ++ idString ++ "\"" , " , controlHtml = Html.text \"" ++ String.fromInt (id + 1) ++ "\"" , " , slideHtml = Html.text \"" ++ String.fromInt (id + 1) ++ " slide\"" , " }" @@ -229,7 +230,7 @@ toCarouselItem id _ = |> String.join "\n " , Carousel.buildItem { id = id - , idString = String.fromInt id ++ "-slide" + , idString = idString , controlHtml = Html.text (String.fromInt (id + 1)) , slideHtml = Html.text (String.fromInt (id + 1) ++ " slide") } diff --git a/component-catalog/src/Routes.elm b/component-catalog/src/Routes.elm index 651a657e..7a3e00b5 100644 --- a/component-catalog/src/Routes.elm +++ b/component-catalog/src/Routes.elm @@ -8,7 +8,7 @@ import Example exposing (Example) import Html.Styled.Attributes as Attributes import Nri.Ui.BreadCrumbs.V2 as BreadCrumbs exposing (BreadCrumbs) import Nri.Ui.Header.V1 as Header -import Nri.Ui.Util exposing (dashify) +import Nri.Ui.Html.Attributes.V2 exposing (safeIdWithPrefix) import Parser exposing ((|.), (|=), Parser) import Url exposing (Url) @@ -168,7 +168,7 @@ categoryCrumb category_ = doodadCrumb : BreadCrumbs (Route state msg) -> Example state msg -> BreadCrumbs (Route state msg) doodadCrumb previous example = BreadCrumbs.after previous - { id = "breadcrumbs__" ++ dashify example.name + { id = safeIdWithPrefix "breadcrumbs" example.name , text = Example.fullName example , route = Doodad example } diff --git a/src/Nri/Ui/Checkbox/V7.elm b/src/Nri/Ui/Checkbox/V7.elm index df5e7ab5..5cd5dec8 100644 --- a/src/Nri/Ui/Checkbox/V7.elm +++ b/src/Nri/Ui/Checkbox/V7.elm @@ -65,7 +65,6 @@ import Nri.Ui.FocusRing.V1 as FocusRing import Nri.Ui.Fonts.V1 as Fonts import Nri.Ui.Html.Attributes.V2 as Extra import Nri.Ui.Svg.V1 exposing (Svg) -import Nri.Ui.Util as Util {-| This disables the input @@ -233,7 +232,7 @@ view { label, selected } attributes = specificId Nothing -> - "checkbox-v7-" ++ Util.safeIdString label + Extra.safeIdWithPrefix "checkbox-v7" label config_ = { identifier = idValue diff --git a/src/Nri/Ui/Html/Attributes/V2.elm b/src/Nri/Ui/Html/Attributes/V2.elm index ae123609..ea2b2440 100644 --- a/src/Nri/Ui/Html/Attributes/V2.elm +++ b/src/Nri/Ui/Html/Attributes/V2.elm @@ -3,6 +3,7 @@ module Nri.Ui.Html.Attributes.V2 exposing , targetBlank , nriDescription, nriDescriptionSelector , testId + , safeIdWithPrefix, safeId ) {-| @@ -21,6 +22,7 @@ This is the new version of Nri.Ui.Html.Attributes.Extra. @docs targetBlank @docs nriDescription, nriDescriptionSelector @docs testId +@docs safeIdWithPrefix, safeId -} @@ -29,6 +31,7 @@ import Css.Global import Html.Styled exposing (Attribute) import Html.Styled.Attributes as Attributes import Json.Encode as Encode +import Regex exposing (Regex) {-| Represents an attribute with no semantic meaning, useful for conditionals. @@ -103,3 +106,47 @@ nriDescription description = nriDescriptionSelector : String -> List Css.Style -> Css.Global.Snippet nriDescriptionSelector description = Css.Global.selector (String.join "" [ "[data-nri-description=\"", description, "\"]" ]) + + +{-| Prepends a prefix to the result of safeId. +-} +safeIdWithPrefix : String -> String -> String +safeIdWithPrefix prefix string = + safeId + (prefix + ++ "-" + ++ string + ) + + +{-| Creates a lowercased string that is safe to use for HTML IDs. +Removes all groups of unsafe characters and replaces each group with a dash. +prepends "id-" to the result to ensure that the ID starts with a letter. (necessary for CSS selectors including getElementById) +See code pen for examples: +-} +safeId : String -> String +safeId unsafe = + let + nonAlphaNumUnderscoreHyphenAnywhere : Regex + nonAlphaNumUnderscoreHyphenAnywhere = + -- any contiguous block of characters that aren't any of + -- + ASCII letters + -- + numbers + -- + underscore + -- + the hyphen-minus character commonly called "dash" (seen in between "hyphen" and "minus" on this line) + -- This does not need the + at the end; Regex.replace is global by default + -- but we pay a penalty for calling the replacement function, so + -- calling it once per contiguous group is an easy way to cut down on that. + "[^a-zA-Z0-9_-]+" + |> Regex.fromString + |> Maybe.withDefault Regex.never + + hyphenMinus : any -> String + hyphenMinus _ = + "-" + in + "id-" + ++ Regex.replace + nonAlphaNumUnderscoreHyphenAnywhere + hyphenMinus + unsafe diff --git a/src/Nri/Ui/PremiumCheckbox/V8.elm b/src/Nri/Ui/PremiumCheckbox/V8.elm index f4a3be66..c0e1a2e2 100644 --- a/src/Nri/Ui/PremiumCheckbox/V8.elm +++ b/src/Nri/Ui/PremiumCheckbox/V8.elm @@ -53,9 +53,6 @@ import Nri.Ui.Fonts.V1 as Fonts import Nri.Ui.Html.Attributes.V2 as Extra import Nri.Ui.Pennant.V2 exposing (premiumFlag) import Nri.Ui.Svg.V1 as Svg -import Nri.Ui.Util exposing (removePunctuation) -import String exposing (toLower) -import String.Extra exposing (dasherize) {-| Set a custom ID for this checkbox and label. If you don't set this, @@ -205,7 +202,7 @@ view { label, onChange } attributes = specificId Nothing -> - "checkbox-" ++ dasherize (removePunctuation (toLower label)) + Extra.safeIdWithPrefix "checkbox" label isPremium = config.premiumDisplay /= PremiumDisplay.Free diff --git a/src/Nri/Ui/RadioButton/V4.elm b/src/Nri/Ui/RadioButton/V4.elm index 8f204995..194e6209 100644 --- a/src/Nri/Ui/RadioButton/V4.elm +++ b/src/Nri/Ui/RadioButton/V4.elm @@ -63,7 +63,6 @@ import Nri.Ui.Fonts.V1 as Fonts import Nri.Ui.Html.Attributes.V2 as Extra import Nri.Ui.Pennant.V2 as Pennant import Nri.Ui.Svg.V1 exposing (Svg) -import Nri.Ui.Util as Util import Svg.Styled as Svg import Svg.Styled.Attributes as SvgAttributes @@ -277,7 +276,7 @@ view { label, name, value, valueToString, selectedValue } attributes = specificId Nothing -> - name ++ "-" ++ Util.safeIdString stringValue + Extra.safeId (name ++ "-" ++ stringValue) isChecked = selectedValue == Just value diff --git a/src/Nri/Ui/SegmentedControl/V14.elm b/src/Nri/Ui/SegmentedControl/V14.elm index 1b38b188..e1af9dad 100644 --- a/src/Nri/Ui/SegmentedControl/V14.elm +++ b/src/Nri/Ui/SegmentedControl/V14.elm @@ -31,9 +31,9 @@ import Nri.Ui.Colors.Extra as ColorsExtra import Nri.Ui.Colors.V1 as Colors import Nri.Ui.FocusRing.V1 as FocusRing import Nri.Ui.Fonts.V1 as Fonts +import Nri.Ui.Html.Attributes.V2 exposing (safeId) import Nri.Ui.Svg.V1 as Svg exposing (Svg) import Nri.Ui.Tooltip.V3 as Tooltip -import Nri.Ui.Util exposing (dashify) import TabsInternal.V2 as TabsInternal @@ -132,7 +132,7 @@ viewRadioGroup config = ) name = - dashify (String.toLower config.legend) + safeId config.legend legendId = "legend-" ++ name diff --git a/src/Nri/Ui/Select/V8.elm b/src/Nri/Ui/Select/V8.elm index 52c7e4a9..a5edc32f 100644 --- a/src/Nri/Ui/Select/V8.elm +++ b/src/Nri/Ui/Select/V8.elm @@ -59,7 +59,6 @@ import Nri.Ui.CssVendorPrefix.V1 as VendorPrefixed import Nri.Ui.Fonts.V1 as Fonts import Nri.Ui.Html.Attributes.V2 as Extra import Nri.Ui.InputStyles.V4 as InputStyles -import Nri.Ui.Util import SolidColor @@ -475,8 +474,8 @@ viewChoice current choice = {-| Pass in the label to generate the default DOM element id used by a `Select.view` with the given label. -} generateId : String -> String -generateId x = - "nri-select-" ++ Nri.Ui.Util.dashify (Nri.Ui.Util.removePunctuation x) +generateId = + Extra.safeIdWithPrefix "nri-select" selectArrowsCss : { config | disabled : Bool } -> Css.Style diff --git a/src/Nri/Ui/Select/V9.elm b/src/Nri/Ui/Select/V9.elm index 1b1ce93f..a4780f45 100644 --- a/src/Nri/Ui/Select/V9.elm +++ b/src/Nri/Ui/Select/V9.elm @@ -66,7 +66,6 @@ import Nri.Ui.Html.Attributes.V2 as Extra import Nri.Ui.Html.V3 exposing (viewJust) import Nri.Ui.InputStyles.V4 as InputStyles import Nri.Ui.Svg.V1 as Svg exposing (Svg) -import Nri.Ui.Util import SolidColor @@ -544,7 +543,7 @@ viewChoice current choice = -} generateId : String -> String generateId x = - "nri-select-" ++ String.toLower (Nri.Ui.Util.dashify (Nri.Ui.Util.removePunctuation x)) + Extra.safeIdWithPrefix "nri-select-" x selectArrowsCss : { config | disabled : Bool } -> Css.Style diff --git a/src/Nri/Ui/TextArea/V5.elm b/src/Nri/Ui/TextArea/V5.elm index b104e5aa..dff7693a 100644 --- a/src/Nri/Ui/TextArea/V5.elm +++ b/src/Nri/Ui/TextArea/V5.elm @@ -92,7 +92,6 @@ import InputLabelInternal import Nri.Ui.Colors.V1 as Colors import Nri.Ui.Html.Attributes.V2 as Extra import Nri.Ui.InputStyles.V4 as InputStyles exposing (Theme(..)) -import Nri.Ui.Util exposing (dashify, removePunctuation) {-| This is private. The public API only exposes `Attribute`. @@ -458,5 +457,5 @@ writingSingleLineHeight = {-| -} generateId : String -> String -generateId labelText = - "nri-ui-text-area-" ++ (dashify <| removePunctuation labelText) +generateId = + Extra.safeIdWithPrefix "nri-ui-text-area" diff --git a/src/Nri/Ui/TextInput/V7.elm b/src/Nri/Ui/TextInput/V7.elm index 1f14967d..4e2783e2 100644 --- a/src/Nri/Ui/TextInput/V7.elm +++ b/src/Nri/Ui/TextInput/V7.elm @@ -72,7 +72,6 @@ import Nri.Ui.Html.Attributes.V2 as Extra import Nri.Ui.InputStyles.V4 as InputStyles exposing (defaultMarginTop) import Nri.Ui.Svg.V1 as Svg import Nri.Ui.UiIcon.V1 as UiIcon -import Nri.Ui.Util exposing (dashify) import Time @@ -982,8 +981,8 @@ view label attributes = This is for use when you need the DOM element id for use in javascript (such as trigger an event to focus a particular text input) -} generateId : String -> String -generateId labelText = - "Nri-Ui-TextInput-" ++ dashify labelText +generateId = + Extra.safeIdWithPrefix "Nri-Ui-TextInput" type alias FloatingContentConfig msg = diff --git a/src/Nri/Ui/Tooltip/V3.elm b/src/Nri/Ui/Tooltip/V3.elm index 230af3ee..5f63864e 100644 --- a/src/Nri/Ui/Tooltip/V3.elm +++ b/src/Nri/Ui/Tooltip/V3.elm @@ -96,7 +96,6 @@ import Nri.Ui.Html.Attributes.V2 as ExtraAttributes import Nri.Ui.MediaQuery.V1 as MediaQuery exposing (mobileBreakpoint, narrowMobileBreakpoint, quizEngineBreakpoint) import Nri.Ui.Shadows.V1 as Shadows import Nri.Ui.UiIcon.V1 as UiIcon -import Nri.Ui.Util as Util import Nri.Ui.WhenFocusLeaves.V1 as WhenFocusLeaves @@ -883,7 +882,7 @@ viewToggleTip : { label : String, lastId : Maybe String } -> List (Attribute msg viewToggleTip { label, lastId } attributes_ = let id = - Util.safeIdString label + ExtraAttributes.safeId label triggerId = "tooltip-trigger__" ++ id diff --git a/src/Nri/Ui/Util.elm b/src/Nri/Ui/Util.elm deleted file mode 100644 index a0079a5b..00000000 --- a/src/Nri/Ui/Util.elm +++ /dev/null @@ -1,89 +0,0 @@ -module Nri.Ui.Util exposing (dashify, removePunctuation, safeIdString) - -import Regex exposing (Regex) - - -{-| Convenience method for going from a string with spaces to a string with dashes. --} -dashify : String -> String -dashify = - let - regex = - Regex.fromString " " - |> Maybe.withDefault Regex.never - in - Regex.replace regex (always "-") - - -{-| Convenience method for removing punctuation -(removes everything that isn't whitespace, alphanumeric, or an underscore). --} -removePunctuation : String -> String -removePunctuation = - let - regex = - Regex.fromString "[^A-Za-z0-9\\w\\s]" - |> Maybe.withDefault Regex.never - in - Regex.replace regex (always "") - - -{-| Creates a lowercased string that is safe to use for HTML IDs. -Ensures that nonletter characters are cut from the front and replaces bad characters with a dash --} -safeIdString : String -> String -safeIdString = - let - nonAlphaAtStart = - -- From the start of the string, match the contiguous block of - -- not ASCII letters at the start of the String. - -- Why [a-zA-Z] and not [A-z]? - -- There are punctuation characters in that range: [\]^_` all come after Z and before a - "^[^a-zA-Z]+" - - nonAlphaNumUnderscoreHyphenAnywhere = - -- any contiguous block of characters that aren't any of - -- + ASCII letters - -- + numbers - -- + underscore - -- + the hyphen-minus character commonly called "dash" (seen in between "hyphen" and "minus" on this line) - -- This does not need the + at the end; Regex.replace is global by default - -- but we pay a penalty for calling the replacement function, so - -- calling it once per contiguous group is an easy way to cut down on that. - "[^a-zA-Z0-9_-]+" - - anyOfThese strs = - "(" ++ String.join "|" strs ++ ")" - - unsafeChar = - [ nonAlphaAtStart - , nonAlphaNumUnderscoreHyphenAnywhere - ] - |> anyOfThese - |> regexFromString - - nonAlphaNumAtEnd = - -- any contiguous block of letters that aren't any of - -- + ASCII letters - -- + numbers - regexFromString "[^a-zA-Z0-9]+$" - - collapsePunctuationToOne = - regexFromString "[_-]+" - in - Regex.replace nonAlphaNumAtEnd (always "") - >> Regex.replace unsafeChar - (\{ index } -> - if index == 0 then - "" - - else - "-" - ) - >> Regex.replace collapsePunctuationToOne (always "-") - >> String.toLower - - -regexFromString : String -> Regex -regexFromString = - Regex.fromString >> Maybe.withDefault Regex.never diff --git a/src/TabsInternal.elm b/src/TabsInternal.elm index a6adf268..0072cb9e 100644 --- a/src/TabsInternal.elm +++ b/src/TabsInternal.elm @@ -16,7 +16,6 @@ import Html.Styled.Events as Events import Html.Styled.Keyed as Keyed import Json.Decode import Nri.Ui.Html.Attributes.V2 as AttributesExtra -import Nri.Ui.Util exposing (dashify) {-| -} @@ -53,7 +52,7 @@ viewTabs : Config id msg -> Html msg viewTabs config = Html.div [ Role.tabList - , Aria.owns (List.map (tabToId << .idString) config.tabs) + , Aria.owns (List.map (AttributesExtra.safeId << .idString) config.tabs) , Attributes.css config.tabListStyles ] (List.map (viewTab_ config) config.tabs) @@ -103,7 +102,7 @@ viewTab_ config tab = , Aria.selected isSelected , Role.tab , Aria.controls [ tabToBodyId tab.idString ] - , Attributes.id (tabToId tab.idString) + , Attributes.id (AttributesExtra.safeId tab.idString) , Events.onFocus (config.onSelect tab.id) , Events.on "keyup" <| Json.Decode.andThen (keyEvents config tab) Events.keyCode @@ -121,7 +120,7 @@ keyEvents { onFocus, tabs } thisTab keyCode = acc ( True, Nothing ) -> - ( True, Just (tabToId tab.idString) ) + ( True, Just (AttributesExtra.safeId tab.idString) ) ( False, Nothing ) -> ( tab.id == thisTab.id, Nothing ) @@ -173,7 +172,7 @@ viewTabPanel : Tab id msg -> Bool -> Html msg viewTabPanel tab selected = Html.div ([ Role.tabPanel - , Aria.labelledBy (tabToId tab.idString) + , Aria.labelledBy (AttributesExtra.safeId tab.idString) , Attributes.id (tabToBodyId tab.idString) ] ++ (if selected then @@ -191,16 +190,11 @@ viewTabPanel tab selected = [ tab.panelView ] -tabToId : String -> String -tabToId tab = - dashify (String.toLower tab) - - tabToBodyId : String -> String -tabToBodyId tab = - "tab-body-" ++ tabToId tab +tabToBodyId = + AttributesExtra.safeIdWithPrefix "tab-body" tabToKeyedNode : String -> String -tabToKeyedNode tab = - "tabs-internal-keyed-node-" ++ tabToId tab +tabToKeyedNode = + AttributesExtra.safeIdWithPrefix "tabs-internal-keyed-node" diff --git a/src/TabsInternal/V2.elm b/src/TabsInternal/V2.elm index cc8749d7..b43582c8 100644 --- a/src/TabsInternal/V2.elm +++ b/src/TabsInternal/V2.elm @@ -20,9 +20,8 @@ import Html.Styled.Attributes as Attributes import Html.Styled.Events as Events import Html.Styled.Keyed as Keyed import Nri.Ui.FocusRing.V1 as FocusRing -import Nri.Ui.Html.Attributes.V2 as AttributesExtra +import Nri.Ui.Html.Attributes.V2 as AttributesExtra exposing (safeId, safeIdWithPrefix) import Nri.Ui.Tooltip.V3 as Tooltip -import Nri.Ui.Util exposing (dashify) {-| -} @@ -93,7 +92,7 @@ viewTabs config = Html.div [] [ Html.div [ Role.tabList - , Aria.owns (List.map (tabToId << .idString) config.tabs) + , Aria.owns (List.map (safeId << .idString) config.tabs) ] [] , Html.div [ Attributes.css config.tabListStyles ] @@ -161,7 +160,7 @@ viewTab_ config index tab = , Aria.selected isSelected , Role.tab , Aria.controls [ tabToBodyId tab.idString ] - , Attributes.id (tabToId tab.idString) + , Attributes.id (safeId tab.idString) , Key.onKeyUpPreventDefault (keyEvents config tab) ] ++ (case tab.labelledBy of @@ -193,7 +192,7 @@ viewTab_ config index tab = ( Nothing, tooltipAttributes ) -> Tooltip.view - { id = "tab-tooltip__" ++ tabToId tab.idString + { id = safeIdWithPrefix "tab-tooltip" tab.idString , trigger = \eventHandlers -> buttonOrLink eventHandlers } ([ Tooltip.smallPadding @@ -209,7 +208,7 @@ keyEvents { focusAndSelect, tabs } thisTab = let onFocus : Tab id msg -> msg onFocus tab = - focusAndSelect { select = tab.id, focus = Just (tabToId tab.idString) } + focusAndSelect { select = tab.id, focus = Just (safeId tab.idString) } findAdjacentTab : Tab id msg -> ( Bool, Maybe msg ) -> ( Bool, Maybe msg ) findAdjacentTab tab ( isAdjacentTab, acc ) = @@ -265,7 +264,7 @@ viewTabPanel : Tab id msg -> Bool -> Html msg viewTabPanel tab selected = Html.div ([ Role.tabPanel - , Aria.labelledBy (tabToId tab.idString) + , Aria.labelledBy (safeId tab.idString) , Attributes.id (tabToBodyId tab.idString) , Attributes.tabindex 0 , Attributes.class FocusRing.customClass @@ -287,16 +286,11 @@ viewTabPanel tab selected = [ tab.panelView ] -tabToId : String -> String -tabToId tab = - dashify (String.toLower tab) - - tabToBodyId : String -> String -tabToBodyId tab = - "tab-body-" ++ tabToId tab +tabToBodyId = + safeIdWithPrefix "tab-body" tabToKeyedNode : String -> String -tabToKeyedNode tab = - "tabs-internal-keyed-node-" ++ tabToId tab +tabToKeyedNode = + safeIdWithPrefix "tabs-internal-keyed-node" diff --git a/tests/Spec/Nri/Ui/Html/Attributes.elm b/tests/Spec/Nri/Ui/Html/Attributes.elm new file mode 100644 index 00000000..5493251b --- /dev/null +++ b/tests/Spec/Nri/Ui/Html/Attributes.elm @@ -0,0 +1,29 @@ +module Spec.Nri.Ui.Html.Attributes exposing (spec) + +import Expect +import Nri.Ui.Html.Attributes.V2 as Attributes +import Test exposing (..) + + +spec : Test +spec = + describe "Nri.Ui.Html.Attributes" + [ describe "safeId transforms strings as expected" + [ test "with an apostrophe and multiple spaces" <| + \() -> + Attributes.safeId "Enable text-to-speech for \t Angela's account" + |> Expect.equal "id-Enable-text-to-speech-for-Angela-s-account" + , test "lotsa hyphens and dashes" <| + \() -> + Attributes.safeId "--__--hellO----_______---HOw----___---____--ArE------___You___--__--__Today" + |> Expect.equal "id---__--hellO----_______---HOw----___---____--ArE------___You___--__--__Today" + ] + , describe "safeIdWithPrefix" + [ test "test everything at once" <| + \() -> + Attributes.safeIdWithPrefix + "000321 ¡¡VERY!! unsafe " + "0--__--hellO----___!?[]____---HOw----___---____--ArE------___You___--__--__Today?" + |> Expect.equal "id-000321-VERY-unsafe--0--__--hellO----___-____---HOw----___---____--ArE------___You___--__--__Today-" + ] + ] diff --git a/tests/Spec/Nri/Ui/Select.elm b/tests/Spec/Nri/Ui/Select.elm index 6a254de2..24e30384 100644 --- a/tests/Spec/Nri/Ui/Select.elm +++ b/tests/Spec/Nri/Ui/Select.elm @@ -16,19 +16,19 @@ spec = ungrouped -- first option is selected automatically |> ensureViewHas - [ id "nri-select-cat" + [ id "id-nri-select--Cat" , selected True , attribute (Attributes.value "Cat") ] |> selectAnimal Dog |> ensureViewHas - [ id "nri-select-dog" + [ id "id-nri-select--Dog" , selected True , attribute (Attributes.value "Dog") ] |> selectAnimal Other |> ensureViewHas - [ id "nri-select-my-favorite-animal-is-something-else" + [ id "id-nri-select--My-favorite-animal-is-something-else" , selected True , attribute (Attributes.value "My favorite animal is something else") ] @@ -38,25 +38,25 @@ spec = grouped -- first option is selected automatically |> ensureViewHas - [ id "nri-select-cat" + [ id "id-nri-select--Cat" , selected True , attribute (Attributes.value "Cat") ] |> selectAnimal Dog |> ensureViewHas - [ id "nri-select-dog" + [ id "id-nri-select--Dog" , selected True , attribute (Attributes.value "Dog") ] |> selectAnimal Axolotl |> ensureViewHas - [ id "nri-select-axolotl" + [ id "id-nri-select--Axolotl" , selected True , attribute (Attributes.value "Axolotl") ] |> selectAnimal Other |> ensureViewHas - [ id "nri-select-my-favorite-animal-is-something-else" + [ id "id-nri-select--My-favorite-animal-is-something-else" , selected True , attribute (Attributes.value "My favorite animal is something else") ] diff --git a/tests/Spec/Nri/Ui/Util.elm b/tests/Spec/Nri/Ui/Util.elm deleted file mode 100644 index e2b83a53..00000000 --- a/tests/Spec/Nri/Ui/Util.elm +++ /dev/null @@ -1,44 +0,0 @@ -module Spec.Nri.Ui.Util exposing (spec) - -import Expect -import Nri.Ui.Util as Util -import Test exposing (..) - - -spec : Test -spec = - describe "Nri.Ui.Util" - [ describe "safeIdString transforms strings as expected" - [ test "with a comma" <| - \() -> - Util.safeIdString "Enable text-to-speech for Angela's account" - |> Expect.equal "enable-text-to-speech-for-angela-s-account" - , test "removes leading non alpha characters" <| - \() -> - Util.safeIdString "#@!!232now we are in business__--__" - |> Expect.equal "now-we-are-in-business" - , test "removes trailing non alphanum characters" <| - \() -> - Util.safeIdString "#@!!232now we are in business__--__123!!@&*%^ @" - |> Expect.equal "now-we-are-in-business-123" - , test "hard mode" <| - \() -> - Util.safeIdString "!@#21something else interesting321$ ... hi" - |> Expect.equal "something-else-interesting321-hi" - , test "with capital letters" <| - \() -> - Util.safeIdString "1232!@#%#@JFEKLfds-----SFJK3@#@jj23FDS........''''\"\"***" - |> Expect.equal "jfeklfds-sfjk3-jj23fds" - , test "lotsa hyphens and dashes" <| - \() -> - Util.safeIdString "--__--hellO----_______---HOw----___---____--ArE------___You___--__--__Today" - |> Expect.equal "hello-how-are-you-today" - ] - , describe "removePunctuation" - [ test "A string with some punctuation" <| - \() -> - Util.removePunctuation - "To sleep? Perchance: to dream? “Alas poor ‘Yorick’” but he's not `in` _this_ \"[play]\"." - |> Expect.equal "To sleep Perchance to dream Alas poor Yorick but hes not in _this_ play" - ] - ]