Repository: Rezact/Rezact (ts)

Issue:
IMPROVEMENT: Allow Signals to Swap Fragments
Consider the example below:

```tsx
import {Signal} from '@rezact/rezact/signals'

export default function Test() {
  
  let $elmRef = new Signal(<p>Not Changed</p>);

  const works = () => {
    $elmRef = <span>
      <p>This</p>
      <p>Works</p>
    </span>
  };

  const doesntWork = () => {
    $elmRef = <>
      <p>This</p>
      <p>Doesn't</p>
    </>
  }

  return (
    <>
      {$elmRef}
      <button onClick={works}>Change (works)</button>
      <button onClick={doesntWork}>Change (doesn't)</button>
    </>
  );
}

```

The first button works fine, but clicking the second button stops even the first button from working.  The current code allows swapping HTMLElements, but not Fragments.


https://github.com/Rezact/Rezact/blob/3983220cf8280366599ee67ffc44389adae4b2eb/src/lib/rezact/signals.ts#L135-L143

Pull request description:
30 improvement allow signals to swap fragments
close #30 

Patch:
diff --git a/src/examples/JsxSignals/JsxSignals.tsx b/src/examples/JsxSignals/JsxSignals.tsx
index 538ab48..74dbb0b 100644
--- a/src/examples/JsxSignals/JsxSignals.tsx
+++ b/src/examples/JsxSignals/JsxSignals.tsx
@@ -4,12 +4,47 @@ export function Page() {
   let $elmRef = <p>Test Signal Element</p>;
   const change = () => ($elmRef = <p>Changed Signal Element</p>);
 
-  let elm1 = <p>Elm 1</p>;
-  let elm2 = <p>Elm 2</p>;
-  let elm3 = <p>Elm 3</p>;
-  let elm4 = <p>Elm 4</p>;
+  const elm1 = <p>Elm 1</p>;
+  const elm2 = <p>Elm 2</p>;
+  const elm3 = <p>Elm 3</p>;
+  const elm4 = <p>Elm 4</p>;
+
+  const elm5and6 = (
+    <>
+      <p>Elm 5</p>
+      <p>Elm 6</p>
+    </>
+  );
+
+  const elm7and8 = (
+    <>
+      <p>Elm 7</p>
+      <p>Elm 8</p>
+    </>
+  );
+
+  const elm9 = <p>Elm 9</p>;
+  const elm10and11 = (
+    <>
+      <p>Elm 10</p>
+      <p>Elm 11</p>
+    </>
+  );
+
+  const elm12and13 = (
+    <>
+      <p>Elm 12</p>
+      <p>Elm 13</p>
+    </>
+  );
+
+  const elm14 = <p>Elm 14</p>;
+
   let $elmRef2 = elm1;
   let $elmRef3 = elm3;
+  let $elmRefArr = elm5and6;
+  let $elmRefArr2 = elm9;
+  let $elmRefArr3 = elm12and13;
 
   const change2 = () => ($elmRef2 = $elmRef2 === elm1 ? elm2 : elm1);
 
@@ -21,6 +56,14 @@ export function Page() {
     }
   };
 
+  const change4 = () =>
+    ($elmRefArr = $elmRefArr === elm5and6 ? elm7and8 : elm5and6);
+
+  const change5 = () =>
+    ($elmRefArr2 = $elmRefArr2 === elm9 ? elm10and11 : elm9);
+  const change6 = () =>
+    ($elmRefArr3 = $elmRefArr3 === elm12and13 ? elm14 : elm12and13);
+
   return (
     <>
       <h1>JSX Signals</h1>
@@ -34,6 +77,18 @@ export function Page() {
       <hr />
       {$elmRef3}
       <button onClick={change3}>Change 3</button>
+
+      <hr />
+      {$elmRefArr}
+      <button onClick={change4}>Change 4</button>
+
+      <hr />
+      {$elmRefArr2}
+      <button onClick={change5}>Change 5</button>
+
+      <hr />
+      {$elmRefArr3}
+      <button onClick={change6}>Change 6</button>
     </>
   );
 }
diff --git a/src/lib/rezact/index.ts b/src/lib/rezact/index.ts
index c9c278b..30cfe73 100644
--- a/src/lib/rezact/index.ts
+++ b/src/lib/rezact/index.ts
@@ -54,22 +54,19 @@ export let attributeHandlers = [];
 
 export const addAttributeHandler = (item) => attributeHandlers.unshift(item);
 
