Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add RunAfterTimeout & RunOnInterval #75

Merged
merged 1 commit into from
Nov 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 32 additions & 13 deletions framework/h/command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"regexp"
"strings"
"testing"
"time"
)

func findScriptById(n *html.Node, id string) *html.Node {
Expand Down Expand Up @@ -87,7 +88,7 @@ func TestJsEval(t *testing.T) {
}

func TestSetText(t *testing.T) {
compareIgnoreSpaces(t, renderJs(t, SetText("Hello World")), "this.innerText = 'Hello World';")
compareIgnoreSpaces(t, renderJs(t, SetText("Hello World")), "(self||this).innerText = 'Hello World';")
}

func TestSetTextOnChildren(t *testing.T) {
Expand All @@ -100,42 +101,42 @@ func TestSetTextOnChildren(t *testing.T) {
}

func TestIncrement(t *testing.T) {
compareIgnoreSpaces(t, renderJs(t, Increment(5)), "this.innerText = parseInt(this.innerText) + 5;")
compareIgnoreSpaces(t, renderJs(t, Increment(5)), "(self||this).innerText = parseInt((self||this).innerText) + 5;")
}

func TestSetInnerHtml(t *testing.T) {
htmlContent := Div(Span(UnsafeRaw("inner content")))
compareIgnoreSpaces(t, renderJs(t, SetInnerHtml(htmlContent)), "this.innerHTML = `<div><span>inner content</span></div>`;")
compareIgnoreSpaces(t, renderJs(t, SetInnerHtml(htmlContent)), "(self||this).innerHTML = `<div><span>inner content</span></div>`;")
}

func TestSetOuterHtml(t *testing.T) {
htmlContent := Div(Span(UnsafeRaw("outer content")))
compareIgnoreSpaces(t, renderJs(t, SetOuterHtml(htmlContent)), "this.outerHTML = `<div><span>outer content</span></div>`;")
compareIgnoreSpaces(t, renderJs(t, SetOuterHtml(htmlContent)), "(self||this).outerHTML = `<div><span>outer content</span></div>`;")
}

func TestAddAttribute(t *testing.T) {
compareIgnoreSpaces(t, renderJs(t, AddAttribute("data-id", "123")), "this.setAttribute('data-id', '123');")
compareIgnoreSpaces(t, renderJs(t, AddAttribute("data-id", "123")), "(self||this).setAttribute('data-id', '123');")
}

func TestSetDisabled(t *testing.T) {
compareIgnoreSpaces(t, renderJs(t, SetDisabled(true)), "this.setAttribute('disabled', 'true');")
compareIgnoreSpaces(t, renderJs(t, SetDisabled(false)), "this.removeAttribute('disabled');")
compareIgnoreSpaces(t, renderJs(t, SetDisabled(true)), "(self||this).setAttribute('disabled', 'true');")
compareIgnoreSpaces(t, renderJs(t, SetDisabled(false)), "(self||this).removeAttribute('disabled');")
}

func TestRemoveAttribute(t *testing.T) {
compareIgnoreSpaces(t, renderJs(t, RemoveAttribute("data-id")), "this.removeAttribute('data-id');")
compareIgnoreSpaces(t, renderJs(t, RemoveAttribute("data-id")), "(self||this).removeAttribute('data-id');")
}

func TestAddClass(t *testing.T) {
compareIgnoreSpaces(t, renderJs(t, AddClass("active")), "this.classList.add('active');")
compareIgnoreSpaces(t, renderJs(t, AddClass("active")), "(self||this).classList.add('active');")
}

func TestRemoveClass(t *testing.T) {
compareIgnoreSpaces(t, renderJs(t, RemoveClass("active")), "this.classList.remove('active');")
compareIgnoreSpaces(t, renderJs(t, RemoveClass("active")), "(self||this).classList.remove('active');")
}

func TestToggleClass(t *testing.T) {
compareIgnoreSpaces(t, renderJs(t, ToggleClass("hidden")), "this.classList.toggle('hidden');")
compareIgnoreSpaces(t, renderJs(t, ToggleClass("hidden")), "(self||this).classList.toggle('hidden');")
}

func TestToggleClassOnElement(t *testing.T) {
Expand Down Expand Up @@ -206,7 +207,7 @@ func TestAlert(t *testing.T) {
}

func TestRemove(t *testing.T) {
compareIgnoreSpaces(t, renderJs(t, Remove()), "this.remove();")
compareIgnoreSpaces(t, renderJs(t, Remove()), "(self||this).remove();")
}

func TestSubmitFormOnEnter(t *testing.T) {
Expand Down Expand Up @@ -394,5 +395,23 @@ func TestConsoleLog(t *testing.T) {

func TestSetValue(t *testing.T) {
t.Parallel()
compareIgnoreSpaces(t, renderJs(t, SetValue("New Value")), "this.value = 'New Value';")
compareIgnoreSpaces(t, renderJs(t, SetValue("New Value")), "(self||this).value = 'New Value';")
}

func TestRunAfterTimeout(t *testing.T) {
t.Parallel()
compareIgnoreSpaces(t, renderJs(t, RunAfterTimeout(time.Second*5, SetText("Hello"))), `
setTimeout(function() {
(self||this).innerText = 'Hello'
}, 5000)
`)
}

func TestRunOnInterval(t *testing.T) {
t.Parallel()
compareIgnoreSpaces(t, renderJs(t, RunOnInterval(time.Second, SetText("Hello"))), `
setInterval(function() {
(self||this).innerText = 'Hello'
}, 1000)
`)
}
59 changes: 47 additions & 12 deletions framework/h/lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/maddalax/htmgo/framework/hx"
"github.com/maddalax/htmgo/framework/internal/util"
"strings"
"time"
)

type LifeCycle struct {
Expand Down Expand Up @@ -163,7 +164,7 @@ func NewComplexJsCommand(command string) ComplexJsCommand {
// SetText sets the inner text of the element.
func SetText(text string) SimpleJsCommand {
// language=JavaScript
return SimpleJsCommand{Command: fmt.Sprintf("this.innerText = '%s'", text)}
return SimpleJsCommand{Command: fmt.Sprintf("(self || this).innerText = '%s'", text)}
}

// SetTextOnChildren sets the inner text of all the children of the element that match the selector.
Expand All @@ -180,25 +181,25 @@ func SetTextOnChildren(selector, text string) ComplexJsCommand {
// Increment increments the inner text of the element by the given amount.
func Increment(amount int) SimpleJsCommand {
// language=JavaScript
return SimpleJsCommand{Command: fmt.Sprintf("this.innerText = parseInt(this.innerText) + %d", amount)}
return SimpleJsCommand{Command: fmt.Sprintf("(self || this).innerText = parseInt((self || this).innerText) + %d", amount)}
}

// SetInnerHtml sets the inner HTML of the element.
func SetInnerHtml(r Ren) SimpleJsCommand {
// language=JavaScript
return SimpleJsCommand{Command: fmt.Sprintf("this.innerHTML = `%s`", Render(r))}
return SimpleJsCommand{Command: fmt.Sprintf("(self || this).innerHTML = `%s`", Render(r))}
}

// SetOuterHtml sets the outer HTML of the element.
func SetOuterHtml(r Ren) SimpleJsCommand {
// language=JavaScript
return SimpleJsCommand{Command: fmt.Sprintf("this.outerHTML = `%s`", Render(r))}
return SimpleJsCommand{Command: fmt.Sprintf("(self || this).outerHTML = `%s`", Render(r))}
}

// AddAttribute adds the given attribute to the element.
func AddAttribute(name, value string) SimpleJsCommand {
// language=JavaScript
return SimpleJsCommand{Command: fmt.Sprintf("this.setAttribute('%s', '%s')", name, value)}
return SimpleJsCommand{Command: fmt.Sprintf("(self || this).setAttribute('%s', '%s')", name, value)}
}

// SetDisabled sets the disabled attribute on the element.
Expand All @@ -213,25 +214,25 @@ func SetDisabled(disabled bool) SimpleJsCommand {
// RemoveAttribute removes the given attribute from the element.
func RemoveAttribute(name string) SimpleJsCommand {
// language=JavaScript
return SimpleJsCommand{Command: fmt.Sprintf("this.removeAttribute('%s')", name)}
return SimpleJsCommand{Command: fmt.Sprintf("(self || this).removeAttribute('%s')", name)}
}

// AddClass adds the given class to the element.
func AddClass(class string) SimpleJsCommand {
// language=JavaScript
return SimpleJsCommand{Command: fmt.Sprintf("this.classList.add('%s')", class)}
return SimpleJsCommand{Command: fmt.Sprintf("(self || this).classList.add('%s')", class)}
}

// RemoveClass removes the given class from the element.
func RemoveClass(class string) SimpleJsCommand {
// language=JavaScript
return SimpleJsCommand{Command: fmt.Sprintf("this.classList.remove('%s')", class)}
return SimpleJsCommand{Command: fmt.Sprintf("(self || this).classList.remove('%s')", class)}
}

// ToggleClass toggles the given class on the element.
func ToggleClass(class string) SimpleJsCommand {
// language=JavaScript
return SimpleJsCommand{Command: fmt.Sprintf("this.classList.toggle('%s')", class)}
return SimpleJsCommand{Command: fmt.Sprintf("(self || this).classList.toggle('%s')", class)}
}

// ToggleText toggles the given text on the element.
Expand Down Expand Up @@ -391,23 +392,29 @@ func Alert(text string) SimpleJsCommand {
// Remove removes the element from the DOM.
func Remove() SimpleJsCommand {
// language=JavaScript
return SimpleJsCommand{Command: "this.remove()"}
return SimpleJsCommand{Command: "(self || this).remove()"}
}

// EvalJs evaluates the given JavaScript code.
func EvalJs(js string) ComplexJsCommand {
return NewComplexJsCommand(js)
}

func EvalCommandsOnSelector(selector string, cmds ...Command) ComplexJsCommand {
func CombineCommands(cmds ...Command) string {
lines := make([]string, len(cmds))
for i, cmd := range cmds {
lines[i] = Render(cmd)
lines[i] = strings.ReplaceAll(lines[i], "(self || this).", "self.")
lines[i] = strings.ReplaceAll(lines[i], "this.", "self.")
// some commands set the element we need to fix it so we arent redeclaring it
lines[i] = strings.ReplaceAll(lines[i], "let element =", "element =")
}
code := strings.Join(lines, "\n")
return code
}

func EvalCommandsOnSelector(selector string, cmds ...Command) ComplexJsCommand {
code := CombineCommands(cmds...)
return EvalJs(fmt.Sprintf(`
let element = document.querySelector("%s");

Expand Down Expand Up @@ -444,7 +451,7 @@ func ConsoleLog(text string) SimpleJsCommand {
// SetValue sets the value of the element.
func SetValue(value string) SimpleJsCommand {
// language=JavaScript
return SimpleJsCommand{Command: fmt.Sprintf("this.value = '%s'", value)}
return SimpleJsCommand{Command: fmt.Sprintf("(self || this).value = '%s'", value)}
}

// SubmitFormOnEnter submits the form when the user presses the enter key.
Expand Down Expand Up @@ -478,3 +485,31 @@ func InjectScriptIfNotExist(src string) ComplexJsCommand {
}
`, src, src))
}

func RunOnInterval(time time.Duration, cmds ...Command) ComplexJsCommand {
code := strings.Builder{}

for _, cmd := range cmds {
code.WriteString(fmt.Sprintf(`
setInterval(function() {
%s
}, %d)
`, Render(cmd), time.Milliseconds()))
}

return EvalJs(code.String())
}

func RunAfterTimeout(time time.Duration, cmds ...Command) ComplexJsCommand {
code := strings.Builder{}

for _, cmd := range cmds {
code.WriteString(fmt.Sprintf(`
setTimeout(function() {
%s
}, %d)
`, Render(cmd), time.Milliseconds()))
}

return EvalJs(code.String())
}
2 changes: 1 addition & 1 deletion framework/h/render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func TestRender(t *testing.T) {

div.attributes.Set("data-attr-1", "value")

expected := `<div data-attr-1="value" id="my-div" data-attr-2="value" data-attr-3="value" hx-on::before-request="this.innerText = &#39;before request&#39;;" hx-on::after-request="this.innerText = &#39;after request&#39;;"><div>hello, world</div>hello, child</div>`
expected := `<div data-attr-1="value" id="my-div" data-attr-2="value" data-attr-3="value" hx-on::before-request="(self || this).innerText = &#39;before request&#39;;" hx-on::after-request="(self || this).innerText = &#39;after request&#39;;"><div>hello, world</div>hello, child</div>`
result := Render(div)

assert.Equal(t,
Expand Down
2 changes: 2 additions & 0 deletions framework/js/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,5 @@ var ToggleText = h.ToggleText
var ToggleTextOnSibling = h.ToggleTextOnSibling
var ToggleTextOnChildren = h.ToggleTextOnChildren
var ToggleTextOnParent = h.ToggleTextOnParent
var RunAfterTimeout = h.RunAfterTimeout
var RunOnInterval = h.RunOnInterval
Loading