Repository: dusk-network/rusk (language: rust)

Issue:
web-wallet: Update Memo Field Position and Display (Send Flow)
## Description

We need to adjust the behavior of the Memo field in the Web Wallet to improve usability and align with typical banking app flows. The changes are as follows:

1. **Move Memo Field:**
   - **Current Behavior:** The Memo field is currently located on the "Address" step.
   - **Proposed Change:** Move the Memo field to the "Amount" step. This adjustment aligns better with user expectations, as reference information (memo) is typically entered after specifying the recipient's address and amount in banking apps.

2. **Include Memo in Review Step:**
   - **Current Behavior:** The memo (if provided) is not displayed in the review step.
   - **Proposed Change:** Display the memo (if provided) in the review step of the Send flow so users can verify all transaction details before confirming.

## Acceptance Criteria

- The Memo field is relocated from the "Address" step to the "Amount" step.
- The UI layout in the "Amount" step is updated to incorporate the Memo field seamlessly.
- When a memo is entered, it is displayed in the review step of the Send flow.
- The change is responsive and tested across relevant devices and screen sizes.
- User experience is validated with appropriate QA tests.

## Additional Context

This change is intended to enhance the user experience by making the transaction process more intuitive and in line with industry standards. If there are any questions or design concerns, please reach out for clarification.

## Steps to Reproduce

1. Navigate to the Web Wallet.
2. Start a new transaction.
3. Observe the current position of the Memo field on the "Address" step.
4. Proceed through the transaction flow and note that the Memo field is not visible on the review step.

## Expected Behavior After Implementation

- The Memo field is located on the "Amount" step.
- If a memo is entered, it appears in the transaction review screen for final verification.


Discussion / hints:

Fix patch:
diff --git a/web-wallet/src/lib/components/Send/Send.svelte b/web-wallet/src/lib/components/Send/Send.svelte
index b5002c738..cc7873784 100644
--- a/web-wallet/src/lib/components/Send/Send.svelte
+++ b/web-wallet/src/lib/components/Send/Send.svelte
@@ -71,7 +71,7 @@
   let scanQrComponent;
 
   /** @type {boolean} */
-  let showMemo = false;
+  let isMemoShown = false;
 
   /** @type {boolean} */
   let isNextButtonDisabled = false;
@@ -234,18 +234,6 @@
             </p>
           </Banner>
         {/if}
