Get width/height of pixels array in Uint8ClampedArray

I am using TypeScript on Deno. I am trying to use blurhash.

This is my code:

const response = await fetch(url);
if (!response.body) {
throw new Error("Body null");
}

const clamped = new Uint8ClampedArray(await response.arrayBuffer());
var blurhash: string = encode(clamped, width, height, 4, 4,);

where width and height is known beforehand. But I get this error:

Width and height must match the pixels array

I read this issue and it seems like the alpha channel is missing or something. The problem is, that all solutions are fixing it by using sharp with ensureAlpha(). But sharp is not running on Deno, so I can’t use it.

Does anyone know how I could fix this?

Data fetched in Client Component not showing in Nexjs 14 when deployed

I am using Nextjs 14 client component to fetch and render data from my headless wordpress cms, this works in my local environment, however, when I deployed the app on aws amplify. The data does not show. Please i would like to know why this is happening and to know a better oh handling this. Here is my code below.

'use client'

function Faqs() {
  const [data, setData] = useState([]);
  const [search, setSearch] = useState(null);
  const [searchResults, setSearchResults] = useState([]);
  const [activeState, setActiveState] = useState({ title: null, index: null });
  const [isSearchPerformed, setIsSearchPerformed] = useState(false);
  const [searchQuery, setSearchQuery] = useState("");
  const [searchWords, setSearchWords] = useState([]);

  const searchResultsRef = useRef(null);

  useEffect(() => {
    async function getFaqData() {
      let receivedData = await getFaqs();
      console.log(receivedData);

      const flattenData = receivedData.flatMap((edge) =>
        edge.node.cta.questionAndAnswer.map((qa) => ({
          id: `${edge.node.id}`,
          title: edge.node.title,
          question: qa.question,
          answer: qa.answer,
        }))
      );
      console.log(flattenData);
      const newSearch = new JsSearch.Search("id");
      newSearch.indexStrategy = new JsSearch.PrefixIndexStrategy();
      newSearch.sanitizer = new JsSearch.LowerCaseSanitizer();
      newSearch.searchIndex = new JsSearch.TfIdfSearchIndex("id");

      // Add indices and documents
      newSearch.addIndex("question");
      newSearch.addIndex("answer");
      newSearch.addDocuments(flattenData);

      // Update state with new search instance
      setSearch(newSearch);
      setData(receivedData);
    }
    getFaqData();
  }, []);

  useEffect(() => {
    setIsSearchPerformed(false);
  }, [searchQuery]);

  const performSearch = () => {
    setIsSearchPerformed(true);
    if (searchQuery === "") {
      setSearchResults([]);
      setIsSearchPerformed(false);
    } else {
      const words = searchQuery.trim().toLowerCase().split(/s+/);
      setSearchWords(words); // Update searchWords state

      let combinedResults = new Set();
      words.forEach((word) => {
        const results = search.search(word);
        results.forEach((result) => combinedResults.add(result));
      });

      setSearchResults(Array.from(combinedResults));

      if (searchResultsRef.current) {
        searchResultsRef.current.scrollIntoView({ behavior: "smooth" });
      }
    }
  };

  function highlightSearchTerm(text, searchWords) {
    let highlightedText = text;
    for (const word of searchWords) {
      if (word.trim() === "") continue;
      const escapedWord = word.replace(/[-/\^$*+?.()|[]{}]/g, "\$&");
      highlightedText = highlightedText.replace(
        new RegExp(escapedWord, "gi"),
        (match) => `<span class="highlight">${match}</span>`
      );
    }
    return DOMPurify.sanitize(highlightedText);
  }
  console.log(searchResults, "SEARCH RESULTS");
  console.log(data, "FAQ data");
  return (
    <>
      {" "}
      <Hero
        leftContent={
          <HeroLeftComponent
            data={data}
            onSearchChange={setSearchQuery}
            onSearch={performSearch}
          />
        }
        rightContent={""}
        leftColW={"1fr"}
        rightColW={"0.2fr"}
        minHeight={"60rem"}
        bgImg={faqsBg}
        bgPosition={"50% 20%"}
      />
      <Container>
        <CustomContainer>
          <Box sx={{ margin: "3rem 0" }}>
            <HomeIcon>
              <Link href={"/faqs"}>
                {" "}
                <Typography
                  variant="body1"
                  fontSize={{ mobile: "1.6rem", portraitTab: "1.4rem" }}
                  fontWeight={"500"}
                >
                  FAQs{" "}
                </Typography>
              </Link>
            </HomeIcon>
          </Box>
          <Box ref={searchResultsRef}>
            {searchQuery !== "" && isSearchPerformed ? (
              searchResults.length > 0 ? (
                searchResults.map((item) => {
                  const highlightedQuestion = highlightSearchTerm(
                    item.question,
                    searchWords
                  );
                  const highlightedAnswer = highlightSearchTerm(
                    item.answer,
                    searchWords
                  );
                  return (
                    <Box margin={"5rem 0"}>
                      <Typography
                        variant="h3"
                        fontSize={{ mobile: "2.8rem", portraitTab: "2.4rem" }}
                        className="color-primary-light"
                        fontWeight={"500"}
                        paddingBottom={"2rem"}
                      >
                        {item.title}
                      </Typography>
                      <Typography
                        variant="h5"
                        fontSize={{ mobile: "2.7rem", portraitTab: "1.8rem" }}
                        fontWeight={"600"}
                        textTransform={"capitalize"}
                        sx={{
                          cursor: "pointer",

                          "&:hover": {
                            color: "#3768b0",
                          },
                        }}
                        className={`text-midnight-blue ${openSans.className}`}
                        dangerouslySetInnerHTML={{
                          __html: highlightedQuestion,
                        }}
                      ></Typography>
                      <Typography
                        variant="h5"
                        fontSize={{ mobile: "1.9rem", portraitTab: "1.6rem" }}
                        fontWeight={"normal"}
                        lineHeight={"1.6"}
                        padding={"1rem 0"}
                        className={`text-midnight-blue ${openSans.className}`}
                        dangerouslySetInnerHTML={{
                          __html: highlightedAnswer,
                        }}
                      ></Typography>
                    </Box>
                  );
                })
              ) : (
                <Box
                  display={"flex"}
                  height={"40vh"}
                  justifyContent={"center"}
                  alignItems={"center"}
                  className="flex min-h-[20vh] items-center justify-center"
                >
                  <Typography
                    variant="h3"
                    fontSize={{ mobile: "2.8rem", portraitTab: "2.4rem" }}
                    className="color-primary-light"
                    fontWeight={"500"}
                    paddingBottom={"2rem"}
                  >
                    No question found
                  </Typography>
                </Box>
              )
            ) : (
              <AccordionWrapper data={data} />
            )}
          </Box>
        </CustomContainer>
      </Container>
    </>
  );
}

