Problem with changing pages on website, instead o 1,2,3,4,5 they go 1,2,4,8,16

I am making a website with option to switch between pages. But here comes the problem. Function works normally but when I want to call inside a function that fetches API, then pages on each click go doubled. Instead of 1,2,3,4,5, it goes 1,2,4,8,16… and so on. How can I fix that?


let page = 1
async function getData(url) {
    const res = await fetch(url + page);
    const data = await res.json();
    const results = data.results;
    showList(results);
    
    pageBtns.forEach((btn) => {
        btn.addEventListener('click', () => changePage(url, btn));
    });
}

function changePage(url, btn) {
    if (btn.classList.contains('next-btn')) {
        document.querySelector('.prev-btn').classList.add('prev-btn--active');
        page++;
        if (page === 5) {
            btn.classList.remove('next-btn--active');
        }
    } else {
        document.querySelector('.next-btn').classList.add('next-btn--active');
        page--;
        if (page === 1) {
            btn.classList.remove('prev-btn--active');
        }
    }
    getData(url)
    console.log(page);
}

I’ve tried to call changePage function without invoking getData inside and it works normally.

Why does accessing ‘window.variable’ return ‘undefined’ when using ‘let’ but not with ‘var’? [duplicate]

I’m puzzled by the differing behavior I’m observing when accessing global variables in JavaScript using var and let declarations in a browser environment.

Consider the following code snippets:

// Using var
var a = 20;
console.log(window.a); // Outputs: 20

// Using let
let a = 20;
console.log(window.a); // Outputs: undefined

In both cases, ‘a’ is declared in the global scope, yet accessing ‘window.a’ returns ‘undefined’ when using ‘let’, while it correctly returns 20 when using ‘var’.

I’m particularly interested in understanding how variable declarations with ‘var’ and ‘let’ differ in their interaction with the global object (‘window’ in a browser environment). What are the underlying reasons for this behavior, and how does it relate to JavaScript’s scoping rules?

Mutiple time form submission inside thymeleaf fragment implementing using AJAX not working

I have a thymleaf page named labStatistics.html. This page implements multiple tabs. On selecting a specific tab a thymleaf fragments will be loaded into this page. The pageload is done using ajax. Inside the labChart fragment there is a form which is responsible for form submission (GET) which will reload the fragment inside the labStatistics.html with updated contents based on end user inputs. During inital load and first form submission there is no issue with loading of fragment. The fragment is loaded using network type fetch in chrome browser console. When the form is submitte dthe second time the network type is document.

The code responsible for loading te tab contents is as follows:

lazyLoadTabBuilder2.js

// lazyLoadLabTabBuilder2.js
import { labChartBuilder } from "./labChartBuilder.js";
import { labReportBuilder } from "./labReportBuilder.js";
import { lc } from "./lc.js";

export function lazyLoadLabTabBuilder2(tabId, element) {
    fetch(`/labtab?tabName=${tabId}`)
        .then(response => response.text())
        .then(html =>
        {
            const contentDiv = document.getElementById(`${tabId}Content`);
             console.log("JS FILE= lazyLoadLabTabBuilder2.js ---> Setting innerHTML for", tabId);
            contentDiv.innerHTML = html;

            if (tabId === 'labChart') 
            {
                console.log("JS FILE= lazyLoadLabTabBuilder2.js ---> Calling lc() for labChart");
                lc();  // Re-bind the event handlers
                
                console.log("JS FILE= lazyLoadLabTabBuilder2.js ---> Calling labChartBuilder() for labChart");
                labChartBuilder();  // Reinitialize chart scripts
            }
             else if (tabId === 'labReport') 
             {
                 console.log("JS FILE= lazyLoadLabTabBuilder2.js ---> Calling labReportBuilder() for labReport");
                labReportBuilder();  // Initialize lab report scripts
             }

            console.log("JS FILE= lazyLoadLabTabBuilder2.js ---> Updating the active state for the clicked tab");
            document.querySelectorAll('.nav-link').forEach(link => link.classList.remove('active'));
            element.classList.add('active');
            console.log("JS FILE= lazyLoadLabTabBuilder2.js ---> Updated");
            
            console.log("JS FILE= lazyLoadLabTabBuilder2.js ---> Executing the logic to show the active tab's content and hide others");
            document.querySelectorAll('.tab-pane').forEach(tabContent => tabContent.classList.remove('active'));
            contentDiv.classList.add('active');
            console.log("JS FILE= lazyLoadLabTabBuilder2.js ---> Active tab contents shown.")
            
        })
        .catch(error => console.error('JS FILE= lazyLoadLabTabBuilder2.js ---> Error loading the tab content:', error));
}