-        <div class="operation__address-wrapper">
-          <p>Memo</p>
-          <Switch onSurface bind:value={showMemo} />
-        </div>
-        {#if showMemo}
-          <Textbox
-            required
-            className="operation__send-address"
-            type="multiline"
-            bind:value={memo}
-          />
-        {/if}
       </div>
     </WizardStep>
     <!-- Amount Step -->
@@ -288,6 +276,27 @@
           />
         </div>
 
+        <div class="operation__input-wrapper">
+          <p>Memo (optional)</p>
+          <Switch
+            on:change={() => {
+              if (!isMemoShown) {
+                memo = "";
+              }
+            }}
+            onSurface
+            bind:value={isMemoShown}
+          />
+        </div>
+        {#if isMemoShown}
+          <Textbox
+            required
+            className="operation__send-memo"
+            type="multiline"
+            bind:value={memo}
+          />
+        {/if}
+
         <GasSettings
           {formatter}
           fee={maxGasFee}
@@ -373,6 +382,17 @@
           </dd>
         </dl>
 
+        {#if memo}
+          <dl class="operation__review-transaction">
+            <dt class="review-transaction__label">
+              <span>Memo</span>
+            </dt>
+            <dd class="operation__review-memo">
+              <span>{memo}</span>
+            </dd>
+          </dl>
+        {/if}
+
         <GasFee {formatter} fee={maxGasFee} />
       </div>
     </WizardStep>
@@ -413,7 +433,8 @@
       flex-direction: column;
       gap: 1.2em;
     }
-    &__review-address {
+    &__review-address,
+    &__review-memo {
       background-color: transparent;
       border: 1px solid var(--primary-color);
       border-radius: 1.5em;
@@ -467,7 +488,10 @@
     font-weight: bold;
   }
 
-  :global(.dusk-textbox.operation__send-address) {
+  :global(
+      .dusk-textbox.operation__send-address,
+      .dusk-textbox.operation__send-memo
+    ) {
     resize: vertical;
     min-height: 5em;
     max-height: 10em;


Test patch:
diff --git a/web-wallet/src/lib/components/__tests__/Send.spec.js b/web-wallet/src/lib/components/__tests__/Send.spec.js
index 6c4c83c41..ebd3cd462 100644
--- a/web-wallet/src/lib/components/__tests__/Send.spec.js
+++ b/web-wallet/src/lib/components/__tests__/Send.spec.js
@@ -27,7 +27,6 @@ describe("Send", () => {
     "zTsZq814KfWUAQujzjBchbMEvqA1FiKBUakMCtAc2zCa74h9YVz4a2roYwS7LHDHeBwS1aap4f3GYhQBrxroYgsBcE4FJdkUbvpSD5LVXY6JRXNgMXgk6ckTPJUFKoHybff";
   const shieldedAddress =
     "47jNTgAhzn9KCKF3msCfvKg3k1P1QpPCLZ3HG3AoNp87sQ5WNS3QyjckYHWeuXqW7uvLmbKgejpP8Xkcip89vnMM";
-  const memo = "";
   const baseProps = {
     availableBalance: 1_000_000_000_000n,
     execute: vi.fn().mockResolvedValue(lastTxId),
@@ -227,10 +226,16 @@ describe("Send", () => {
         target: { value: shieldedAddress },
       });
       await fireEvent.click(getByRole("button", { name: "Next" }));
+      await fireEvent.click(getByRole("switch"));
 
       const amountInput = getByRole("spinbutton");
+      const memoInput = getByRole("textbox");
 
       await fireEvent.input(amountInput, { target: { value: amount } });
+      await fireEvent.input(memoInput, {
+        target: { value: "abc-example-memo" },
+      });
+
       await fireEvent.click(getByRole("button", { name: "Next" }));
 
       const value = getAsHTMLElement(
@@ -241,9 +246,11 @@ describe("Send", () => {
         container,
         ".operation__review-address span"
       );
+      const memo = getAsHTMLElement(container, ".operation__review-memo span");
 
       expect(value.textContent).toBe(baseProps.formatter(amount));
       expect(key.textContent).toBe(shieldedAddress);
+      expect(memo.textContent).toBe("abc-example-memo");
       expect(container.firstChild).toMatchSnapshot();
     });
   });
@@ -279,7 +286,46 @@ describe("Send", () => {
       expect(baseProps.execute).toHaveBeenCalledWith(
         shieldedAddress,
         duskToLux(amount),
-        memo,
+        "",
+        baseProps.gasSettings.gasPrice,
+        baseProps.gasSettings.gasLimit
+      );
+
+      const explorerLink = getByRole("link", { name: /explorer/i });
+
+      expect(getByText("Transaction created")).toBeInTheDocument();
+      expect(explorerLink).toHaveAttribute("target", "_blank");
+      expect(explorerLink).toHaveAttribute("href", expectedExplorerLink);
+    });
+
+    it("should perform a transfer for the desired amount, with a memo, give a success message and supply a link to see the transaction in the explorer", async () => {
+      const { getByRole, getByText } = render(Send, baseProps);
+      const addressInput = getByRole("textbox");
+
+      await fireEvent.input(addressInput, {
+        target: { value: shieldedAddress },
+      });
+      await fireEvent.click(getByRole("button", { name: "Next" }));
+      await fireEvent.click(getByRole("switch"));
+
+      const amountInput = getByRole("spinbutton");
+      const memoInput = getByRole("textbox");
+
+      await fireEvent.input(amountInput, { target: { value: amount } });
+      await fireEvent.input(memoInput, {
+        target: { value: "abc-example-memo" },
+      });
+
+      await fireEvent.click(getByRole("button", { name: "Next" }));
+      await fireEvent.click(getByRole("button", { name: "SEND" }));
+
+      await vi.advanceTimersToNextTimerAsync();
+
+      expect(baseProps.execute).toHaveBeenCalledTimes(1);
+      expect(baseProps.execute).toHaveBeenCalledWith(
+        shieldedAddress,
+        duskToLux(amount),
+        "abc-example-memo",
         baseProps.gasSettings.gasPrice,
         baseProps.gasSettings.gasLimit
       );
@@ -315,7 +361,7 @@ describe("Send", () => {
       expect(baseProps.execute).toHaveBeenCalledWith(
         shieldedAddress,
         duskToLux(amount),
-        memo,
+        "",
         baseProps.gasSettings.gasPrice,
         baseProps.gasSettings.gasLimit
       );
@@ -345,7 +391,7 @@ describe("Send", () => {
       expect(baseProps.execute).toHaveBeenCalledWith(
         shieldedAddress,
         duskToLux(amount),
-        memo,
+        "",
         baseProps.gasSettings.gasPrice,
         baseProps.gasSettings.gasLimit
       );
diff --git a/web-wallet/src/lib/components/__tests__/__snapshots__/Send.spec.js.snap b/web-wallet/src/lib/components/__tests__/__snapshots__/Send.spec.js.snap
index 6b78e47e4..645f9db99 100644
--- a/web-wallet/src/lib/components/__tests__/__snapshots__/Send.spec.js.snap
+++ b/web-wallet/src/lib/components/__tests__/__snapshots__/Send.spec.js.snap
@@ -2,7 +2,7 @@
 
 exports[`Send > Address step > should display a warning if the address input is a public account 1`] = `
 <div
-  class="operation svelte-npcavd"
+  class="operation svelte-xvysel"
 >
   <div
     class="dusk-wizard"
@@ -79,10 +79,10 @@ exports[`Send > Address step > should display a warning if the address input is
      
      
     <div
-      class="operation__send svelte-npcavd"
+      class="operation__send svelte-xvysel"
     >
       <div
-        class="operation__address-wrapper svelte-npcavd"
+        class="operation__address-wrapper svelte-xvysel"
       >
         <p>
           Address
@@ -165,23 +165,6 @@ exports[`Send > Address step > should display a warning if the address input is
         </button>
       </div>
        
-       
-      <div
-        class="operation__address-wrapper svelte-npcavd"
-      >
-        <p>
-          Memo
-        </p>
-         
-        <div
-          aria-checked="false"
-          aria-disabled="false"
-          class="dusk-switch dusk-switch--on-surface"
-          role="switch"
-          tabindex="0"
-        />
-      </div>
-       
     </div>
      
     <div
@@ -249,7 +232,7 @@ exports[`Send > Address step > should display a warning if the address input is
 
 exports[`Send > Address step > should display a warning if the address input is self-referential 1`] = `
 <div
-  class="operation svelte-npcavd"
+  class="operation svelte-xvysel"
 >
   <div
     class="dusk-wizard"
@@ -326,10 +309,10 @@ exports[`Send > Address step > should display a warning if the address input is
      
      
     <div
-      class="operation__send svelte-npcavd"
+      class="operation__send svelte-xvysel"
     >
       <div
-        class="operation__address-wrapper svelte-npcavd"
+        class="operation__address-wrapper svelte-xvysel"
       >
         <p>
           Address
@@ -441,23 +424,6 @@ exports[`Send > Address step > should display a warning if the address input is
           </p>
         </div>
       </div>
-       
-      <div
-        class="operation__address-wrapper svelte-npcavd"
-      >
-        <p>
-          Memo
-        </p>
-         
-        <div
-          aria-checked="false"
-          aria-disabled="false"
-          class="dusk-switch dusk-switch--on-surface"
-          role="switch"
-          tabindex="0"
-        />
-      </div>
-       
     </div>
      
     <div
@@ -525,7 +491,7 @@ exports[`Send > Address step > should display a warning if the address input is
 
 exports[`Send > Address step > should render the Send component Address step 1`] = `
 <div
-  class="operation svelte-npcavd"
+  class="operation svelte-xvysel"
 >
   <div
     class="dusk-wizard"
@@ -602,10 +568,10 @@ exports[`Send > Address step > should render the Send component Address step 1`]
      
      
     <div
-      class="operation__send svelte-npcavd"
+      class="operation__send svelte-xvysel"
     >
       <div
-        class="operation__address-wrapper svelte-npcavd"
+        class="operation__address-wrapper svelte-xvysel"
       >
         <p>
           Address
@@ -658,23 +624,6 @@ exports[`Send > Address step > should render the Send component Address step 1`]
         </button>
       </div>
        
-       
-      <div
-        class="operation__address-wrapper svelte-npcavd"
-      >
-        <p>
-          Memo
-        </p>
-         
-        <div
-          aria-checked="false"
-          aria-disabled="false"
-          class="dusk-switch dusk-switch--on-surface"
-          role="switch"
-          tabindex="0"
-        />
-      </div>
-       
     </div>
      
     <div
@@ -743,7 +692,7 @@ exports[`Send > Address step > should render the Send component Address step 1`]
 
 exports[`Send > Amount step > should render the Send component Amount step 1`] = `
 <div
-  class="operation svelte-npcavd"
+  class="operation svelte-xvysel"
 >
   <div
     class="dusk-wizard"
@@ -822,11 +771,11 @@ exports[`Send > Amount step > should render the Send component Amount step 1`] =
      
      
     <div
-      class="operation__send svelte-npcavd"
+      class="operation__send svelte-xvysel"
       style="animation: __svelte_3516408220_0 400ms linear 0ms 1 both;"
     >
       <div
-        class="operation__amount-wrapper svelte-npcavd"
+        class="operation__amount-wrapper svelte-xvysel"
       >
         <p>
           Amount
@@ -845,7 +794,7 @@ exports[`Send > Amount step > should render the Send component Amount step 1`] =
       </div>
        
       <div
-        class="operation__input-wrapper svelte-npcavd"
+        class="operation__input-wrapper svelte-xvysel"
       >
         <input
           class="dusk-textbox dusk-textbox-number operation__input-field"
@@ -871,6 +820,23 @@ exports[`Send > Amount step > should render the Send component Amount step 1`] =
         
       </div>
        
+      <div
+        class="operation__input-wrapper svelte-xvysel"
+      >
+        <p>
+          Memo (optional)
+        </p>
+         
+        <div
+          aria-checked="false"
+          aria-disabled="false"
+          class="dusk-switch dusk-switch--on-surface"
+          role="switch"
+          tabindex="0"
+        />
+      </div>
+       
+       
       <div
         class="gas-settings svelte-1daay07"
       >
@@ -996,7 +962,7 @@ exports[`Send > Amount step > should render the Send component Amount step 1`] =
 
 exports[`Send > Review step > should render the Send component Review step 1`] = `
 <div
-  class="operation svelte-npcavd"
+  class="operation svelte-xvysel"
 >
   <div
     class="dusk-wizard"
@@ -1077,7 +1043,7 @@ exports[`Send > Review step > should render the Send component Review step 1`] =
      
      
     <div
-      class="operation__send svelte-npcavd"
+      class="operation__send svelte-xvysel"
       style="animation: __svelte_3516408220_0 400ms linear 0ms 1 both;"
     >
       <span
@@ -1087,10 +1053,10 @@ exports[`Send > Review step > should render the Send component Review step 1`] =
       </span>
        
       <dl
-        class="operation__review-transaction svelte-npcavd"
+        class="operation__review-transaction svelte-xvysel"
       >
         <dt
-          class="review-transaction__label svelte-npcavd"
+          class="review-transaction__label svelte-xvysel"
         >
           <svg
             class="dusk-icon dusk-icon--size--default"
@@ -1109,7 +1075,7 @@ exports[`Send > Review step > should render the Send component Review step 1`] =
            
         </dt>
         <dd
-          class="review-transaction__value operation__review-amount svelte-npcavd"
+          class="review-transaction__value operation__review-amount svelte-xvysel"
         >
           <span>
             2,345.000000000
@@ -1131,10 +1097,10 @@ exports[`Send > Review step > should render the Send component Review step 1`] =
       </dl>
        
       <dl
-        class="operation__review-transaction svelte-npcavd"
+        class="operation__review-transaction svelte-xvysel"
       >
         <dt
-          class="review-transaction__label svelte-npcavd"
+          class="review-transaction__label svelte-xvysel"
         >
           <svg
             class="dusk-icon dusk-icon--size--default"
@@ -1153,7 +1119,7 @@ exports[`Send > Review step > should render the Send component Review step 1`] =
            
         </dt>
         <dd
-          class="operation__review-address svelte-npcavd"
+          class="operation__review-address svelte-xvysel"
         >
           <span>
             47jNTgAhzn9KCKF3msCfvKg3k1P1QpPCLZ3HG3AoNp87sQ5WNS3QyjckYHWeuXqW7uvLmbKgejpP8Xkcip89vnMM
@@ -1161,6 +1127,26 @@ exports[`Send > Review step > should render the Send component Review step 1`] =
         </dd>
       </dl>
        
+      <dl
+        class="operation__review-transaction svelte-xvysel"
+      >
+        <dt
+          class="review-transaction__label svelte-xvysel"
+        >
+          <span>
+            Memo
+          </span>
+           
+        </dt>
+        <dd
+          class="operation__review-memo svelte-xvysel"
+        >
+          <span>
+            abc-example-memo
+          </span>
+        </dd>
+      </dl>
+       
       <div
         class="gas-fee svelte-1c6b481"
       >
diff --git a/web-wallet/src/routes/(app)/dashboard/send/__tests__/__snapshots__/page.spec.js.snap b/web-wallet/src/routes/(app)/dashboard/send/__tests__/__snapshots__/page.spec.js.snap
index a12cf38c3..8c8c3179d 100644
--- a/web-wallet/src/routes/(app)/dashboard/send/__tests__/__snapshots__/page.spec.js.snap
+++ b/web-wallet/src/routes/(app)/dashboard/send/__tests__/__snapshots__/page.spec.js.snap
@@ -81,7 +81,7 @@ exports[`Send page > should render the send page 1`] = `
     </div>
      
     <div
-      class="operation svelte-npcavd"
+      class="operation svelte-xvysel"
     >
       <div
         class="dusk-wizard"
@@ -158,10 +158,10 @@ exports[`Send page > should render the send page 1`] = `
          
          
         <div
-          class="operation__send svelte-npcavd"
+          class="operation__send svelte-xvysel"
         >
           <div
-            class="operation__address-wrapper svelte-npcavd"
+            class="operation__address-wrapper svelte-xvysel"
           >
             <p>
               Address
@@ -214,23 +214,6 @@ exports[`Send page > should render the send page 1`] = `
             </button>
           </div>
            
-           
-          <div
-            class="operation__address-wrapper svelte-npcavd"
-          >
-            <p>
-              Memo
-            </p>
-             
-            <div
-              aria-checked="false"
-              aria-disabled="false"
-              class="dusk-switch dusk-switch--on-surface"
-              role="switch"
-              tabindex="0"
-            />
-          </div>
-           
         </div>
          
         <div