export default Faqs;

How to use Rangy with PDFjs text layer?

I’m using Rangy library to select text and apply highlight class on it, the problem is when is select a partial of text the rest become unselectable!

My highlight function:

  applyHighlight() {
   rangy.init()
   let selection = rangy.getSelection();
   if (selection.rangeCount > 0) {
   let highlighter = rangy.createHighlighter();
   highlighter.addClassApplier(rangy.createClassApplier('highlight-yellow'));
   highlighter.highlightSelection('highlight-yellow');
   rangy.getSelection().removeAllRanges();
}}

CSS class

 .highlight-yellow {
  margin: -1px !important;
  padding: 1px !important;
  background-color: rgb(252, 238, 124) !important;
  border-radius: 4px !important;
  opacity: 50%;
  cursor: pointer;}

A screenshots to describe the issue..

here is the highlighted spans
enter image description here

and this is what it looks like from the console
enter image description here

As you can see the “copies are not made or distributed” part is becomes unselectable and it goes out from the span scope.

How to perform this dynamic Find and replace in Javascript over user generated data?

I have an app that allows user to generate text with HTML code in the folowing format:

<h2>User generated Dynamic Data 1</h2>
    <h3>User generated text 1.1</h3>
    <h3>User generated text 1.2</h3>


<h2>User generated Dynamic Data 2</h2>
    <h3>User generated text 2.1</h3>
    <h3>User generated text 2.2</h3>
    <h3>User generated text 2.3</h3>
    
<h2>User generated Dynamic Data 3</h2>
    <h3>User generated text 3.1</h3>
    <h3>User generated text 3.2</h3>

This is how it looks like in a browser:

This is how it looks like in a browser

Is there any way to replace what user generated with the one below, using javascript?