the form is submitted inside lc.js

// lc.js
import { formDateValidator } from '../formValidator/formValidator.js';
import { labChartBuilder } from "./labChartBuilder.js";

// Function to attach the submit event listener to the form
function attachFormSubmitEventListener(form) {
    form.addEventListener('submit', function(e) {
        e.preventDefault(); // Prevent default form submission

        const sdate = document.getElementById('labySdate').value;
        const edate = document.getElementById('labyEdate').value;
        const oid = document.getElementById('labyClinicId').value;
        const errorDiv = document.getElementById('labchart-error-message');
        console.log("JS FILE= lc.js ---> Form submission triggered with:", sdate, edate, oid);
        
        // Perform form validation
        if (!formDateValidator(sdate, edate, errorDiv)) {
            console.error('Validation failed.');
            return; // Exit if validation fails
        }

        // Prepare and send the AJAX request
        const formData = new FormData(form);
        const queryString = new URLSearchParams(formData).toString();
        fetch(`/labChart?${queryString}`, { method: 'GET' })
            .then(response => {
                if (!response.ok) throw new Error('Network response was not ok');
                console.log("JS FILE= lc.js ---> Response OK");
                return response.text();
            })
            .then(html => {
                document.getElementById('labChartContent').innerHTML = html;
                labChartBuilder(); // Reinitialize the chart or other scripts
            })
            .catch(error => console.error('JS FILE= lc.js ---> Error submitting labChart form:', error));
    });
}

export function lc() {
    let form = document.getElementById('labFormBeanChart');
    if (!form) {
        console.error('Form #labFormBeanChart not found.');
        return;
    }

    // Check if the form has the 'data-has-listener' attribute
    if (form.getAttribute('data-has-listener')) {
        // Reset the form to remove the event listener
        form.outerHTML = form.outerHTML;
        // Re-select the form as the previous reference is now obsolete
        form = document.getElementById('labFormBeanChart');
    } else {
        // If the form does not have the listener, set the attribute
        form.setAttribute('data-has-listener', 'true');
    }

    // Attach the submit event listener to the form
    attachFormSubmitEventListener(form);
}

// Call lc() to ensure form handling is set up on script load
lc();

The springboot backend logic is executing as per logic that is required. the issue is with loading the fragment inside labStatics.html page when form is submitted multuple time.

Initial Load

first form submission after the fragment with loaded

when we submit the form multiple times after first submission

THREE.WebGLRenderer : Texture marked for update but no image data found

I’ve been stuck for several days on this situation, I am trying to create a distorsion pixel effect with ThreeJS by using both a texture (using the textureLoader) and a textureData (created in regenerateGrid()). The original image is perfectly loaded but I can’t figure out why am I having this warning : THREE.WebGLRenderer : Texture marked for update but no image data found

Does someone have an answer ?

Here is the code :

<script setup>
  import * as THREE from 'three';
  import GUI from 'lil-gui';
  import { useElementBounding } from '@vueuse/core'


