React Draggable Element Causes Null Appending to Elements

Got a weird one! I have this React app that has a component “OHSList” that builds out a table of results from the Python API backend. I can set it to be sortable using HTML’s dragging feature. Each row is draggable and can be placed somewhere else in the lines of rows. There are a couple of on-events (see code) to handle the dragging events. I am able to drag the rows around and have it save the order to the backend. But sometimes if you drag something slow or move it around in a certain area, it will start duplicating sub-elements of other rows and appending “null” under it. I have no clue what this could be. Please see the video of it happening below. Thoughts? I am an amateur coder, please forgive any non-standard practices 🙂

Screen recording

export default function OHSList (props) {
   // States and Variables
   const listID = 'ohslist-' + Math.floor(Math.random() * 999999);
   //const [popups, setPopups] = useState({});
   var dragRow = null;
   var dragPosition = 0;

   // Functions
   function showPopup (index) {
      props.setPopups({...props.popups, [index]: true});
   }
   function hidePopup (index) {
      props.setPopups({...props.popups, [index]: false});
   }
   function dragStart (event) {
      dragRow = event.target;
   }
   function dragRun (event) {
      event.preventDefault();
      event.dataTransfer.dropEffect = 'move';
      if ((event.target.tagName == "TD") && (event.target.parentNode.parentNode.tagName == "TBODY")) {
         // Build Children Array
         let children = Array.from(event.target.parentNode.parentNode.children);

         // Position Dragged Element
         if (children.indexOf(event.target.parentNode) > children.indexOf(dragRow)) {
            event.target.parentNode.after(dragRow);
         } else {
            event.target.parentNode.before(dragRow);
         }

         // Set Final Position
         dragPosition = children.indexOf(event.target.parentNode);
      }
   }
   function dragEnd () {
      // Acquire New Order
      var order = [];
      var i = 1;
      $('#' + listID + '-rows').children().each(function () {
         order.push({
            'id': $(this).data('ohslistdataid'),
            'display_order': i
         });
         i++;
      });

      // Send Back New Order
      props.handleDragging(JSON.stringify({
         order: order
      }));
   }


   // Component
   return (
      <div id={listID} className='ohslist'>
         {props.content ? props.content.length > 0 ? 
            <>
               <table>
                  <thead>
                     <tr>
                        {props.enableDragging && <th className='ohslist-header-dragger'></th>}
                        <th className='ohslist-header-selector'></th>
                        {Object.values(props.content[0]).map(function (header, i) {
                           if ((header.display !== false) && (header.metadata !== true)) {
                              return (
                                 <th key={`ohslist-header-${i}`}>
                                    <p>{header.title}</p>
                                 </th>
                              );
                           }
                        })}
                     </tr>
                  </thead>
                  <tbody id={`${listID}-rows`}>
                     {props.content.map(function (item, i) {
                        // Build OHS Filter attribute
                        var ohsfilter = "";
                        Object.values(item).map(function (subitem) {
                           if ((subitem.display !== false) && (subitem.metadata !== true)) {
                              ohsfilter = ohsfilter + " " + subitem.value;
                           }
                        });

                        // Build rows
                        return (
                           <tr key={`ohslist-item-${i}`} data-ohsfilter={ohsfilter} data-ohslistdataid={item.id.value} draggable={props.enableDragging} onDragStart={(event) => {dragStart(event);}} onDragOver={(event) => {dragRun(event);}} onDragEnd={() => {dragEnd();}}>
                              {props.enableDragging &&
                                 <td className='ohslist-body-dragger'>
                                    <div>
                                       <div></div>
                                       <div></div>
                                       <div></div>
                                    </div>
                                 </td>
                              }
                              <td className='ohslist-body-selector'>
                                 <div>
                                    <input type='checkbox' className='ohsinput' disabled />
                                 </div>
                              </td>
                              {Object.values(item).map(function (subitem, j) {
                                 if ((subitem.display !== false) && (subitem.metadata !== true)) {
                                    return (
                                       <td key={`ohslist-subitem-${i}-${j}`} onClick={() => {showPopup(i);}}>
                                          <div>
                                             <p>{subitem.value}</p>
                                          </div>
                                       </td>
                                    );
                                 }
                              })}
                           </tr>
                        );
                     })}
                  </tbody>
               </table>
               {props.enableDragging && <p>Drag and drop a row to set the order of the entries (not supported on mobile)</p>}
               {props.template && props.content.map(function (item, i) {
                  return (
                     <OHSListPopup key={`ohslist-popup-${i}`} index={i} popups={props.popups} hidePopup={hidePopup}>
                        {cloneElement(props.template, {
                           content: item,
                           index: i,
                           showPopup: showPopup,
                           hidePopup: hidePopup
                        })}
                     </OHSListPopup>
                  );
               })}
            </>
         : <p className='ohslist-noitems'>There are no items to display</p> : <p className='ohslist-noitems'>There are no items to display</p>}
      </div>
   );
}

