Send Livewire data from component to JS library

I have a livewire component that incorporates a js library, gridstack.js.

I’ve never tried to incorporate an external js library without a livewire wrapper before. Caleb made it so easy to implement livewire/sortable since I didn’t have to write any js. I’m trying to use Gridstack.js as if I were using Shopify/draggable directly with livewire without using livewire/sortable.

This is what I have so far and my interactive grid loads correctly. I’m able to adjust gridstack items on the page and save them to the database via $wire.taskMoved(newItems) in the @script, all as expected. I need to be able to display a new or edited $task that’s created with livewire. Right now I need refresh the page to see any edited or added $task that was created in the backend.

I started to experiment with this below with the following code. It works, everytime I edit or add a task I receive a console log but I’m not sure what to put here from the gridstack api to have those items updated.

document.addEventListener('taskAdjusted', () => {
   console.log('HERE after taskAdjusted')
})

I also tried to add wire:key="project-{{$project->id}}" in the $projects foreach and wire:key="task-{{$task->id}}" in the $tasks foreach but it didnt work so I found that wire:ignore on the div works best.

<div>
    @foreach($projects as $project)
        <div class="grid-stack gs-id-{{$project->id}}" id="{{$project->id}}" wire:ignore>
            @foreach($days as $day_index => $day)
                @foreach($day->tasks as $task)
                    <div class="grid-stack-item" gs-x="{{$day_index}}" gs-w="{{$task->duration}}" gs-y="1" gs-id="{{$task->id}}">
                        <div
                            wire:click="$dispatchTo('tasks.task-create', 'editTask', { task_id: {{$task->id}} })"
                            class="grid-stack-item-content"
                            >
                            <span>{{$task->title}}</span>
                        </div>
                    </div>
                @endforeach
            @endforeach
        </div>
    @endforeach

    <livewire:tasks.task-create :projects="$projects" :days="$days"/>
</div>

@assets
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gridstack.js/10.1.2/gridstack-all.js" defer></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/gridstack.js/10.1.2/gridstack.min.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/gridstack.js/10.1.2/gridstack-extra.min.css">
@endassets

@script
    <script>
        let grids = GridStack.initAll({
            column: 6,
            cellHeight: '60px',
            cellWidth: '100px',
            float: false,
            resizable: {
                handles: 'e'
            },
            margin: 2
        })

        grids.forEach(grid => {
            grid.on('change', function(event, items) {
                let newItems = []

                items.forEach ((el) => {
                    newItems.push({_id: el._id, x: el.x, y: el.y, w: el.w, task_id: el.id})
                });

                $wire.taskMoved(newItems)
            })
        })

        document.addEventListener('taskAdjusted', () => {
            console.log('HERE after taskAdjusted')
        })
    </script>
@endscript

This is a follow up question to a previous one that you guys were able to help me with, Thank you!

What am I doing wrong on this dark mode button?