// PB AVEC LA SIZE DU RENDER UTILISER REF
  const gui = new GUI();
  var test = 0;
  const image = ref();
  var img;
  const hero = ref();
    var width;
    var height;
   
 
  var time = 0;
  var settingsItem;
  var textureData;

  var renderer;
  const experience = ref();
  const scene = new THREE.Scene();
  var frustumSize = 1;
  
  var aspect = window.innerWidth / window.innerHeight;
  const camera = new THREE.OrthographicCamera(frustumSize / -2, frustumSize / 2, frustumSize / 2, frustumSize / -2, -1000, 1000);
  camera.position.set(0, 0, 2);
  scene.add(camera);

  var settingsItem;
  var mouse = {
      x: 0,
      y: 0,
      prevX: 0,
      prevY: 0,
      vX: 0,
      vY: 0
  }
  var isPlaying;
  var size;
  var texture;
  var material;
  var geometry;
  var plane;

  function mouseEvents() {
    window.addEventListener('mousemove', (e) => {
      mouse.x = e.clientX / width;
      mouse.y = e.clientY / height;
      mouse.vX = mouse.x - mouse.prevX;
      mouse.vY = mouse.y - mouse.prevY;
      mouse.prevX = mouse.x;
      mouse.prevY = mouse.y;
      // console.log('coucou je bouge');
    })
  }

  function render(){

    if (!isPlaying) return;
    time += 0.05;
    updateDataTexture()
    material.uniforms.time.value = time;
    requestAnimationFrame(render.bind(this));
    renderer.render(scene, camera);

  }

  function clamp(number, min, max) {
    return Math.max(min, Math.min(number, max));
  }

  function updateDataTexture() {
    let data = textureData.image.data;
    for (let i = 0; i < data.length; i += 3) {
      data[i] *= settingsItem.relaxation;
      data[i+1] *= settingsItem.relaxation
    }

    let gridMouseX = size * mouse.x;
    let gridMouseY = size * (1 - mouse.y);
    let maxDist = size * settingsItem.mouse;
    let aspect = height / width;

    for (let i = 0; i < size; i++) {
      for (let j = 0; j< size; j++) {
        let distance = ((gridMouseX - i) ** 2) / aspect + (gridMouseY - j) ** 2;
        let maxDistq = maxDist ** 2;
        if (distance < maxDistq) {
          let index = 3 * (i + size * j);
          let power = maxDist / Math.sqrt(distance);
          power = clamp(power, 0, 10);
          data[index]+= settingsItem.strength * 100 * mouse.vX * power;
          data[index + 1] -= settingsItem.strength * 100 * mouse.vY * power;
        }
      }
    }

    mouse.vX *= 0.9;
    mouse.vY *= 0.9;
    textureData.needsUpdate = true;
  }

  function settings(){
    settingsItem = {
      grid: 34,
      mouse: 0.25,
      strength: 1,
      relaxation: 0.9
    }

    gui.add(settingsItem, "grid", 2, 1000, 1).onFinishChange(() => {
      regenerateGrid()
    });
    gui.add(settingsItem, "mouse", 0, 1, 0.01);
    gui.add(settingsItem, "strength", 0, 1, 0.01);
    gui.add(settingsItem, "relaxation", 0, 1, 0.01);
  }

  function regenerateGrid(){
    size = settingsItem.grid;
    width = size;
    height =  size;
    const newSize = width * height;
    const data = new Float32Array(4*newSize);
    const color = new THREE.Color(0xffffff);

    const r  = Math.floor(color.r * 255);
    const g  = Math.floor(color.g * 255);
    const b  = Math.floor(color.b * 255);
    const a =  255;

    for (let i = 0; i < size; i++) {
      let r = Math.random() * 255 - 125;
      let r1 = Math.random() * 255 - 125;

      const stride = i * 4;

      data[stride] = r;
      data[stride + 1] = r1;
      data[stride + 2] = r;
      data[stride + 3] = a;
    }

    console.log('width : ' + width + ', height : ' + height);

    textureData = new THREE.DataTexture(data, width, height, THREE.RGBAFormat, THREE.FloatType);
    textureData.magFilter = textureData.minFilter = THREE.NearestFilter;
    console.log('textureData : ' + textureData);

    if(material) {
      material.uniforms.uDataTexture.value = textureData;
      material.uniforms.uDataTexture.value.needsUpdate = true;
    }
  }

  function addObjects() {
    regenerateGrid();
    let textureLoader = new THREE.TextureLoader();
    texture = textureLoader.load(img.src);
    console.log('img src : ' + img.src );
    console.log('texture : ' + texture);
    texture.needsUpdate = true;
    material = new THREE.ShaderMaterial({
      extensions: {
        derivatives: "#extension GL_OES_standard_derivatives : enable"
      },
      side: THREE.DoubleSide,
      uniforms: {
        time: {
          value: 0
        },
        resolution: {
          value: new THREE.Vector4()
        },
        uTexture: {
          value: texture
        },
        uDataTexture: {
          value: textureData
        }
      }, 
      vertexShader: `
      
      uniform float time;
      varying vec2 vUv;

      void main() {
        vUv = uv;
        gl_Position = vec4( position, 1.0 );
      }
      `,
      fragmentShader: `
      
      uniform float time;
      uniform sampler2D uDataTexture;
      uniform sampler2D uTexture;
      uniform vec4 resolution;
      varying vec2 vUv;

      void main()   {
        vec2 newUV = (vUv - vec2(0.5))*resolution.zw + vec2(0.5);
        vec4 color = texture2D(uTexture,newUV);
        vec4 offset = texture2D(uDataTexture,vUv);
        gl_FragColor = vec4(vUv,0.0,1.);
        gl_FragColor = vec4(offset.r,0.,0.,1.);
        gl_FragColor = color;
        gl_FragColor = texture2D(uTexture,newUV - 0.02*offset.rg);
        // gl_FragColor = offset;

      }
      `,
    });

    geometry = new THREE.PlaneGeometry(1.8,1.8,1,1);
    plane = new THREE.Mesh(geometry, material);
    scene.add(plane);
  }

  onMounted(() => {
    console.log('hero : ' + hero.value);
    width = hero.value.offsetWidth;
    height = hero.value.offsetHeight;
    img = image.value;
    console.log('image : ' + img)
    console.log('width 1 : ' + width);
    console.log('height 1 : ' + height);
    isPlaying = true;
    img.onload = () => {
      renderer = new THREE.WebGLRenderer({
        canvas: experience.value,
        antialias: true,
        alpha: true
      });
      renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
      renderer.setSize(width, height);
      renderer.setClearColor(0xeeeeee, 1);
      renderer.physicallyCorrectLights = true;
      renderer.outputEncoding = THREE.sRGBEncoding;
      settings();
      console.log('chargé !!! : ' + img.complete);
      addObjects();
      render();
      mouseEvents();
    }
    
  });