Audiospur automatisch in Safari auf IOS abspielen

Mein Problem ist, dass ich eine MP3 Datei automatisch abspielen will, wenn der Benutzer einen Button anklickt. Anscheinend sieht es so aus, als würde Safari auf dem Iphone das verhindern. Ich habe das auch auf dem PC ausprobiert und da hat es funktiorniert, aber erst, nachdem ich es explizit freigegeben habe. Gibt es auf dem Iphone eine Einstellung, die es einer Webseite ermöglicht, eine Audiodatei automatisch abzuspielen oder kann man das auch per JavaScript erreichen?

Mein HTML Code sah so aus:

echo "<audio src='../config/mp3/Beleidigung.mp3' autoplay controls></audio>";
            echo "<script>audioElement.play();</script>";

Pixi.js – How to merge circles and outline them?

I have 2 circles, I would like to merge them and have a single outline for the merged shape without having them overlap. Is this possible?

const mergedCircle = new PIXI.Graphics();

mergedCircle.alpha = 0.5;
mergedCircle.beginFill(0xffffff);
mergedCircle.drawCircle(300, 300, 150);
mergedCircle.drawCircle(450, 300, 150); // This shape should not "overlap" the first circle
mergedCircle.endFill();

app.stage.addChild(mergedCircle);

// Now create an outline for the entire shape

What I have

What I’m trying to achieve

Merged Circles

How to fix non-reactive nested objects inside a $state([]) in Svelte?

Here’s the link to repro (Svelte Playground).

The problem is that when you add a “waypoint” and toggle the checkbox to enable its bearings (thus changing the respective nested object’s enabled property), Svelte won’t see these changes. I can see that because the effect on the lines 41-44 won’t log anything. If I use $inpsect instead, there’s also nothing.

let { directions, configuration } = {directions: {waypointsBearings: [], configuration: {bearings: true}}, on: () => {}, configuration: {}}

  // @ts-expect-error It's safe to read the plugin's protected properties here.
  if (!directions.configuration.bearings) {
    console.warn("The Bearings Control is used, but the `bearings` configuration option is not enabled!");
  }

    function addWaypoint() {
        directions.waypointsBearings.push({enabled: false})
        onWaypointsChanged()
    }

  let waypointsBearings: {
    enabled: boolean;
    angle: number;
    degrees: number;
  }[] = $state([]);

  // directions.on("addwaypoint", onWaypointsChanged);
  // directions.on("removewaypoint", onWaypointsChanged);
  // directions.on("movewaypoint", onWaypointsChanged);
  // directions.on("setwaypoints", onWaypointsChanged);

  function onWaypointsChanged() {
    waypointsBearings = directions.waypointsBearings.map((waypointBearing, index) => {
      if (waypointsBearings[index]) return waypointsBearings[index];

      return {
        enabled: configuration.defaultEnabled || !!waypointBearing,
        angle: waypointBearing ? waypointBearing[0] : configuration.angleDefault,
        degrees: waypointBearing
          ? waypointBearing[1]
          : configuration.fixedDegrees
            ? configuration.fixedDegrees
            : configuration.degreesDefault,
      };
    });
  }

  $effect(() => {
    console.log("PIA");
    console.log(waypointsBearings);
  });

  onWaypointsChanged();

(The addWaypoint function is artifical and serves to replace the commented-out directions.on calls. It’s here just to replace the real map and its interactivity).