I started coding today and I’m trying to make a simple static dark mode button, but the background of the button doesn’t change his color, i already fixed one thing on the line 36 on CSS it was “–bg: var(–gren);” but the screen would be black when i clicked (https://i.stack.imgur.com/CYUB4.png):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>site</title>
    <link rel="stylesheet" href="main.css">
</head>
<body class="light-theme">
    <h1>Task List</h1>
    <p id="msg">Current tasks:</p>
    <ul>
        <li class="list">Add visual styles</li>
        <li class="list">Add light and dark themes</li>
        <li> Enable switching the theme</li>
    </ul>
    <div>
        <button class="btn">Dark</button>
    </div>
    <script src="app.js" defer></script>
    <noscript>You need to enable JavaScript to view the full site.</noscript>
</body>
</html>

body {
    font-family: monospace;
}
ul {
    font-family: Arial, Helvetica, sans-serif;
}

li {
    list-style: circle;
}

.list {
    list-style: square;
}

.light-theme {
    color: black;
    background: #00FF00;
    
}

:root {
    --green: #00FF00;
    --white: #ffffff;
    --black: #000000;
}

.light-theme {
    --bg: var(--green);
    --fontColor: var(--black);
    --btng: var(--black)
    --btnFontColor: var(--white)
}

.dark-theme {
    background: var(--black);
    --fontColor: var(--green);
    --btnBg: var(--white)
    --btnFontColor: var(--black)
}

* {
    color: var(--fontColor);
    font-family: Arial, Helvetica, sans-serif;
}

body  {
    background: var(--bg);
}

.btn {
    color: var(--btnFontColor);
    background-color: var(--btnBg);
}

.btn {
    position: absolute;
    top: 20px;
    left: 250px;
    height: 50px;
    width: 50px;
    border-radius: 50%;
    border: none;
    color: var(--btnFontColor);
    background-color: var(--btnBg);
}


.btn:focus { outline: none; }




 'use scrict'

const switcher = document.querySelector('.btn');

switcher.addEventListener('click', function() {
    document.body.classList.toggle('dark-theme')

    var className = document.body.className;
    if(className == "light-theme") {
        this.textContent = "Dark";
    }   
    else {
        this.textContent = "Light"
    }

    console.log('current class name: ' + className);
});
enter image description here

how it was supposed to be: (https://i.stack.imgur.com/SGUMf.png)

Open a new HTML page in a JS function and then write some HTML on it when the previous html is a login page

hi im trying to make a login page that after you push the button it goes to the index. I did that but now i want to change part of the html to say welcome back ( and the name of the username)

i’ve wrote this so far but i dont know how to save the username so i can use it again in the next page.

this is the html:

<form id="login-form">
<!--Error Message-->
        <div id="login-error-msg-holder">
      <p id="login-error-msg">Invalid username <span id="error-msg-second-line">and/or password</span></p>
    </div>

<!--Form-->
    <div class="form-floating">
      <input type="name" class="form-control name " name="name" id="floatingInput" id="name-field" placeholder="user123">
      <label for="floatingInput">Username</label>
    </div>
    <div class="form-floating">
      <input type="password" id="password-field" class="form-control" id="floatingPassword" name="password" placeholder="Password">
      <label for="floatingPassword">Password</label>
    </div>

    <div class="form-check text-start my-3">
      <input class="form-check-input" type="checkbox" value="remember-me" id="flexCheckDefault">
      <label class="form-check-label" for="flexCheckDefault">
        Remember me
      </label>
    </div>
    <button class="btn w-100 py-2" id="login-form-submit" value="Login" type="submit">Sign in</button>
    <p class="mt-5 mb-3 text-body-secondary">&copy; 2017–2024</p>
  </form>
</main>

And this is the js:

window.onload=loginButton.addEventListener("click",(e) => {
    e.preventDefault();
    const name = loginForm.name.value;
    const password = loginForm.password.value;

    if (name === "user123" && password === "1") {
        window.location.href = 'index.html';

    } else {
        loginErrorMsg.style.opacity = 1;
    }
})

function message(){

        if (window.history='Login.html' ) {
        document.getElementById('navlogin').innerHTML='Welcome back'+ name + '!';
        }
        
        else{
            document.getElementById('navlogin').innerHTML='';
        }
}

i tried using the console.log but i cant save it for a little time so i can use it later.

any ideas?

How to show a data-toggle class with ajax and php

I have difficulties with the legagy code. I need to implement the function that opens de data-toggle, I have include parsedResponse in the verify data, since is the responsible to show the response of info. But it’s not working.. sobody help?

function checkInfo(input_data) {
var requestData = {
data: input_data
};
var info = input_data;
$.ajax({
url: “/endpoint/search”,
type: ‘POST’,
data: requestData,
success: function(response) {
$(‘.search_result’).html(response);
process_contacts();
process_opportunities();
verify_data(info, function(parsedResponse){
if ($(‘#collapseOne’).hasClass(‘collapse’) && !($(‘#collapseOne’).hasClass(‘show’)) && parsedResponse.success === true){
$(‘.open_contact’).click();
console.log(parsedResponse.success);
}
});
}
});
}

function verify_data(data) {
$.ajax({
url: ‘/endpoint/verify’,
type: ‘POST’,
dataType: ‘json’,
data: {
data: data
}
})
.done(function(response) {
var parsedResponse = $.parseJSON(response);
if (parsedResponse) {
$(‘.save_info’).removeAttr(‘disabled’);
} else {
$(‘.save_info’).attr(‘disabled’, ‘disabled’);
}
return parsedResponse;
})
}

I expect to be able to reach the parsedResponse as true and do the click event.

The preferred way to place content inside a custom element as an input to the component?

I’m trying to develop a syntax highlighting web component and it will take content placed inside it and syntax highlight it. So something like this:

<fs-highlight data-line-numbers="true" data-language="html">
   any content here ....
</fs-highlight>

This is a prototype implementation and it gets and highlights the content within the element like this:

this._code = hljs.highlight(this.innerHTML.replace(/^s+/g, ''), {
        language: this.language,
      }).value;

And I’m wondering whether it’s correct / fine to use this.innerHTML to grab the content?

The implementation is not using a slot, so the content is not rendered within the element.

However in this demo, vite fails to compile the element. However I’m wondering if that’s just due to how the Stackblitz vite compilation works …(I put a non trivial amount of markup within the element to test it out…)?

Perhaps with local compilation and publishing this goes away … Thoughts?

Vue: ref does not create a reactive property

TestPage.vue:

<template>

  <div>
    <TestChild :dog = "this.parentDog"  />

    <div v-on:click="this.changeName" id="change-button">
      Change Parent Dog name
    </div>
  </div>

</template>

<script>
 import TestChild from "@/test/TestChild";
 export default {
  components: {TestChild },

  methods:  {
    changeName()  {
      this.parentDog.name = "Changed First Name";
      alert("Changed!");
    }
  },

   data() {
     return {
       parentDog: {
         name: "First Dog"
       }
     };
   },
};

</script>

<style>

#change-button  {
  margin: 2em;
  padding: 1em;

  background-color: green;
}

</style>

TestChild.vue:

<template>

  <div>{{this.betterDog.name}}</div>

</template>

<script>
import {ref} from 'vue';

export default {
  name: "TestChild",

  props:  {
    dog: Object
  },

  data() {
    return {
      betterDog: Object
    };
  },

  created() {
    this.betterDog = {};
    this.betterDog.name = ref(this.dog.name);
  }
};
</script>

The output before the green button is clicked:

enter image description here

The data for the TestPage:
enter image description here
The data for the TestChild:
enter image description here

The names of parentDog in TestPage and betterDog in TestChild are the same.

After the green button is clicked, the page output doesn’t change. The data changes as follows:

The data for the TestPage:
enter image description here
The data for the TestChild:
enter image description here

So after the button is clicked, the dog prop in the TestChild component reflects the change, but the betterDog data does not, despite using ref.

How do I make betterDog.name reactive as well? Without using watch on dog prop.

Is there a way in Javascript to modify the clipboard without the user clicking a button?

I’m working on a small script to streamline the process of re-formatting pasted text, particularly phone numbers. My goal is to paste the text into a textbox and have the formatted result automatically copied to my clipboard to minimize manual clicks.

The function to modify the clipboard only works when a real click event occurs. I’ve attempted various approaches such as programmatically calling .click(), dispatching a “click” event, utilizing async/await, and promises

JSFiddle Code:
https://jsfiddle.net/ByteBender/7bukf5xh/9/

document.getElementById('myInput').addEventListener('paste', event => {
    const contents = event.clipboardData.getData('text')
    if (contents.length >= 9) {
        event.preventDefault()
        const formattedNumber = formatPhoneNumber(contents)
        myInput.value = formattedNumber
        copyToClipboard()
        
      // Also failed to copy
      //myButton.click()
      // Also failed to copy
      //setTimeout( () => myButton.dispatchEvent(new Event('click')), 500 )
    }
});

function copyToClipboard() {
  var copyText = document.getElementById("myInput");
  copyText.select();
  copyText.setSelectionRange(0, 99999); // For mobile devices
  navigator.clipboard.writeText(copyText.value)
    .then(()=> console.log('success!'))
    .catch(err=>console.error(`fail: ${err}`))
}


// Also failed to copy
/* function tempButton(){
  var button = document.createElement('button');
  button.innerHTML = 'click me';
  button.onclick = function(){
    copyToClipboard();
        return false;
  };
  document.body.appendChild(button);
  //button.click()
  
} */

Invariant Violation: A state mutation was detected between dispatches, in the path

I would like to concatenate one item in the array with another one and remove the second item from the array. When I tried the old way, I get the state mutation detected error. When I tried the Object.assign, I am unable to get the values concatenated.

EDIT:
What is the equivalent of interests[makeIndex].value = ``${interests[makeIndex].value}, ${interests[secondPreferredMakeIndex].value}`` using Object.assign?

For e.g., for the below code segment the output I expect is,

Preferred Make - before Chevrolet
Preferred Make - after Chevrolet, Porsche
// Interests array from state
let interests = [
  {
    type: 'Preferred Make',
    value: 'Chevrolet',
  },
  {
    type: 'Second Preferred Make',
    value: 'Porsche',
  },
  {
    type: 'Preferred Model',
    value: 'Corvette',
  },
  {
    type: 'Second Preferred Model',
    value: 'Macan',
  }
];

console.log("Preferred Make - before", interests[0].value);

const secondPreferredMakeIndex = interests
  .map(x => x.type)
  .indexOf('Second Preferred Make');

if (secondPreferredMakeIndex > -1) {
  let makeIndex = interests.map(x => x.type).indexOf('Preferred Make');

  if (makeIndex > -1) {
    // For the below, I get mutation error. But it works
    // interests[makeIndex].value = `${interests[makeIndex].value}, ${interests[secondPreferredMakeIndex].value}`;

    // Concatenate and use Object.assign to avoid mutation
    interests = Object.assign([], interests, { makeIndex: `${interests[makeIndex].value}, ${interests[secondPreferredMakeIndex].value}` });
    /*
    // Tried the below as well in vain
    interests = Object.assign([], interests, {
      makeIndex: {
        type: 'Preferred Make',
        value: `${interests[makeIndex].value}, ${interests[secondPreferredMakeIndex].value}`
      },
    });
    */
  }

  // Delete the second Preferred Make
  interests.splice(secondPreferredMakeIndex, 1);
}

console.log("Preferred Make - after", interests[0].value);

Appreciate the helps

Timing and direction of keyframe animation for SVG

I have an svg that I’m using CSS keyframe on in order to animate it at a certain point and it animates, but the issue is that I need to time out the animations as well as make sure they ‘draw’ in the right direction. The ‘normal’ paths on the right need to draw left to right and the ‘reverse’ paths on the left need to draw right to left (this should end up like the lines, from top to bottom, are drawing center->out.

My issue is that I can’t seem to get a delay working on them so that they can draw properly. If I set the reverse keyframe to draw for a negative value of the path length, it draws the path all the way to the right before the right paths even show so that won’t work.

How can I make sure the reverse paths draw properly but also time the paths out and have the ability to adjust them as needed?

    document.addEventListener("DOMContentLoaded", function () {
        var drawingStarted = false;
        var lastScrollTop = 0;
        const normalPath1  = document.querySelector("#footerline1");
        const reversePath1 = document.querySelector("#footerline2");
        const normalPath2  = document.querySelector("#footerline3");
        const reversePath2 = document.querySelector("#footerline4");
        const normalPath3  = document.querySelector("#footerline5");
        const reversePath3 = document.querySelector("#footerline6");
        const normalPath4  = document.querySelector("#footerline7");
        const reversePath4 = document.querySelector("#footerline8");


        window.addEventListener('message', function (event) {

                var footerscrollpercent = event.data.value;
                
                // REMOVE DEBUG
                footerscrollpercent = 1;
                if (!drawingStarted && footerscrollpercent > 0.9) {
                    // Function to add class with delay
                    const addClassWithDelay = (element, className, delay) => {
                        setTimeout(() => {
                            element.classList.add(className);
                        }, delay);
                    };

                    // Add classes with delay
                    addClassWithDelay(normalPath1, 'path-test', 5000);
                    addClassWithDelay(reversePath1, 'path-test-reverse', 2000); // Delay of 5ms
                    addClassWithDelay(normalPath2, 'path-test', 0);
                    addClassWithDelay(reversePath2, 'path-test-reverse', 5); // Delay of 5ms
                    addClassWithDelay(normalPath3, 'path-test', 0);
                    addClassWithDelay(reversePath3, 'path-test-reverse', 5); // Delay of 5ms
                    addClassWithDelay(normalPath4, 'path-test', 0);
                    addClassWithDelay(reversePath4, 'path-test-reverse', 5); // Delay of 5ms
                }

            // }
        });
    });
.path-test {
    stroke-dasharray: 1000;
    stroke-dashoffset: 1000;
    animation: dash 5s linear forwards;
}

@keyframes dash {
    from {
            stroke-dashoffset: 1000;
            /* Start with full dash length */
        }
    to {
        stroke-dashoffset: 0;
    }
}

.path-test-reverse {
    stroke-dasharray: 1000;
    stroke-dashoffset: 1000;
    /* Start with the dash fully visible */
    animation: reverse-dash 5s linear forwards;
    /* Use the reverse-dash animation */
}

@keyframes reverse-dash {
    from {
        stroke-dashoffset: -10;
        /* Start with full dash length */
    }

    to {
        stroke-dashoffset: 0;
        /* Gradually offset the dash to 0 */
    }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<svg viewBox="0 0 758.57 337.81">
                <defs>
                    <style>
                        .complexfooter-1 {
                            stroke-width: 12.67px;
                        }
                        .complexfooter-1,
                        .complexfooter-2 {
                            fill: none;
                            stroke: #072334;
                            stroke-miterlimit: 10;
                        }
                        .complexfooter-3 {
                            fill: #072334;
                            stroke-width: 0px;
                        }
                        .complexfooter-2 {
                            stroke-linecap: square;
                            stroke-width: 23.8px;
                        }
                    </style>
                </defs>
                <path  id="footerline1" fill="white" stroke="black" stroke-width="4" class="normal-draw complexfooter-2"
                   d="M381.22,12.6c14.3.44,29.45.94,30.7.94l-12.26-1.5c2.36,0,4.95-.3,7.28-.03,2.08.24,3.02,1.17,4.97,1.54,5.7,1.08,12.66-.05,18.47-.05,17.5,0,35,0,52.51,0,19.74,0,39.31,1.02,59.01,1.39,4.79.09,9.24,1.49,14.02,1.54,2.68.03,5.78-.51,8.4.03,3.97.82,8.32,4.68,11.55,6.88,3.74,2.54,6.2,5.18,9.38,8.36,4.39,4.39,9.92,7.61,13.85,12.46,1.65,2.04,2.36,3.74,2.94,6.21,1.52,6.39,5.13,11.72,8.74,17.24,3.01,4.6,5.11,9.51,7.8,14.28,2.67,4.74,6.14,8.8,8.66,13.65,3.61,6.95,8.33,12.29,13.02,18.55,5.73,7.65,10.65,15.86,16.39,23.5,5.34,7.11,8.98,15.31,13.71,22.84,4.31,6.86,8.91,13.66,13.02,20.64s7.07,14.29,11.67,20.55c3.72,5.06,8.81,9.82,13.07,14.48,4.52,4.93,7.99,10.11,11.58,15.77,3.8,6,8.11,11.53,11.65,17.71,4.28,7.47,8.57,14.94,12.61,22.54,2.29,4.3,2.69,12.16,2.7,17.09" />
                <path  id="footerline2" fill="white" stroke="black" stroke-width="4" class=" reverse-draw complexfooter-2" fill="white" stroke="black" stroke-width="4"
                    d="M11.91,286.97c0-4.93,4.27-10.56,6.56-14.86,4.05-7.6,8.34-15.07,12.61-22.54,3.53-6.18,7.84-11.71,11.65-17.71,3.59-5.66,7.06-10.83,11.58-15.77,4.27-4.66,9.36-9.41,13.07-14.48,4.6-6.26,7.74-13.89,11.67-20.55,4.11-6.98,8.71-13.78,13.02-20.64,4.73-7.53,8.37-15.73,13.71-22.84,5.74-7.64,10.66-15.85,16.39-23.5,4.69-6.26,9.41-11.6,13.02-18.55,2.52-4.85,5.99-8.91,8.66-13.65,2.69-4.77,4.79-9.68,7.8-14.28,3.61-5.52,7.22-10.85,8.74-17.24.59-2.47,1.29-4.17,2.94-6.21,3.93-4.85,9.46-8.07,13.85-12.46,3.18-3.18,5.64-5.82,9.38-8.36,3.23-2.19,7.58-6.06,11.55-6.88,2.62-.54,5.72,0,8.4-.03,4.78-.05,9.23-1.45,14.02-1.54,19.7-.37,39.27-1.39,59.01-1.39,17.5,0,35,0,52.51,0,5.82,0,12.78,1.14,18.47.05,1.95-.37,2.89-1.3,4.97-1.54,1.1-.12,13.08.21,25.73.59" />
                <path  id="footerline3" fill="white" stroke="black" stroke-width="4" class="complexfooter-2"
                    d="M378.92,74.83c25.06.73,53.61,1.51,55.29.43,0,0-4.69,0,0,0,18.33.01,10.99,0,29.32,0,6.86,0,13.71.05,20.57-.03,5.58-.07,10.85,1.48,16.39,1.6,2.97.07,5.96-.4,8.89.21,9.14,1.89,17.67,6.32,23.72,13.25,3.96,4.54,8.26,8.94,12.4,13.32,3.12,3.29,5.41,7.06,8.27,10.57,7.73,9.47,14.82,19.88,21.08,30.45,4.39,7.41,8.11,15.17,12.64,22.49,5.84,9.44,11.28,20.06,18.07,28.81,5.62,7.26,9.76,15.31,14.41,23.26,7.04,12.03,14.28,23.66,22.44,34.98,6.67,9.25,12.92,18.72,19.3,28.16,4.92,7.28,13.14,14.47,11.65,23.91"/>
                <path  id="footerline4" fill="white" stroke="black" stroke-width="4" class="complexfooter-2"
                    d="M89.06,306.23c-1.49-9.43,6.73-16.62,11.65-23.91,6.38-9.44,12.63-18.92,19.3-28.16,8.16-11.31,15.4-22.95,22.44-34.98,4.66-7.96,8.79-16.01,14.41-23.26,6.79-8.76,12.23-19.37,18.07-28.81,4.53-7.32,8.25-15.08,12.64-22.49,6.26-10.57,13.36-20.98,21.08-30.45,2.86-3.51,5.15-7.27,8.27-10.57,4.14-4.38,8.44-8.78,12.4-13.32,6.05-6.94,14.58-11.37,23.72-13.25,2.93-.6,5.92-.14,8.89-.21,5.54-.12,10.81-1.67,16.39-1.6,6.86.09,13.71.03,20.57.03,18.33,0,10.99.01,29.32,0,4.69,0,8.59-.15,12.95-1.17,1.47-.35,18.59.19,37.76.75"/>
                <path  id="footerline5" fill="white" stroke="black" stroke-width="4" class="complexfooter-2"
                    d="M381.22,132.42c-26.3,0-56.96,0-61.4,0-5.9,0-12.03-.49-17.9-.06-3.02.22-5.93,1.32-8.97,1.94-3.3.67-6.74.74-9.47,2.2-2.17,1.16-4.65,3.01-6.58,4.47-5.98,4.52-8.82,11.16-14.19,15.68-2.84,2.38-3.99,5.49-6.03,8.37-2.91,4.11-5.92,8.05-8.47,12.44-5.17,8.9-8.84,18.4-13.47,27.54-2.29,4.52-5.74,8.16-8.16,12.57-5.76,10.51-10.63,22.21-15.13,33.31-3.97,9.8-8.46,18.38-14.44,27.06-4.89,7.1-18.8,34.09-17.53,43.23"/>
                <path  id="footerline6" fill="white" stroke="black" stroke-width="4" class="complexfooter-2"
                    d="M578.38,317.03c1.27-9.14-8.07-31.99-12.96-39.08-5.98-8.67-10.47-17.25-14.44-27.06-4.5-11.1-9.37-22.8-15.13-33.31-2.42-4.41-5.87-8.05-8.16-12.57-4.63-9.14-8.3-18.64-13.47-27.54-2.55-4.39-5.56-8.32-8.47-12.44-2.04-2.88-3.19-5.99-6.03-8.37-5.37-4.51-8.21-11.15-14.19-15.68-1.93-1.46-4.41-3.31-6.58-4.47-2.72-1.45-6.17-1.53-9.47-2.2-3.04-.62-5.95-1.71-8.97-1.94-5.87-.43-12.01.06-17.9.06-7.62,0-15.24,0-22.87,0-3.18,0-19.74,0-38.54,0"/>
                    <path  id="footerline7" fill="white" stroke="black" stroke-width="4" class="complexfooter-2"
                    d="M378.95,195.39c25.94-.12,51.45.02,55.27.02l-15.7-.8c7.82,0,16.53.19,23.52,2.54,3.82,1.29,7.21,2.52,9.5,6.08,2.99,4.66,4.92,10.38,7.56,15.3,5.14,9.56,11.6,17.28,14.57,28,2.37,8.53,6.51,15.55,10.15,23.52,1.27,2.77,1.77,5.83,3.08,8.56,1.39,2.89,3.62,5.66,5.22,8.41,4.96,8.54,7.97,28.36,7.6,38.44"/>
                    <path  id="footerline8" fill="white" stroke="black" stroke-width="4" class="complexfooter-2"
                    d="M262.68,324.92c-.37-10.08,2.68-29.36,7.63-37.9,1.6-2.75,3.83-5.52,5.22-8.41,1.31-2.73,1.82-5.79,3.08-8.56,3.64-7.98,7.78-15,10.15-23.52,2.98-10.73,9.44-18.44,14.57-28,2.64-4.92,4.57-10.63,7.56-15.3,2.29-3.57,5.68-4.8,9.5-6.08,3.58-1.21,31.3-1.63,58.54-1.76"/>
            </svg>

Title is not showing when uploading a video to TikTok via api

I am creating videos on TikTok using their Api. With the video, I also provide a title. The video is showing up in my Inbox for approval as expected, but the title is not showing. Instead I only get a HashTag of the name of the App I am using.

I checked the code a few times and made sure the json structure is correct. What am I missing?

 return await fetch('https://open.tiktokapis.com/v2/post/publish/inbox/video/init/', {
        method: 'POST',
        headers: {
            'Authorization': `Bearer ${accessToken}`,
            'Content-Type': 'application/json; charset=UTF-8'
        },
        body: JSON.stringify({
            post_info: {
                privacy_level: 'PUBLIC_TO_EVERYONE',
                title: "Hello #world",
            },
            source_info: {
                source: 'PULL_FROM_URL',
                video_url: `THE VIDEO URL`
            }
        })
    })

Circuimventing third party cookie blocking

Outer website: outer.com; my website: my.com

Outer website wants to integrate my website using iframe.

I need to integrate auth as well. They use OpenID Connect or something like that.

When I tried to auth inside iframe using redirects, it didn’t work. Their auth logics using intermediate cookie and it was not saved.

Right now to authenticate user, I’m using code like parent.location.href = "outer.com/auth/redirect=my.com/accept-auth". After authentication I’m receiving request from the user, creating session cookie and issuing redirect to “outer.com”. outer.com loads my.com in the iframe and sends cookie set earlier. Now my website inside iframe is authorized and works well.

Please note that URL in the user browser changes in the following way: outer.com -> outer.com/auth/... -> my.com/accept-auth -> outer.com

Initially I had a problem: cookie set after redirect was not sent when page loaded inside iframe.

I solved this problem using SameSite=None cookie attribute.

Right now the website works well in the Chrome.

However after testing in Firefox and Chrome Incognito mode it turned out that this is not enough. Cookie is not send inside iframe.

After I did research, it turned out to be caused by a feature called “Third-party cookie blocking”.

I found an advice to use “Partitioned” cookie attribute. However further testing revealed that this approach does not work. It allows iframe to set cookie “inside”, however if cookie was set from the outside, it still is not accessible inside iframe.

I also tried localStorage as alternative way to store session id, but it didn’t work as well. It seems that browser maintains a separate localStorage object for iframe to prevent sharing data.

How do I proceed from there? I can ask user to disable this feature, but I’d like a cleaner solution.

Tick function in angular seems not working

I have simple test of mat-checkbox wrapper in Angular. I want to check if mat-ripple disappeared after blur event. Looks like fakeAsync in that case doesn’t work

it('test for checkbox', fakeAsync(() => {
  expect(fixture.nativeElement.querySelectorAll('.mat-ripple-element').length)
      .toBe(0, 'NO RIPPLE');

  // DISPATCH PART
  dispatchFakeEvent(inputElement, 'keydown');
  dispatchFakeEvent(inputElement, 'focus');

  tick(5000);

  expect(fixture.nativeElement.querySelectorAll('.mat-ripple-element').length)
      .toBe(1, 'RIPPLE VISABLE');

  dispatchFakeEvent(checkboxInstance._inputElement.nativeElement, 'blur');
  tick(15000); // here in normal after blur mat-ripple-element disappeared, but in test no

  expect(fixture.nativeElement.querySelectorAll('.mat-ripple-element').length)
      .toBe(0, 'RIPPLE NOT VISABLE');
}));

To check it I create simple function directly in component to trigger actions:

code from component

testActionMethod(){
  const input = document.querySelector('input');
  if (input) {
      const event1 = new KeyboardEvent('keydown');
      const event2 = new KeyboardEvent('focus');
      input.dispatchEvent(event1);
      input.dispatchEvent(event2);

      const classx = document.querySelectorAll('.mat-ripple-element')
      console.log(classx,'.mat-ripple-element exist?') // shows that exist

      const event3 = new KeyboardEvent('blur');
      input.dispatchEvent(event3);

      const classx12 = document.querySelectorAll('.mat-ripple-element')
      console.log(classx12,'mat-ripple-element exist?') // shows that exist

      setTimeout(() => {
          const classx1 = document.querySelectorAll('.mat-ripple-element')
          console.log(classx1,'mat-ripple-element exist?') //shows that doesn't exist
      },5000)
}

Of course after blur event when I just click element it works <mat-ripple-element does disappeared after blur, in settimeout also, but in test no>

anyone knows why it can behave like that?

How to check if an attribute that belongs to multiple elements has one of several values

I’m trying to test whether an attribute that is shared by 3 elements has one of several values. Here’s the html:


    <div class="wrapper">
      <a title="value 1"</a> 
      <a title="value 2"</a> 
      <a title="value 3"</a> 
    </div>

I need a way to assert that the title attribute of all three elements will have one of the 3 values, because their order might change in the future but I just need to make sure that they’re there.

Here’s what I’ve tried but didn’t work for me:

cy.get('.wrapper')
    .children().as('socialLinks');

cy.get('@socialLinks')
    .should('have.attr', 'title').as('title');

expect('@title').to.contain.oneOf('value 1', 'value 2', 'value 3');

I suspect it might need a for-loop but I can’t wrap my head around how to approach it.