Fix remove fragment (#665)

* Replace delay function call

* Add test

* Improve tests

* Add release note
This commit is contained in:
Ben Croker 2025-02-13 09:38:29 -06:00 committed by GitHub
parent 2a16577829
commit 1e9c7a820b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 127 additions and 49 deletions

View File

@ -4,15 +4,8 @@ Each tagged version of Datastar is accompanied by a release note. Read the [rele
# WIP Release Notes # WIP Release Notes
## v1.0.0-beta.6 ## v1.0.0-beta.7
### Changed
- The `data-on-load` callback is now delayed to the next microtask, making it possible to place the `data-indicator` attribute later on the same element.
### Fixed ### Fixed
- Fixed a bug in which the `data-on-signals-change` attribute expression was not being executed when signal values changed ([#641](https://github.com/starfederation/datastar/issues/641)). - Fixed a bug in which `datastar-remove-fragments` events were not having any effect ([#664](https://github.com/starfederation/datastar/issues/664)).
- Fixed a bug in which the signal created by `data-ref` was being removed during the cleanup process ([#642](https://github.com/starfederation/datastar/issues/642)).
- Fixed a bug in which errors were being caught in preact core, which was obfuscating the original error ([#643](https://github.com/starfederation/datastar/issues/643)).
- Fixed a bug in which morphed DOM elements could lose their focus, due to a change of behavior in Idiomorph ([#645](https://github.com/starfederation/datastar/issues/645)).

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -18,7 +18,6 @@ import {
} from '../../../../engine/types' } from '../../../../engine/types'
import { elUniqId, walkDOM } from '../../../../utils/dom' import { elUniqId, walkDOM } from '../../../../utils/dom'
import { camel, isBoolString } from '../../../../utils/text' import { camel, isBoolString } from '../../../../utils/text'
import { delay } from '../../../../utils/timing'
import { import {
docWithViewTransitionAPI, docWithViewTransitionAPI,
supportsViewTransitions, supportsViewTransitions,
@ -94,7 +93,6 @@ function applyToTargets(
walkDOM(fragmentWithIDs, (el) => { walkDOM(fragmentWithIDs, (el) => {
if (!el.id?.length && Object.keys(el.dataset).length) { if (!el.id?.length && Object.keys(el.dataset).length) {
el.id = elUniqId(el) el.id = elUniqId(el)
console.log(el.id)
} }
}) })
@ -168,7 +166,7 @@ function applyToTargets(
const cl = modifiedTarget.classList const cl = modifiedTarget.classList
cl?.add(SWAPPING_CLASS) cl?.add(SWAPPING_CLASS)
delay(() => { setTimeout(() => {
initialTarget.classList.remove(SWAPPING_CLASS) initialTarget.classList.remove(SWAPPING_CLASS)
cl?.remove(SWAPPING_CLASS) cl?.remove(SWAPPING_CLASS)
}, settleDuration) }, settleDuration)
@ -177,7 +175,7 @@ function applyToTargets(
if (cl && originalHTML !== revisedHTML) { if (cl && originalHTML !== revisedHTML) {
cl.add(SETTLING_CLASS) cl.add(SETTLING_CLASS)
delay(() => { setTimeout(() => {
cl.remove(SETTLING_CLASS) cl.remove(SETTLING_CLASS)
}, settleDuration) }, settleDuration)
} }

View File

@ -10,7 +10,6 @@ import {
import { initErr } from '../../../../engine/errors' import { initErr } from '../../../../engine/errors'
import { PluginType, type WatcherPlugin } from '../../../../engine/types' import { PluginType, type WatcherPlugin } from '../../../../engine/types'
import { isBoolString } from '../../../../utils/text' import { isBoolString } from '../../../../utils/text'
import { delay } from '../../../../utils/timing'
import { import {
docWithViewTransitionAPI, docWithViewTransitionAPI,
supportsViewTransitions, supportsViewTransitions,
@ -43,7 +42,7 @@ export const RemoveFragments: WatcherPlugin = {
target.classList.add(SWAPPING_CLASS) target.classList.add(SWAPPING_CLASS)
} }
delay(() => { setTimeout(() => {
for (const target of removeTargets) { for (const target of removeTargets) {
target.remove() target.remove()
} }

View File

@ -80,7 +80,7 @@ export const On: AttributePlugin = {
if (key === 'load') { if (key === 'load') {
// Delay the callback to the next microtask so that indicators can be set // Delay the callback to the next microtask so that indicators can be set
delay(() => callback(), 0)() setTimeout(() => callback(), 0)
return () => {} return () => {}
} }

4
sdk/go/consts.go generated
View File

@ -7,8 +7,8 @@ import "time"
const ( const (
DatastarKey = "datastar" DatastarKey = "datastar"
Version = "1.0.0-beta.6" Version = "1.0.0-beta.6"
VersionClientByteSize = 39873 VersionClientByteSize = 39885
VersionClientByteSizeGzip = 14814 VersionClientByteSizeGzip = 14805
//region Default durations //region Default durations

View File

@ -30,9 +30,11 @@ func setupTests(ctx context.Context, router chi.Router, signals sessions.Store)
Label: "tests", Label: "tests",
Links: []*SidebarLink{ Links: []*SidebarLink{
{ID: "key_casing"}, {ID: "key_casing"},
{ID: "merge_fragment"},
{ID: "merge_fragment_signal"}, {ID: "merge_fragment_signal"},
{ID: "on_load"}, {ID: "on_load"},
{ID: "ref"}, {ID: "ref"},
{ID: "remove_fragment"},
{ID: "signals_change"}, {ID: "signals_change"},
{ID: "signals_change_path"}, {ID: "signals_change_path"},
}, },
@ -87,8 +89,10 @@ func setupTests(ctx context.Context, router chi.Router, signals sessions.Store)
}) })
if err := errors.Join( if err := errors.Join(
setupTestsMergeFragment(testsRouter),
setupTestsMergeFragmentSignal(testsRouter), setupTestsMergeFragmentSignal(testsRouter),
setupTestsOnLoad(testsRouter), setupTestsOnLoad(testsRouter),
setupTestsRemoveFragment(testsRouter),
); err != nil { ); err != nil {
panic(fmt.Sprintf("error setting up tests routes: %s", err)) panic(fmt.Sprintf("error setting up tests routes: %s", err))
} }

View File

@ -0,0 +1,19 @@
package site
import (
"net/http"
"github.com/go-chi/chi/v5"
datastar "github.com/starfederation/datastar/sdk/go"
)
func setupTestsMergeFragment(testsRouter chi.Router) error {
testsRouter.Get("/merge_fragment/data", func(w http.ResponseWriter, r *http.Request) {
sse := datastar.NewSSE(w, r)
c := mergeFragmentTest()
sse.MergeFragmentTempl(c)
})
return nil
}

View File

@ -0,0 +1,5 @@
package site
templ mergeFragmentTest() {
<code id="result">1</code>
}

View File

@ -0,0 +1,18 @@
package site
import (
"net/http"
"github.com/go-chi/chi/v5"
datastar "github.com/starfederation/datastar/sdk/go"
)
func setupTestsRemoveFragment(testsRouter chi.Router) error {
testsRouter.Delete("/remove_fragment/data", func(w http.ResponseWriter, r *http.Request) {
sse := datastar.NewSSE(w, r)
sse.RemoveFragments("#remove")
})
return nil
}

View File

@ -5,5 +5,5 @@ import (
) )
func TestKeyCasing(t *testing.T) { func TestKeyCasing(t *testing.T) {
setupLoadPageTest(t, "tests/key_casing") setupPageTestOnLoad(t, "tests/key_casing")
} }

View File

@ -0,0 +1,9 @@
package smoketests
import (
"testing"
)
func TestMergeFragment(t *testing.T) {
setupPageTestOnClick(t, "tests/merge_fragment")
}

View File

@ -4,6 +4,6 @@ import (
"testing" "testing"
) )
func TestMergeFragment(t *testing.T) { func TestMergeFragmentSignal(t *testing.T) {
setupButtonPageTest(t, "tests/merge_fragment_signal") setupPageTestOnClick(t, "tests/merge_fragment_signal")
} }

View File

@ -5,5 +5,5 @@ import (
) )
func TestExampleOnLoad(t *testing.T) { func TestExampleOnLoad(t *testing.T) {
setupLoadPageTest(t, "tests/on_load") setupPageTestOnLoad(t, "tests/on_load")
} }

View File

@ -5,5 +5,5 @@ import (
) )
func TestRef(t *testing.T) { func TestRef(t *testing.T) {
setupLoadPageTest(t, "tests/ref") setupPageTestOnLoad(t, "tests/ref")
} }

View File

@ -0,0 +1,9 @@
package smoketests
import (
"testing"
)
func TestRemoveFragment(t *testing.T) {
setupPageTestOnClick(t, "tests/remove_fragment")
}

View File

@ -65,7 +65,7 @@ func setupPageTest(t *testing.T, subURL string, gen func(runner runnerFn)) {
wg.Wait() wg.Wait()
} }
func setupButtonPageTest(t *testing.T, subURL string) { func setupPageTestOnClick(t *testing.T, subURL string) {
setupPageTest(t, subURL, func(runner runnerFn) { setupPageTest(t, subURL, func(runner runnerFn) {
runner(subURL, func(t *testing.T, page *rod.Page) { runner(subURL, func(t *testing.T, page *rod.Page) {
result := page.MustElement("#result") result := page.MustElement("#result")
@ -83,7 +83,7 @@ func setupButtonPageTest(t *testing.T, subURL string) {
}) })
} }
func setupLoadPageTest(t *testing.T, subURL string) { func setupPageTestOnLoad(t *testing.T, subURL string) {
setupPageTest(t, subURL, func(runner runnerFn) { setupPageTest(t, subURL, func(runner runnerFn) {
runner(subURL, func(t *testing.T, page *rod.Page) { runner(subURL, func(t *testing.T, page *rod.Page) {
result := page.MustElement("#result") result := page.MustElement("#result")

View File

@ -5,5 +5,5 @@ import (
) )
func TestExampleSignalsChangePath(t *testing.T) { func TestExampleSignalsChangePath(t *testing.T) {
setupButtonPageTest(t, "tests/signals_change_path") setupPageTestOnClick(t, "tests/signals_change_path")
} }

View File

@ -5,5 +5,5 @@ import (
) )
func TestExampleSignalsChange(t *testing.T) { func TestExampleSignalsChange(t *testing.T) {
setupButtonPageTest(t, "tests/signals_change") setupPageTestOnClick(t, "tests/signals_change")
} }

View File

@ -0,0 +1,12 @@
# Merge Fregment
Tests that merging a fragment works.
<div>
<button data-on-click="@get('/tests/merge_fragment/data')" class="btn">Merge</button>
<hr />
Result:
<code id="result">0</code>
<hr />
Expected result on click: <code>1</code>
</div>

View File

@ -0,0 +1,12 @@
# Remove Fregment
Tests that removing a fragment works.
<div>
<button data-on-click="@delete('/tests/remove_fragment/data')" class="btn">Remove</button>
<hr />
Result:
<code id="result"><span id="remove">0</span>1</code>
<hr />
Expected result on click: <code>1</code>
</div>