<button onclick={addWaypoint}>Add</button>
<div class="bearings-control {configuration.class}" style="display: {waypointsBearings.length ? 'block' : 'none'}">
  <div class="bearings-control__list">
    {#each waypointsBearings as waypointBearing, i}
      <div
        class="
        bearings-control__list-item
        {waypointBearing.enabled ? 'bearings-control__list-item--enabled' : 'bearings-control__list-item--disabled'}
        "
      >
        <span class="bearings-control__number">{i + 1}. </span>
        <input type="checkbox" bind:checked={waypointBearing.enabled} class="bearings-control__checkbox" />

I assume that this absence of reactivity for the inner objects is just a part of the broader issue (see the context below), but should be enough to start.

Here’s some broader context for the problem. I have a working code (Svelte 4) which I’m trying to rewrite to Svelte 5. You can see the working Svelte 4 code here and you can see it in action (see that it actually works fine) here (try clicking the map to add waypoints, so that the “bearings” bar appears).

Confusion over how to return result from an async function using Promise [duplicate]

I haven’t touched JS for a while. Can someone explain why am I getting such an unexpected result?

My goal is to return a result from an async function. (Or, in other words, to convert an asynchronous function into a synchronous one and return the result from it.)

Let me show it in code:

<!DOCTYPE html>
<html>
<head>
</head>

<script>
function test1()
{
    return new Promise((resolve, reject) => {

        setTimeout(() => {
            console.log("Timer fires");
            resolve("Done");
        }
        , 2000);

    });
}

async function test2()
{
    console.log("Before promise");
    let result = await test1();
    console.log("After promise");

    console.log("result=" + result);
    return result;
}
</script>

<body>
    <script>
        let final_result = test2();
        console.log("Got final result=" + final_result);
    </script>

</body>
</html>

My assumption was that when I call my test2 it will wait for the result of test1, so I would get in the log:

Before promise
Timer fires
After promise
result=Done
Got final result=Done

But what I’m getting is this:

Before promise
Got final result=[object Promise]
Timer fires
After promise
result=Done

It’s almost like let result = await test1(); executes twice. Once it returns a Promise right away and somehow skips all the console.logs that follow it, and then after the timer fires, it does execute all those console.log statements.

I’m obviously missing something in the code flow of this latest JS.

SyntaxError: No number after minus sign in JSON at position 1 (line 1 column 2) [duplicate]

I get this error on the backend side:

Create error: SyntaxError: No number after minus sign in JSON at position 1 (line 1 column 2)
at JSON.parse ()
at parseJSONFromBytes (node:internal/deps/undici/undici:4292:19)
at successSteps (node:internal/deps/undici/undici:4274:27)
at fullyReadBody (node:internal/deps/undici/undici:2695:9)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async consumeBody (node:internal/deps/undici/undici:4283:7)
at async POST (webpack-internal:///(rsc)/./app/api/admin/products/route.ts:94:29)

Here’s my router code:

import { NextResponse } from "next/server";
import { connectMongoDB } from "@/app/lib/mongodb";
import { getServerSession } from "next-auth";
import Product from "@/app/lib/models/product";
import path from "path";
import fs from "fs";
export const dynamic = 'force-dynamic';
export const revalidate = 0;

function slugify(text: string): string {
  return text
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, "-")
    .replace(/(^-|-$)+/g, "");
}

export async function POST(req: Request) {
  try {
    await connectMongoDB();
    const session = await getServerSession();
    if (session?.user?.email !== process.env.ADMIN_EMAIL) {
      return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
    }

    const productData = await req.json();
    let bannerPath = "";

    if (productData.banner) {
      const matches = productData.banner.match(/^data:(.+);base64,(.+)$/);
      if (matches) {
        const [, mimeType, base64Data] = matches;
        const fileExt = mimeType.split("/")[1];

        if (!["jpeg", "jpg", "png", "webp"].includes(fileExt)) {
          return NextResponse.json(
            {
              error:
                "Invalid file format. Only JPEG, JPG, PNG, and WEBP are allowed.",
            },
            { status: 400 }
          );
        }

        const uploadDir = path.join(process.cwd(), "public", "banner");
        if (!fs.existsSync(uploadDir)) {
          fs.mkdirSync(uploadDir, { recursive: true });
        }

        const slugifiedName = slugify(productData.name || "product");
        const filename = `${slugifiedName}.${fileExt}`;
        const filepath = path.join(uploadDir, filename);

        const buffer = Buffer.from(base64Data, "base64");
        fs.writeFileSync(filepath, buffer);

        bannerPath = `/banner/${filename}`;
      }
    }

    const { ...createData } = productData;
    if (bannerPath) {
      createData.banner = bannerPath;
    }

    const newProduct = await Product.create(createData);
    return NextResponse.json(newProduct);
  } catch (error) {
    console.error("Create error:", error.stack);
    return NextResponse.json(
      { error: "Failed to create product" },
      { status: 500 }
    );
  }
}

That’s the payload:

{
    "name": "title",
    "subtitle": "subtitle",
    "status": "Active",
    "description": "description",
    "durations": [
        {
            "lemonId": "123456",
            "days": "30",
            "name": "Name",
            "value": "USD30.00",
            "buy_now_url": "URL IN HERE"
        },
        {
            "lemonId": "654321",
            "days": "7",
            "name": "Name",
            "value": "USD30.00",
            "buy_now_url": "URL IN HERE"
        }
    ],
    "functions": [
        "1231231"
    ],
    "requirements": [
        {
            "text": "213241421"
        }
    ],
    "banner": "The base64 data is here (I converted the data to png and verified it and the base64 is correct)."
}

I use these versions:

Next.js: 14.2.22

Node.js: v21.7.3

Continue after clearing browsing data in extension

I have this code in my extension which keeps track what URLs were visited, and how many times, and then it changes the proxy credentials after reaching the limit:

chrome.webRequest.onBeforeRequest.addListener(
    function (details) {
        if (details.method === "GET" && details.type === "main_frame") {
            for (let state_iin in states) {
                if (details.url.indexOf(states[state_iin].url) !== -1) {
                    if (states[state_iin].counter === states[state_iin].limit) {
                        let options = {};
                        const rootDomain = 'my.proxy.server'; // for example domain.com
                        options.origins = [];
                        options.origins.push("http://" + rootDomain);
                        options.origins.push("https://" + rootDomain);

                        let types = {"cookies": true};
                        chrome.browsingData.remove(options, types, function () {
                            // some code for callback function
                        });
                        states[state_iin].counter = 0
                        return {}
                    }

                    states[state_iin].counter++
                    return {}
                }
            }
        }
    },
    {urls: ["<all_urls>"]},
);

It works only until the time the invocation of chrome.browserData.remove is made – there it just stays on the page and doesn’t load the new URL.

I’m not sure what to do to make it continue after browsing data was cleared – tried to redirect to same URL but it doesn’t work. What do I need to add in order to avoid this kind of behavior?

ERROR Registration Error: [TypeError: this._firestore.native.documentSet is not a function (it is undefined)]

I’m developing a user registration feature in a React Native app using Firebase Firestore to store user details. However, I keep encountering a TypeError during the Firestore document set operation.

Environment:

react-native”: “0.76.5”,
@react-native-firebase/app”: “^21.6.2″,
@react-native-firebase/firestore”: “^21.6.2”,

Testing Device/Emulator: [iOS and Android ]

I implemented user registration and data storage using Firestore in my React Native app. Here’s what I expected and what I tried:

Expected: Successfully register a user and store their information (first name, last name, email, and registration timestamp) in Firestore.

Attempted: Used the Firestore set method to write user data after successful registration.

Code:

const handleRegister = async () => {
        if (validateInput()) {
            try {
                const userCredential = await registerUserWithEmail(email, password);
                console.log('User registered:', userCredential.user);
    
                const db = firestore();
                const userRef = db.collection('users').doc(userCredential.user.uid);
                console.log('Firestore Reference:', userRef);
    
                // Attempt to write to Firestore and log the process
                userRef.set({
                    firstName: firstName,
                    lastName: lastName,
                    email: email,
                    createdAt: firestore.FieldValue.serverTimestamp()
                })
                .then(() => {
                    console.log('User data set in Firestore');
                    Alert.alert('Success', 'Account Created Successfully');
                    navigation.navigate('Login');
                })
                .catch(error => {
                    console.error('Firestore set error:', error);
                    Alert.alert('Registration Failed', error.message);
                });
    
            } catch (error) {
                console.error('Registration Error:', error);
                Alert.alert('Registration Failed', `${error.message} [${error.code}]`);
            }
        }
    };

However, I keep encountering a TypeError when the set method is called, specifically complaining about documentSet not being a function. I expected the Firestore set operation to run without errors and store the user data.

Debugging Steps Taken:

Ensured the Firestore and Firebase SDKs are up-to-date.
Checked Firestore permissions to ensure write access is enabled.
Reviewed Firebase documentation to confirm usage of APIs.
Added console logs to trace the values and confirm the presence of Firestore methods.

Error Message:
[TypeError: this._firestore.native.documentSet is not a function (it is undefined)]

I need to decode or decrypt this, please [closed]

I am sending you the username and password encrypted using our software Narada. Please use the software to decrypt and access the system.

Wfvb+dlz2RuZO3szeXMJm8NDW+Pz4w==

However, there is no software that I know of. Please, I need urgent help.

I hope the message is decoded so I can find the username and password they are asking for from my university.

{“errors”:[{“debugMessage”:”User Error: expected iterable, but did not find one for field professeurspaginated.data

iwould like to understand
query:

public function resolve($root, $args, $context, ResolveInfo $resolveInfo, Closure $getSelectFields)
{
    $query = Professeur::query();

    if (isset($args['id']))
    {
        $query = $query->where('id', $args['id']);
    }

    if (isset($args['nom']))
    {

    $count = Arr::get($args, 'count', 10);
    $page  = Arr::get($args, 'page', 1);
    return $query->orderBy('nom')->paginate($count, ['*'], 'page', $page);
}

While clicking the anchor tag(), it flickers

Here I have this piece of code, When I click on this despite not having any onClick functionality it flickers which i believe is a standard functionality of Anchor tag(). So is there a way we can avoid/stop the flickering of this the user clicks on anchor tag.

<lightning-carousel-image   
    class="carousel-image slds-carousel__panel"   
    data-handles-touch="true">  
    <div role="tabpanel" id="carousel-image-88" aria-hidden="true">  
        <a class="slds-carousel__panel-action slds-text-link_reset" tabindex="-1" style="/* background-color: red; */">  
            <div class="slds-carousel__image">  
                <img src="https***" alt="No Image">  
            </div>  
            <div class="slds-carousel__content content-container">  
                <h2 class="slds-carousel__content-title">Join ***** Some content</h2>  
                <p>Some content.</p>  
            </div>  
        </a>  
    </div>  
</lightning-carousel-image> 

Trying to exclude certain events from a range of events in fullCalendar

I am using fullCalendar. I have, for each event, a start date, and an end date. I also have an array of “except_dates”. These are the dates I want to be EXCLUDED from that event. So, for example, if I have

Start Date: 2024-01-08

End Date: 2025-02-08

and Except Dates: [“2025-01-11”, “2025-01-24”, “2025-02-02”]

It will show all of the dates on the calendar except 2025-01-11, 2025-01-24, and 2025-02-02 for that specific event.

jest.fn doesn’t call mock implementation

Context

I am writing tests for a React component that allows file uploads from the user. When the user selects a file for upload, it appears in an uploads list as a button with text matching the name of the file. The user can click “Upload” to confirm they want to upload this file, or they can click the button showing the filename to remove the file from the list.

I’ve written the following test for removing the file from the list by clicking the button:

describe('FileUploadForm', () => {

  // "setFiles" mocks the state setter of the parent component
  const setFiles = (newFiles => {appState.files = [...newFiles]});
  
  // generally "files" would be a parent component state, and
  // appState would contain more fields for tracking other components
  let appState = {files: [], setFiles: setFiles};

  // These are part of the component but not used in this test
  let setUploadFailed = false;
  let setUploadResult = '';
  document.cookie = 'csrftoken=verySecureToken';

  // mock "images" to test with
  let image1 = {
    'name': 'test-image-1',
    'type': 'image/jpeg'
  };
  let image2 = {
    'name': 'test-image-2',
    'type': 'image/bmp'
  };
  let image3 = {
    'name': 'test-image-3',
    'type': 'image/webp'
  };
  let image4 = {
    'name': 'test-image-4',
    'type': 'image/png'
  };


  test('removes individual file when clicked', () => {
    // load the "images" into the files mock state
    appState.files = [image1, image2, image3, image4];
    
    const {rerender} = render(
      <FileUploadForm
        files={appState.files}
        setFiles={appState.setFiles}  // this should update "files" on click
        setUploadFailed={setUploadFailed}  // not used for this test
        setUploadResult={setUploadResult}  // not used for this test
      />
    );

    // assert all images and their buttons appear in list to begin with
    expect(screen.getByText('test-image-1')).toBeInTheDocument();
    expect(screen.getByText('test-image-2')).toBeInTheDocument();
    expect(screen.getByText('test-image-3')).toBeInTheDocument();
    expect(screen.getByText('test-image-4')).toBeInTheDocument();

    // click on one of the buttons
    fireEvent(
      screen.getByText('test-image-2'),
      new MouseEvent('click', {
        bubbles: true,
        cancelable: true
      })
    );

    rerender(
      <FileUploadForm
        files={appState.files}
        setFiles={appState.setFiles}
        setUploadFailed={setUploadFailed}
        setUploadResult={setUploadResult}
      />
    );

    // assert that the clicked filename is removed from list
    expect(screen.getByText('test-image-1')).toBeInTheDocument();
    expect(screen.queryByText('test-image-2')).not.toBeInTheDocument();
    expect(screen.getByText('test-image-3')).toBeInTheDocument();
    expect(screen.getByText('test-image-4')).toBeInTheDocument();
  });

As written above, this all works. However, I’d like to make two changes to this to incorporate jest.fn(), marked by arrows below.

describe('FileUploadForm', () => {

  // "setFiles" mocks the state setter of the parent component
  const setFiles = jest.fn(newFiles => {appState.files = [...newFiles]}); // <-----------
                   ^^^^^^^  

  // generally "files" would be a parent component state, and
  // appState would contain more fields for tracking other components
  let appState = {files: [], setFiles: setFiles};

  // These are part of the component but not used in this test
  let setUploadFailed = false;
  let setUploadResult = '';
  document.cookie = 'csrftoken=verySecureToken';

  // mock "images" to test with
  let image1 = {
    'name': 'test-image-1',
    'type': 'image/jpeg'
  };
  let image2 = {
    'name': 'test-image-2',
    'type': 'image/bmp'
  };
  let image3 = {
    'name': 'test-image-3',
    'type': 'image/webp'
  };
  let image4 = {
    'name': 'test-image-4',
    'type': 'image/png'
  };


  test('removes individual file when clicked', () => {
    // load the "images" into the files mock state
    appState.files = [image1, image2, image3, image4];
    
    const {rerender} = render(
      <FileUploadForm
        files={appState.files}
        setFiles={appState.setFiles}  // this should update "files" on click
        setUploadFailed={setUploadFailed}  // not used for this test
        setUploadResult={setUploadResult}  // not used for this test
      />
    );

    // assert all images and their buttons appear in list to begin with
    expect(screen.getByText('test-image-1')).toBeInTheDocument();
    expect(screen.getByText('test-image-2')).toBeInTheDocument();
    expect(screen.getByText('test-image-3')).toBeInTheDocument();
    expect(screen.getByText('test-image-4')).toBeInTheDocument();

    // click on one of the buttons
    fireEvent(
      screen.getByText('test-image-2'),
      new MouseEvent('click', {
        bubbles: true,
        cancelable: true
      })
    );

    rerender(
      <FileUploadForm
        files={appState.files}
        setFiles={appState.setFiles}
        setUploadFailed={setUploadFailed}
        setUploadResult={setUploadResult}
      />
    );

    // assert that the clicked filename is removed from list
    expect(setFiles).toHaveBeenCalledTimes(1);  // <--------------------------------------
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    expect(screen.getByText('test-image-1')).toBeInTheDocument();
    expect(screen.queryByText('test-image-2')).not.toBeInTheDocument();
    expect(screen.getByText('test-image-3')).toBeInTheDocument();
    expect(screen.getByText('test-image-4')).toBeInTheDocument();
  });

When I make these changes, the setFiles function stops working, as if it isn’t being called at all. The assertion that setFiles be called once passes, so it is getting called, but it doesn’t actually update appState.files. The only thing I am changing in the function definition is adding jest.fn to the beginning, for the goal of being able to call expect(setFiles).toHaveBeenCalledTimes(1) later.

My Question:

Is there something special I need to do in order for a function wrapped in jest.fn() to actually be called? The docs certainly don’t make it seem that way. Not sure what’s getting stuck here.


In case it’s relevant, the component under testing (with styling stripped out):

/**
 * Sends upload request to backend (via HTTP).
 * 
 * @param {Event} e For preventing default browser behavior.
 * @param {Array} files The selected files to be uploaded.
 * @param {function} setFiles Resets state to empty after request is complete.
 * @param {function} setUploadFailed Says whether the upload failed or not.
 */
async function handleUpload(e, files, setFiles, setUploadFailed) {
    e.preventDefault()
    const formData = new FormData();
    files.forEach(file => {formData.append('file', file)});
    try {
        await sendCSRFRequest(formData, 'api/upload/');
        setUploadFailed(false);
    } catch (err) {
        setUploadFailed(true);
    }
    
    await setFiles([]);
};


/**
 * Button for file that will remove it from the form when clicked.
 * Fits inside the FileUploadForm.
 */
function FileButton(props) {
  const [isHovered, setIsHovered] = useState(false);

  const buttonStyle = {
    background: 'none',
    border: 'none',
    padding: 0,
    font: 'inherit',
    color: '#000000',
    cursor: 'pointer',
    textDecoration: isHovered ? 'underline' : 'none',
  };

  const handleMouseEnter = () => {
    setIsHovered(true);
  };

  const handleMouseLeave = () => {
    setIsHovered(false);
  };

  const handleFileClick = (e, file, files) => {
    e.preventDefault()
    const modifiedFiles = files.slice();  // make a copy of the array
    const index = modifiedFiles.indexOf(file);
    modifiedFiles.splice(index, 1);  // cut out the target file
    props.setFiles(modifiedFiles);  // set new files array
  };

  return(
    <>
      <button
        style={buttonStyle}
        onClick={(e) => handleFileClick(e, props.file, props.files)}
        onMouseEnter={handleMouseEnter}
        onMouseLeave={handleMouseLeave}>
          {props.file.name}
      </button>
      <br/>
    </>
  );
};



export default function FileUploadForm(props) {
    return (
      <>
        <form method='POST' action={''}>


          {/* lists files passed into the form */}
          {/* updates when files are selected for upload */}
          {/* removes individual file when clicked */}
          <List>
            {props.files.map((file, index) => (
              <FileButton
              file={file}
              files={props.files}
              key={index}
              setFiles={props.setFiles}/>
            ))}
          </List>


          {/* submit button submits form */}
          <Button
            type='submit'
            onClick={(e) => handleUpload(
              e, props.files, props.setFiles, props.setUploadFailed
            )}
          >
            Upload
          </Button>


          {/* clears file selections */}
          <Button onClick={() => props.setFiles([])}>
            Clear all
          </Button>


          {/* passes in CSRF token */}
          <CSRFToken/>


        </form>
      </>
    );
};


Show on hover javascript working great but how do I add or change to click for mobile?

I have a map where a user can mouse over a number on the map and have an info card pop up. Then when they click on the number it links to the card infos website.

This doesnt work on mobile because there is no way to hover over the number.

I’m wondering how i can get the info card to pop up on clicking the number and then hide on clicking a second time?

Or if theres a better way to get this function on both desktop and mobile that would be awesome?

I did a search but cant find anything similiar to the javascript Im using. I am not familiar with java script at all but got it working from other samples.

Checked the post – Jquery show on hover hide on click –

This is the link to the map
https://illomaps.net/comox_hotlist.html


<script type="text/javascript">
    let paths = document.querySelectorAll("path");
let cardWidth = card.getBoundingClientRect().width;
let cardHeight = card.getBoundingClientRect().height;

paths.forEach((p) => {
  p.addEventListener("mouseleave", (evt) => {
    card.style.visibility = "hidden";
  });
  p.addEventListener("mousemove", (evt) => {
    let pos = oMousePos(svg, evt);
    let text = p.dataset.text;
    card.style.visibility = "visible";
    card.style.top = pos.y + "px";
    card.style.left = pos.x + "px";
    card.innerHTML = text;
  });
});

function oMousePos(element, evt) {
  let ClientRect = element.getBoundingClientRect();
  let currentX = Math.round(evt.clientX - ClientRect.left);
  let currentY = Math.round(evt.clientY - ClientRect.top);

  //close to right
  if (evt.clientX + cardWidth >= ClientRect.right) {
    currentX = Math.round(evt.clientX - ClientRect.left - cardWidth);
  }
  //close to bottom
  if (evt.clientY + cardHeight>= ClientRect.bottom) {
    currentY = Math.round(evt.clientY - ClientRect.top - cardHeight);
  }
  return {
    x: currentX,
    y: currentY
  };
}
</script>

Logic to add vertical stepper lines : ReactJS

I have a created a dummy stepper application, functionality wise the app works fine . I am trying to add a progress line right below the circle and between all circles. The circles indicating the steps have three states

  1. Green, when the step is completed

  2. Blue, when on the current step

  3. White, when its untouched.

In the similar lines, I want green line when the current step is completed and the line directed towards the next step should be blue, else case it should be grey in color. Can someone help?

https://codesandbox.io/p/sandbox/stepper-ppnqgc

interface StepsProps {
  currentStep: number;
  isSubmitted: boolean;
  stepsData: { title: string }[];
}

const Steps: React.FC<StepsProps> = ({
  currentStep,
  isSubmitted,
  stepsData,
}) => (
  <nav className="steps">
    <ul>
      {stepsData.map((step, index) => (
        <li
          key={index}
          className={`${
            index < currentStep ||
            (index === stepsData.length - 1 && isSubmitted)
              ? "complete"
              : ""
          } ${currentStep === index ? "active" : ""}`}
        >
          <span className="circle"></span>
          <span className="step-label">{step.title}</span>
        </li>
      ))}
    </ul>
  </nav>
);

interface BodyProps {
  currentStep: number;
  isSubmitted: boolean;
  stepsData: { title: string; content: string }[];
}

const Body: React.FC<BodyProps> = ({ currentStep, isSubmitted, stepsData }) => (
  <div className="body">
    <div className="left">
      <Steps
        currentStep={currentStep}
        isSubmitted={isSubmitted}
        stepsData={stepsData}
      />
    </div>
    <div className="right">
      <StepContent currentStep={currentStep} stepsData={stepsData} />
    </div>
  </div>
);

const StepContent = () => { return null }

const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
  <Body
    currentStep={1}
    isSubmitted={false}
    stepsData={[
      { title: "Example 1", content: "Hello, world!" },
      { title: "Example 2", content: "Hello, world!" },
    ]}
  />
)
body {
  margin: 0;
  font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
  background-color: #f9f9f9;
  color: #333;
}

h1,
h2,
h3,
p {
  margin: 0;
}

.header {
  text-align: center;
  padding: 20px;
  background-color: #007bff;
  color: white;
}

.footer {
  display: flex;
  justify-content: center;
  gap: 15px;
  padding: 15px;
  background-color: #007bff;
}

.footer-button {
  padding: 10px 20px;
  font-size: 16px;
  color: white;
  background-color: #0056b3;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  transition: background-color 0.3s ease;
}

.footer-button:disabled {
  background-color: #ccc;
  cursor: not-allowed;
}

.footer-button:hover:not(:disabled) {
  background-color: #004085;
}

.footer-button.submit {
  background-color: #28a745;
}

.footer-button.submit:hover:not(:disabled) {
  background-color: #218838;
}

.body {
  display: flex;
  flex-direction: row;
}

.left {
  width: 30%;
  padding: 20px;
  background-color: #f4f4f4;
  border-right: 1px solid #ddd;
}

.steps {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  position: relative;
}

.steps ul {
  list-style: none;
  padding: 0;
  margin: 0;
}

.steps li {
  display: flex;
  align-items: center;
  margin-bottom: 40px;
  cursor: pointer;
}

.steps li .circle {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: 2px solid #ccc;
  background-color: #fff;
  position: relative;
  z-index: 1;
  transition: background-color 0.3s, border-color 0.3s;
}

.steps li.complete .circle {
  background-color: #28a745;
  border-color: #28a745;
}

.steps li.active .circle {
  background-color: #007bff;
  border-color: #007bff;
}

.steps li .step-label {
  margin-left: 15px;
  font-size: 16px;
  color: #333;
}

.right {
  flex: 1;
  padding: 20px;
}

.step-content {
  padding: 10px;
  background-color: #fff;
  border-radius: 4px;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

.step-content p {
  font-size: 16px;
  line-height: 1.6;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.production.min.js"></script>

<div id="root"></div>