<h2>User generated Dynamic Data 1 <button class="something" onclick="bubble_fn_add_headers({output1: 'User generated Dynamic Data 1', output2: 'User generated text 1.1nUser generated text 1.2'});">Save</button></h2>
    <h3>User generated text 1.1</h3>
    <h3>User generated text 1.2</h3>


<h2>User generated Dynamic Data 2 <button class="something" onclick="bubble_fn_add_headers({output1: 'User generated Dynamic Data 2', output2: 'User generated text 2.1nUser generated text 2.2nUser generated text 2.3'});">Save</button></h2>
    <h3>User generated text 2.1</h3>
    <h3>User generated text 2.2</h3>
    <h3>User generated text 2.3</h3>
    
    
<h2>User generated Dynamic Data 3 <button class="something" onclick="bubble_fn_add_headers({output1: 'User generated Dynamic Data 3', output2: 'User generated text 3.1nUser generated text 2.2'});">Save</button></h2>
    <h3>User generated text 3.1</h3>
    <h3>User generated text 3.2</h3>    

This is how the above would look like in a browser:

This is how the above would look like in a browser

The situation is very trickey because:

  • All the texts surrounded by <h2></h2> and <h3></h3> tags are user generated.
  • Users can generate any number of <h2> Texts followed by any or even zero number of <h3> texts.

Can you guys suggest any work around this using javascript?

Thanks

I would have tried

s.replace('<h2>', '<h2>User generated Dynamic Data 1 <button class="something" onclick="bubble_fn_add_headers({output1: 'User generated Dynamic Data 1', output2: 'User generated text 1.1nUser generated text 1.2'});">Save</button></h2>')

But it just isn’t possible because the texts are dynamically generated and are unique each time.

Cycling through iframes and minimising loading

I have a sequence of embedded excel iframes that I would like to cycle through. Each iframe must be refreshed to show the latest data, but this takes several seconds. I have tried to run this in the background by refreshing the iframes in the background and waiting to display them, but I am quite new to javascript. Will this script perform correctly?

<script>
// Array of iframe IDs
var iframes = ['iframe1', 'iframe2', 'iframe3', 'iframe4','iframe5','iframe6','iframe7'];
var currentIframeIndex = 0;

function reloadAndSwitchIframes() {
    var currentIframe = document.getElementById(iframes[currentIframeIndex]);
    var nextIframeIndex = (currentIframeIndex + 1) % iframes.length;
    var futureIframeIndex = (currentIframeIndex + 2) % iframes.length;

    var nextIframe = document.getElementById(iframes[nextIframeIndex]);
    var futureIframe = document.getElementById(iframes[futureIframeIndex]);

    futureIframe.src = futureIframe.src; 

    currentIframe.style.display = 'none';
    nextIframe.style.display = 'block';

    
    currentIframeIndex = nextIframeIndex;
}

// Call reloadAndSwitchIframes function when page loads
window.onload = function() {
    setInterval(reloadAndSwitchIframes, 20000);
};
</script>

Sort the list of regions in ascending order of occurence while using WaveSurfer.js

While using the getRegions() method of the Regions plugin of WaveSurfer.js, , the method returns an array of objects that contain all the regions created. I wish to print out the contents of this array, but in the increasing order of occurence. Note that, the default way it gets stored is in order of creation, not occurence.

For example,
The display order of this while printing should be Regions 1,2,3 and then 4. But it is 4,1,3,2 instead.

Reference: https://wavesurfer.xyz/docs/classes/plugins_regions.default#getRegions

How to close an ngbactive modal programatically without the modal being closed

Table-component.ts

openModal(rowData: any = null) {
const config: Partial<DynamicFormComponent> = {
    form: this.form,
    data: rowData
};

const options: NgbModalOptions = { size: 'sm', centered: true };

const modalRef = this.ModalService.show((DynamicFormComponent), config, options)

}

dynamic-form.html

 constructor(
  private activeModal: NgbActiveModal
 ){}

onSubmit(){this.activeModal.close(this.dynamicFodmGroup.value) //here the modal is closing but i dont want that
  }

The show method returns an observable which is triggered when modal is closed. How to achieve the same without closing the modal?

Please help 🙁
Many thanks

In JS, why can a function’s parentheses be skipped, if the first argument is an fstring? [duplicate]

The title says it.

Here’s what I mean:

console.log`insert text here`