</script>

<template>
  <div ref="hero" data-grid="15" data-mouse="0.13" data-strength="0.15" class="hero">
    <canvas ref="experience"/>
    <img ref="image" src="/images/TitreMael.png" alt=""/>
  </div>
</template>

<style scoped lang="scss">
  .hero {
    width: 100%;
    height: 50rem;
    position: relative;
    z-index: -1;
    display: flex;
    justify-content: center;
    align-items: center;
    margin-top: -5rem;

    canvas {
      width: 100%;
      height: 100%;
    }

    img {
      visibility: hidden;
        pointer-events: none;
        position: absolute;
    }
  }
</style>

PDF kit reverse numbers in Arabic fonts

I’m using pdfkit package for node js application to generate PDFs, but I faced a problem when I attempted to generate Arabic text. It always reverses the numbers in Arabic texts.

if I want to write: ساڵی 1992 (The year 1992), it always changes to (ساڵی ٢٩٩١), This issue persists even when I use Unicode custom fonts.

Here are my codes:

function generatePDFForRuleAndRegulation(data) {
  const fileNamePDF = `${data.name}.pdf`;
  const doc = new PDFDocument();

  // Pipe the PDF document to a writable stream (file in this case)
  const stream = fs.createWriteStream(fileNamePDF);

  doc.pipe(stream);

  doc
    .font(`${path.join(__dirname, "../../assets/fonts/NRT-Reg.ttf")}`)
    .fontSize(12)
    .text(`${data.name}`, {
      align: "center",
    });
  doc.moveDown(); // Move cursor down

  doc
    .font(`${path.join(__dirname, "../../assets/fonts/Rabar_021.ttf")}`)
    .fontSize(10)
    .text(data.text, { align: "right", features: ["rtla"] });

  // Finalize the PDF document
  doc.end();

  // Inform when the file has been created
  stream.on("finish", () => {
    console.log(`PDF created: ${fileNamePDF}`);
  });
}

Is there any solution or workaround you can suggest? Alternatively, do you know of another package to generate PDFs that also supports Arabic fonts?

How to stay on page after page refresh with react native web?

I have a react native web app. And I load the app in the browser. But at the moment everytime a user navigate to a page and refesh the page the state of the app goes back to the main page. What not has to be.

So this is a component:

export const SubCategoryScreen = ({ route, navigation }) => {
    const [subCatgoryList, setSubCategoryList] = useState([]);
    const [isLoading, setLoading] = useState(true);
    const [searchAnimal, setSearchAnimal] = useState("");
    const [input, setInput] = useState("");
    const { performSearch, results } = useContext(SearchAnimalContext);
    const [searchTimer, setSearchTimer] = useState(null);

    const filteredData = subCatgoryList.filter((item) =>
        item.name.toLowerCase().includes(searchAnimal.toLowerCase())
    );
    return (
        <SafeArea>
            {isLoading && (
                <LoadingContainer>
                    <ActivityIndicator animating={true} color={MD2Colors.green200} />
                </LoadingContainer>
            )}
            <Searchbar
                placeholder="Searchie"
                onChangeText={(text) => {
                    if (searchTimer) {
                        clearTimeout(searchTimer);
                    }
                    if (text.length === 0) {
                        console.log(text, "TEXT");
                        return null;
                    }
                    setInput(text);
                    setSearchTimer(setTimeout(performSearch(text), 1000));
                }}
                value={input}
            />
            

            {results.length > 0 ? (
                <CategoryList
                    data={results}
                    renderItem={({ item }) => {                     
                        return (
                            <>
                                <Spacer>
                                    <SubCategoryInfoCard subcategories={item} />
                                </Spacer>                               
                            </>
                        );
                    }}
                    keyExtractor={(item) => item.id}
                />
            ) : (
                <CategoryList
                    data={filteredData}
                    renderItem={({ item }) => {
                        return (
                            <>
                                <TouchableOpacity
                                    onPress={() => navigation.navigate("groepen", { subcategories: item.id })}
                                    disabled={
                                        isLoading ||
                                        (!item.hasOwnProperty("animals") && !item.hasOwnProperty("subcategories"))
                                    }>
                                    <Spacer>
                                        <SubCategoryInfoCard subcategories={item} />
                                    </Spacer>
                                </TouchableOpacity>                             
                            </>
                        );
                    }}
                    keyExtractor={(item) => item.id}
                />
            )}
        </SafeArea>
    );
};

And this is the App component:

/* eslint-disable prettier/prettier */

import { Lato_400Regular, Lato_900Black, useFonts as useLato } from "@expo-google-fonts/lato";
import { Oswald_400Regular, useFonts as useOswald } from "@expo-google-fonts/oswald";

import { AuthContextProvider } from "./src/services/authentication/authentication.context";
import ExpoStatusBar from "expo-status-bar/build/ExpoStatusBar";
import { Navigation } from "./src/infrastructure/navigation";
import React from "react";
import { ThemeProvider } from "styled-components/native";
import { ToastProvider } from "react-native-toast-notifications";
import { theme } from "./src/infrastructure/theme";

/* eslint-disable prettier/prettier */

export default function App() {
    const [oswaldLoaded] = useOswald({
        Oswald_400Regular,
    });

    const [latoLoaded] = useLato({
        Lato_400Regular,
        Lato_900Black,
    });

    if (!oswaldLoaded || !latoLoaded) {
        return null;
    }

    return (
        <>
            <ToastProvider>
                <ThemeProvider theme={theme}>
                    <AuthContextProvider>
                        <Navigation />
                    </AuthContextProvider>
                </ThemeProvider>
            </ToastProvider>
            <ExpoStatusBar style="auto" />
        </>
    );
}

Of course I searched a lot. And I found this link:

Navigation in React Native doesn’t work after page refresh

But I don’t have an index.js file.

Question: how to stay on page after page fresh?

Parent page doesn’t get visibility change event when safari view controller is opened

I have a Java script page opened in my App in a webview. I have registered visibility change event on this page. When I call safari view controller from it, then the page doesn’t get visibility change event at all and neither it gets that when safari view controller is closed.

My main aim is to know in the page when safari view controller is closed by user so that I can trigger some functionality by calling a URL. How can I achieve this, it seems visibility change event doesn’t work. Any other solution are welcomed.

NOTE : Visibility change event work in case of Android when we call Chrome custom tab from that page.

How to stop audio when I click another image?

I want to play audio when I click to image. But when I click another image are playing both. I need to stop or pause first sound to play the new sound.

I don’t want to use <audio> tag in html as soon as possible; not for something special just to get the challenge.

I am trying this:

HTML:

  <div class="container">
    <div class="box">
      <img id="beach" src="./images/beach.jpeg" alt="" >
    </div>
    <div class="box">
     <img id="river" src="./images/river.jpg" alt="">
    </div>
    <div class="box">
     <img id="mountains" src="./images/mountain.jpg" alt="">
    </div>
    <div class="box">
      <img id="rain" src="./images/rain.jpg" alt="">
    </div>
    <div class="box">
     <img id="jungle" src="./images/jungle.png" alt="">
    </div>
    <div class="box">
      <img id="fire" src="./images/fire.jpeg" alt="">
    </div>
  </div>
Javascript: 

