v4, beta5 (#20)
Reviewed-on: #20
This commit was merged in pull request #20.
This commit is contained in:
@@ -10,11 +10,11 @@ htmx uses attributes and HTTP headers to attain its interactivity; the libraries
|
||||
|
||||
`Giraffe.Htmx` provides extensions that facilitate using htmx on the server side, primarily reading and setting headers. `Giraffe.ViewEngine.Htmx` provides attributes and helpers to produce views that utilize htmx. Both can be installed from NuGet via standard methods.
|
||||
|
||||
| Server Side | View Engine |
|
||||
|---|---|
|
||||
|[](https://www.nuget.org/packages/Giraffe.Htmx/)|[](https://www.nuget.org/packages/Giraffe.ViewEngine.Htmx/)|
|
||||
| Server Side | View Engine |
|
||||
|------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| [](https://www.nuget.org/packages/Giraffe.Htmx/) | [](https://www.nuget.org/packages/Giraffe.ViewEngine.Htmx/) |
|
||||
|
||||
Both of these packages will also install `Giraffe.Htmx.Common`, which has some common definitions and provides a local-to-your-project version of the htmx JavaScript _(as of v2.0.8)_.
|
||||
Both of these packages will also install `Giraffe.Htmx.Common`, which has some common definitions and provides a local-to-your-project version of the htmx JavaScript _(as of v2.0.8)_ and htmax htmx-plus-extensions bundle _(as of v4.0.0-beta4)_.
|
||||
|
||||
## Server Side (`Giraffe.Htmx`)
|
||||
|
||||
@@ -74,7 +74,7 @@ If you want to use the package-provided htmx library, `Htmx.Script.local` will c
|
||||
|
||||
## Feedback / Help
|
||||
|
||||
The author hangs out in the #dotnet-htmx channel (and most others) of the [htmx Discord server](https://htmx.org/discord) and the #web channel of the [F# Software Foundation's Slack server](https://fsharp.org/guides/slack/).
|
||||
The author hangs out in the #dotnet-htmx channel (and most others) of the [htmx Discord server](https://htmx.org/discord) and the #web channel (and most others) of the [F# Community Discord server](https://discord.gg/R6n7c54).
|
||||
|
||||
## Thanks
|
||||
|[<img src="https://giraffe.wiki/giraffe.png" alt="Giraffe logo" width="200">](https://giraffe.wiki)| [<img src="https://bitbadger.solutions/upload/bit-badger/2024/01/htmx-black-transparent.svg" alt="htmx logo" width="200">](https://htmx.org) |[<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.png" alt="JetBrains Logo (Main)" width="200">](https://jb.gg/OpenSource)|
|
||||
|
||||
+113
-111
@@ -1,111 +1,113 @@
|
||||
/// <summary>Common definitions shared between attribute values and response headers</summary>
|
||||
[<AutoOpen>]
|
||||
module Giraffe.Htmx.Common
|
||||
|
||||
/// <summary>The version of htmx embedded in the package</summary>
|
||||
let HtmxVersion = "4.0.0-beta4"
|
||||
|
||||
/// <summary>The path for the provided htmx script</summary>
|
||||
let internal htmxLocalScript = $"/_content/Giraffe.Htmx.Common/htmx.min.js?ver={HtmxVersion}"
|
||||
|
||||
/// <summary>The path for the provided htmax script</summary>
|
||||
let internal htmaxLocalScript = $"/_content/Giraffe.Htmx.Common/htmax.min.js?ver={HtmxVersion}"
|
||||
|
||||
/// <summary>Serialize a list of key/value pairs to JSON (very rudimentary)</summary>
|
||||
/// <param name="pairs">The key/value pairs to be serialized to JSON</param>
|
||||
/// <returns>A string with the key/value pairs serialized to JSON</returns>
|
||||
let internal toJson (pairs: (string * string) list) =
|
||||
pairs
|
||||
|> List.map (fun pair -> sprintf "\"%s\": \"%s\"" (fst pair) ((snd pair).Replace ("\"", "\\\"")))
|
||||
|> String.concat ", "
|
||||
|> sprintf "{ %s }"
|
||||
|
||||
/// <summary>Convert a boolean to lowercase "true" or "false"</summary>
|
||||
/// <param name="boolValue">The boolean value to convert</param>
|
||||
/// <returns>"true" for <c>true</c>, "false" for <c>false</c></returns>
|
||||
let internal toLowerBool (boolValue: bool) =
|
||||
(string boolValue).ToLowerInvariant()
|
||||
|
||||
|
||||
/// <summary>Valid values for the <c>hx-swap</c> attribute / <c>HX-Reswap</c> header</summary>
|
||||
/// <remarks>May be combined with <c>swap</c> / <c>scroll</c> / <c>show</c> config)</remarks>
|
||||
/// <seealso href="https://four.htmx.org/attributes/hx-swap/">Documentation</seealso>
|
||||
[<RequireQualifiedAccess>]
|
||||
module HxSwap =
|
||||
|
||||
/// <summary>The default, replace the inner HTML of the target element</summary>
|
||||
[<Literal>]
|
||||
let InnerHtml = "innerHTML"
|
||||
|
||||
/// <summary>Replace the entire target element with the response</summary>
|
||||
[<Literal>]
|
||||
let OuterHtml = "outerHTML"
|
||||
|
||||
/// <summary>Morph the inner HTML of the target to the new content</summary>
|
||||
[<Literal>]
|
||||
let InnerMorph = "innerMorph"
|
||||
|
||||
/// <summary>Morph the outer HTML of the target to the new content</summary>
|
||||
[<Literal>]
|
||||
let OuterMorph = "outerMorph"
|
||||
|
||||
/// <summary>Morph the outer HTML of the target to the new content, recreating all children</summary>
|
||||
/// <remarks>This is used internally by the new history extension, but can be used by others if desired</remarks>
|
||||
[<Literal>]
|
||||
let OuterSync = "outerSync"
|
||||
|
||||
/// <summary>Replace the text content of the target without parsing the response as HTML</summary>
|
||||
[<Literal>]
|
||||
let TextContent = "textContent"
|
||||
|
||||
/// <summary>Insert the response before the target element</summary>
|
||||
[<Literal>]
|
||||
let Before = "before"
|
||||
|
||||
/// <summary>Insert the response before the target element (pre-v4 name)</summary>
|
||||
[<Literal>]
|
||||
let BeforeBegin = Before
|
||||
|
||||
/// <summary>Insert the response before the first child of the target element</summary>
|
||||
[<Literal>]
|
||||
let Prepend = "prepend"
|
||||
|
||||
/// <summary>Insert the response before the first child of the target element (pre-v4 name)</summary>
|
||||
[<Literal>]
|
||||
let AfterBegin = Prepend
|
||||
|
||||
/// <summary>Insert the response after the last child of the target element</summary>
|
||||
[<Literal>]
|
||||
let Append = "append"
|
||||
|
||||
/// <summary>Insert the response after the last child of the target element (pre-v4 name)</summary>
|
||||
[<Literal>]
|
||||
let BeforeEnd = Append
|
||||
|
||||
/// <summary>Insert the response after the target element</summary>
|
||||
[<Literal>]
|
||||
let After = "after"
|
||||
|
||||
/// <summary>Insert the response after the target element (pre-v4 name)</summary>
|
||||
[<Literal>]
|
||||
let AfterEnd = After
|
||||
|
||||
/// <summary>Delete the target element regardless of response</summary>
|
||||
[<Literal>]
|
||||
let Delete = "delete"
|
||||
|
||||
/// <summary>Does not append content from response (out of band items will still be processed)</summary>
|
||||
[<Literal>]
|
||||
let None = "none"
|
||||
|
||||
/// <summary>Update existing elements by <c>id</c> and add new ones</summary>
|
||||
/// <remarks>This requires the <c>upsert</c> extension</remarks>
|
||||
/// <seealso href="https://four.htmx.org/extensions/upsert">Extension</seealso>
|
||||
[<Literal>]
|
||||
let Upsert = "upsert"
|
||||
|
||||
/// <summary>Specify that the target of the htmx request should be downloaded</summary>
|
||||
/// <remarks>This requires the <c>hx-download</c> extension (included in the htmax bundle)</remarks>
|
||||
/// <seealso href="https://four.htmx.org/extensions/hx-download#explicit-swap-style">Documentation</seealso>
|
||||
[<Literal>]
|
||||
let Download = "download"
|
||||
/// <summary>Common definitions shared between attribute values and response headers</summary>
|
||||
[<AutoOpen>]
|
||||
module Giraffe.Htmx.Common
|
||||
|
||||
/// <summary>The version of htmx embedded in the package</summary>
|
||||
let HtmxVersion = "4.0.0-beta5"
|
||||
|
||||
/// <summary>URLs for the included htmx library static web assets</summary>
|
||||
module StaticAssetUrl =
|
||||
/// <summary>The path for the provided htmx script</summary>
|
||||
let htmx = $"/_content/Giraffe.Htmx.Common/htmx.min.js?ver={HtmxVersion}"
|
||||
|
||||
/// <summary>The path for the provided htmax script</summary>
|
||||
let htmax = $"/_content/Giraffe.Htmx.Common/htmax.min.js?ver={HtmxVersion}"
|
||||
|
||||
/// <summary>Serialize a list of key/value pairs to JSON (very rudimentary)</summary>
|
||||
/// <param name="pairs">The key/value pairs to be serialized to JSON</param>
|
||||
/// <returns>A string with the key/value pairs serialized to JSON</returns>
|
||||
let internal toJson (pairs: (string * string) list) =
|
||||
pairs
|
||||
|> List.map (fun pair -> sprintf "\"%s\": \"%s\"" (fst pair) ((snd pair).Replace ("\"", "\\\"")))
|
||||
|> String.concat ", "
|
||||
|> sprintf "{ %s }"
|
||||
|
||||
/// <summary>Convert a boolean to lowercase "true" or "false"</summary>
|
||||
/// <param name="boolValue">The boolean value to convert</param>
|
||||
/// <returns>"true" for <c>true</c>, "false" for <c>false</c></returns>
|
||||
let internal toLowerBool (boolValue: bool) =
|
||||
(string boolValue).ToLowerInvariant()
|
||||
|
||||
|
||||
/// <summary>Valid values for the <c>hx-swap</c> attribute / <c>HX-Reswap</c> header</summary>
|
||||
/// <remarks>May be combined with <c>swap</c> / <c>scroll</c> / <c>show</c> config)</remarks>
|
||||
/// <seealso href="https://four.htmx.org/attributes/hx-swap/">Documentation</seealso>
|
||||
[<RequireQualifiedAccess>]
|
||||
module HxSwap =
|
||||
|
||||
/// <summary>The default, replace the inner HTML of the target element</summary>
|
||||
[<Literal>]
|
||||
let InnerHtml = "innerHTML"
|
||||
|
||||
/// <summary>Replace the entire target element with the response</summary>
|
||||
[<Literal>]
|
||||
let OuterHtml = "outerHTML"
|
||||
|
||||
/// <summary>Morph the inner HTML of the target to the new content</summary>
|
||||
[<Literal>]
|
||||
let InnerMorph = "innerMorph"
|
||||
|
||||
/// <summary>Morph the outer HTML of the target to the new content</summary>
|
||||
[<Literal>]
|
||||
let OuterMorph = "outerMorph"
|
||||
|
||||
/// <summary>Morph the outer HTML of the target to the new content, recreating all children</summary>
|
||||
/// <remarks>This is used internally by the new history extension, but can be used by others if desired</remarks>
|
||||
[<Literal>]
|
||||
let OuterSync = "outerSync"
|
||||
|
||||
/// <summary>Replace the text content of the target without parsing the response as HTML</summary>
|
||||
[<Literal>]
|
||||
let TextContent = "textContent"
|
||||
|
||||
/// <summary>Insert the response before the target element</summary>
|
||||
[<Literal>]
|
||||
let Before = "before"
|
||||
|
||||
/// <summary>Insert the response before the target element (pre-v4 name)</summary>
|
||||
[<Literal>]
|
||||
let BeforeBegin = Before
|
||||
|
||||
/// <summary>Insert the response before the first child of the target element</summary>
|
||||
[<Literal>]
|
||||
let Prepend = "prepend"
|
||||
|
||||
/// <summary>Insert the response before the first child of the target element (pre-v4 name)</summary>
|
||||
[<Literal>]
|
||||
let AfterBegin = Prepend
|
||||
|
||||
/// <summary>Insert the response after the last child of the target element</summary>
|
||||
[<Literal>]
|
||||
let Append = "append"
|
||||
|
||||
/// <summary>Insert the response after the last child of the target element (pre-v4 name)</summary>
|
||||
[<Literal>]
|
||||
let BeforeEnd = Append
|
||||
|
||||
/// <summary>Insert the response after the target element</summary>
|
||||
[<Literal>]
|
||||
let After = "after"
|
||||
|
||||
/// <summary>Insert the response after the target element (pre-v4 name)</summary>
|
||||
[<Literal>]
|
||||
let AfterEnd = After
|
||||
|
||||
/// <summary>Delete the target element regardless of response</summary>
|
||||
[<Literal>]
|
||||
let Delete = "delete"
|
||||
|
||||
/// <summary>Does not append content from response (out of band items will still be processed)</summary>
|
||||
[<Literal>]
|
||||
let None = "none"
|
||||
|
||||
/// <summary>Update existing elements by <c>id</c> and add new ones</summary>
|
||||
/// <remarks>This requires the <c>upsert</c> extension</remarks>
|
||||
/// <seealso href="https://four.htmx.org/extensions/upsert">Extension</seealso>
|
||||
[<Literal>]
|
||||
let Upsert = "upsert"
|
||||
|
||||
/// <summary>Specify that the target of the htmx request should be downloaded</summary>
|
||||
/// <remarks>This requires the <c>hx-download</c> extension (included in the htmax bundle)</remarks>
|
||||
/// <seealso href="https://four.htmx.org/extensions/hx-download#explicit-swap-style">Documentation</seealso>
|
||||
[<Literal>]
|
||||
let Download = "download"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
## Giraffe.Htmx.Common
|
||||
|
||||
This package contains common code shared between [`Giraffe.Htmx`](https://www.nuget.org/packages/Giraffe.Htmx) and [`Giraffe.ViewEngine.Htmx`](https://www.nuget.org/packages/Giraffe.ViewEngine.Htmx), and will be automatically installed when you install either one. It also contains htmx as a static web asset, allowing it to be loaded from your local (or published) project.
|
||||
This package contains common code shared between [`Giraffe.Htmx`](https://www.nuget.org/packages/Giraffe.Htmx) and [`Giraffe.ViewEngine.Htmx`](https://www.nuget.org/packages/Giraffe.ViewEngine.Htmx), and will be automatically installed when you install either one. It also contains htmx and htmax as static web assets, allowing them to be loaded from your local (or published) project.
|
||||
|
||||
**htmx version: 4.0.0-beta4**
|
||||
**htmx version: 4.0.0-beta5**
|
||||
|
||||
_**NOTE:** Pay special attention to breaking changes highlighted in the packages listed above._
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -3,15 +3,14 @@
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
|
||||
<VersionPrefix>4.0.0</VersionPrefix>
|
||||
<VersionSuffix>beta4</VersionSuffix>
|
||||
<VersionSuffix>beta5</VersionSuffix>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<PackageReleaseNotes>Update htmx 4 to beta4
|
||||
- [Common] Update provided htmx 4 to 4.0.0-beta4
|
||||
- [Common] Add htmax bundle to provided version
|
||||
- [Server] Update HX-Target header to return tag and ID (similar to HX-Source)
|
||||
- [Server] Add support for HX-Request-Type header
|
||||
- [View Engine] Add hx-status attribute, outerSync swap
|
||||
- [View Engine] Updated script tags to pull htmx 4.0.0-beta4, added "max" bundle links for local and CDN
|
||||
<PackageReleaseNotes>Update htmx 4 to beta5
|
||||
- [Common] Update provided htmx/htmax 4 to 4.0.0-beta5
|
||||
- [Common] Add StaticAssetUrl module with static asset paths for htmx and htmax
|
||||
- [Server] Unobsolete HX-Prompt header, note that it requires hx-prompt extension
|
||||
- [View Engine] Unobsolete hx-prompt attribute, note that it requires hx-prompt extension
|
||||
- [View Engine] Updated CDN script tags to pull htmx / htmax 4.0.0-beta5
|
||||
|
||||
See package and prior alpha release READMEs; v2 to v4 is not an update-and-forget-it release
|
||||
</PackageReleaseNotes>
|
||||
|
||||
+19
-17
@@ -8,7 +8,7 @@ open System
|
||||
type HxRequestTypes =
|
||||
/// <summary>A request targeting the <c>body</c> tag or using an <c>hx-select</c> attribute</summary>
|
||||
| HxFullRequest
|
||||
|
||||
|
||||
/// <summary>A request for partial content</summary>
|
||||
| HxPartialRequest
|
||||
|
||||
@@ -18,15 +18,15 @@ let private hdr (headers : IHeaderDictionary) hdr =
|
||||
|
||||
/// Extensions to the header dictionary
|
||||
type IHeaderDictionary with
|
||||
|
||||
|
||||
/// <summary>Indicates that the request is via an element using <c>hx-boost</c></summary>
|
||||
member this.HxBoosted
|
||||
with get () = hdr this "HX-Boosted" |> Option.map bool.Parse
|
||||
|
||||
|
||||
/// <summary>The current URL of the browser <em>(note that this does not update until after settle)</em></summary>
|
||||
member this.HxCurrentUrl
|
||||
with get () = hdr this "HX-Current-URL" |> Option.map Uri
|
||||
|
||||
|
||||
/// <summary><c>true</c> if the request is for history restoration after a miss in the local history cache</summary>
|
||||
member this.HxHistoryRestoreRequest
|
||||
with get () = hdr this "HX-History-Restore-Request" |> Option.map bool.Parse
|
||||
@@ -35,9 +35,9 @@ type IHeaderDictionary with
|
||||
/// <remarks><c>preload</c> is part of the htmax htmx-plus-extensions bundle</remarks>
|
||||
member this.HxPreloaded
|
||||
with get () = hdr this "HX-Preloaded" |> Option.map bool.Parse
|
||||
|
||||
|
||||
/// <summary>The user response to an <c>hx-prompt</c></summary>
|
||||
[<Obsolete "hx-prompt is removed in v4">]
|
||||
/// <remarks><b>NEW IN v4:</b> This functionality is dependent on the hx-prompt extension being loaded</remarks>
|
||||
member this.HxPrompt
|
||||
with get () = hdr this "HX-Prompt"
|
||||
|
||||
@@ -49,7 +49,7 @@ type IHeaderDictionary with
|
||||
/// <remarks><c>hx-ws</c> is part of the htmax htmx-plus-extensions bundle</remarks>
|
||||
member this.HxRequestId
|
||||
with get () = hdr this "HX-Request-ID"
|
||||
|
||||
|
||||
/// <summary>The request type sent by htmx</summary>
|
||||
/// <seealso cref="HxRequestTypes" />
|
||||
member this.HxRequestType
|
||||
@@ -59,7 +59,7 @@ type IHeaderDictionary with
|
||||
| Some typ when typ = "partial" -> Some HxPartialRequest
|
||||
| Some _ -> None
|
||||
| None -> None
|
||||
|
||||
|
||||
/// <summary>The tag name (fst) and <c>id</c> attribute (snd) of the element triggering this request</summary>
|
||||
member this.HxSource
|
||||
with get () =
|
||||
@@ -119,7 +119,7 @@ type HttpRequest with
|
||||
module Handlers =
|
||||
|
||||
open Giraffe.Htmx.Common
|
||||
|
||||
|
||||
/// <summary>Instruct htmx to download a response from another path / URL</summary>
|
||||
/// <param name="path">The path or URL where the downloadable content is found</param>
|
||||
/// <returns>An HTTP handler with the <c>HX-Download</c> header set</returns>
|
||||
@@ -127,14 +127,14 @@ module Handlers =
|
||||
/// <seealso href="https://four.htmx.org/extensions/hx-download#hx-download-header">Documentation</seealso>
|
||||
let withHxDownload (path: string) : HttpHandler =
|
||||
setHttpHeader "HX-Download" path
|
||||
|
||||
|
||||
/// <summary>Instruct htmx to perform a client-side redirect for content</summary>
|
||||
/// <param name="path">The path where the content should be found</param>
|
||||
/// <returns>An HTTP handler with the <c>HX-Location</c> header set</returns>
|
||||
/// <seealso href="https://htmx.org/headers/hx-location/">Documentation</seealso>
|
||||
let withHxLocation (path: string) : HttpHandler =
|
||||
setHttpHeader "HX-Location" path
|
||||
|
||||
|
||||
/// <summary>Pushes a new url into the history stack</summary>
|
||||
/// <param name="url">The URL to be pushed</param>
|
||||
/// <returns>An HTTP handler with the <c>HX-Push-Url</c> header set</returns>
|
||||
@@ -148,7 +148,7 @@ module Handlers =
|
||||
/// <seealso href="https://htmx.org/headers/hx-push-url/">Documentation</seealso>
|
||||
let withHxNoPushUrl : HttpHandler =
|
||||
toLowerBool false |> withHxPushUrl
|
||||
|
||||
|
||||
/// <summary>Can be used to do a client-side redirect to a new location</summary>
|
||||
/// <param name="url">The URL to which the client should be redirected</param>
|
||||
/// <returns>An HTTP handler with the <c>HX-Redirect</c> header set</returns>
|
||||
@@ -175,13 +175,13 @@ module Handlers =
|
||||
/// <seealso href="https://htmx.org/headers/hx-replace-url/">Documentation</seealso>
|
||||
let withHxNoReplaceUrl : HttpHandler =
|
||||
toLowerBool false |> withHxReplaceUrl
|
||||
|
||||
|
||||
/// <summary>Override which portion of the response will be swapped into the target document</summary>
|
||||
/// <param name="target">The selector for the new response target</param>
|
||||
/// <returns>An HTTP handler with the <c>HX-Reselect</c> header set</returns>
|
||||
let withHxReselect (target: string) : HttpHandler =
|
||||
setHttpHeader "HX-Reselect" target
|
||||
|
||||
|
||||
/// <summary>Override the <c>hx-swap</c> attribute from the initiating element</summary>
|
||||
/// <param name="swap">The swap value to override</param>
|
||||
/// <returns>An HTTP handler with the <c>HX-Reswap</c> header set</returns>
|
||||
@@ -245,10 +245,12 @@ module Handlers =
|
||||
/// <summary>Load the package-provided version of the htmx script</summary>
|
||||
[<RequireQualifiedAccess>]
|
||||
module HtmxScript =
|
||||
|
||||
|
||||
open Giraffe.Htmx.Common
|
||||
open Microsoft.AspNetCore.Html
|
||||
|
||||
/// <summary><c>script</c> tag to load the package-provided version of the htmx script</summary>
|
||||
let local = HtmlString $"""<script src="{htmxLocalScript}"></script>"""
|
||||
|
||||
let local = HtmlString $"""<script src="{StaticAssetUrl.htmx}"></script>"""
|
||||
|
||||
/// <summary><c>script</c> tag to load the package-provided version of the htmax script</summary>
|
||||
let localMax = HtmlString $"""<script src="{StaticAssetUrl.htmax}"></script>"""
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
|
||||
This package enables server-side support for [htmx](https://htmx.org) within [Giraffe](https://giraffe.wiki) and ASP.NET's `HttpContext`.
|
||||
|
||||
**htmx version: 4.0.0-beta4**
|
||||
**htmx version: 4.0.0-beta5**
|
||||
|
||||
_Upgrading from v2.x: the [migration guide](https://four.htmx.org/docs/get-started/migration) lists changes for v4. For this package, the `HX-Trigger` and `HX-Trigger-Name` headers are marked obsolete. They are replaced by `HX-Source`, which provides the triggering tag name and `id` attribute. The `HX-Prompt` header has also been marked as obsolete, as the `hx-prompt` attribute which generated its content has been removed._
|
||||
|
||||
@@ -36,7 +36,7 @@ let myHandler : HttpHander =
|
||||
|
||||
The `HxSwap` module has constants to use for the `HX-Reswap` header. These may be extended with settle, show, and other qualifiers; see the htmx documentation for the `hx-swap` attribute for more information.
|
||||
|
||||
To load the package-provided htmx library without using Giraffe.ViewEngine, use `HtmxScript.local`.
|
||||
To load the package-provided htmx library without using Giraffe.ViewEngine, use `HtmxScript.local`; to load the htmax bundle, use `HtmxScript.localMax`.
|
||||
|
||||
### Learn
|
||||
|
||||
|
||||
+18
-2
@@ -6,9 +6,25 @@ open Giraffe.Htmx
|
||||
/// Test to ensure the version was updated
|
||||
let version =
|
||||
test "HtmxVersion is correct" {
|
||||
Expect.equal HtmxVersion "4.0.0-beta4" "htmx version incorrect"
|
||||
Expect.equal HtmxVersion "4.0.0-beta5" "htmx version incorrect"
|
||||
}
|
||||
|
||||
let staticAssetUrl =
|
||||
testList "StaticAssetUrl" [
|
||||
test "htmx is correct" {
|
||||
Expect.equal
|
||||
StaticAssetUrl.htmx
|
||||
$"/_content/Giraffe.Htmx.Common/htmx.min.js?ver={HtmxVersion}"
|
||||
"Static htmx URL incorrect"
|
||||
}
|
||||
test "htmax is correct" {
|
||||
Expect.equal
|
||||
StaticAssetUrl.htmax
|
||||
$"/_content/Giraffe.Htmx.Common/htmax.min.js?ver={HtmxVersion}"
|
||||
"Static htmx URL incorrect"
|
||||
}
|
||||
]
|
||||
|
||||
/// Tests for the HxSwap module
|
||||
let swap =
|
||||
testList "HxSwap" [
|
||||
@@ -69,4 +85,4 @@ let swap =
|
||||
]
|
||||
|
||||
/// All tests for this module
|
||||
let allTests = testList "Htmx.Common" [ version; swap ]
|
||||
let allTests = testList "Htmx.Common" [ version; staticAssetUrl; swap ]
|
||||
|
||||
+28
-22
@@ -97,6 +97,21 @@ let dictExtensions =
|
||||
Expect.isFalse ctx.Request.Headers.HxPreloaded.Value "The header should have been false"
|
||||
}
|
||||
]
|
||||
testList "HxPrompt" [
|
||||
test "succeeds when the header is not present" {
|
||||
let ctx = Substitute.For<HttpContext>()
|
||||
ctx.Request.Headers.ReturnsForAnyArgs(HeaderDictionary()) |> ignore
|
||||
Expect.isNone ctx.Request.Headers.HxPrompt "There should not have been a header returned"
|
||||
}
|
||||
test "succeeds when the header is present" {
|
||||
let ctx = Substitute.For<HttpContext>()
|
||||
let dic = HeaderDictionary()
|
||||
dic.Add("HX-Prompt", "of course")
|
||||
ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore
|
||||
Expect.isSome ctx.Request.Headers.HxPrompt "There should be a header present"
|
||||
Expect.equal ctx.Request.Headers.HxPrompt.Value "of course" "The header value was incorrect"
|
||||
}
|
||||
]
|
||||
testList "HxRequest" [
|
||||
test "succeeds when the header is not present" {
|
||||
let ctx = Substitute.For<HttpContext>()
|
||||
@@ -216,7 +231,7 @@ let dictExtensions =
|
||||
ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore
|
||||
let hdr = ctx.Request.Headers.HxTarget
|
||||
Expect.isSome hdr "There should be a header present"
|
||||
Expect.equal (fst hdr.Value) "div" "The target tag was incorrect"
|
||||
Expect.equal (fst hdr.Value) "div" "The target tag was incorrect"
|
||||
Expect.isSome (snd hdr.Value) "There should be a target ID present"
|
||||
Expect.equal (snd hdr.Value).Value "leItem" "The header value was incorrect"
|
||||
}
|
||||
@@ -227,7 +242,7 @@ let dictExtensions =
|
||||
ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore
|
||||
let hdr = ctx.Request.Headers.HxTarget
|
||||
Expect.isSome hdr "There should be a header present"
|
||||
Expect.equal (fst hdr.Value) "span" "The target tag was incorrect"
|
||||
Expect.equal (fst hdr.Value) "span" "The target tag was incorrect"
|
||||
Expect.isNone (snd hdr.Value) "There should not be a target ID present"
|
||||
}
|
||||
test "succeeds when the header is present and ID is missing" {
|
||||
@@ -237,7 +252,7 @@ let dictExtensions =
|
||||
ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore
|
||||
let hdr = ctx.Request.Headers.HxTarget
|
||||
Expect.isSome hdr "There should be a header present"
|
||||
Expect.equal (fst hdr.Value) "aside" "The target tag was incorrect"
|
||||
Expect.equal (fst hdr.Value) "aside" "The target tag was incorrect"
|
||||
Expect.isNone (snd hdr.Value) "There should not be a target ID present"
|
||||
}
|
||||
]
|
||||
@@ -251,7 +266,7 @@ let reqExtensions =
|
||||
let ctx = Substitute.For<HttpContext>()
|
||||
ctx.Request.Headers.ReturnsForAnyArgs(HeaderDictionary()) |> ignore
|
||||
Expect.isFalse ctx.Request.IsHtmx "The request should not be an htmx request"
|
||||
}
|
||||
}
|
||||
test "succeeds when request is from htmx" {
|
||||
let ctx = Substitute.For<HttpContext>()
|
||||
let dic = HeaderDictionary()
|
||||
@@ -288,7 +303,7 @@ open System.Threading.Tasks
|
||||
|
||||
/// Dummy "next" parameter to get the pipeline to execute/terminate
|
||||
let next (ctx: HttpContext) = Task.FromResult(Some ctx)
|
||||
|
||||
|
||||
/// Tests for the HttpHandler functions provided in the Handlers module
|
||||
let handlers =
|
||||
testList "HandlerTests" [
|
||||
@@ -373,7 +388,7 @@ let handlers =
|
||||
let! _ = withHxReselect "#test" next ctx
|
||||
Expect.isTrue (dic.ContainsKey "HX-Reselect") "The HX-Reselect header should be present"
|
||||
Expect.equal dic["HX-Reselect"].[0] "#test" "The HX-Reselect value was incorrect"
|
||||
}
|
||||
}
|
||||
testTask "withHxReswap succeeds" {
|
||||
let ctx = Substitute.For<HttpContext>()
|
||||
let dic = HeaderDictionary()
|
||||
@@ -381,7 +396,7 @@ let handlers =
|
||||
let! _ = withHxReswap HxSwap.BeforeEnd next ctx
|
||||
Expect.isTrue (dic.ContainsKey "HX-Reswap") "The HX-Reswap header should be present"
|
||||
Expect.equal dic["HX-Reswap"].[0] HxSwap.BeforeEnd "The HX-Reswap value was incorrect"
|
||||
}
|
||||
}
|
||||
testTask "withHxRetarget succeeds" {
|
||||
let ctx = Substitute.For<HttpContext>()
|
||||
let dic = HeaderDictionary()
|
||||
@@ -418,26 +433,17 @@ let script =
|
||||
$"""<script src="/_content/Giraffe.Htmx.Common/htmx.min.js?ver={HtmxVersion}"></script>"""
|
||||
"htmx script link is incorrect"
|
||||
}
|
||||
test "localMax generates correct link" {
|
||||
Expect.equal
|
||||
(string HtmxScript.localMax)
|
||||
$"""<script src="/_content/Giraffe.Htmx.Common/htmax.min.js?ver={HtmxVersion}"></script>"""
|
||||
"htmx script link is incorrect"
|
||||
}
|
||||
]
|
||||
|
||||
#nowarn 44 // Obsolete items still have tests
|
||||
let dictExtensionsObs =
|
||||
testList "IHeaderDictionaryExtensions (Obsolete)" [
|
||||
testList "HxPrompt" [
|
||||
test "succeeds when the header is not present" {
|
||||
let ctx = Substitute.For<HttpContext>()
|
||||
ctx.Request.Headers.ReturnsForAnyArgs(HeaderDictionary()) |> ignore
|
||||
Expect.isNone ctx.Request.Headers.HxPrompt "There should not have been a header returned"
|
||||
}
|
||||
test "succeeds when the header is present" {
|
||||
let ctx = Substitute.For<HttpContext>()
|
||||
let dic = HeaderDictionary()
|
||||
dic.Add("HX-Prompt", "of course")
|
||||
ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore
|
||||
Expect.isSome ctx.Request.Headers.HxPrompt "There should be a header present"
|
||||
Expect.equal ctx.Request.Headers.HxPrompt.Value "of course" "The header value was incorrect"
|
||||
}
|
||||
]
|
||||
testList "HxTrigger" [
|
||||
test "succeeds when the header is not present" {
|
||||
let ctx = Substitute.For<HttpContext>()
|
||||
|
||||
+13
-13
@@ -395,7 +395,7 @@ let hxTrigger =
|
||||
testList "FromDocument" [
|
||||
test "succeeds when it is the first modifier" {
|
||||
Expect.equal (HxTrigger.FromDocument "") "from:document" "FromDocument modifier incorrect"
|
||||
}
|
||||
}
|
||||
test "succeeds when it is not the first modifier" {
|
||||
Expect.equal (HxTrigger.FromDocument "click") "click from:document" "FromDocument modifier incorrect"
|
||||
}
|
||||
@@ -500,7 +500,7 @@ let hxVals =
|
||||
]
|
||||
]
|
||||
|
||||
/// Pipe-able assertion for a rendered node
|
||||
/// Pipe-able assertion for a rendered node
|
||||
let shouldRender expected node =
|
||||
Expect.equal (RenderView.AsString.htmlNode node) expected "Rendered HTML incorrect"
|
||||
|
||||
@@ -569,6 +569,10 @@ let attributes =
|
||||
test "_hxPreserve succeeds" {
|
||||
img [ _hxPreserve ] |> shouldRender """<img hx-preserve>"""
|
||||
}
|
||||
test "_hxPrompt succeeds" {
|
||||
strong [ _hxPrompt "Who goes there?" ] []
|
||||
|> shouldRender """<strong hx-prompt="Who goes there?"></strong>"""
|
||||
}
|
||||
test "_hxPushUrl succeeds" {
|
||||
dl [ _hxPushUrl "/a-b-c" ] [] |> shouldRender """<dl hx-push-url="/a-b-c"></dl>"""
|
||||
}
|
||||
@@ -677,14 +681,14 @@ let script =
|
||||
let html = RenderView.AsString.htmlNode Script.cdnMinified
|
||||
Expect.equal
|
||||
html
|
||||
$"""<script src="https://cdn.jsdelivr.net/npm/htmx.org@{HtmxVersion}/dist/htmx.min.js" integrity="sha384-aWZK1NtOs/aWb/+YZdTM8q2JkWEshlMc9mgZ189numT9bwFhyAyYEoO4nO/2dTXt" crossorigin="anonymous"></script>"""
|
||||
$"""<script src="https://cdn.jsdelivr.net/npm/htmx.org@{HtmxVersion}/dist/htmx.min.js" integrity="sha384-5dnhUXCt1hXGvYrjAnKwgNX3I8xtIJiW6eIHIbeo7oWyXv2XpWYC/rl+ZiWfuYO5" crossorigin="anonymous"></script>"""
|
||||
"CDN minified script tag is incorrect"
|
||||
}
|
||||
test "cdnUnminified succeeds" {
|
||||
let html = RenderView.AsString.htmlNode Script.cdnUnminified
|
||||
Expect.equal
|
||||
html
|
||||
$"""<script src="https://cdn.jsdelivr.net/npm/htmx.org@{HtmxVersion}/dist/htmx.js" integrity="sha384-OFLRIZpuqI2wwFozxvDGcuF3TQ36ySMgp44WEthOiR4wFzRkhZbK72HFaBo2C3cx" crossorigin="anonymous"></script>"""
|
||||
$"""<script src="https://cdn.jsdelivr.net/npm/htmx.org@{HtmxVersion}/dist/htmx.js" integrity="sha384-RZoQSZlu2BAuZMuM5lTKAWXXSKC+7X6eVzP1pwkUBcyfPmOswexqVOsUqQMKbAFA" crossorigin="anonymous"></script>"""
|
||||
"CDN unminified script tag is incorrect"
|
||||
}
|
||||
testList "Max" [
|
||||
@@ -699,14 +703,14 @@ let script =
|
||||
let html = RenderView.AsString.htmlNode Script.Max.cdnMinified
|
||||
Expect.equal
|
||||
html
|
||||
$"""<script src="https://cdn.jsdelivr.net/npm/htmx.org@{HtmxVersion}/dist/htmax.min.js" integrity="sha384-Qoqie5IRtOE79SDFFRSb/yKi+pkzpSnfjgwr1KksyP14OaHkLHar0KrLVxUwlsJF" crossorigin="anonymous"></script>"""
|
||||
$"""<script src="https://cdn.jsdelivr.net/npm/htmx.org@{HtmxVersion}/dist/htmax.min.js" integrity="sha384-VVbrNR6a+H8puV17ZlJ8aUUMTgbcDiqM1vlLYEmaaU4oFANllvTK2pLynNonElP+" crossorigin="anonymous"></script>"""
|
||||
"CDN minified script tag is incorrect"
|
||||
}
|
||||
test "cdnMaxUnminified succeeds" {
|
||||
let html = RenderView.AsString.htmlNode Script.Max.cdnUnminified
|
||||
Expect.equal
|
||||
html
|
||||
$"""<script src="https://cdn.jsdelivr.net/npm/htmx.org@{HtmxVersion}/dist/htmax.js" integrity="sha384-gGi3Urue6ZkE4NrJCmXWIZkfNkrt1IrdP3fr0kb/v06GWg3V1RnD9Pg/Ul3qhtAK" crossorigin="anonymous"></script>"""
|
||||
$"""<script src="https://cdn.jsdelivr.net/npm/htmx.org@{HtmxVersion}/dist/htmax.js" integrity="sha384-kjhVuvnX3/TsR1qH4JaIcHR6muh/WLMU5CTQRacCQZERzQlP4/r9p/TK7ucFwqvV" crossorigin="anonymous"></script>"""
|
||||
"CDN unminified script tag is incorrect"
|
||||
}
|
||||
]
|
||||
@@ -717,11 +721,11 @@ open System.Text
|
||||
/// Tests for the RenderFragment module
|
||||
let renderFragment =
|
||||
testList "RenderFragment" [
|
||||
|
||||
|
||||
/// Validate that the two object references are the same object
|
||||
let isSame obj1 obj2 message =
|
||||
Expect.isTrue (obj.ReferenceEquals(obj1, obj2)) message
|
||||
|
||||
|
||||
testList "findIdNode" [
|
||||
test "fails with a Text node" {
|
||||
Expect.isNone (RenderFragment.findIdNode "blue" (Text "")) "There should not have been a node found"
|
||||
@@ -787,7 +791,7 @@ let renderFragment =
|
||||
]
|
||||
]
|
||||
testList "AsBytes" [
|
||||
|
||||
|
||||
/// Alias for UTF-8 encoding
|
||||
let utf8 = Encoding.UTF8
|
||||
|
||||
@@ -1314,10 +1318,6 @@ let attributesObs =
|
||||
test "_hxParams succeeds" {
|
||||
br [ _hxParams "[p1,p2]" ] |> shouldRender """<br hx-params="[p1,p2]">"""
|
||||
}
|
||||
test "_hxPrompt succeeds" {
|
||||
strong [ _hxPrompt "Who goes there?" ] []
|
||||
|> shouldRender """<strong hx-prompt="Who goes there?"></strong>"""
|
||||
}
|
||||
test "_hxRequest succeeds" {
|
||||
u [ _hxRequest "noHeaders" ] [] |> shouldRender """<u hx-request="noHeaders"></u>"""
|
||||
}
|
||||
|
||||
+182
-178
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
||||
|
||||
This package enables [htmx](https://htmx.org) support within the [Giraffe](https://giraffe.wiki) view engine.
|
||||
|
||||
**htmx version: 4.0.0-beta4**
|
||||
**htmx version: 4.0.0-beta5**
|
||||
|
||||
_Upgrading from v2.x: see [the migration guide](https://four.htmx.org/docs/get-started/migration) for changes, which are plentiful. htmx switches from `XMLHTTPRequest` to `fetch`, and many changes are related to the new event cycle._
|
||||
|
||||
|
||||
Reference in New Issue
Block a user