This is valid JS, which I found in a solution to a CodeWars kata, in the shape of string.split`` and array.join``.

After some playing around in node.js, this is what I found:

> console.log`insert text here`
[ 'insert text here' ]

> console.log(`insert text here`)
insert text here

> console.log([`insert text here`])
[ 'insert text here' ]

> console.log`one`,`two`
[ 'one' ]
'two'

> console.log`one`,'two'
[ 'one' ]
'two'

> console.log`one`,'two',3
[ 'one' ]
3

> console.log'one'
             ^^^^^
Uncaught SyntaxError: Unexpected string

Turns out, that it actually logs an array, containing only the string.

How does this even work, what is causing this behaviour, and why is an error not thrown, only if it’s an fstring?

I have a search button that contains options and each option will show data to associated query, I want to make some sub-options for certain options

I have a search button that contains options and each option will show data according to its corresponding query, the thing is i want to make some sub-options for certain options that looks like when you right click the desktop windows it shows a list and some elements contain more elements inside them

this is what i’m working on:
this is what i'm working on

this is what i want to do:
this is what i want to do

and this is my code

    <?php
// Assuming you have a database connection
// Replace these values with your actual database credentials
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "stage";

// Create a connection
$conn = new mysqli($servername, $username, $password, $dbname);

// Check the connection
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}

$output = "";
// Handle form submission
if ($_SERVER["REQUEST_METHOD"] == "POST") {
    $search1 = $_POST["search1"];
    $search2 = $_POST["search2"];

    // Perform different queries based on the selected options
    if ($search1 === 'finance') {
        if ($search2 === 'Clients') {
            $queryName = "Total Clients";
            $query = "SELECT count(id_client) as Total_Clients FROM `client`;";
            $result = $conn->query($query);
            $output = displaycount($queryName, $result);
        } elseif ($search2 === 'Total_Devis') {
            $queryName = "Total Devis";
            $query = "SELECT ROUND(SUM(totale_devis) , 3) as Total_Devis FROM `devis_client`;";
            $result = $conn->query($query);
            $output = displaycount($queryName, $result);
        } elseif ($search2 === 'societe') {
            $queryName = "Total Societes";
            $query = "SELECT count(id_societe) as Total_Societés from societe;";
            $result = $conn->query($query);
            $output = displaycount($queryName, $result);
        } elseif ($search2 === 'fournisseurs') {
            $queryName = "Total Fournisseurs";
            $query = "SELECT count(id_fournisseur) as Total_Fournisseurs FROM `fournisseur`;";
            $result = $conn->query($query);
            $output = displaycount($queryName, $result);
        } elseif ($search2 === 'produit') {
            $queryName = "Total Produit";
            $query = "SELECT COUNT(id_produit) as Nom FROM produit;";
            $result = $conn->query($query);
            $output = displaycount($queryName, $result);
        }
    } elseif ($search1 === 'crm') {
        if ($search2 === 'leads') {
            $queryName = "Total Leads";
            $query = "SELECT COUNT(id_task) as Total_Leads FROM `lead`;";
            $result = $conn->query($query);
            $output = displaycount($queryName, $result);
        } elseif ($search2 === 'opportunites') {
            $queryName = "Total Opportunites";
            $query = "SELECT count(id) as Total_Opportunités FROM `opportunite`;";
            $result = $conn->query($query);
            $output = displaycount($queryName, $result);
        } elseif ($search2 === 'cases') {
            $queryName = "Total Cases";
            $query = "SELECT COUNT(id_case) as Total_Cases FROM `cases`;";
            $result = $conn->query($query);
            $output = displaycount($queryName, $result);
        } elseif ($search2 === 'contacts') {
            $queryName = "Total Contacts";
            $query = "SELECT count(id_contact) as Total_Contacts FROM `contact`;";
            $result = $conn->query($query);
            $output = displaycount($queryName, $result);
        } elseif ($search2 === 'contrats') {
            $queryName = "Total Contrats";
            $query = "SELECT COUNT(id_contrat) as Total_Contrats FROM `contrat_client`;";
            $result = $conn->query($query);
            $output = displaycount($queryName, $result);
        }
        
    } elseif ($search1 === 'projets') {
        if ($search2 === 'projets') {
            $queryName = "Total Projets";
            $query = "SELECT COUNT(id_project) as Total_Projets FROM `projects`;";
            $result = $conn->query($query);
            $output = displaycount($queryName, $result);
        }
    }
}