window.addEventListener('load',
    function () {
        let images = document.querySelectorAll("img");
        images.forEach(image =>{
            image.addEventListener("click", playAudio);
                  
        })
        
    }, false);


function playAudio (event){
    
    let imgName = event.currentTarget.id;
    let audioPlay = new Audio(`./sounds/${imgName}.wav`);

    audioPlay.onloadedmetadata = function (eSound){
        
        eSound.currentTarget.loop = true;
        eSound.currentTarget.play()
       
    }
console.log(event)

}

Issue with WhatsApp Cloud API Upload: Receiving “invalid_request” Error due to Incorrect MIME Type

I encountered an issue while trying to upload an image to the WhatsApp Cloud API using Axios. Despite setting the content type of the form data to image/png for an image with a .png extension, I keep receiving an error message from WhatsApp indicating an invalid_request. The error message specifically mentions that the parameter “file” must be a file with one of the specified MIME types, but it’s receiving a file of type “application/octet-stream”.

The error message received from WhatsApp is:

OAuth ‘Facebook Platform’ ‘invalid_request’ ‘(#100) Param file must be a file with one of the following types:
audio/aac, audio/mp4, audio/mpeg, audio/amr, audio/ogg, audio/opus, application/vnd.ms-powerpoint, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/vnd.openxmlformats-officedocument.presentationml.presentation, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/pdf, text/plain, application/vnd.ms-excel, image/jpeg, image/png, image/webp, video/mp4, video/3gpp. Received file of type ‘application/octet-stream’

Here is the screen shot of pipdream post request

Here is code for the node.js file

(async () => {
      const formData = new FormData();
      formData.append("messaging_product", "whatsapp");
      formData.append("type", "image/png");
      const stream = await streamToBlob(
        fs.createReadStream(
          "/filepath"
        )
      );
      formData.append("file", stream);

      console.log("formData", formData);
      let config = {
        method: "post",
        maxBodyLength: Infinity,
        url: `https://graph.facebook.com/v18.0/${phoneNumberId}/media`,
        headers: {
          Authorization:
            `Bearer ${token}`,
          "Content-Type": "multipart/form-data;image/png",
        },
        data: formData,
      };

      axios
        .request(config)
        .then((response) => {
          console.log(JSON.stringify(response.data));
        })
        .catch((error) => {
          console.log(error);
        });
    })();

use Next.js setting `output: ‘export’` with api proxy at local development

I’m using Next.js 14 with the app route. I have set the output to ‘export’ in the Next config file. I would like it to be a static site. I also need a proxy to avoid the cors during the development. Most of the answers to questions about the next.js proxy use rewrites in config.

So, I tried the rewrites API in the config with no luck. Then, I found it would not work with export and rewrites.

Here is the doc link.
https://nextjs.org/docs/pages/building-your-application/deploying/static-exports#unsupported-features

const nextConfig = {
  output: 'export',
  reactStrictMode: true,
  async rewrites() {
    return [
      {
        destination: 'https://my.domain/api/:slug*',
        source: '/api/:slug*',
      },
    ]
  },
  // .......
}

Is there any way I can get the proxy with output: 'export' similar to the CRA proxy?
Thanks.

Bun:HTMLRewriter infinite loop issue

I was using HTMLRewriter to select each element. This works but the issue is it gets stuck in an infinite loop. Now there is also a twist. If log the index directly:

$(".item").each((index, element) => {
    console.log("index", index);
});

It logs:

index 0
index 1

But if I use this and try to log the element content

$(".item").each((index, element) => {
    console.log("index", index);
    console.log($(element).text().trim());
});

It gets stuck in an infinite loop and logs the element value multiple time and also the index gets increased. I am pretty sure it’s an issue with .each() method. Here’s the code of the .each() function:

    each(callback: (index: number, element: string) => void) {
        let index = 0;

        this.beerio.rewriter
            .on(this.selector, {
                element: (element) => {
                    callback(index++, element.tagName);
                },
            })
            .transform(this.beerio.raw_html);

        return this;
    }

Stop React app refreshing when resizing and scrolling

I am using https://www.creative-tim.com/product/material-dashboard-react?AFFILIATE=128200 template and facing a issue when resizing and scrolling the app. It keeps refreshing the component when doing so.

here is the index.js

import React from "react";
import { createRoot } from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import { Auth0Provider } from "@auth0/auth0-react";
import App from "App";
import authConfig from "./auth0-config";
// Material Dashboard 2 React Context Provider
import { MaterialUIControllerProvider } from "context";

const container = document.getElementById("app");
const root = createRoot(container);

root.render(
  <BrowserRouter>
    <Auth0Provider {...authConfig}>
      <MaterialUIControllerProvider>
        <App />
      </MaterialUIControllerProvider>
    </Auth0Provider>
  </BrowserRouter>
);

App.js

/**
=========================================================
* Material Dashboard 2 React - v2.2.0
=========================================================

* Product Page: https://www.creative-tim.com/product/material-dashboard-react
* Copyright 2023 Creative Tim (https://www.creative-tim.com)

Coded by www.creative-tim.com

 =========================================================

* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/

import { useState, useEffect, useMemo } from "react";

// react-router components
import { Routes, Route, Navigate, useLocation } from "react-router-dom";

// @mui material components
import { ThemeProvider } from "@mui/material/styles";
import CssBaseline from "@mui/material/CssBaseline";
import Icon from "@mui/material/Icon";

// Material Dashboard 2 React components
import MDBox from "components/MDBox";

// Material Dashboard 2 React example components
import Sidenav from "examples/Sidenav";
import Configurator from "examples/Configurator";

// Material Dashboard 2 React themes
import theme from "assets/theme";
import themeRTL from "assets/theme/theme-rtl";

// Material Dashboard 2 React Dark Mode themes
import themeDark from "assets/theme-dark";
import themeDarkRTL from "assets/theme-dark/theme-rtl";

// RTL plugins
import rtlPlugin from "stylis-plugin-rtl";
import { CacheProvider } from "@emotion/react";
import createCache from "@emotion/cache";

// Material Dashboard 2 React routes
import routes from "routes";

// Material Dashboard 2 React contexts
import { useMaterialUIController, setMiniSidenav, setOpenConfigurator } from "context";

// Images
import brandWhite from "assets/images/logo-ct.png";
import brandDark from "assets/images/logo-ct-dark.png";

import { BrowserRouter } from "react-router-dom";
import store from "./store/store";
import { useApolloClient } from "./api/gqlClient";
import { ApolloProvider } from "@apollo/client";
import { useAuth0 } from "@auth0/auth0-react";
export default function App() {
  const [controller, dispatch] = useMaterialUIController();
  const {
    miniSidenav,
    direction,
    layout,
    openConfigurator,
    sidenavColor,
    transparentSidenav,
    whiteSidenav,
    darkMode,
  } = controller;
  const [onMouseEnter, setOnMouseEnter] = useState(false);
  const [rtlCache, setRtlCache] = useState(null);
  const { pathname } = useLocation();
  const apolloClient = useApolloClient(store);
  const { isAuthenticated, isLoading, loginWithRedirect } = useAuth0();

  // Cache for the rtl
  useMemo(() => {
    const cacheRtl = createCache({
      key: "rtl",
      stylisPlugins: [rtlPlugin],
    });

    setRtlCache(cacheRtl);
  }, []);

  // Open sidenav when mouse enter on mini sidenav
  const handleOnMouseEnter = () => {
    if (miniSidenav && !onMouseEnter) {
      setMiniSidenav(dispatch, false);
      setOnMouseEnter(true);
    }
  };

  // Close sidenav when mouse leave mini sidenav
  const handleOnMouseLeave = () => {
    if (onMouseEnter) {
      setMiniSidenav(dispatch, true);
      setOnMouseEnter(false);
    }
  };

  // Change the openConfigurator state
  const handleConfiguratorOpen = () => setOpenConfigurator(dispatch, !openConfigurator);

  // Setting the dir attribute for the body element
  useEffect(() => {
    document.body.setAttribute("dir", direction);
  }, [direction]);

  // Setting page scroll to 0 when changing the route
  useEffect(() => {
    document.documentElement.scrollTop = 0;
    document.scrollingElement.scrollTop = 0;
  }, [pathname]);

  const getRoutes = (allRoutes) =>
    allRoutes.map((route) => {
      if (route.collapse) {
        return getRoutes(route.collapse);
      }

      if (route.route) {
        return <Route exact path={route.route} element={route.component} key={route.key} />;
      }

      return null;
    });

  const configsButton = (
    <MDBox
      display="flex"
      justifyContent="center"
      alignItems="center"
      width="3.25rem"
      height="3.25rem"
      bgColor="white"
      shadow="sm"
      borderRadius="50%"
      position="fixed"
      right="2rem"
      bottom="2rem"
      zIndex={99}
      color="dark"
      sx={{ cursor: "pointer" }}
      onClick={handleConfiguratorOpen}
    >
      <Icon fontSize="small" color="inherit">
        settings
      </Icon>
    </MDBox>
  );

  if (!isAuthenticated && !isLoading) {
    loginWithRedirect();
    return null;
  }
  return isAuthenticated ? (
    <ThemeProvider theme={darkMode ? themeDark : theme}>
      <ApolloProvider client={apolloClient}>
        <CssBaseline />
        {layout === "dashboard" && (
          <>
            <Sidenav
              color={sidenavColor}
              brand={(transparentSidenav && !darkMode) || whiteSidenav ? brandDark : brandWhite}
              brandName="Material Dashboard 2"
              routes={routes}
              onMouseEnter={handleOnMouseEnter}
              onMouseLeave={handleOnMouseLeave}
            />
            <Configurator />
            {configsButton}
          </>
        )}
        {layout === "vr" && <Configurator />}
        <Routes>
          {getRoutes(routes)}
          <Route path="*" element={<Navigate to="/dashboard" />} />
        </Routes>
      </ApolloProvider>
    </ThemeProvider>
  ) : null;
}

And the sideNav contains logic on resizing

useEffect(() => {
    // A function that sets the mini state of the sidenav.
    function handleMiniSidenav() {
      setMiniSidenav(dispatch, window.innerWidth < 1200);
      setTransparentSidenav(dispatch, window.innerWidth < 1200 ? false : transparentSidenav);
      setWhiteSidenav(dispatch, window.innerWidth < 1200 ? false : whiteSidenav);
    }

    /** 
     The event listener that's calling the handleMiniSidenav function when resizing the window.
    */
    window.addEventListener("resize", handleMiniSidenav);

    // Call the handleMiniSidenav function to set the state with the initial value.
    handleMiniSidenav();

    // Remove event listener on cleanup
    return () => window.removeEventListener("resize", handleMiniSidenav);
  }, [dispatch, location]);