-const evKeys = {
-  onClick: "click",
-  onDblClick: "dblclick",
-  onKeyDown: "keydown",
-  onSubmit: "submit",
-  onChange: "change",
-  onInput: "input",
-};
+const skipEvents = new Set(["onMount", "onUnmount"]);
 function handleAttributes(elm, attrs) {
   const keys = Object.keys(attrs);
   const keyLen = keys.length;
   outer: for (let i = 0; i < keyLen; i++) {
     const key = keys[i];
     const attrVal = attrs[key];
-    if (evKeys[key] && typeof attrs[key] === "function") {
-      elm.addEventListener(evKeys[key], attrVal);
+    if (
+      key.startsWith("on") &&
+      typeof attrVal === "function" &&
+      !skipEvents.has(key)
+    ) {
+      elm.addEventListener(key.slice(2).toLowerCase(), attrVal);
       continue;
     }
     if (typeof attrVal === "boolean" && attrVal) {
@@ -91,17 +88,47 @@ function handleAttributes(elm, attrs) {
 
 export const childArrayHandler: any = {
   matches: (child) => isArray(child),
-  handler: (parent, child) => {
-    const childLen = child.length;
-    for (let i = 0; i < childLen; i++) {
-      appendChild(parent, child[i]);
+  handler: (parent, child, insertAfter, removeElm) => {
+    const len = child.length;
+    if (insertAfter && isArray(child)) {
+      for (let i = len - 1; i > -1; i--) {
+        appendChild(parent, child[i], insertAfter, removeElm);
+      }
+    } else {
+      for (let i = 0; i < len; i++) {
+        appendChild(parent, child[i], insertAfter, removeElm);
+      }
     }
   },
 };
 
+function insertNodeAfter(currentNode: any, childNode: any) {
+  if (currentNode.nextSibling) {
+    currentNode.parentNode.insertBefore(childNode, currentNode.nextSibling);
+  } else if (currentNode.parentNode) {
+    currentNode.parentNode.appendChild(childNode);
+  }
+}
+
+function appendChildNode(
+  parentNode: any,
+  childNode: any,
+  insertAfter: boolean = false,
+  removeElm: boolean = false
+) {
+  if (removeElm) return childNode.remove();
+  if (parentNode instanceof Comment) insertAfter = true;
+  if (parentNode.state) return;
+  // console.log({ parentNode, childNode, insertAfter });
+  insertAfter
+    ? insertNodeAfter(parentNode, childNode)
+    : parentNode.appendChild(childNode);
+}
+
 export const childNodeHandler: any = {
   matches: (child) => child instanceof Node,
-  handler: (parent, child) => parent.appendChild(child),
+  handler: (parent, child, insertAfter, removeElm) =>
+    appendChildNode(parent, child, insertAfter, removeElm),
 };
 
 let appendChildHooks: any = [childArrayHandler, childNodeHandler];
diff --git a/src/lib/rezact/mapState.ts b/src/lib/rezact/mapState.ts
index f56f448..38def9b 100644
--- a/src/lib/rezact/mapState.ts
+++ b/src/lib/rezact/mapState.ts
@@ -1,41 +1,35 @@
-import { Signal, computeSub, effect, overrideEffect } from "./signals";
-import {
-  addAppendChildHook,
-  appendChild,
-  childArrayHandler,
-  childNodeHandler,
-  createComment,
-  createComponent,
-  createDocumentFragment,
-  createElement,
-  isArray,
-} from ".";
-
-overrideEffect(_effect);
-
-function _effect(func: (obj: any) => {}, deps: any[]) {
-  const NewState = deps[0] instanceof MapSignal ? MapSignal : Signal;
-  const newState: any = new NewState(func(deps) as any);
-  newState.computed = true;
-  const depsLen = deps.length;
-  for (let i = 0; i < depsLen; i++) {
-    deps[i].subscribe({ funcRef: computeSub, obj: { newState, func, deps } });
-  }
-  return newState;
+import { Signal, effect } from "./signals";
+import { createComponent, isArray } from ".";
+
+function isIgnoredInstance(obj: any): boolean {
+  return (
+    obj instanceof Window ||
+    obj instanceof Document ||
+    obj instanceof HTMLElement ||
+    obj instanceof Node
+  );
 }
 
-function findNestedStates(obj, results = [], skipKeys = []) {
-  if (obj instanceof Window) return [];
-  if (obj instanceof Document) return [];
-  if (obj instanceof HTMLElement) return [];
-  if (obj instanceof Node) return [];
+const skipKeys = new Set([
+  "elmRef",
+  "idxState",
+  "deps",
+  "subs",
+  "func",
+  "associatedState",
+  "state",
+  "computed",
+]);
+
+function findNestedStates(obj, results = []) {
+  if (isIgnoredInstance(obj)) return [];
 
   // Check if the input is an object
   if (typeof obj === "object" && obj !== null) {
     // If it's an array, iterate over each element
     if (Array.isArray(obj)) {
       for (let item of obj) {
-        findNestedStates(item, results, skipKeys);
+        findNestedStates(item, results);
       }
     } else {
       // If it's an object, iterate over each property
@@ -43,39 +37,19 @@ function findNestedStates(obj, results = [], skipKeys = []) {
         if (key.startsWith("$")) {
           results.push(obj[key]);
         }
-        if (skipKeys.includes(key)) continue;
-        if (obj[key] instanceof Window) continue;
-        if (obj[key] instanceof Document) continue;
-        if (obj[key] instanceof HTMLElement) continue;
-        if (obj[key] instanceof Node) continue;
+        if (skipKeys.has(key)) continue;
+        if (isIgnoredInstance(obj[key])) continue;
 
-        findNestedStates(obj[key], results, skipKeys);
+        findNestedStates(obj[key], results);
       }
     }
   }
   return results;
 }
 
-function findNestedMapSignals(item) {
-  return findNestedStates(
-    item,
-    [],
-    [
-      "elmRef",
-      "idxState",
-      "deps",
-      "subs",
-      "func",
-      "associatedState",
-      "state",
-      "computed",
-    ]
-  );
-}
-
 function subscribeToNestedStates(item, mapStateObj) {
   if (!(item.elmRef instanceof HTMLElement)) return;
-  const nestedStates = findNestedMapSignals(item.value);
+  const nestedStates = findNestedStates(item);
   nestedStates.forEach((state) => {
     item.nestedSubscribed = true;
 
@@ -216,171 +190,6 @@ export class MapSignal<T> extends Signal<T> {
   };
 }
 
-const addChildren = (values: any, parentNode) => {
-  if (values === undefined) return;
-  const len = values.length;
-  let nextNode = parentNode;
-  for (let i = 0; i < len; i++) {
-    const elm = values[i].elmRef;
-    if (!(nextNode.nextSibling === (elm[0] || elm))) {
-      appendChild(nextNode, elm, true);
-    }
-    nextNode = isArray(elm) ? elm.at(-1) : elm;
-  }
-};
-
-const remPlaceHolder = createElement("span");
-const removeStaleChildren = (parentNode, endNode, parent, child) => {
-  const values = child.value;
-  let nextNode = parentNode.nextSibling;
-
-  if (!parentNode.parentNode) return;
-
-  if (
-    parentNode.parentNode.childNodes.length === child.previousChildLen + 2 &&
-    values.length === 0
-  ) {
-    child.removeStaleElmRefCacheItems();
-    return (parentNode.parentNode.innerHTML = "");
-  }
-
-  if (values.length === 0) {
-    let frag = createDocumentFragment();
-    const placeholder = createElement("span");
-    parent.parentNode.insertBefore(placeholder, parent);
-    frag.appendChild(parent);
-    while (nextNode !== endNode) {
-      const thisNode = nextNode;
-      nextNode = nextNode.nextSibling;
-      thisNode.remove();
-    }
-    placeholder.parentNode.insertBefore(frag, placeholder);
-    parent.appendChild(frag);
-    placeholder.remove();
-    return;
-  }
-
-  let elmRefIdx = 0;
-  const childLen = values[0].elmRef.length || 1;
-  let inc = 0;
-  while (nextNode !== endNode) {
-    const idx = Math.floor(inc / childLen);
-    elmRefIdx = inc % childLen;
-    const val = values[idx];
-    let elmRef = val && val.elmRef && (val?.elmRef[elmRefIdx] || val?.elmRef);
-
-    if (values.indexOf(nextNode.associatedState) < 0) {
-      nextNode = nextNode.nextSibling;
-      const prevElm = nextNode.previousSibling;
-      if (prevElm.replaceWith && elmRef instanceof HTMLElement) {
-        prevElm.replaceWith(elmRef);
-        nextNode = elmRef.nextSibling;
-      } else {
-        prevElm.remove();
-      }
-    } else if (val && elmRef && nextNode !== elmRef) {
-      nextNode = nextNode.nextSibling;
-      const prevElm = nextNode.previousSibling;
-      if (
-        prevElm.replaceWith &&
-        elmRef instanceof HTMLElement &&
-        elmRef.parentNode
-      ) {
-        elmRef.replaceWith(remPlaceHolder);
-        prevElm.replaceWith(elmRef);
-        remPlaceHolder.replaceWith(prevElm);
-      } else {
-        if (elmRef instanceof HTMLElement) elmRef.remove();
-      }
-    } else {
-      nextNode = nextNode.nextSibling;
-    }
-    inc += 1;
-  }
-};
-
-const handleArray = (parent: any, child: any) => {
-  const frag = createDocumentFragment();
-  const frac = frag.appendChild.bind(frag);
-  const parentNode = createComment("start map");
-  const endNode = createComment("end map");
-  const placeHolder = createComment("map-placeholder");
-
-  frac(parentNode);
-  frac(endNode);
-
-  child.subscribe((newVal: any) => {
-    // if (!parentNode.isConnected || !endNode.isConnected) return;
-    if (child.previousChildLen === 0) {
-      if (parentNode.parentNode) parent.insertBefore(placeHolder, parentNode);
-
-      frac(parentNode);
-      frac(endNode);
-    }
-    removeStaleChildren(parentNode, endNode, parent, child);
-    addChildren(newVal, parentNode);
-    if (child.previousChildLen === 0) {
-      if (placeHolder.parentNode) {
-        parent.insertBefore(frag, placeHolder);
-      } else {
-        parent.appendChild(frag);
-      }
-    }
-    child.previousChildLen = child.value.length;
-    placeHolder.remove();
-  });
-
-  child.previousChildLen = child.value.length;
-  addChildren(child.value, parentNode);
-  parent.appendChild(frag);
-};
-
-const childStateHandler = {
-  matches: (child) => isArray(child.value) && child.state,
-  handler: (parent, child) => handleArray(parent, child),
-};
-
-addAppendChildHook(childStateHandler);
-
-function insertNodeAfter(currentNode: any, childNode: any) {
-  if (currentNode.nextSibling) {
-    currentNode.parentNode.insertBefore(childNode, currentNode.nextSibling);
-  } else if (currentNode.parentNode) {
-    currentNode.parentNode.appendChild(childNode);
-  }
-}
-
-function appendChildNode(
-  parentNode: any,
-  childNode: any,
-  insertAfter: boolean = false,
-  removeElm: boolean = false
-) {
-  if (removeElm) return childNode.remove();
-  if (parentNode instanceof Comment) insertAfter = true;
-  if (parentNode.state) return;
-  // console.log({ parentNode, childNode, insertAfter });
-  insertAfter
-    ? insertNodeAfter(parentNode, childNode)
-    : parentNode.appendChild(childNode);
-}
-
-childNodeHandler.handler = (parent, child, insertAfter, removeElm) =>
-  appendChildNode(parent, child, insertAfter, removeElm);
-
-childArrayHandler.handler = (parent, child, insertAfter, removeElm) => {
-  const len = child.length;
-  if (insertAfter && isArray(child)) {
-    for (let i = len - 1; i > -1; i--) {
-      appendChild(parent, child[i], insertAfter, removeElm);
-    }
-  } else {
-    for (let i = 0; i < len; i++) {
-      appendChild(parent, child[i], insertAfter, removeElm);
-    }
-  }
-};
-
 export const mapEffect = (func, deps) => {
   const computed: any = effect(func, deps);
   computed.deps = deps;
diff --git a/src/lib/rezact/signals.ts b/src/lib/rezact/signals.ts
index b50a4ca..0bfc662 100644
--- a/src/lib/rezact/signals.ts
+++ b/src/lib/rezact/signals.ts
@@ -9,6 +9,7 @@ import {
   createElement,
   createTextNode,
   handleInputValue,
+  createDocumentFragment,
 } from ".";
 
 let batchSubs = [];
@@ -182,13 +183,8 @@ export class Signal<T> {
 
 export const computeSub = (obj) => obj.newState.set(obj.func(obj.deps));
 
-export let effect = _effect;
-export function overrideEffect(func: any) {
-  effect = func;
-}
-
-function _effect(func: (obj: any) => {}, deps: any[]) {
-  const newState: any = new Signal(func(deps));
+export function effect(func: (obj: any) => {}, deps: any[]) {
+  const newState: any = new deps[0].constructor(func(deps));
   newState.computed = true;
   const depsLen = deps.length;
   for (let i = 0; i < depsLen; i++) {
@@ -207,7 +203,7 @@ function handleStateTypes(parent: any, child: any) {
   if (textTypes[typeof val]) {
     handleTextNode(parent, child);
   } else if (val instanceof Node && !child.computed) {
-    appendChild(parent, val);
+    handleArray(parent, child);
   } else if (child.computed) {
     const placeholder = createElement("span");
     const newState = new Signal(placeholder);
@@ -285,3 +281,130 @@ addAfterRenderHook(() => {
   });
   subscFunctionsArr = [];
 });
+
+const addChildren = (values: any, parentNode) => {
+  if (values === undefined) return;
+  values = values instanceof Element ? [values] : values;
+  const len = values.length;
+  let nextNode = parentNode;
+  for (let i = 0; i < len; i++) {
+    const elm = values[i].elmRef || values[i];
+    if (!(nextNode.nextSibling === (elm[0] || elm))) {
+      appendChild(nextNode, elm, true);
+    }
+    nextNode = isArray(elm) ? elm.at(-1) : elm;
+  }
+};
+
+const remPlaceHolder = createElement("span");
+const removeStaleChildren = (parentNode, endNode, parent, child) => {
+  const values = child.value instanceof Element ? [child.value] : child.value;
+  let nextNode = parentNode.nextSibling;
+
+  if (!parentNode.parentNode) return;
+
+  if (
+    parentNode.parentNode.childNodes.length === child.previousChildLen + 2 &&
+    values.length === 0
+  ) {
+    child.removeStaleElmRefCacheItems();
+    return (parentNode.parentNode.innerHTML = "");
+  }
+
+  if (values.length === 0) {
+    let frag = createDocumentFragment();
+    const placeholder = createElement("span");
+    parent.parentNode.insertBefore(placeholder, parent);
+    frag.appendChild(parent);
+    while (nextNode !== endNode) {
+      const thisNode = nextNode;
+      nextNode = nextNode.nextSibling;
+      thisNode.remove();
+    }
+    placeholder.parentNode.insertBefore(frag, placeholder);
+    parent.appendChild(frag);
+    placeholder.remove();
+    return;
+  }
+
+  let elmRefIdx = 0;
+  const childElm = values[0].elmRef || values[0];
+  const childLen = childElm.length || 1;
+  let inc = 0;
+  while (nextNode !== endNode) {
+    const idx = Math.floor(inc / childLen);
+    elmRefIdx = inc % childLen;
+    const val = values[idx];
+    let elmRef = val && val.elmRef && (val?.elmRef[elmRefIdx] || val?.elmRef);
+
+    if (values.indexOf(nextNode.associatedState) < 0) {
+      nextNode = nextNode.nextSibling;
+      const prevElm = nextNode.previousSibling;
+      if (prevElm.replaceWith && elmRef instanceof HTMLElement) {
+        prevElm.replaceWith(elmRef);
+        nextNode = elmRef.nextSibling;
+      } else {
+        prevElm.remove();
+      }
+    } else if (val && elmRef && nextNode !== elmRef) {
+      nextNode = nextNode.nextSibling;
+      const prevElm = nextNode.previousSibling;
+      if (
+        prevElm.replaceWith &&
+        elmRef instanceof HTMLElement &&
+        elmRef.parentNode
+      ) {
+        elmRef.replaceWith(remPlaceHolder);
+        prevElm.replaceWith(elmRef);
+        remPlaceHolder.replaceWith(prevElm);
+      } else {
+        if (elmRef instanceof HTMLElement) elmRef.remove();
+      }
+    } else {
+      nextNode = nextNode.nextSibling;
+    }
+    inc += 1;
+  }
+};
+
+const handleArray = (parent: any, child: any) => {
+  const frag = createDocumentFragment();
+  const frac = frag.appendChild.bind(frag);
+  const parentNode = createComment("start map");
+  const endNode = createComment("end map");
+  const placeHolder = createComment("map-placeholder");
+
+  frac(parentNode);
+  frac(endNode);
+
+  child.subscribe((newVal: any) => {
+    if (child.previousChildLen === 0) {
+      if (parentNode.parentNode) parent.insertBefore(placeHolder, parentNode);
+
+      frac(parentNode);
+      frac(endNode);
+    }
+    removeStaleChildren(parentNode, endNode, parent, child);
+    addChildren(newVal, parentNode);
+    if (child.previousChildLen === 0) {
+      if (placeHolder.parentNode) {
+        parent.insertBefore(frag, placeHolder);
+      } else {
+        parent.appendChild(frag);
+      }
+    }
+    child.previousChildLen = child.value.length;
+    placeHolder.remove();
+  });
+
+  child.previousChildLen = child.value.length;
+  addChildren(child.value, parentNode);
+  parent.appendChild(frag);
+};
+
+const childArrayStateHandler = {
+  matches: (child) => isArray(child.value) && child.state,
+  handler: (parent, child) => handleArray(parent, child),
+};
+
+addAppendChildHook(childArrayStateHandler);


Test patch:
diff --git a/src/examples/JsxSignals/jsx-signals.test.ts b/src/examples/JsxSignals/jsx-signals.test.ts
index 1d93089..5216a5b 100644
--- a/src/examples/JsxSignals/jsx-signals.test.ts
+++ b/src/examples/JsxSignals/jsx-signals.test.ts
@@ -49,4 +49,59 @@ describe("JSX Signals suite", () => {
     await clickToggleButton.click();
     expect(screen.getByText("Elm 4")).not.toBeNull();
   });
+
+  it("switches fragments with fragments", async () => {
+    const clickToggleButton = screen.getByRole("button", {
+      name: /change 4/i,
+    });
+    expect(clickToggleButton).not.toBeNull();
+    expect(screen.getByText("Elm 5")).not.toBeNull();
+    expect(screen.getByText("Elm 6")).not.toBeNull();
+    await clickToggleButton.click();
+    expect(screen.getByText("Elm 7")).not.toBeNull();
+    expect(screen.getByText("Elm 8")).not.toBeNull();
+    await clickToggleButton.click();
+    expect(screen.getByText("Elm 5")).not.toBeNull();
+    expect(screen.getByText("Elm 6")).not.toBeNull();
+    await clickToggleButton.click();
+    expect(screen.getByText("Elm 7")).not.toBeNull();
+    expect(screen.getByText("Elm 8")).not.toBeNull();
+  });
+
+  it("switches fragments with elements", async () => {
+    const clickToggleButton = screen.getByRole("button", {
+      name: /change 5/i,
+    });
+    expect(clickToggleButton).not.toBeNull();
+    expect(screen.getByText("Elm 9")).not.toBeNull();
+    await clickToggleButton.click();
+    expect(screen.getByText("Elm 10")).not.toBeNull();
+    expect(screen.getByText("Elm 11")).not.toBeNull();
+    await clickToggleButton.click();
+    expect(screen.getByText("Elm 9")).not.toBeNull();
+    await clickToggleButton.click();
+    expect(screen.getByText("Elm 10")).not.toBeNull();
+    expect(screen.getByText("Elm 11")).not.toBeNull();
+    await clickToggleButton.click();
+    expect(screen.getByText("Elm 9")).not.toBeNull();
+  });
+
+  it("switches elements with fragments", async () => {
+    const clickToggleButton = screen.getByRole("button", {
+      name: /change 6/i,
+    });
+    expect(clickToggleButton).not.toBeNull();
+    expect(screen.getByText("Elm 12")).not.toBeNull();
+    expect(screen.getByText("Elm 13")).not.toBeNull();
+    await clickToggleButton.click();
+    expect(screen.getByText("Elm 14")).not.toBeNull();
+    await clickToggleButton.click();
+    expect(screen.getByText("Elm 12")).not.toBeNull();
+    expect(screen.getByText("Elm 13")).not.toBeNull();
+    await clickToggleButton.click();
+    expect(screen.getByText("Elm 14")).not.toBeNull();
+    await clickToggleButton.click();
+    expect(screen.getByText("Elm 12")).not.toBeNull();
+    expect(screen.getByText("Elm 13")).not.toBeNull();
+  });
 });