// Function to display the query result
function displaycount($queryName, $result) {
    $output = "";
    if($result->num_rows > 0){
        
            $output .= "<ul>"; // Start an unordered list for multiple rows

            // Get column names from the result set metadata
            $columns = $result->fetch_fields();
            $columnNames = array_map(function ($column) {
                return $column->name;
            }, $columns);
            
            while ($row = $result->fetch_assoc()) {
                // Display values for each column dynamically
                $output .= "<li>";
                foreach ($columnNames as $columnName) {
                    $output .= "$columnName: " . $row[$columnName] . " | ";
                }
                $output = rtrim($output, ' | '); // Remove the trailing comma and space
                $output .= "</li>";
            }
            $output .= "</ul>"; // Close the unordered list
        
    }else {
        $output .= "<strong>No results found.</strong></p>";
    }
    return $output;
}
?>

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
  .dropdown-container {
    display: inline-block;
    margin-right: 5px;
  }
  select {
    margin: 0;
    padding: 5px;
  }
  </style>
  </head>
  <body>
  <h2>Formulaire</h2>

  <form method="post" action="">
    <label for="search1">Search 1:</label>
    <div class="dropdown-container">
        <li style="list-style-type: none;">
            <select id="search1" name="search1" onchange="updateSearch2Options()">
                <option value="" disabled selected>Choisissez une option</option>
                <option value="finance">Finance</option>
                <option value="crm">CRM</option>
                <option value="projets">Projet</option>
            </select>
        </li>
    </div>

    <label for="search2">Search 2:</label>
    <div class="dropdown-container">
        <li style="list-style-type: none;">
            <select id="search2" name="search2">

            </select>
        </li>
    </div>

    <input type="submit" value="Search">
  </form>
  <?php echo $output; ?>

<script>
var optionsMapping = {
  'finance': {
    'Clients': {
        'Sub-Options': ['annee', 'devis', 'total'],
        },
        'Total_Devis': [],
        'societe': [],
        'fournisseurs': [],
        'produit': [],
    },
    'crm': {
        'leads': [],
        'opportunites': [],
        'cases': [],
        'contacts': [],
        'contrats': [],
    },
    'projets': {
        'projets': [],
    },
};


function updateSearch2Options() {
    var search1Value = document.getElementById('search1').value;
    var search2Select = document.getElementById('search2');

    // Clear existing options
    search2Select.innerHTML = '';

    // Get options for the selected value in search1
    var options = optionsMapping[search1Value];

    // Add new options based on the selected value in search1
    addOptions(search2Select, Object.keys(options));

    // Check if there are sub-options and update the second dropdown accordingly
    var selectedSearch2Value = search2Select.value;
    if (options[selectedSearch2Value] && Array.isArray(options[selectedSearch2Value])) {
        addOptions(search2Select, options[selectedSearch2Value]);
  }
}

        function addOptions(selectElement, optionsArray) {
            for (var i = 0; i < optionsArray.length; i++) {
                addOption(selectElement, optionsArray[i]);
            }
        }

        function addOption(selectElement, optionValue) {
            var option = document.createElement('option');
            option.value = optionValue;
            option.text = optionValue;
            selectElement.appendChild(option);
        }

        // Initial call to populate the second dropdown
        updateSearch2Options();
  </script>
</body>
</html>

Make iOS Capacitor CAPPluginCall notifyListener to wait for reactjs async indexdb “put” call

I have a capacitor plugin to bridge Swift and Reactjs, basically to run a PWA on an iOS device, but some part of the code is written in Swift in this app, thus when I switch from Reactjs to Swift, the javascript main thread is blocked by swiftui screens, and all the background async task in javascript comes to a halt. Here in Swift, I have defined some listeners using the capacitor notifyListener function, which sends notifications to Reactjs when some event occurs in Swift, one such listener is responsible for setting some data in the Reactjs indexedDB, but since the main thread is blocked (according to my knowledge), that indexedDB updation async call is thrown into some queue, and I do not get any response back from the DB, but when I close the swiftui screens and come back to the Reactjs code, the updation happens and I get the desired response.

So my question here is, is there some way by which I can wait in Swiftui, till that DB async call finishes and I can get the result at the same time without closing the Swiftui screens and coming back again?

Here is the sample code for your reference. Any help will be appreciated.