Is it possible to pass an array via cucumber datatable?

I have an e2e test that will in part, check that various elements are visible on the page.

Currently the feature file has a test that looks like

Scenario Outline: Logged in user goes to home page
    Given I'm logged in on <device>
    When I am on the home page
    Then I should see X element is visible
    And I should see Y element is visible
    And I should see Z element is visible
    And ...

Examples:
    | device    |
    | "desktop" |
    | "tablet"  |
    | "mobile"  |

where for example the element checks look something like

And("Element X is visible", () => {
  getElementX().should("be.visible");
});

Would it be possible to consolidate all of the I should see ... steps into one and pass an array of elements that I want to check are visible within the datatable?

Scenario Outline: Logged in user goes to home page
    Given I'm logged in on <device>
    When I am on the home page
    Then I should see <elements> is visible

Examples:
    | device    | elements  |
    | "desktop" | [x, y, z] |
    | "tablet"  | [x, y, z] |
    | "mobile"  | [x, y, z] |

With something like

const elementChecks = {
    getElX = () => getElementX().should("be.visible"),
    ...
};

Then("I should see elements {array}", (elements: string[]) => {
    elements.forEach((el: string) => elementChecks?.['getEl' + el.toUpperCase()]);
});

I’ve not tried this just yet, just a hypothetical.

Create new Object by reading other object with recursive values

I have a object obj1 which has mapping information which is present in other as values vals. I want to create new object by reading both and with mapped values

const obj1 = [
  {
    mapping: "cities",
  },
  {
    mapping: "category",
    children: {
      mapping: "type",
      children: {
        mapping: "otherType",
      },
    },
  },
  {
    mapping: "age",
  },
];

const vals = [
  {
    mapping: "category",
    values: [{}],
  },
  {
    mapping: "type",
    values: [{}],
  },
  {
    mapping: "otherType",
    values: [{}],
  },
  {
    mapping: "cities",
    values: [{}],
  },
  {
    mapping: "age",
    values: [{}],
  },
];


I want expected data in below fromat


const exectedData = {
    cities: {
        values: [{}],
    },
    category: {
        values: [{}],
        children: {
            type: {
                values: [{}],
                children: {
                    otherType: {
                        values: [{}],
                    }
                }
            }
        }
    },
    age: {
        values: [{}]
    }
}