From 5cd489bbf09064e2e694728618dcb67636b5ee2d Mon Sep 17 00:00:00 2001 From: Will Ockelmann-Wagner Date: Sun, 25 Feb 2024 16:01:36 -0800 Subject: [PATCH] safer and easier selector creation --- Taskfile.yml | 4 +- hx/hx.go | 245 +++++++++++++++---------------------- hx/hx_test.go | 59 ++++++--- hx/trigger/event.go | 61 ++++----- hx/trigger/trigger_test.go | 24 ++-- 5 files changed, 190 insertions(+), 203 deletions(-) diff --git a/Taskfile.yml b/Taskfile.yml index 86e4976..7efea7f 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -39,7 +39,7 @@ tasks: lint:go: desc: Run golangci-lint cmds: - - golangci-lint run + - go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.56.2 run publish: desc: Publish a version @@ -56,7 +56,7 @@ tasks: desc: Run goimports cmds: - go mod tidy - - goimports -w -local github.com/will-wow/typed-htmx-go . + - go run golang.org/x/tools/cmd/goimports@v0.18.0 -w -local github.com/will-wow/typed-htmx-go . fmt:prettier: desc: Run prettier on non-go files diff --git a/hx/hx.go b/hx/hx.go index ecb6147..49d27f6 100644 --- a/hx/hx.go +++ b/hx/hx.go @@ -56,6 +56,9 @@ func (hx *HX) String() string { return strings.Join(attributes, " ") } +// A StandardCSSSelector is any valid CSS selector, like #element or `.class > button`. +type StandardCSSSelector string + // Boost allows you to “boost” normal anchors and form tags to use AJAX instead. This has the [nice fallback] that, if the user does not have javascript enabled, the site will continue to work. // // For anchor tags, clicking on the anchor will issue a GET request to the url specified in the href and will push the url so that a history entry is created. The target is the tag, and the innerHTML swap strategy is used by default. All of these can be modified by using the appropriate attributes, except the click trigger. @@ -274,8 +277,8 @@ func (hx *HX) PushURLPath(url string) *HX { // HTMX Attribute: [hx-select] // // [hx-select]: https://htmx.org/attributes/hx-select/ -func (hx *HX) Select(selector string) *HX { - return hx.set(Select, selector) +func (hx *HX) Select(selector StandardCSSSelector) *HX { + return hx.set(Select, string(selector)) } // SelectOOB allows you to select content from a response to be swapped in via an out-of-band swap. @@ -324,12 +327,12 @@ func (hx *HX) Select(selector string) *HX { // HTMX Attribute: [hx-select-oob] // // [hx-select-oob]: https://htmx.org/attributes/hx-select-oob/ -func (hx *HX) SelectOOB(selectors ...string) *HX { - return hx.set(SelectOOB, strings.Join(selectors, ",")) +func (hx *HX) SelectOOB(selectors ...StandardCSSSelector) *HX { + return hx.set(SelectOOB, joinStringLikes(selectors, ",")) } type SelectOOBStrategy struct { - Selector string + Selector StandardCSSSelector Strategy swap.Strategy } @@ -368,7 +371,7 @@ func (hx *HX) SelectOOBWithStrategy(selectors ...SelectOOBStrategy) *HX { values := make([]string, len(selectors)) for i, s := range selectors { if s.Strategy == "" { - values[i] = s.Selector + values[i] = string(s.Selector) } else { values[i] = fmt.Sprintf("%s:%s", s.Selector, s.Strategy) } @@ -489,115 +492,32 @@ func (hx *HX) SwapOOBWithStrategy(strategy swap.Strategy) *HX { // HTMX Attribute: [hx-swap-oob] // // [hx-swap-oob]: https://htmx.org/attributes/hx-swap-oob -func (hx *HX) SwapOOBSelector(strategy swap.Strategy, selector string) *HX { - return hx.set(SwapOOB, fmt.Sprintf("%s:%s", strategy, selector)) +func (hx *HX) SwapOOBSelector(strategy swap.Strategy, extendedSelector string) *HX { + return hx.set(SwapOOB, fmt.Sprintf("%s:%s", strategy, extendedSelector)) } -// Target allows you to target a different element for swapping than the one issuing the AJAX request. The value of this attribute can be: -// -// - A CSS query selector of the element to target. -// - this which indicates that the element that the hx-target attribute is on is the target. -// - closest which will find the closest ancestor element or itself, that matches the given CSS selector (e.g. closest tr will target the closest table row to the element). -// - find which will find the first child descendant element that matches the given CSS selector. -// - next which resolves to element.nextElementSibling -// - next which will scan the DOM forward for the first element that matches the given CSS selector. (e.g. next .error will target the closest following sibling element with error class) -// - previous which resolves to element.previousElementSibling -// - previous which will scan the DOM backwards for the first element that matches the given CSS selector. (e.g previous .error will target the closest previous sibling with error class) -// -// For targeting a special target like `this`, see [HX.TargetNonStandardSelector()]. -// -// For targeting finding the nearest element, see [HX.TargetRelative()]. -// -// Here is an example that targets a div: -// -//
-//
-// -//
-// -// The response from the /register url will be appended to the div with the id response-div. -// -// # Notes -// -// hx-target is inherited and can be placed on a parent element -// -// HTMX Attribute: [hx-target] -// -// [hx-target]: https://htmx.org/attributes/hx-target -func (hx *HX) Target(selector string) *HX { - return hx.set(Target, selector) -} - -// A TargetNonStandardSelector is a special HTMX target for swapping. -type TargetNonStandardSelector string +type TargetSelector string const ( - TargetThis TargetNonStandardSelector = "this" // indicates that the element that the hx-target attribute is on is the target. - TargetNext TargetNonStandardSelector = "next" // resolves to element.nextElementSibling - TargetPrevious TargetNonStandardSelector = "previous" // resolves to element.previousElementSibling + TargetThis TargetSelector = "this" // indicates that the element that the hx-target attribute is on is the target. + TargetNext TargetSelector = "next" // resolves to element.nextElementSibling + TargetPrevious TargetSelector = "previous" // resolves to element.previousElementSibling ) -// TargetNonStandard allows you to target a different element for swapping than the one issuing the AJAX request. The value of this attribute can be: -// -// - this which indicates that the element that the hx-target attribute is on is the target. -// - next which resolves to element.nextElementSibling -// - previous which resolves to element.previousElementSibling -// -// For targeting with a general selector target, see [HX.Target()]. -// -// For targeting finding the nearest element, see [HX.TargetRelative()]. -// -// This example uses hx-target="this" to make a link that updates itself when clicked: -// -// New link -// -// # Notes -// -// hx-target is inherited and can be placed on a parent element -// -// HTMX Attribute: [hx-target] -// -// [hx-target]: https://htmx.org/attributes/hx-target -func (hx *HX) TargetNonStandard(target TargetNonStandardSelector) *HX { - return hx.set(Target, string(target)) -} - -// A SelectorModifier is a relative modifier to a CSS selector. This is used for most "extended selectors". -type SelectorModifier string - -const ( - SelectorClosest SelectorModifier = "closest" // find the closest ancestor element or itself, that matches the given CSS selector - SelectorFind SelectorModifier = "find" // find the first child descendant element that matches the given CSS selector - SelectorNext SelectorModifier = "next" // scan the DOM forward for the first element that matches the given CSS selector. (e.g. next .error will target the closest following sibling element with error class) - SelectorPrevious SelectorModifier = "previous" // scan the DOM backwards for the first element that matches the given CSS selector. (e.g previous .error will target the closest previous sibling with error class) -) +var TargetRelative = makeRelativeSelector[SelectorModifier, TargetSelector]() -// TargetRelative allows you to target a different element for swapping than the one issuing the AJAX request, and find the target relative to the current element. The value of this attribute can be: -// -// - closest which will find the closest ancestor element or itself, that matches the given CSS selector (e.g. closest tr will target the closest table row to the element). -// - find which will find the first child descendant element that matches the given CSS selector. -// - next which will scan the DOM forward for the first element that matches the given CSS selector. (e.g. next .error will target the closest following sibling element with error class) -// - previous which will scan the DOM backwards for the first element that matches the given CSS selector. (e.g previous .error will target the closest previous sibling with error class) +// Target allows you to target a different element for swapping than the one issuing the AJAX request. // -// For targeting a special target like `this`, see [HX.TargetElement()]. +// You can pass an extended selector to this method, using [RelativeSelector]. // -// Here is an example that targets the previous div by ID: +// Here is an example that targets a div: // //
-//
Not me
//
// //
// -// The response from the /register url will be appended to the first previous div with the id response-div. +// The response from the /register url will be appended to the div with the id response-div. // // # Notes // @@ -614,8 +534,8 @@ const ( // HTMX Attribute: [hx-target] // // [hx-target]: https://htmx.org/attributes/hx-target -func (hx *HX) TargetRelative(modifier SelectorModifier, selector string) *HX { - return hx.set(Target, fmt.Sprintf("%s %s", modifier, selector)) +func (hx *HX) Target(extendedSelector TargetSelector) *HX { + return hx.set(Target, string(extendedSelector)) } // Trigger allows you to specify what event triggers an AJAX request. @@ -769,13 +689,23 @@ func (hx *HX) Disable() *HX { return hx.set(Disable, true) } +type DisabledEltModifier string + +const DisabledEltClosest DisabledEltModifier = "closest" + +type DisabledEltSelector string + +const DisabledEltThis DisabledEltSelector = "this" + +var DisabledEltRelative = makeRelativeSelector[DisabledEltModifier, DisabledEltSelector]() + // DisabledElt allows you to specify elements that will have the disabled attribute added to them for the duration of the request. // // The value of this attribute is a CSS query selector of the element or elements to apply the class to, or the keyword closest, followed by a CSS selector, which will find the closest ancestor element or itself, that matches the given CSS selector (e.g. closest tr), or the keyword this // // Here is an example with a button that will disable itself during a request: // -// // @@ -784,8 +714,8 @@ func (hx *HX) Disable() *HX { // HTMX Attribute: [hx-disabled-elt] // // [hx-disabled-elt]: https://htmx.org/attributes/hx-disabled-elt -func (hx *HX) DisabledElt(selector string) *HX { - return hx.set(DisabledElt, selector) +func (hx *HX) DisabledElt(selector DisabledEltSelector) *HX { + return hx.set(DisabledElt, string(selector)) } // Disinherit allows you to disable automatic attribute inheritance for one or multiple specified attributes. @@ -1000,27 +930,38 @@ func (hx *HX) HistoryElt() *HX { return hx.set(HistoryElt, true) } -// include additional data in requests -func (hx *HX) Include(selector string) *HX { - return hx.set(Include, selector) -} +type IncludeSelector string -// include additional data in requests -func (hx *HX) IncludeThis() *HX { - return hx.set(Include, "this") -} +const IncludeThis IncludeSelector = "this" -// include additional data in requests -func (hx *HX) IncludeRelative(modifier SelectorModifier, selector string) *HX { - return hx.set(Include, fmt.Sprintf("%s %s", modifier, selector)) -} +var IncludeRelative = makeRelativeSelector[SelectorModifier, IncludeSelector]() -func (hx *HX) Indicator(selector string) *HX { - return hx.set(Indicator, selector) +// Include allows you to include additional element values in an AJAX request. +// +// HTMX Attribute: [hx-include] +// +// [hx-include]: https://htmx.org/attributes/hx-include/ +func (hx *HX) Include(selector IncludeSelector) *HX { + return hx.set(Include, string(selector)) } -func (hx *HX) IndicatorRelative(modifier SelectorModifier, selector string) *HX { - return hx.set(Indicator, fmt.Sprintf("%s %s", modifier, selector)) +type IndicatorModifier string + +const IndicatorClosest IndicatorModifier = "closest" + +type IndicatorSelector string + +var IndicatorRelative = makeRelativeSelector[IndicatorModifier, IndicatorSelector]() + +// The hx-indicator attribute allows you to specify the element that will have the htmx-request class added to it for the duration of the request. This can be used to show spinners or progress indicators while the request is in flight. +// +// The value of this attribute is a CSS query selector of the element or elements to apply the class to, or the keyword `closest` followed by a CSS selector, which will find the closest ancestor element or itself, that matches the given CSS selector (e.g. closest tr); +// +// HTMX Attribute: [hx-indicator] +// +// [hx-indicator]: https://htmx.org/attributes/hx-indicator/ +func (hx *HX) Indicator(extendedSelector IndicatorSelector) *HX { + return hx.set(Indicator, string(extendedSelector)) } // ParamsAll allows you to include all parameters with an AJAX request (default). @@ -1241,27 +1182,17 @@ const ( SyncQueueAll SyncStrategy = "queue all" // queue all requests that show up while a request is in flight ) -// SyncStrategy allows you to synchronize AJAX requests between multiple elements. -// -// The hx-sync attribute consists of a CSS selector to indicate the element to synchronize on. By default, this will use the [SyncDrop] strategy. -// -// You can pass "this" as a selector to synchronize requests from the current element. -// -// # Notes -// - hx-sync is inherited and can be placed on a parent element -// -// HTMX Attribute: [hx-sync] -// -// [hx-sync]: https://htmx.org/attributes/hx-sync/ -func (hx *HX) Sync(selector string) *HX { - return hx.set(Sync, selector) -} +type SyncSelector string + +const SyncThis SyncSelector = "this" + +var SyncRelative = makeRelativeSelector[SelectorModifier, SyncSelector]() // SyncStrategy allows you to synchronize AJAX requests between multiple elements. // -// The hx-sync attribute consists of a CSS selector to indicate the element to synchronize on, followed optionally by a colon and then by an optional syncing strategy. +// The hx-sync attribute consists of a CSS selector to indicate the element to synchronize on. By default, this will use the [SyncDrop] strategy. // -// You can pass "this" as a selector to synchronize requests from the current element. +// You can pass [hx.SyncThis] as a selector to synchronize requests from the current element. // // # Notes // - hx-sync is inherited and can be placed on a parent element @@ -1269,15 +1200,15 @@ func (hx *HX) Sync(selector string) *HX { // HTMX Attribute: [hx-sync] // // [hx-sync]: https://htmx.org/attributes/hx-sync/ -func (hx *HX) SyncStrategy(selector string, strategy SyncStrategy) *HX { - return hx.set(Sync, fmt.Sprintf("%s:%s", selector, strategy)) +func (hx *HX) Sync(extendedSelector SyncSelector) *HX { + return hx.set(Sync, string(extendedSelector)) } // SyncStrategy allows you to synchronize AJAX requests between multiple elements. // // The hx-sync attribute consists of a CSS selector to indicate the element to synchronize on, followed optionally by a colon and then by an optional syncing strategy. // -// You can pass "this" as a selector to synchronize requests from the current element. +// You can pass "hx.This" as a selector to synchronize requests from the current element. // // # Notes // - hx-sync is inherited and can be placed on a parent element @@ -1285,8 +1216,8 @@ func (hx *HX) SyncStrategy(selector string, strategy SyncStrategy) *HX { // HTMX Attribute: [hx-sync] // // [hx-sync]: https://htmx.org/attributes/hx-sync/ -func (hx *HX) SyncStrategyRelative(modifier SelectorModifier, selector string, strategy SyncStrategy) *HX { - return hx.set(Sync, fmt.Sprintf("%s %s:%s", modifier, selector, strategy)) +func (hx *HX) SyncStrategy(extendedSelector SyncSelector, strategy SyncStrategy) *HX { + return hx.set(Sync, fmt.Sprintf("%s:%s", extendedSelector, strategy)) } // Validate will cause an element to validate itself by way of the HTML5 Validation API before it submits a request. @@ -1375,6 +1306,17 @@ const ( WS Attribute = "hx-ws" ) +// A SelectorModifier is a relative modifier to a CSS selector. This is used for "extended selectors". +// Some attributes only support a subset of these, but any Relative function that takes this type supports the full set.. +type SelectorModifier string + +const ( + Closest SelectorModifier = "closest" // find the closest ancestor element or itself, that matches the given CSS selector + Find SelectorModifier = "find" // find the first child descendant element that matches the given CSS selector + Next SelectorModifier = "next" // scan the DOM forward for the first element that matches the given CSS selector. (e.g. next .error will target the closest following sibling element with error class) + Previous SelectorModifier = "previous" // scan the DOM backwards fo +) + func boolToString(hx bool) string { if hx { return "true" @@ -1407,3 +1349,18 @@ func quoteJSIdentifier(identifier string) string { } return fmt.Sprintf(`"%s"`, identifier) } + +// joinStringLikes joins a slice of string-like values into a single string. +func joinStringLikes[T ~string](elems []T, sep string) string { + var stringElems = make([]string, len(elems)) + for i, x := range elems { + stringElems[i] = string(x) + } + return strings.Join(stringElems, sep) +} + +func makeRelativeSelector[Modifier ~string, Selector ~string]() func(Modifier, string) Selector { + return func(modifier Modifier, selector string) Selector { + return Selector(fmt.Sprintf("%s %s", modifier, selector)) + } +} diff --git a/hx/hx_test.go b/hx/hx_test.go index 9fbc8a3..712b4f9 100644 --- a/hx/hx_test.go +++ b/hx/hx_test.go @@ -106,14 +106,16 @@ func TestHX(t *testing.T) { want: `hx-target='#example'`, }, { - name: "TargetNonStandard", - attrs: hx.New().TargetNonStandard(hx.TargetThis), + name: "Target non-standard", + attrs: hx.New().Target(hx.TargetThis), want: `hx-target='this'`, }, { - name: "TargetSelector", - attrs: hx.New().TargetRelative(hx.SelectorClosest, "#example"), - want: `hx-target='closest #example'`, + name: "TargetSelector", + attrs: hx.New().Target( + hx.TargetRelative(hx.Closest, "#example"), + ), + want: `hx-target='closest #example'`, }, { name: "Trigger", @@ -162,7 +164,19 @@ func TestHX(t *testing.T) { }, { name: "DisabledElt", - attrs: hx.New().DisabledElt("this"), + attrs: hx.New().DisabledElt("#example"), + want: `hx-disabled-elt='#example'`, + }, + { + name: "DisabledElt closest", + attrs: hx.New().DisabledElt( + hx.DisabledEltRelative(hx.DisabledEltClosest, "#example"), + ), + want: `hx-disabled-elt='closest #example'`, + }, + { + name: "DisabledElt this", + attrs: hx.New().DisabledElt(hx.DisabledEltThis), want: `hx-disabled-elt='this'`, }, { @@ -221,14 +235,16 @@ func TestHX(t *testing.T) { want: `hx-include='#example'`, }, { - name: "IncludeThis", - attrs: hx.New().IncludeThis(), + name: "Include this", + attrs: hx.New().Include(hx.IncludeThis), want: `hx-include='this'`, }, { - name: "IncludeRelative", - attrs: hx.New().IncludeRelative(hx.SelectorClosest, "#example"), - want: `hx-include='closest #example'`, + name: "Include relative", + attrs: hx.New().Include( + hx.IncludeRelative(hx.Closest, "#example"), + ), + want: `hx-include='closest #example'`, }, { name: "Indicator", @@ -236,9 +252,11 @@ func TestHX(t *testing.T) { want: `hx-indicator='#example'`, }, { - name: "IndicatorRelative", - attrs: hx.New().IndicatorRelative(hx.SelectorClosest, "#example"), - want: `hx-indicator='closest #example'`, + name: "Indicator relative", + attrs: hx.New().Indicator( + hx.IndicatorRelative(hx.IndicatorClosest, "#example"), + ), + want: `hx-indicator='closest #example'`, }, { name: "ParamsAll", @@ -292,18 +310,21 @@ func TestHX(t *testing.T) { }, { name: "Sync", - attrs: hx.New().Sync("this"), + attrs: hx.New().Sync(hx.SyncThis), want: `hx-sync='this'`, }, { name: "SyncStrategy", - attrs: hx.New().SyncStrategy("this", hx.SyncDrop), + attrs: hx.New().SyncStrategy(hx.SyncThis, hx.SyncDrop), want: `hx-sync='this:drop'`, }, { - name: "SyncStrategyRelative", - attrs: hx.New().SyncStrategyRelative(hx.SelectorClosest, "this", hx.SyncDrop), - want: `hx-sync='closest this:drop'`, + name: "SyncStrategy relative", + attrs: hx.New().SyncStrategy( + hx.SyncRelative(hx.Closest, "#example"), + hx.SyncDrop, + ), + want: `hx-sync='closest #example:drop'`, }, { name: "Validate", diff --git a/hx/trigger/event.go b/hx/trigger/event.go index a2862ff..e7fa6ff 100644 --- a/hx/trigger/event.go +++ b/hx/trigger/event.go @@ -2,6 +2,7 @@ package trigger import ( "fmt" + "regexp" "strconv" "strings" "time" @@ -99,43 +100,47 @@ func (e *Event) Throttle(timing time.Duration) *Event { return e } -// From allows the event that triggers a request to come from another element in the document (e.g. listening to a key event on the body, to support hot keys) -// A standard CSS selector resolves to all elements matching that selector. Thus, from:input would listen on every input on the page. -// If the selector contains whitespace, it will be wrapped in () to disambiguate it from other modifiers. -func (e *Event) From(selector string) *Event { - e.modifiers[From] = disambiguateSelector(selector) - return e -} - -// A FromNonStandardSelector is a non-standard selector for the From modifier. -type FromNonStandardSelector string +// A SelectorModifier is a relative modifier to a CSS selector. This is used for "extended selectors". +// Some attributes only support a subset of these, but any Relative function that takes this type supports the full set.. +type SelectorModifier string const ( - FromDocument FromNonStandardSelector = "document" // listen for events on the document - FromWindow FromNonStandardSelector = "window" // listen for events on the window - FromNext FromNonStandardSelector = "next" // resolves to element.nextElementSibling - FromPrevious FromNonStandardSelector = "previous" // resolves to element.previousElementSibling + Closest SelectorModifier = "closest" // find the closest ancestor element or itself, that matches the given CSS selector + Find SelectorModifier = "find" // find the first child descendant element that matches the given CSS selector + Next SelectorModifier = "next" // scan the DOM forward for the first element that matches the given CSS selector. (e.g. next .error will target the closest following sibling element with error class) + Previous SelectorModifier = "previous" // scan the DOM backwards fo ) -// FromNonStandard allows the event that triggers a request to come from another element in the document. The extended CSS selector here allows for the non-standard CSS values in [FromNonStandardSelector]. -func (e *Event) FromNonStandard(selector FromNonStandardSelector) *Event { - e.modifiers[From] = string(selector) - return e -} - -type FromSelectorModifier string +// A FromSelector is a non-standard selector for the From modifier. +type FromSelector string const ( - FromSelectorClosest FromSelectorModifier = "closest" // find the closest ancestor element or itself, that matches the given CSS selector - FromSelectorFind FromSelectorModifier = "find" // find the first child descendant element that matches the given CSS selector. - FromSelectorNext FromSelectorModifier = "next" // scan the DOM forward for the first element that matches the given CSS selector. (e.g. next .error will target the closest following sibling element with error class) - FromSelectorPrevious FromSelectorModifier = "previous" // scan the DOM backwards for the first element that matches the given CSS selector. (e.g previous .error will target the closest previous sibling with error class) + FromDocument FromSelector = "document" // listen for events on the document + FromWindow FromSelector = "window" // listen for events on the window + FromNext FromSelector = "next" // resolves to element.nextElementSibling + FromPrevious FromSelector = "previous" // resolves to element.previousElementSibling ) -// FromRelative allows the event that triggers a request to come from another element in the document. The extended CSS selector here allows for finding a CSS selector relative to the current element. +// FromRelative creates a relative selector for an Event.From modifier. +// It always wraps the selector in (), in case it contains a space. +func FromRelative(modifier SelectorModifier, selector string) FromSelector { + return FromSelector(fmt.Sprintf("%s (%s)", modifier, selector)) +} + +var disambiguatedRe = regexp.MustCompile(`\(`) + +// From allows the event that triggers a request to come from another element in the document (e.g. listening to a key event on the body, to support hot keys) +// A standard CSS selector resolves to all elements matching that selector. Thus, from:input would listen on every input on the page. // If the selector contains whitespace, it will be wrapped in () to disambiguate it from other modifiers. -func (e *Event) FromRelative(modifier FromSelectorModifier, selector string) *Event { - e.modifiers[From] = fmt.Sprintf("%s %s", modifier, disambiguateSelector(selector)) +func (e *Event) From(extendedSelector FromSelector) *Event { + // Wrap the selector in () to disambiguate it, if not done already by [FromRelative]. + var selector string + if disambiguatedRe.MatchString(string(extendedSelector)) { + selector = string(extendedSelector) + } else { + selector = fmt.Sprintf("(%s)", extendedSelector) + } + e.modifiers[From] = selector return e } diff --git a/hx/trigger/trigger_test.go b/hx/trigger/trigger_test.go index 026eb0e..1522403 100644 --- a/hx/trigger/trigger_test.go +++ b/hx/trigger/trigger_test.go @@ -46,7 +46,7 @@ func TestNewEvent(t *testing.T) { { name: "From", trigger: trigger.NewEvent("click").From("#element"), - want: "click from:#element", + want: "click from:(#element)", }, { name: "From with spaces", @@ -54,19 +54,23 @@ func TestNewEvent(t *testing.T) { want: "click from:(parent > child)", }, { - name: "FromNonStandard", - trigger: trigger.NewEvent("click").FromNonStandard(trigger.FromDocument), - want: "click from:document", + name: "From non-standard", + trigger: trigger.NewEvent("click").From(trigger.FromDocument), + want: "click from:(document)", }, { - name: "FromRelative", - trigger: trigger.NewEvent("click").FromRelative(trigger.FromSelectorNext, "#alert"), - want: "click from:next #alert", + name: "From relative", + trigger: trigger.NewEvent("click").From( + trigger.FromRelative(trigger.Next, "#alert"), + ), + want: "click from:next (#alert)", }, { - name: "FromRelative with whitespace", - trigger: trigger.NewEvent("click").FromRelative(trigger.FromSelectorNext, "#alert > button"), - want: "click from:next (#alert > button)", + name: "From relative with whitespace", + trigger: trigger.NewEvent("click").From( + trigger.FromRelative(trigger.Next, "#alert > button"), + ), + want: "click from:next (#alert > button)", }, { name: "Target",