Listener in swift –

do {
      try activationScheduler?.startActivationTimer()
      self.notifyListeners(self.onTripStart, data: ["transactionId": options.booking.id])
   } catch {
      debugPrint("Failed to start activation timer: (error)")
   }

Code in Reactjs which runs when notification is raised from swift –

useEffect(() => {
    const activationFlowTripStartListener = ActivationFlowPlugin.addListener('onTripStart', (eventData) => {
      handleSetPassAndTicketWithDB(eventData);
    });

    return () => {
      activationFlowTripStartListener?.remove();
    };
  }, [userId]);

const handleSetPassAndTicketWithDB = useCallback(async (passData) => {
    const decryptkey = getKeyForEncryptAndDecrypt(userId);
    const { get } = IndexDB(objectStoreKeys.myPassHistory);
    let pass = await get(passData?.transactionId, decryptkey);
    const newCachedPassData = {
      ...pass,
      ...pass?.cachedPass,
      isContactLessValidationVisited: true,
      isRidePresent: true,
    };
    await setUserPass(newCachedPassData);
  }, [userId, setUserPass]);

Also one more thing as you can see here, I am “getting” and “setting” data in the IndexedDB. The get function is working correctly, the issue arises only when I use the set function of the IndexedDB object.

The await setUserPass function call above calls this function to update the indexedDB –

const setHistoryData = useCallback(async (newData) => {
    if (newData?.transactionId) {
      const decryptkey = getKeyForEncryptAndDecrypt(phoneNumber);
      const { set } = IndexDB(objectStoreKeys.myPassHistory);
      await set(undefined, { newData }, decryptkey);
    }
    return 0;
  }, [phoneNumber]);

And here is the set async function of the IndexedDB object –

set: async function set(key, val, encryptKey) {
    const encryptData = encryptObject(val, encryptKey);
    return (await dbPromise).put(`${objectStore}`, encryptData, key);
  },
get: async function get(key, decryptKey) {
    const data = await (await dbPromise).get(`${objectStore}`, key);
    return decryptObject(data, decryptKey);
  },

How to implement text-to-speech in React-native for heteronyms?

I have an array of words and want to enable my users to listen to them. However, there’s a chance of encountering a heteronym (a word spelled the same as another but pronounced differently).

My idea is to find a TTS (Text-to-Speech) library that accepts transcriptions (like dʌk) as input. Do you have any ideas about such libraries, or perhaps know of another approach to solve my issue?

Thanks.

Can’t bind to the input of a standalone angular directive in unit test

I’m trying to write a test for a directive that modifies the Angular CDK table. To test this I made a test component decalred in my *.spec.ts file, where I setup a simple table and add my directive.

However when run, on fixture.detectChanges() the component throws two errors. One is that the input property is unknown, the second is from the directive sayingthe required input is not set.

Can't bind to 'sortableColumns' since it isn't a known property of 'table' (used in the 'TestComponent' component template)

ERROR RuntimeError: NG0950: Input is required but no value is available yet.

The test component looks like so (I have removed some logic for brevity):

@Component({
  standalone: true,
  imports: [CdkTableModule, SortableTableDirective],
  template: ` <table cdk-table sortableTable [dataSource]="dataSource" [sortableColumns]="sortableColumns">
    <ng-container cdkColumnDef="columnA">
      <th cdk-header-cell *cdkHeaderCellDef>The First Column</th>
      <td cdk-cell *cdkCellDef="let element">{{ element.columnA }}</td>
    </ng-container>
    <ng-container cdkColumnDef="columnB">
      <th cdk-header-cell *cdkHeaderCellDef>The Second Column</th>
      <td cdk-cell *cdkCellDef="let element">{{ element.columnB }}</td>
    </ng-container>
    <ng-container cdkColumnDef="columnC">
      <th cdk-header-cell *cdkHeaderCellDef>The Third Column</th>
      <td cdk-cell *cdkCellDef="let element">{{ element.columnC }}</td>
    </ng-container>
    <tr cdk-header-row *cdkHeaderRowDef="displayedColumns"></tr>
    <tr cdk-row *cdkRowDef="let row; columns: displayedColumns"></tr>
  </table>`,
})
class TestComponent {
  dataSource = //data-source creation etc removed for brevity
  sortableColumns = ['columnA', 'columnB'];
  displayedColumns = ['columnA', 'columnB', 'columnC'];
}

