Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fa813ff0fe | |||
| 5163da2e1c | |||
| 26d23de15d | |||
| 62af36e833 | |||
| d4a7e0c9ce |
@@ -3,11 +3,14 @@
|
||||
module Giraffe.Htmx.Common
|
||||
|
||||
/// <summary>The version of htmx embedded in the package</summary>
|
||||
let HtmxVersion = "4.0.0-alpha6"
|
||||
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>
|
||||
@@ -44,7 +47,7 @@ module HxSwap =
|
||||
|
||||
/// <summary>Morph the outer HTML of the target to the new content</summary>
|
||||
[<Literal>]
|
||||
let OuterMorph = "innerMorph"
|
||||
let OuterMorph = "outerMorph"
|
||||
|
||||
/// <summary>Replace the text content of the target without parsing the response as HTML</summary>
|
||||
[<Literal>]
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Update="FSharp.Core" Version="6.0.0" />
|
||||
<PackageReference Include="FSharp.Core" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
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.
|
||||
|
||||
**htmx version: 4.0.0-alpha6**
|
||||
**htmx version: 4.0.0-beta4**
|
||||
|
||||
_**NOTE:** Pay special attention to breaking changes highlighted in the packages listed above._
|
||||
Vendored
+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,17 @@
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
|
||||
<VersionPrefix>4.0.0</VersionPrefix>
|
||||
<VersionSuffix>alpha6a</VersionSuffix>
|
||||
<VersionSuffix>beta4</VersionSuffix>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<PackageReleaseNotes>First htmx 4 alpha release of these libraries
|
||||
- [Server] Marked removed headers as obsolete; added new HX-Source header
|
||||
- [View Engine] Marked removed attributes as obsolete
|
||||
- [View Engine] Added new attributes, modifiers, and support for new hx-partial tag
|
||||
- Updated script tags to pull htmx 4.0.0-alpha6
|
||||
<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
|
||||
- [View Engine] Updated script tags to pull htmx 4.0.0-beta4, added "max" bundle links for local and CDN
|
||||
|
||||
See package READMEs; this is not an update-and-forget-it release
|
||||
See package and prior alpha release READMEs; v2 to v4 is not an update-and-forget-it release
|
||||
</PackageReleaseNotes>
|
||||
<Authors>danieljsummers</Authors>
|
||||
<Company>Bit Badger Solutions</Company>
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="Expecto" Version="11.0.0" />
|
||||
<PackageVersion Include="Giraffe" Version="8.2.0" />
|
||||
<PackageVersion Include="Giraffe.ViewEngine" Version="1.4.0" />
|
||||
<PackageVersion Include="NSubstitute" Version="5.3.0" />
|
||||
<PackageVersion Include="FSharp.Core" Version="10.1.301" />
|
||||
<!-- <PackageVersion Update="FSharp.Core" Version="10.0.0" /> -->
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,40 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30114.105
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Giraffe.Htmx", "Htmx\Giraffe.Htmx.fsproj", "{8AB3085C-5236-485A-8565-A09106E72E1E}"
|
||||
EndProject
|
||||
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Giraffe.ViewEngine.Htmx", "ViewEngine.Htmx\Giraffe.ViewEngine.Htmx.fsproj", "{F718B3C1-EE01-4F04-ABCE-BF2AE700FDA9}"
|
||||
EndProject
|
||||
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Giraffe.Htmx.Common", "Common\Giraffe.Htmx.Common.fsproj", "{75D66845-F93A-4463-AD29-A8B16E4D4BA9}"
|
||||
EndProject
|
||||
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Tests", "Tests\Tests.fsproj", "{39823773-4311-4E79-9CA0-F9DDC40CAF6A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{8AB3085C-5236-485A-8565-A09106E72E1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8AB3085C-5236-485A-8565-A09106E72E1E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8AB3085C-5236-485A-8565-A09106E72E1E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8AB3085C-5236-485A-8565-A09106E72E1E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F718B3C1-EE01-4F04-ABCE-BF2AE700FDA9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F718B3C1-EE01-4F04-ABCE-BF2AE700FDA9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F718B3C1-EE01-4F04-ABCE-BF2AE700FDA9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F718B3C1-EE01-4F04-ABCE-BF2AE700FDA9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{75D66845-F93A-4463-AD29-A8B16E4D4BA9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{75D66845-F93A-4463-AD29-A8B16E4D4BA9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{75D66845-F93A-4463-AD29-A8B16E4D4BA9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{75D66845-F93A-4463-AD29-A8B16E4D4BA9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{39823773-4311-4E79-9CA0-F9DDC40CAF6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{39823773-4311-4E79-9CA0-F9DDC40CAF6A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{39823773-4311-4E79-9CA0-F9DDC40CAF6A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{39823773-4311-4E79-9CA0-F9DDC40CAF6A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,6 @@
|
||||
<Solution>
|
||||
<Project Path="Common/Giraffe.Htmx.Common.fsproj" />
|
||||
<Project Path="Htmx/Giraffe.Htmx.fsproj" />
|
||||
<Project Path="Tests/Tests.fsproj" />
|
||||
<Project Path="ViewEngine.Htmx/Giraffe.ViewEngine.Htmx.fsproj" />
|
||||
</Solution>
|
||||
@@ -14,8 +14,8 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Giraffe" Version="6.4.0" />
|
||||
<PackageReference Update="FSharp.Core" Version="6.0.0" />
|
||||
<PackageReference Include="Giraffe" />
|
||||
<PackageReference Include="FSharp.Core" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
+42
-5
@@ -4,6 +4,14 @@ open Microsoft.AspNetCore.Http
|
||||
open Microsoft.Extensions.Primitives
|
||||
open System
|
||||
|
||||
/// <summary>The request types which may be set in the <c>HX-Request</c> header</summary>
|
||||
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
|
||||
|
||||
/// Determine if the given header is present
|
||||
let private hdr (headers : IHeaderDictionary) hdr =
|
||||
match headers[hdr] with it when it = StringValues.Empty -> None | it -> Some it[0]
|
||||
@@ -32,6 +40,16 @@ type IHeaderDictionary with
|
||||
member this.HxRequest
|
||||
with get () = hdr this "HX-Request" |> Option.map bool.Parse
|
||||
|
||||
/// <summary>The request type sent by htmx</summary>
|
||||
/// <seealso cref="HxRequestTypes" />
|
||||
member this.HxRequestType
|
||||
with get () =
|
||||
match hdr this "HX-Request-Type" with
|
||||
| Some typ when typ = "full" -> Some HxFullRequest
|
||||
| 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 () =
|
||||
@@ -45,8 +63,23 @@ type IHeaderDictionary with
|
||||
| None -> None
|
||||
|
||||
/// <summary>The <c>id</c> attribute of the target element if it exists</summary>
|
||||
/// <remarks>
|
||||
/// In v4, this changed to tag name (fst) and <c>id</c> (snd); to resolve build errors, and restore the prior
|
||||
/// behavior of <c>[id] option</c>, replace
|
||||
/// <code>ctx.HxTarget</code>
|
||||
/// with
|
||||
/// <code>ctx.HxTarget |> Option.iter snd</code>
|
||||
/// </remarks>
|
||||
member this.HxTarget
|
||||
with get () = hdr this "HX-Target"
|
||||
with get () =
|
||||
match hdr this "HX-Target" with
|
||||
| Some src ->
|
||||
let parts = src.Split "#"
|
||||
if parts.Length = 1 then
|
||||
Some (parts[0], None)
|
||||
else
|
||||
Some (parts[0], if parts[1] <> "" then Some parts[1] else None)
|
||||
| None -> None
|
||||
|
||||
/// <summary>The <c>id</c> attribute of the triggered element if it exists</summary>
|
||||
[<Obsolete "HX-Trigger is removed in v4; use the second item of HX-Source">]
|
||||
@@ -162,29 +195,33 @@ module Handlers =
|
||||
/// <param name="evt">The call to the event that should be triggered</param>
|
||||
/// <returns>An HTTP handler with the <c>HX-Trigger-After-Settle</c> header set</returns>
|
||||
/// <seealso href="https://htmx.org/headers/hx-trigger/">Documentation</seealso>
|
||||
[<Obsolete "Removed in v4; use withHxTrigger">]
|
||||
let withHxTriggerAfterSettle (evt: string) : HttpHandler =
|
||||
setHttpHeader "HX-Trigger-After-Settle" evt
|
||||
setHttpHeader "HX-Trigger" evt
|
||||
|
||||
/// <summary>Allows you to trigger multiple client side events after changes have settled</summary>
|
||||
/// <param name="evts">The calls to events that should be triggered</param>
|
||||
/// <returns>An HTTP handler with the <c>HX-Trigger-After-Settle</c> header set for all given events</returns>
|
||||
/// <seealso href="https://htmx.org/headers/hx-trigger/">Documentation</seealso>
|
||||
[<Obsolete "Removed in v4; use withHxTrigger">]
|
||||
let withHxTriggerManyAfterSettle evts : HttpHandler =
|
||||
toJson evts |> setHttpHeader "HX-Trigger-After-Settle"
|
||||
toJson evts |> setHttpHeader "HX-Trigger"
|
||||
|
||||
/// <summary>Allows you to trigger a single client side event after DOM swapping occurs</summary>
|
||||
/// <param name="evt">The call to the event that should be triggered</param>
|
||||
/// <returns>An HTTP handler with the <c>HX-Trigger-After-Swap</c> header set</returns>
|
||||
/// <seealso href="https://htmx.org/headers/hx-trigger/">Documentation</seealso>
|
||||
[<Obsolete "Removed in v4; use withHxTrigger">]
|
||||
let withHxTriggerAfterSwap (evt: string) : HttpHandler =
|
||||
setHttpHeader "HX-Trigger-After-Swap" evt
|
||||
setHttpHeader "HX-Trigger" evt
|
||||
|
||||
/// <summary>Allows you to trigger multiple client side events after DOM swapping occurs</summary>
|
||||
/// <param name="evts">The calls to events that should be triggered</param>
|
||||
/// <returns>An HTTP handler with the <c>HX-Trigger-After-Swap</c> header set for all given events</returns>
|
||||
/// <seealso href="https://htmx.org/headers/hx-trigger/">Documentation</seealso>
|
||||
[<Obsolete "Removed in v4; use withHxTrigger">]
|
||||
let withHxTriggerManyAfterSwap evts : HttpHandler =
|
||||
toJson evts |> setHttpHeader "HX-Trigger-After-Swap"
|
||||
toJson evts |> setHttpHeader "HX-Trigger"
|
||||
|
||||
|
||||
/// <summary>Load the package-provided version of the htmx script</summary>
|
||||
|
||||
+2
-2
@@ -2,9 +2,9 @@
|
||||
|
||||
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-alpha6**
|
||||
**htmx version: 4.0.0-beta4**
|
||||
|
||||
_Upgrading from v2.x: the [migration guide](https://four.htmx.org/migration-guide-htmx-4/) 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._
|
||||
_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._
|
||||
|
||||
_Obsolete elements will be removed in the first production v4 release._
|
||||
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ open Giraffe.Htmx
|
||||
/// Test to ensure the version was updated
|
||||
let version =
|
||||
test "HtmxVersion is correct" {
|
||||
Expect.equal HtmxVersion "4.0.0-alpha6" "htmx version incorrect"
|
||||
Expect.equal HtmxVersion "4.0.0-beta4" "htmx version incorrect"
|
||||
}
|
||||
|
||||
/// Tests for the HxSwap module
|
||||
@@ -22,7 +22,7 @@ let swap =
|
||||
Expect.equal HxSwap.InnerMorph "innerMorph" "Inner Morph swap value incorrect"
|
||||
}
|
||||
test "OuterMorph is correct" {
|
||||
Expect.equal HxSwap.OuterMorph "innerMorph" "Outer Morph swap value incorrect"
|
||||
Expect.equal HxSwap.OuterMorph "outerMorph" "Outer Morph swap value incorrect"
|
||||
}
|
||||
test "TextContent is correct" {
|
||||
Expect.equal HxSwap.TextContent "textContent" "Text Content swap value incorrect"
|
||||
|
||||
+94
-43
@@ -96,6 +96,36 @@ let dictExtensions =
|
||||
Expect.isFalse ctx.Request.Headers.HxRequest.Value "The header should have been false"
|
||||
}
|
||||
]
|
||||
testList "HxRequestType" [
|
||||
test "succeeds when the header is not present" {
|
||||
let ctx = Substitute.For<HttpContext>()
|
||||
ctx.Request.Headers.ReturnsForAnyArgs(HeaderDictionary()) |> ignore
|
||||
Expect.isNone ctx.Request.Headers.HxRequestType "There should not have been a header returned"
|
||||
}
|
||||
test "succeeds when the header is invalid" {
|
||||
let ctx = Substitute.For<HttpContext>()
|
||||
let dic = HeaderDictionary()
|
||||
dic.Add("HX-Request-Type", "relaxed")
|
||||
ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore
|
||||
Expect.isNone ctx.Request.Headers.HxRequestType "There should not have been a header returned"
|
||||
}
|
||||
test "succeeds for a full request" {
|
||||
let ctx = Substitute.For<HttpContext>()
|
||||
let dic = HeaderDictionary()
|
||||
dic.Add("HX-Request-Type", "full")
|
||||
ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore
|
||||
Expect.isSome ctx.Request.Headers.HxRequestType "There have been a header returned"
|
||||
Expect.equal ctx.Request.Headers.HxRequestType.Value HxFullRequest "The header value is incorrect"
|
||||
}
|
||||
test "succeeds for a partial request" {
|
||||
let ctx = Substitute.For<HttpContext>()
|
||||
let dic = HeaderDictionary()
|
||||
dic.Add("HX-Request-Type", "partial")
|
||||
ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore
|
||||
Expect.isSome ctx.Request.Headers.HxRequestType "There have been a header returned"
|
||||
Expect.equal ctx.Request.Headers.HxRequestType.Value HxPartialRequest "The header value is incorrect"
|
||||
}
|
||||
]
|
||||
testList "HxSource" [
|
||||
test "succeeds when the header is not present" {
|
||||
let ctx = Substitute.For<HttpContext>()
|
||||
@@ -140,13 +170,36 @@ let dictExtensions =
|
||||
ctx.Request.Headers.ReturnsForAnyArgs(HeaderDictionary()) |> ignore
|
||||
Expect.isNone ctx.Request.Headers.HxTarget "There should not have been a header returned"
|
||||
}
|
||||
test "succeeds when the header is present" {
|
||||
test "succeeds when the header is present and both parts exist" {
|
||||
let ctx = Substitute.For<HttpContext>()
|
||||
let dic = HeaderDictionary()
|
||||
dic.Add("HX-Target", "#leItem")
|
||||
dic.Add("HX-Target", "div#leItem")
|
||||
ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore
|
||||
Expect.isSome ctx.Request.Headers.HxTarget "There should be a header present"
|
||||
Expect.equal ctx.Request.Headers.HxTarget.Value "#leItem" "The header value was incorrect"
|
||||
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.isSome (snd hdr.Value) "There should be a target ID present"
|
||||
Expect.equal (snd hdr.Value).Value "leItem" "The header value was incorrect"
|
||||
}
|
||||
test "succeeds when the header is present and ID is blank" {
|
||||
let ctx = Substitute.For<HttpContext>()
|
||||
let dic = HeaderDictionary()
|
||||
dic.Add("HX-Target", "span#")
|
||||
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.isNone (snd hdr.Value) "There should not be a target ID present"
|
||||
}
|
||||
test "succeeds when the header is present and ID is missing" {
|
||||
let ctx = Substitute.For<HttpContext>()
|
||||
let dic = HeaderDictionary()
|
||||
dic.Add("HX-Target", "aside")
|
||||
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.isNone (snd hdr.Value) "There should not be a target ID present"
|
||||
}
|
||||
]
|
||||
]
|
||||
@@ -307,44 +360,6 @@ let handlers =
|
||||
Expect.equal
|
||||
dic["HX-Trigger"].[0] """{ "blah": "foo", "bleh": "bar" }""" "The HX-Trigger value was incorrect"
|
||||
}
|
||||
testTask "withHxTriggerAfterSettle succeeds" {
|
||||
let ctx = Substitute.For<HttpContext>()
|
||||
let dic = HeaderDictionary()
|
||||
ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore
|
||||
let! _ = withHxTriggerAfterSettle "byTheWay" next ctx
|
||||
Expect.isTrue
|
||||
(dic.ContainsKey "HX-Trigger-After-Settle") "The HX-Trigger-After-Settle header should be present"
|
||||
Expect.equal dic["HX-Trigger-After-Settle"].[0] "byTheWay" "The HX-Trigger-After-Settle value was incorrect"
|
||||
}
|
||||
testTask "withHxTriggerManyAfterSettle succeeds" {
|
||||
let ctx = Substitute.For<HttpContext>()
|
||||
let dic = HeaderDictionary()
|
||||
ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore
|
||||
let! _ = withHxTriggerManyAfterSettle [ "oof", "ouch"; "hmm", "uh" ] next ctx
|
||||
Expect.isTrue
|
||||
(dic.ContainsKey "HX-Trigger-After-Settle") "The HX-Trigger-After-Settle header should be present"
|
||||
Expect.equal
|
||||
dic["HX-Trigger-After-Settle"].[0] """{ "oof": "ouch", "hmm": "uh" }"""
|
||||
"The HX-Trigger-After-Settle value was incorrect"
|
||||
}
|
||||
testTask "withHxTriggerAfterSwap succeeds" {
|
||||
let ctx = Substitute.For<HttpContext>()
|
||||
let dic = HeaderDictionary()
|
||||
ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore
|
||||
let! _ = withHxTriggerAfterSwap "justASec" next ctx
|
||||
Expect.isTrue (dic.ContainsKey "HX-Trigger-After-Swap") "The HX-Trigger-After-Swap header should be present"
|
||||
Expect.equal dic["HX-Trigger-After-Swap"].[0] "justASec" "The HX-Trigger-After-Swap value was incorrect"
|
||||
}
|
||||
testTask "withHxTriggerManyAfterSwap succeeds" {
|
||||
let ctx = Substitute.For<HttpContext>()
|
||||
let dic = HeaderDictionary()
|
||||
ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore
|
||||
let! _ = withHxTriggerManyAfterSwap [ "this", "1"; "that", "2" ] next ctx
|
||||
Expect.isTrue (dic.ContainsKey "HX-Trigger-After-Swap") "The HX-Trigger-After-Swap header should be present"
|
||||
Expect.equal
|
||||
dic["HX-Trigger-After-Swap"].[0] """{ "this": "1", "that": "2" }"""
|
||||
"The HX-Trigger-After-Swap value was incorrect"
|
||||
}
|
||||
]
|
||||
|
||||
/// Tests for the HtmxScript module
|
||||
@@ -408,5 +423,41 @@ let dictExtensionsObs =
|
||||
]
|
||||
]
|
||||
|
||||
let handlerObs =
|
||||
testList "Handler Tests (Obsolete)" [
|
||||
testTask "withHxTriggerAfterSettle succeeds" {
|
||||
let ctx = Substitute.For<HttpContext>()
|
||||
let dic = HeaderDictionary()
|
||||
ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore
|
||||
let! _ = withHxTriggerAfterSettle "byTheWay" next ctx
|
||||
Expect.isTrue (dic.ContainsKey "HX-Trigger") "The HX-Trigger header should be present"
|
||||
Expect.equal dic["HX-Trigger"].[0] "byTheWay" "The HX-Trigger value was incorrect"
|
||||
}
|
||||
testTask "withHxTriggerManyAfterSettle succeeds" {
|
||||
let ctx = Substitute.For<HttpContext>()
|
||||
let dic = HeaderDictionary()
|
||||
ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore
|
||||
let! _ = withHxTriggerManyAfterSettle [ "oof", "ouch"; "hmm", "uh" ] next ctx
|
||||
Expect.isTrue (dic.ContainsKey "HX-Trigger") "The HX-Trigger header should be present"
|
||||
Expect.equal dic["HX-Trigger"].[0] """{ "oof": "ouch", "hmm": "uh" }""" "The HX-Trigger value was incorrect"
|
||||
}
|
||||
testTask "withHxTriggerAfterSwap succeeds" {
|
||||
let ctx = Substitute.For<HttpContext>()
|
||||
let dic = HeaderDictionary()
|
||||
ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore
|
||||
let! _ = withHxTriggerAfterSwap "justASec" next ctx
|
||||
Expect.isTrue (dic.ContainsKey "HX-Trigger") "The HX-Trigger header should be present"
|
||||
Expect.equal dic["HX-Trigger"].[0] "justASec" "The HX-Trigger value was incorrect"
|
||||
}
|
||||
testTask "withHxTriggerManyAfterSwap succeeds" {
|
||||
let ctx = Substitute.For<HttpContext>()
|
||||
let dic = HeaderDictionary()
|
||||
ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore
|
||||
let! _ = withHxTriggerManyAfterSwap [ "this", "1"; "that", "2" ] next ctx
|
||||
Expect.isTrue (dic.ContainsKey "HX-Trigger") "The HX-Trigger header should be present"
|
||||
Expect.equal dic["HX-Trigger"].[0] """{ "this": "1", "that": "2" }""" "The HX-Trigger value was incorrect"
|
||||
}
|
||||
]
|
||||
|
||||
/// All tests for this module
|
||||
let allTests = testList "Htmx" [ dictExtensions; reqExtensions; handlers; script; dictExtensionsObs ]
|
||||
let allTests = testList "Htmx" [ dictExtensions; reqExtensions; handlers; script; dictExtensionsObs; handlerObs ]
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Expecto" Version="10.2.1" />
|
||||
<PackageReference Include="NSubstitute" Version="5.1.0" />
|
||||
<PackageReference Update="FSharp.Core" Version="8.0.300" />
|
||||
<PackageReference Include="Expecto" />
|
||||
<PackageReference Include="NSubstitute" />
|
||||
<PackageReference Include="FSharp.Core" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
+76
-23
@@ -1,6 +1,7 @@
|
||||
module ViewEngine
|
||||
|
||||
open Expecto
|
||||
open Giraffe.Htmx
|
||||
open Giraffe.ViewEngine
|
||||
open Giraffe.ViewEngine.Htmx
|
||||
|
||||
@@ -129,6 +130,14 @@ let hxEvent =
|
||||
Expect.equal (AfterRequest.ToHxOnString()) "after:request" "AfterRequest hx-on event name not correct"
|
||||
}
|
||||
]
|
||||
testList "AfterSettle" [
|
||||
test "ToString succeeds" {
|
||||
Expect.equal (string AfterSettle) "afterSettle" "AfterSettle event name not correct"
|
||||
}
|
||||
test "ToHxOnString succeeds" {
|
||||
Expect.equal (AfterSettle.ToHxOnString()) "after:settle" "AfterSettle hx-on event name not correct"
|
||||
}
|
||||
]
|
||||
testList "AfterSseMessage" [
|
||||
test "ToString succeeds" {
|
||||
Expect.equal (string AfterSseMessage) "afterSseMessage" "AfterSseMessage event name not correct"
|
||||
@@ -184,6 +193,15 @@ let hxEvent =
|
||||
Expect.equal (BeforeInit.ToHxOnString()) "before:init" "BeforeInit hx-on event name not correct"
|
||||
}
|
||||
]
|
||||
testList "BeforeProcess" [
|
||||
test "ToString succeeds" {
|
||||
Expect.equal (string BeforeProcess) "beforeProcess" "BeforeProcess event name not correct"
|
||||
}
|
||||
test "ToHxOnString succeeds" {
|
||||
Expect.equal
|
||||
(BeforeProcess.ToHxOnString()) "before:process" "BeforeProcess hx-on event name not correct"
|
||||
}
|
||||
]
|
||||
testList "BeforeRestoreHistory" [
|
||||
test "ToString succeeds" {
|
||||
Expect.equal
|
||||
@@ -205,6 +223,15 @@ let hxEvent =
|
||||
(BeforeRequest.ToHxOnString()) "before:request" "BeforeRequest hx-on event name not correct"
|
||||
}
|
||||
]
|
||||
testList "BeforeResponse" [
|
||||
test "ToString succeeds" {
|
||||
Expect.equal (string BeforeResponse) "beforeResponse" "BeforeResponse event name not correct"
|
||||
}
|
||||
test "ToHxOnString succeeds" {
|
||||
Expect.equal
|
||||
(BeforeResponse.ToHxOnString()) "before:response" "BeforeResponse hx-on event name not correct"
|
||||
}
|
||||
]
|
||||
testList "BeforeSseMessage" [
|
||||
test "ToString succeeds" {
|
||||
Expect.equal (string BeforeSseMessage) "beforeSseMessage" "BeforeSseMessage event name not correct"
|
||||
@@ -279,6 +306,15 @@ let hxEvent =
|
||||
(FinallyRequest.ToHxOnString()) "finally:request" "FinallyRequest hx-on event name not correct"
|
||||
}
|
||||
]
|
||||
testList "ResponseError" [
|
||||
test "ToString succeeds" {
|
||||
Expect.equal (string ResponseError) "responseError" "ResponseError event name not correct"
|
||||
}
|
||||
test "ToHxOnString succeeds" {
|
||||
Expect.equal
|
||||
(ResponseError.ToHxOnString()) "response:error" "ResponseError hx-on event name not correct"
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
/// Tests for the HxHeaders module
|
||||
@@ -572,7 +608,7 @@ let attributes =
|
||||
}
|
||||
test "_hxOnHxEvent succeeds" {
|
||||
strong [ _hxOnHxEvent BeforeSwap "changeStuff()" ] []
|
||||
|> shouldRender """<strong hx-on::before:swap="changeStuff()"></strong>"""
|
||||
|> shouldRender """<strong hx-on:htmx:before:swap="changeStuff()"></strong>"""
|
||||
}
|
||||
test "_hxPatch succeeds" {
|
||||
div [ _hxPatch "/arrrgh" ] [] |> shouldRender """<div hx-patch="/arrrgh"></div>"""
|
||||
@@ -598,6 +634,18 @@ let attributes =
|
||||
test "_hxSelectOob succeeds" {
|
||||
section [ _hxSelectOob "#oob" ] [] |> shouldRender """<section hx-select-oob="#oob"></section>"""
|
||||
}
|
||||
testList "_hxStatus" [
|
||||
test "succeeds for a full code with no action" {
|
||||
div [ _hxStatus "403" HxSwap.After "" ] [] |> shouldRender """<div hx-status:403="swap:after"></div>"""
|
||||
}
|
||||
test "succeeds for a two-digit range with action" {
|
||||
span [ _hxStatus "41" HxSwap.Before "target:main" ] []
|
||||
|> shouldRender """<span hx-status:41x="swap:before target:main"></span>"""
|
||||
}
|
||||
test "succeeds for a one-digit range with no action" {
|
||||
p [ _hxStatus "5" HxSwap.None "" ] [] |> shouldRender """<p hx-status:5xx="swap:none"></p>"""
|
||||
}
|
||||
]
|
||||
test "_hxSwap succeeds" {
|
||||
del [ _hxSwap "innerHTML" ] [] |> shouldRender """<del hx-swap="innerHTML"></del>"""
|
||||
}
|
||||
@@ -683,18 +731,39 @@ let script =
|
||||
$"""<script src="/_content/Giraffe.Htmx.Common/htmx.min.js?ver={HtmxVersion}"></script>"""
|
||||
"Local script tag is incorrect"
|
||||
}
|
||||
test "localMax succeeds" {
|
||||
let html = RenderView.AsString.htmlNode Script.localMax
|
||||
Expect.equal
|
||||
html
|
||||
$"""<script src="/_content/Giraffe.Htmx.Common/htmax.min.js?ver={HtmxVersion}"></script>"""
|
||||
"Local script tag is incorrect"
|
||||
}
|
||||
test "cdnMinified succeeds" {
|
||||
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-/5n21KGM472oBhvzUrvju8FRDq/4WNMS3TGw5RWFkZR/kq+sCevqNXFcakqRtaHu" crossorigin="anonymous"></script>"""
|
||||
$"""<script src="https://cdn.jsdelivr.net/npm/htmx.org@{HtmxVersion}/dist/htmx.min.js" integrity="sha384-aWZK1NtOs/aWb/+YZdTM8q2JkWEshlMc9mgZ189numT9bwFhyAyYEoO4nO/2dTXt" 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-I3uRy2RjSNcS8Oi2SNWZD9IrafyrrrBb4QaHNA9pWVWfJuKtL0p8qAwHSrEyXoKh" crossorigin="anonymous"></script>"""
|
||||
$"""<script src="https://cdn.jsdelivr.net/npm/htmx.org@{HtmxVersion}/dist/htmx.js" integrity="sha384-OFLRIZpuqI2wwFozxvDGcuF3TQ36ySMgp44WEthOiR4wFzRkhZbK72HFaBo2C3cx" crossorigin="anonymous"></script>"""
|
||||
"CDN unminified script tag is incorrect"
|
||||
}
|
||||
test "cdnMaxMinified succeeds" {
|
||||
let html = RenderView.AsString.htmlNode Script.cdnMaxMinified
|
||||
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>"""
|
||||
"CDN minified script tag is incorrect"
|
||||
}
|
||||
test "cdnMaxUnminified succeeds" {
|
||||
let html = RenderView.AsString.htmlNode Script.cdnMaxUnminified
|
||||
Expect.equal
|
||||
html
|
||||
$"""<script src="https://cdn.jsdelivr.net/npm/htmx.org@{HtmxVersion}/dist/htmax.js" integrity="sha384-gGi3Urue6ZkE4NrJCmXWIZkfNkrt1IrdP3fr0kb/v06GWg3V1RnD9Pg/Ul3qhtAK" crossorigin="anonymous"></script>"""
|
||||
"CDN unminified script tag is incorrect"
|
||||
}
|
||||
]
|
||||
@@ -864,15 +933,7 @@ let hxEventObs =
|
||||
}
|
||||
test "ToHxOnString succeeds" {
|
||||
Expect.equal
|
||||
(AfterProcessNode.ToHxOnString()) "after:init" "AfterProcessNode hx-on event name not correct"
|
||||
}
|
||||
]
|
||||
testList "AfterSettle" [
|
||||
test "ToString succeeds" {
|
||||
Expect.equal (string AfterSettle) "afterSettle" "AfterSettle event name not correct"
|
||||
}
|
||||
test "ToHxOnString succeeds" {
|
||||
Expect.equal (AfterSettle.ToHxOnString()) "after:swap" "AfterSettle hx-on event name not correct"
|
||||
(AfterProcessNode.ToHxOnString()) "after:process" "AfterProcessNode hx-on event name not correct"
|
||||
}
|
||||
]
|
||||
testList "BeforeCleanupElement" [
|
||||
@@ -912,7 +973,7 @@ let hxEventObs =
|
||||
}
|
||||
test "ToHxOnString succeeds" {
|
||||
Expect.equal
|
||||
(BeforeProcessNode.ToHxOnString()) "before:init" "BeforeProcessNode hx-on event name not correct"
|
||||
(BeforeProcessNode.ToHxOnString()) "before:process" "BeforeProcessNode hx-on event name not correct"
|
||||
}
|
||||
]
|
||||
testList "BeforeSend" [
|
||||
@@ -1049,14 +1110,6 @@ let hxEventObs =
|
||||
"PushedIntoHistory hx-on event name not correct"
|
||||
}
|
||||
]
|
||||
testList "ResponseError" [
|
||||
test "ToString succeeds" {
|
||||
Expect.equal (string ResponseError) "responseError" "ResponseError event name not correct"
|
||||
}
|
||||
test "ToHxOnString succeeds" {
|
||||
Expect.equal (ResponseError.ToHxOnString()) "error" "ResponseError hx-on event name not correct"
|
||||
}
|
||||
]
|
||||
testList "SendError" [
|
||||
test "ToString succeeds" {
|
||||
Expect.equal (string SendError) "sendError" "SendError event name not correct"
|
||||
@@ -1144,7 +1197,7 @@ let hxEventObs =
|
||||
Expect.equal (string XhrAbort) "xhr:abort" "XhrAbort event name not correct"
|
||||
}
|
||||
test "ToHxOnString succeeds" {
|
||||
Expect.equal (XhrAbort.ToHxOnString()) "xhr:abort" "XhrAbort hx-on event name not correct"
|
||||
Expect.equal (XhrAbort.ToHxOnString()) "error" "XhrAbort hx-on event name not correct"
|
||||
}
|
||||
]
|
||||
testList "XhrLoadEnd" [
|
||||
@@ -1152,7 +1205,7 @@ let hxEventObs =
|
||||
Expect.equal (string XhrLoadEnd) "xhr:loadend" "XhrLoadEnd event name not correct"
|
||||
}
|
||||
test "ToHxOnString succeeds" {
|
||||
Expect.equal (XhrLoadEnd.ToHxOnString()) "xhr:loadend" "XhrLoadEnd hx-on event name not correct"
|
||||
Expect.equal (XhrLoadEnd.ToHxOnString()) "finally:request" "XhrLoadEnd hx-on event name not correct"
|
||||
}
|
||||
]
|
||||
testList "XhrLoadStart" [
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Giraffe.ViewEngine" Version="1.4.0" />
|
||||
<PackageReference Update="FSharp.Core" Version="6.0.0" />
|
||||
<PackageReference Include="Giraffe.ViewEngine" />
|
||||
<PackageReference Include="FSharp.Core" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
+71
-18
@@ -71,8 +71,11 @@ type HxEvent =
|
||||
/// <summary>Triggered after an AJAX request has completed processing a successful response</summary>
|
||||
| [<System.Obsolete "Removed in v4; use AfterInit">] AfterOnLoad
|
||||
|
||||
/// <summary>Triggered after htmx has initialized a DOM node or subtree</summary>
|
||||
| AfterProcess
|
||||
|
||||
/// <summary>Triggered after htmx has initialized a node</summary>
|
||||
| [<System.Obsolete "Removed in v4; use AfterInit">] AfterProcessNode
|
||||
| [<System.Obsolete "Removed in v4; use AfterProcess">] AfterProcessNode
|
||||
|
||||
/// <summary>Triggered after new content is saved to the history cache</summary>
|
||||
| AfterPushIntoHistory
|
||||
@@ -84,7 +87,7 @@ type HxEvent =
|
||||
| AfterRequest
|
||||
|
||||
/// <summary>Triggered after the DOM has settled</summary>
|
||||
| [<System.Obsolete "Removed in v4; use AfterSwap">] AfterSettle
|
||||
| AfterSettle
|
||||
|
||||
/// <summary>Triggered after a Server Sent Events (SSE) message is read</summary>
|
||||
| AfterSseMessage
|
||||
@@ -116,12 +119,18 @@ type HxEvent =
|
||||
/// <summary>Triggered before any response processing occurs</summary>
|
||||
| [<System.Obsolete "Removed in v4; use BeforeInit">] BeforeOnLoad
|
||||
|
||||
/// <summary>Triggered before htmx begins processing a DOM node or subtree</summary>
|
||||
| BeforeProcess
|
||||
|
||||
/// <summary>Triggered before htmx initializes a node</summary>
|
||||
| [<System.Obsolete "Removed in v4; use BeforeInit">] BeforeProcessNode
|
||||
| [<System.Obsolete "Removed in v4; use BeforeProcess">] BeforeProcessNode
|
||||
|
||||
/// <summary>Triggered before an HTTP request is made</summary>
|
||||
| BeforeRequest
|
||||
|
||||
/// <summary>Triggered before an HTTP response is processed (cancelable)</summary>
|
||||
| BeforeResponse
|
||||
|
||||
/// <summary>Triggered before a history restore request is made</summary>
|
||||
| BeforeRestoreHistory
|
||||
|
||||
@@ -199,7 +208,7 @@ type HxEvent =
|
||||
| [<System.Obsolete "Removed in v4; use AfterPushIntoHistory (or AfterHistoryUpdate to catch new and updated content)">] PushedIntoHistory
|
||||
|
||||
/// <summary>Triggered when an HTTP response error (non-200 or 300 response code) occurs</summary>
|
||||
| [<System.Obsolete "Removed in v4; use Error (all errors consolidated)">] ResponseError
|
||||
| ResponseError
|
||||
|
||||
/// <summary>Triggered when a network error prevents an HTTP request from happening</summary>
|
||||
| [<System.Obsolete "Removed in v4; use Error (all errors consolidated)">] SendError
|
||||
@@ -229,10 +238,10 @@ type HxEvent =
|
||||
| [<System.Obsolete "Removed in v4; use native validation API">] ValidationHalted
|
||||
|
||||
/// <summary>Triggered when an ajax request aborts</summary>
|
||||
| [<System.Obsolete "Removed in v4; use fetch event listeners">] XhrAbort
|
||||
| [<System.Obsolete "Removed in v4; use fetch event listeners or Error event">] XhrAbort
|
||||
|
||||
/// <summary>Triggered when an ajax request ends</summary>
|
||||
| [<System.Obsolete "Removed in v4; use fetch event listeners">] XhrLoadEnd
|
||||
| [<System.Obsolete "Removed in v4; use fetch event listeners or FinallyRequest event">] XhrLoadEnd
|
||||
|
||||
/// <summary>Triggered when an ajax request starts</summary>
|
||||
| [<System.Obsolete "Removed in v4; use fetch event listeners">] XhrLoadStart
|
||||
@@ -247,11 +256,11 @@ type HxEvent =
|
||||
AfterHistoryUpdate, ("afterHistoryUpdate", "after:history:update")
|
||||
AfterInit, ("afterInit", "after:init")
|
||||
AfterOnLoad, ("afterOnLoad", "after:init")
|
||||
AfterProcessNode, ("afterProcessNode", "after:init")
|
||||
AfterProcessNode, ("afterProcessNode", "after:process")
|
||||
AfterPushIntoHistory, ("afterPushIntoHistory", "after:push:into:history")
|
||||
AfterReplaceIntoHistory, ("afterReplaceIntoHistory", "after:replace:into:history")
|
||||
AfterRequest, ("afterRequest", "after:request")
|
||||
AfterSettle, ("afterSettle", "after:swap")
|
||||
AfterSettle, ("afterSettle", "after:settle")
|
||||
AfterSseMessage, ("afterSseMessage", "after:sse:message")
|
||||
AfterSseStream, ("afterSseStream", "after:sse:stream")
|
||||
AfterSwap, ("afterSwap", "after:swap")
|
||||
@@ -262,9 +271,11 @@ type HxEvent =
|
||||
BeforeHistoryUpdate, ("beforeHistoryUpdate", "before:history:update")
|
||||
BeforeInit, ("beforeInit", "before:init")
|
||||
BeforeOnLoad, ("beforeOnLoad", "before:init")
|
||||
BeforeProcessNode, ("beforeProcessNode", "before:init")
|
||||
BeforeRestoreHistory, ("beforeRestoreHistory", "before:restore:history")
|
||||
BeforeProcess, ("beforeProcess", "before:process")
|
||||
BeforeProcessNode, ("beforeProcessNode", "before:process")
|
||||
BeforeRequest, ("beforeRequest", "before:request")
|
||||
BeforeResponse, ("beforeResponse", "before:response")
|
||||
BeforeRestoreHistory, ("beforeRestoreHistory", "before:restore:history")
|
||||
BeforeSend, ("beforeSend", "before:request")
|
||||
BeforeSseMessage, ("beforeSseMessage", "before:sse:message")
|
||||
BeforeSseReconnect, ("beforeSseReconnect", "before:sse:reconnect")
|
||||
@@ -288,7 +299,7 @@ type HxEvent =
|
||||
OobErrorNoTarget, ("oobErrorNoTarget", "error")
|
||||
Prompt, ("prompt", "prompt")
|
||||
PushedIntoHistory, ("pushedIntoHistory", "after:push:into:history")
|
||||
ResponseError, ("responseError", "error")
|
||||
ResponseError, ("responseError", "response:error")
|
||||
SendError, ("sendError", "error")
|
||||
SseError, ("sseError", "error")
|
||||
SseOpen, ("sseOpen", "sse-open")
|
||||
@@ -298,8 +309,8 @@ type HxEvent =
|
||||
ValidationValidate, ("validation:validate", "validation:validate")
|
||||
ValidationFailed, ("validation:failed", "validation:failed")
|
||||
ValidationHalted, ("validation:halted", "validation:halted")
|
||||
XhrAbort, ("xhr:abort", "xhr:abort")
|
||||
XhrLoadEnd, ("xhr:loadend", "xhr:loadend")
|
||||
XhrAbort, ("xhr:abort", "error")
|
||||
XhrLoadEnd, ("xhr:loadend", "finally:request")
|
||||
XhrLoadStart, ("xhr:loadstart", "xhr:loadstart")
|
||||
XhrProgress, ("xhr:progress", "xhr:progress")
|
||||
]
|
||||
@@ -757,7 +768,7 @@ module HtmxAttrs =
|
||||
/// <seealso cref="HxEvent" />
|
||||
/// <seealso href="https://htmx.org/attributes/hx-on/">Documentation</seealso>
|
||||
let _hxOnHxEvent (hxEvent: HxEvent) handler =
|
||||
_hxOnEvent $":{hxEvent.ToHxOnString()}" handler
|
||||
_hxOnEvent $"htmx:{hxEvent.ToHxOnString()}" handler
|
||||
|
||||
/// <summary>Filters the parameters that will be submitted with a request</summary>
|
||||
/// <param name="toInclude">The fields to include (use <c>HxParams</c> to generate this value)</param>
|
||||
@@ -852,6 +863,31 @@ module HtmxAttrs =
|
||||
let _hxSelectOob selectors =
|
||||
attr "hx-select-oob" selectors
|
||||
|
||||
/// <summary>Define a response to an HTTP status code or code range</summary>
|
||||
/// <param name="code">
|
||||
/// The code or code range; for a code, pass the full code as a string (ex. <c>404</c>), and for a range, pass a
|
||||
/// string with one or two characters (ex. <c>5</c> for all 500s, <c>42</c> for 420-429)
|
||||
/// </param>
|
||||
/// <param name="swap">The swap style to use for this action(use <c>HxSwap</c> values)</param>
|
||||
/// <param name="action">
|
||||
/// The action which should be taken for responses matching the status code; can be a combination of the following,
|
||||
/// with each item separated by a space:
|
||||
/// <ul>
|
||||
/// <li><c>target:</c> and a CSS selector for the swap target (overrides original <c>hx-target</c>)</li>
|
||||
/// <li><c>select:</c> and a CSS selector for the content to be swapped (overrides original <c>hx-select</c>)</li>
|
||||
/// <li><c>push:</c> and <c>true</c>, <c>false</c>, or a URL; <c>false</c> does not update URL history, <c>true</c>
|
||||
/// updates with the current URL, and a string URL will be pushed into history as-is</li>
|
||||
/// <li><c>replace:</c> and <c>true</c>, <c>false</c>, or a URL; same as <c>push:</c>, but replaces in history
|
||||
/// instead</li>
|
||||
/// <li><c>transition:</c> and <c>true</c> or <c>false</c>; whether to use view transitions</li>
|
||||
/// </ul>
|
||||
/// If only the type of swap is being changed, pass an empty string to this parameter.
|
||||
/// </param>
|
||||
/// <seealso href="https://four.htmx.org/reference/attributes/hx-status">Documentation</seealso>
|
||||
let _hxStatus code swap action =
|
||||
let range = $"%s{code}xx".Substring(0, 3)
|
||||
attr $"hx-status:{range}" ($"swap:%s{swap} %s{action}".Trim())
|
||||
|
||||
/// <summary>
|
||||
/// Controls how the response content is swapped into the DOM (e.g. <c>outerHTML</c> or <c>beforeEnd</c>)
|
||||
/// </summary>
|
||||
@@ -975,18 +1011,35 @@ module Script =
|
||||
/// <summary>Script tag to load the package-provided version of htmx</summary>
|
||||
let local = script [ _src htmxLocalScript ] []
|
||||
|
||||
/// <summary>Script tag to load the package-provided version of the htmx-plus-extensions bundle</summary>
|
||||
let localMax = script [ _src htmaxLocalScript ] []
|
||||
|
||||
/// <summary>Script tag to load the minified version from jsdelivr.net</summary>
|
||||
/// <remarks>Ensure <c>cdn.jsdelivr.net</c> is in your CSP <c>script-src</c> list (if applicable)</remarks>
|
||||
let cdnMinified =
|
||||
script [ _src "https://cdn.jsdelivr.net/npm/htmx.org@4.0.0-alpha6/dist/htmx.min.js"
|
||||
_integrity "sha384-/5n21KGM472oBhvzUrvju8FRDq/4WNMS3TGw5RWFkZR/kq+sCevqNXFcakqRtaHu"
|
||||
script [ _src $"https://cdn.jsdelivr.net/npm/htmx.org@{HtmxVersion}/dist/htmx.min.js"
|
||||
_integrity "sha384-aWZK1NtOs/aWb/+YZdTM8q2JkWEshlMc9mgZ189numT9bwFhyAyYEoO4nO/2dTXt"
|
||||
_crossorigin "anonymous" ] []
|
||||
|
||||
/// <summary>Script tag to load the unminified version from jsdelivr.net</summary>
|
||||
/// <remarks>Ensure <c>cdn.jsdelivr.net</c> is in your CSP <c>script-src</c> list (if applicable)</remarks>
|
||||
let cdnUnminified =
|
||||
script [ _src "https://cdn.jsdelivr.net/npm/htmx.org@4.0.0-alpha6/dist/htmx.js"
|
||||
_integrity "sha384-I3uRy2RjSNcS8Oi2SNWZD9IrafyrrrBb4QaHNA9pWVWfJuKtL0p8qAwHSrEyXoKh"
|
||||
script [ _src $"https://cdn.jsdelivr.net/npm/htmx.org@{HtmxVersion}/dist/htmx.js"
|
||||
_integrity "sha384-OFLRIZpuqI2wwFozxvDGcuF3TQ36ySMgp44WEthOiR4wFzRkhZbK72HFaBo2C3cx"
|
||||
_crossorigin "anonymous" ] []
|
||||
|
||||
/// <summary>Script tag to load the minified htmx-plus-extensions bundle from jsdelivr.net</summary>
|
||||
/// <remarks>Ensure <c>cdn.jsdelivr.net</c> is in your CSP <c>script-src</c> list (if applicable)</remarks>
|
||||
let cdnMaxMinified =
|
||||
script [ _src $"https://cdn.jsdelivr.net/npm/htmx.org@{HtmxVersion}/dist/htmax.min.js"
|
||||
_integrity "sha384-Qoqie5IRtOE79SDFFRSb/yKi+pkzpSnfjgwr1KksyP14OaHkLHar0KrLVxUwlsJF"
|
||||
_crossorigin "anonymous" ] []
|
||||
|
||||
/// <summary>Script tag to load the unminified htmx-plus-extensions bundle from jsdelivr.net</summary>
|
||||
/// <remarks>Ensure <c>cdn.jsdelivr.net</c> is in your CSP <c>script-src</c> list (if applicable)</remarks>
|
||||
let cdnMaxUnminified =
|
||||
script [ _src $"https://cdn.jsdelivr.net/npm/htmx.org@{HtmxVersion}/dist/htmax.js"
|
||||
_integrity "sha384-gGi3Urue6ZkE4NrJCmXWIZkfNkrt1IrdP3fr0kb/v06GWg3V1RnD9Pg/Ul3qhtAK"
|
||||
_crossorigin "anonymous" ] []
|
||||
|
||||
/// <summary>Script tag to load the minified version from jsdelivr.net</summary>
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
This package enables [htmx](https://htmx.org) support within the [Giraffe](https://giraffe.wiki) view engine.
|
||||
|
||||
**htmx version: 4.0.0-alpha6**
|
||||
**htmx version: 4.0.0-beta4**
|
||||
|
||||
_Upgrading from v2.x: see [the migration guide](https://four.htmx.org/migration-guide-htmx-4/) for changes, which are plentiful. htmx switches from `XMLHTTPRequest` to `fetch`, and many changes are related to the new event cycle._
|
||||
_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._
|
||||
|
||||
_Inheritance is now explicit; to have an attribute's value inherited to its children, wrap the attribute in `hxInherited` (ex. `hxInherited (_hxTarget "#main")`). Values can be appended to inherited values as well using the `hxAppend` modifier._
|
||||
|
||||
@@ -34,7 +34,9 @@ Support modules include:
|
||||
- `HxTrigger`
|
||||
- `HxVals`
|
||||
|
||||
`Htmx.Script.local` creates an `XmlNode` to load the package-provided htmx library. There are also two `XmlNode`s that will load the htmx script from jsdelivr; `Htmx.Script.cdnMinified` loads the minified version, and `Htmx.Script.cdnUnminified` loads the unminified version (useful for debugging). When using the CDN nodes and a Content Security Policy (CSP) header, `cdn.jsdelivr.net` needs to be listed as an allowable `script-src`.
|
||||
`Htmx.Script.local` creates an `XmlNode` to load the package-provided htmx library. There are also two `XmlNode`s that will load the htmx script from jsdelivr; `Htmx.Script.cdnMinified` loads the minified version, and `Htmx.Script.cdnUnminified` loads the unminified version (useful for debugging). htmx v4 also distributes a "max" bundle which contains some common extensions; these are available as `Htmx.Script.localMax`, `Htmx.Script.cdnMaxMinified`, and `Htmx.Script.cdnMaxUnminified`.
|
||||
|
||||
_NOTE: When using the CDN nodes and a Content Security Policy (CSP) header, `cdn.jsdelivr.net` needs to be listed as an allowable `script-src`._
|
||||
|
||||
This also supports [fragment rendering](https://bitbadger.solutions/blog/2022/fragment-rendering-in-giraffe-view-engine.html), providing the flexibility to render an entire template, or only a portion of it (based on the element's `id` attribute).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user