diff --git a/Guide/hsx.markdown b/Guide/hsx.markdown index 08be852e8..0ff71ce0c 100644 --- a/Guide/hsx.markdown +++ b/Guide/hsx.markdown @@ -338,6 +338,47 @@ The [`preEscapedToHtml`](https://ihp.digitallyinduced.com/api-docs/IHP-ViewPrelu {"" |> preEscapedToHtml} ``` +#### HTML Attributes without Escaping + +Variable attributes are also escaped: + +```haskell +html = [hsx| +
+ Hello World +
+|] + where + style = "someClassName&" :: Text +``` + +The above code will generate the following HTML, where the `&` symbol has been replaced with it's escpaed form `&`: + +```html +
Hello World
+``` + +You might want to have this `&` character not escaped. E.g. to use [Tailwind Arbitrary Variants](https://tailwindcss.com/blog/tailwindcss-v3-1#arbitrary-values-but-for-variants). In that case wrap the attribute value with a call to `preEscapedTextValue`: + + +```haskell +html = [hsx| +
+ Hello World +
+|] + where + style = preEscapedTextValue "someClassName&" +``` + +The HTML will now render without escaping the attribute: + +```html +
Hello World
+``` + +Keep in mind that you're now responsible for making sure that there's no bad input inside the string passed to `preEscapedTextValue`. You might accidentally open the door for XSS. + ## Example: HSX and the equivalent BlazeHtml The following code using HSX: diff --git a/IHP/ViewPrelude.hs b/IHP/ViewPrelude.hs index d372abba3..b4d3c7d06 100644 --- a/IHP/ViewPrelude.hs +++ b/IHP/ViewPrelude.hs @@ -13,6 +13,7 @@ module IHP.ViewPrelude ( hsx, toHtml, preEscapedToHtml, + preEscapedTextValue, module IHP.ValidationSupport, pathTo, urlTo, @@ -41,7 +42,7 @@ import IHP.Prelude import IHP.ViewErrorMessages () import IHP.ViewSupport import Text.Blaze (preEscapedText, stringValue, text, (!)) -import Text.Blaze.Html5 (preEscapedToHtml) +import Text.Blaze.Html5 (preEscapedToHtml, preEscapedTextValue) import IHP.View.Form import IHP.HSX.QQ (hsx) import IHP.HSX.ToHtml diff --git a/Test/HSX/QQSpec.hs b/Test/HSX/QQSpec.hs index cd0670e7a..d42be1f22 100644 --- a/Test/HSX/QQSpec.hs +++ b/Test/HSX/QQSpec.hs @@ -8,6 +8,7 @@ import Test.Hspec import IHP.Prelude import IHP.HSX.QQ import qualified Text.Blaze.Renderer.Text as Blaze +import Text.Blaze (preEscapedTextValue) tests = do describe "HSX" do @@ -163,6 +164,12 @@ tests = do -- See https://github.com/digitallyinduced/ihp/issues/1226 [hsx|
|] `shouldBeHtml` "
" + + it "should support pre escaped class names" do + -- See https://github.com/digitallyinduced/ihp/issues/1527 + + let className = preEscapedTextValue "a&" + [hsx|
|] `shouldBeHtml` "
" data Project = Project { name :: Text } diff --git a/ihp-hsx/IHP/HSX/ConvertibleStrings.hs b/ihp-hsx/IHP/HSX/ConvertibleStrings.hs index 47449500b..f81bc4065 100644 --- a/ihp-hsx/IHP/HSX/ConvertibleStrings.hs +++ b/ihp-hsx/IHP/HSX/ConvertibleStrings.hs @@ -36,4 +36,8 @@ instance ConvertibleStrings LBS.ByteString Html5.AttributeValue where instance ConvertibleStrings Text Html5.Html where {-# INLINE convertString #-} - convertString = Html5.text \ No newline at end of file + convertString = Html5.text + +instance ConvertibleStrings Html5.AttributeValue Html5.AttributeValue where + {-# INLINE convertString #-} + convertString value = value