The test is setup as follows. The table variable gets a value despite an error being thrown on the fixture setup. When I later want to check that the directive has run however, it has not. I have tried with and without the compileComponents function, it does not make a difference.

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [SortableTableDirective, TestComponent, CdkTableModule],
    }).compileComponents();
    fixture = TestBed.createComponent(TestComponent);

    fixture.detectChanges();

    table = fixture.debugElement.query(By.directive(SortableTableDirective));
  });

The directive itself is fairly simple from a dependency standpoint, not depending on anything outside of some core angular things

@Directive({
  selector: 'table[sortableTable]',
  standalone: true,
})
export class SortableTableDirective<T> implements OnDestroy {
  dataSource = input.required<SortableDatasource<T>>();
  sortableColumns = input.required<string[]>();

  public constructor(
    private renderer: Renderer2,
    private elRef: ElementRef,
    private containerRef: ViewContainerRef,
  ) {
    // code removed
  }

The cdk-table directive works and runs as it should, with its created elements all being available in the fixture. The code runs perfectly fine “live”.

React Hooks with React Router – how do I redirect to another route?

I have a simple react hooks application – a list of Todos – with react router v4

On the List of Todos, when a Todo is clicked I need to:

Dispatch the current todo in context
Redirect to another route (from /todos to /todos/:id)
In the previous React Class based implementation I could use this.context.history.push to redirect to another route.

google-site-verification: google97d89d494f462800.html

How would I handle that using React Hooks in combination of React Router v4 (in code below see my comment in function editRow())?

Babel cannot parse coalescing operator in my React JSX

I tried adding babel support for ?? but it still cannot parse this for some reason.

I am using:

"@babel/core": "^7.24.0"
"typescript": "^5.4.2"
"webpack": "^4.44.1"
 "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4",

I also tried installing and using: @babel/plugin-proposal-nullish-coalescing-operator

It cannot parse the following 2 lines when I compile my ReactJS app because it cannot parse ??:

const organization = company ?? university;
<MainLayout title={company.name ?? university.name}>

.bablerc

{
  "presets": [
    ["@babel/preset-env", {
      "targets": {
        "node": "current"
      }
    }],
    ["@babel/preset-react", { "development": true }]
  ],
  "plugins": [
    ["@babel/plugin-proposal-object-rest-spread"],
    ["@babel/plugin-transform-runtime"],
    ["@babel/plugin-proposal-export-default-from"],
    ["@babel/plugin-transform-optional-chaining"],
    ["@babel/plugin-transform-nullish-coalescing-operator"]
  ]
}

tsconfig.json

"compilerOptions": {
    /* Visit https://aka.ms/tsconfig.json to read more about this file */
    "noEmit": true,
    "rootDir": "./",
    /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
    "target": "esnext",
    /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
    "module": "esnext",
    /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
    "lib": [
      "es5",
      "es6",
      "dom",
      "ESNext"
    ],
    /* Specify library files to be included in the compilation. */
    "moduleResolution": "node",
    "allowJs": false,
    /* Allow javascript files to be compiled. */
    "jsx": "react",

InterviewContainer.tsx

render() {
        const {company, university} = this.props;
        if(!company || !university) return null

        const organization = company ?? university;

        return (
            <Main>
                <MainLayout title={company.name ?? university.name}>
                    <div>
                        <div id="ft-interview">
                            <div className="panel vertical-space">
                                <CompanyHeader className="ft-companyHeader" company={company}/>
                                <CompanyProfile className="ft-companyProfile" company={company}/>
                                <InterviewContentMain className="ft-interviewContentMain" company={company}/>
                            </div>
                        </div>
                    </div>
                </MainLayout>
            </Main>
        )
    }

Error (I get this when webpack uses tsconfig to transpile my webpack dev build)

ERROR in ./views/Interview/InterviewContainer.tsx 28:38
Module parse failed: Unexpected token (28:38)
File was processed with these loaders:
 * ./node_modules/ts-loader/index.js
You may need an additional loader to handle the result of these loaders.
|         if (!company || !university)
|             return null;
>         const organization = company ?? university;
|         return (React.createElement(Main, null,
|             React.createElement(MainLayout, { title: company.name ?? university.name },
 @ ./views/App.tsx 5:0-64 38:36-54 40:36-54
 @ ./index.tsx