Syncfusion Scheduler misaligned rows in Timeline view

I’m using the Syncfusion Scheduler in TimelineWeek view to manage a calendar of equipment bookings, organized in a resource hierarchy:

Category → Subcategory → Equipment

Each equipment item has a very long description, and I’m displaying it using the resourceHeaderTemplate, like this:

resourceHeaderTemplate: props => {
    const col = props.resourceData.color || '#eee';
    return `
      <div class="my-resource-header"
           style="background:${col};color:${getContrastColor(col)}">
        ${props.resourceData.text}
      </div>`;
}

I enabled rowAutoHeight: true and also use a helper function to manually sync the row heights:

function forceContentRowHeightSync(retries = 10) {
    const resTableRows = document.querySelectorAll('.e-resource-table tbody tr');
    const contentTableRows = document.querySelectorAll('.e-content-table tbody tr.e-content-row');

    const count = Math.min(resTableRows.length, contentTableRows.length);

    for (let i = 0; i < count; i++) {
        const resourceCell = resTableRows[i].querySelector('.my-resource-header');
        const height = resourceCell ? resourceCell.offsetHeight : resTableRows[i].offsetHeight;

        resTableRows[i].style.height = `${height}px`;
        resTableRows[i].style.minHeight = `${height}px`;
        contentTableRows[i].style.height = `${height}px`;
        contentTableRows[i].style.minHeight = `${height}px`;
    }

    if (retries > 0) {
        requestAnimationFrame(() => forceContentRowHeightSync(retries - 1));
    }
}

This function is called inside dataBound, contentReady, and actionComplete events.

When a resource (equipment) has a long description, the left-hand resource cell grows taller to fit the content, but the corresponding timeline row on the right does not grow in height, leading to visual misalignment between the two.

Here’s an image to illustrate the issue:

enter image description here

What I’ve Tried:

  • Enabled rowAutoHeight: true
  • Enabled allowRowHeightAdjustment: true
  • Applied consistent box-sizing, padding, and font-size to both sides
  • Applied CSS: white-space: normal, overflow-wrap: anywhere, etc.
  • Used a JavaScript offsetHeight sync as shown above

Is there an official or recommended way to ensure proper row height alignment between the resource table (left) and content table (right) in Timeline views of the Syncfusion Scheduler?

Am I missing something in the templating or row height syncing logic?

 window.scheduleObj = new ej.schedule.Schedule({
        height: 'auto',
        width: '100%',
        rowAutoHeight: true,
        allowRowHeightAdjustment: true,
        selectedDate: new Date(2025, 5, 9),
        locale: 'it',
        cssClass: 'custom-scheduler',
        currentView: 'TimelineWeek',
        views: [
            { option: 'TimelineWeek', displayName: 'Settimana' },
            { option: 'TimelineMonth', displayName: 'Mese' },
            { option: 'Agenda', displayName: 'Agenda' }
        ],
        agendaViewSettings: { allowVirtualScrolling: false, hideEmptyAgendaDays: false },
        group: { resources: ['Categorie', 'Sottocategorie', 'Mezzi'], byGroupID: true, allowGroupEdit: false },
        timeScale: { enable: false },
        showQuickInfo: true,
        allowResizing: false,
        showHeaderBar: true,
        eventSettings: {
            dataSource: eventi,
            fields: {
                subject: { name: 'Subject', title: 'Titolo' },
                startTime: { name: 'StartTime' },
                endTime: { name: 'EndTime' },
                isAllDay: { name: 'IsAllDay' }
            }
        },
        resources: [
            {
                field: 'CategoriaId', title: 'Categoria', name: 'Categorie',
                dataSource: risorse.categorie, textField: 'text', idField: 'id', colorField: 'color'
            },
            {
                field: 'SottocategoriaId', title: 'Sottocategoria', name: 'Sottocategorie',
                dataSource: risorse.sottocategorie, textField: 'text', idField: 'id',
                groupIDField: 'groupID', colorField: 'color'
            },
            {
                field: 'MezzoId', title: 'Mezzo', name: 'Mezzi',
                dataSource: risorse.mezzi, textField: 'text', idField: 'id',
                groupIDField: 'groupID', colorField: 'color'
            }
        ],
        resourceHeaderTemplate: props => {
            const col = props.resourceData.color || '#eee';
            return `
              <div class="my-resource-header"
                   style="background:${col};color:${getContrastColor(col)}">
                ${props.resourceData.text}
              </div>`;
        },
        eventRendered: args => {
            const sc = risorse.sottocategorie.find(x => x.id === args.data.SottocategoriaId);
            const col = sc?.color || '#607d8b';
            args.element.style.backgroundColor = col;
            args.element.style.borderColor = col;
            args.element.style.color = getContrastColor(col);
        },
        dataBound:    () => setTimeout(() => forceContentRowHeightSync(), 100),
        contentReady: () => setTimeout(() => forceContentRowHeightSync(), 100),
        actionComplete: args => {
            if (['view','dateNavigate','resourceExpand','resourceCollapse'].includes(args.requestType)) {
                setTimeout(() => forceContentRowHeightSync(), 100);
            }
        }
    });
    scheduleObj.appendTo('#scheduleContainer');
});

This is my css file

.e-resource-text,
.my-resource-header {
    white-space: normal !important;
    word-break: break-word;
    overflow-wrap: anywhere;
}

.disabled-cell {
    background-color: #ff0000 !important;
    pointer-events: none;
    opacity: 0.6;
}

.e-schedule .e-resource-column {
    width: 130px !important;
    max-width: none !important;
    white-space: normal !important;
    word-wrap: break-word;
}

.e-schedule .e-timeline-month-view .e-work-cells.e-parent-node.e-resource-group-cells.e-work-days,
.e-schedule .e-timeline-week-view .e-work-cells.e-resource-group-cells.e-work-hours,
.e-schedule .e-timeline-week-view .e-resource-group-cells,
.e-schedule .e-timeline-month-view .e-work-cells.e-parent-node.e-resource-group-cells {
    background-color: #e8e5e5 !important;
    border: none !important;
    padding: 0 !important;
    margin: 0 !important;
}

#scheduleContainer {
    min-height: 600px;
    overflow: visible;
    position: relative;
}

.e-schedule .e-resource-table th,
.e-schedule .e-resource-table td,
.e-schedule .e-content-table th,
.e-schedule .e-content-table td {
    box-sizing: border-box;
}

.my-resource-header {
    padding: 8px 12px;
    border-radius: 8px;
    box-shadow: 0 0 4px rgba(0, 0, 0, 0.1);
    display: flex;
    align-items: center;
    white-space: normal;
    line-height: 1.2;
    height: 100%;
    box-sizing: border-box;
}

.custom-scheduler .e-resource-column .e-resource-cell {
    overflow: hidden;
    padding: 0 !important;
}

.custom-scheduler .e-content-table .e-work-cells,
.custom-scheduler .e-content-table .e-work-hours {
    box-sizing: border-box;
    padding: 8px 12px;
    overflow: hidden;
}

.custom-scheduler .e-content-table tbody tr.e-content-row td {
    box-sizing: border-box;
    padding: 8px 12px;
}

.e-schedule .e-resource-table td,
.e-schedule .e-content-table td {
    padding-top: 8px !important;
    padding-bottom: 8px !important;
}

Laravel front-end changes not persisting after stopping composer run dev

I’m trying to set up a Laravel project to help myself relearn the basics after 2 years of unemployment due to health issues.

I’ve just installed a fresh project using the Inertia-Vue starter pack and am serving/developing it using Sail on MacOS.

I’m not familiar with the sail composer run dev command, but I noticed that while running it my changes to the front end are instantly reflected in my browser (I understand this is called ‘hot module reloading’). However, after terminating the command my changes disappear, and will only return and persist if I execute sail npm run build.

Is this intended behaviour? It feels strange to have my changes disappear after they’ve been built already, albeit in a ‘dev’ script.

How to debug Firebase error in Angular app that is missing a full stacktrace?

I’m incurring the following error in my Angular + Firebase app:

ERROR FirebaseError: Expected first argument to collection() to be a CollectionReference, a DocumentReference or FirebaseFirestore

The stacktrace in Chrome is a single line:

handleError @ root_effect_scheduler-CWV89N3_.mjs:3517

How can I get a better stacktrace? This is after I’ve finally upgraded an old project that was on Firebase v8 to v9 which had some substantial changes as any Firebase user is I’m sure aware.

How to capture scrollable content with html-to-image without showing temporary layout changes to users?

I’m using the html-to-image library to capture marketing message cards that contain long scrollable content areas. I want to capture the entire content including parts that are not visible in the viewport.
The commonly suggested solution I found through Google searches is:

Temporarily change overflow to visible
Capture the image
Restore the original overflow value

However, this approach causes the card to visually expand momentarily when the user clicks the capture button, which creates an undesirable user experience.
What I want to achieve:

Capture the full scrollable content (including non-visible parts)
Hide the temporary layout expansion from users
Provide a smooth, clean capture experience

Current approach (problematic):

// Temporarily modify overflow
element.style.overflow = 'visible';

// Capture image
await htmlToImage.toPng(element);

// Restore original overflow
element.style.overflow = 'auto'; // or original value

Question: What alternative methods can I use to capture scrollable content without exposing the temporary layout changes to users?
Any suggestions for a cleaner approach would be greatly appreciated.

HTMX: addEventListener stacking..?

In my htmx proyect there are a bunch of hx-boost to navigate between pages. The problem is that, for some reason, whenever I navigate, i think that the addEventListener javascript atribute stacks with more and more of these, instead of keeping just 1 addEventListener.

This could be because every page contains a base template, which has those event listeners, and since the user isn’t reloading the page (because of hx-boost), the document.addEventListener never is cleared from the browser because the document itself, making it to stack multiple events listeners because, the document isn’t actually being reloaded?

The solution I got so far is adding an event listener of 'htmx:afterSettle' removing all events listeners, works but applies more than just hx-boost so it causes issues between the rest of the code.

Jest setup with React native cannot use import statement outside of a module

I am trying to setup Jest in my Turborepo monorepo specifically in my UI package. I’m trying to setup Jest so I have a very basic test to see how it works:

Button.spec.tsx

import {describe, it, expect} from "@jest/globals";
import {render} from "@testing-library/react-native";
import Button from "./Button.tsx";
import React from "react";

describe("Button", () => {
  it("renders the button with text", () => {
    expect(true).toBe(true);
    render(<Button>Click me</Button>);
  });
});

However, the test fails with a long error. Here is the important part:

    <monorepoRoot>/node_modules/.pnpm/[email protected]_@[email protected]_@[email protected][email protected]/node_modules/react-native/index.js:28
    
    import typeof * as ReactNativePublicAPI from './index.js.flow';
    ^^^^^^

    SyntaxError: Cannot use import statement outside a module

      1 | import {describe, it, expect} from "@jest/globals";
    > 2 | import {render} from "@testing-library/react-native";
        | ^
      3 | import Button from "./Button.tsx";
      4 | import React from "react";
      5 |

I have tried a lot to fix this error and feel like I have hit wall this. Here is everything:

  • I have tried creating my config from scratch with the jest cli
  • Using the react-native preset
  • Using the jsWithBabelPreset transform
  • Using ts-jest with transforms
  • Using ts-jest config helper, see here
  • Using module file extensions
  • Using babel instead and in addition to ts-jest
  • Using babel’s module:metro-react-native-babel-preset
  • Using babel-plugin-strip-flow-types
  • Using babel/plugin-transform-modules-commonjs
  • Using experimental-vm-modules
  • Transforming all of node_modules which only led to new errors

Here is my current config files:

babel.config.js

/** @type {import('@babel/core').ConfigFunction} */
export default {
  presets: ["module:metro-react-native-babel-preset"],
  plugins: [
    "@babel/plugin-transform-flow-strip-types",
    "@babel/plugin-transform-modules-commonjs",
  ],
};

jest.config.ts

import type {Config} from "jest";
import {createJsWithBabelPreset} from "ts-jest";

const jsWithBabelPreset = createJsWithBabelPreset({
  tsconfig: "tsconfig.json",
  babelConfig: true,
});

const jestConfig: Config = {
  preset: "react-native",
  transform: jsWithBabelPreset.transform,
  transformIgnorePatterns: [
    "/node_modules/(?!(@react-native|react-native)/).*/",
  ],
  moduleFileExtensions: ["ts", "tsx", "js", "flow"],
};

export default jestConfig;

tsconfig.json

{
  "extends": "../typescript-config/react-library.json",
  "compilerOptions": {
    "jsx": "react",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "target": "ESNext",
    "esModuleInterop": true,
    "allowImportingTsExtensions": true,
    "allowJs": true
  }
}

tsconfig.json (base)

{
  "$schema": "https://json.schemastore.org/tsconfig",
  "extends": "@react-native/typescript-config",
  "compilerOptions": {
    "jsx": "react-jsx",
    "allowImportingTsExtensions": true,
    "noEmit": true
  }
}

Test command

node --experimental-vm-modules node_modules/jest/bin/jest.js

I suspect the issue is with React native shipping flow code into the bundle but I do not know how to fix it. Here are my Jest related dependencies:

    "@testing-library/react-native": "^12.9.0",
    "babel-jest": "^30.0.1",
    "jest": "^30.0.0",
    "metro-react-native-babel-preset": "^0.77.0",
    "ts-jest": "^29.4.0",
    "@babel/core": "catalog:",
    "@babel/plugin-transform-flow-strip-types": "^7.27.1",
    "@babel/plugin-transform-private-methods": "^7.27.1",
    "@babel/plugin-transform-private-property-in-object": "^7.27.1",
    "@babel/plugin-transform-react-jsx": "^7.27.1",
    "@babel/preset-env": "^7.27.2",
    "@babel/preset-react": "^7.27.1",
    "@jest/globals": "^30.0.0",

As far as I am aware these are the latest versions:

System specs:

  • Node v23.4.0
  • macOS 15.5
  • pnpm 10.12.1
  • turbo 2.5.4

What should I do to fix this.

P.S. I have already had to patch a react native package shipping flow code in a .js extension.

Railway deploy: App stops immediately with SIGTERM after ‘Server running on port 8080

You reached the start of the range → Jun 19, 2025 6:43 PM

Starting Container

npm warn config production Use --omit=dev instead.

[email protected] start

node server.js

Server running on port 8080

Stopping Container

npm error path /app

npm error command failed

npm error signal SIGTERM

npm error command sh -c node server.js

npm error A complete log of this run can be found in: /root/.npm/_logs/2025-06-19T22_44_46_507Z-debug-0.log

i tried all methods, the tech stack is ejs, nodejs and python

Can I retrieve Cordova localStorage in React Native?

I had a mobile app built in Cordova, in which I saved information using the localStorage method.
When switching from Cordova to React Native, I kept the same package but can’t access the information in that localStorage.

How would I do it? Is it possible to recover this information, or would it be unrecoverable?

In Cordova, I use: localStorage.setItem("key", value);

How to combine two states into a single object?

I have two state/setter pairs from different sources (i.e. one from store and another from component state) and I need to combine them into one – because the view component needs to modify both atomically. How do I actually implement that?

const [state1, setState1] = ...
const [state2, setState2] = ...

const state3 = { ...state1, ...state2 } // straightforward
const setState3 = (func: (state: State3) => State3) => {
  // what here?
  // option 1: defeats the purpose of callback update and can lead to bugs
  const value = func(state3)
  setState1(_.pick(func, ...))
  setState2(_.pick(func, ...))
  // option 2: mostly works, but bad if func is expensive or has side effects (oh no!)
  setState1(state1 => _.pick(func({ ...state1, ...state2 }, ...)))
  setState2(state2 => _.pick(func({ ...state1, ...state2 }, ...)))
}

How can I add a responsive toggle button for a mobile nav menu using JavaScript?

I’m working on a CSS lab task where we had to make a webpage responsive using media queries. One of the bonus challenges was to add a toggle button for the navigation menu on mobile and I’ve been struggling with it a bit.
The problem is the toggle button shows up on small screens, and it seems like the .show class is being added, but the menu still doesn’t appear. I’m not sure if the issue is my selector (ul vs nav), or how I’m toggling the class. I tried targeting just the directly too but no luck.

Any tips on the right way to handle this kind of responsive menu toggle using plain JavaScript and CSS?

const menuToggle = document.getElementById("menu-toggle");
const mainMenu = document.getElementById("main-menu");

menuToggle.addEventListener("click", () => {
  mainMenu.classList.toggle("show");
});
@media (max-width: 480px) {
  #main-menu ul {
    display: none;
    flex-direction: column;
  }

  #main-menu.show ul {
    display: flex;
  }

  #menu-toggle {
    display: block;
  }
}
<header id="header">
  <h2 id="site-name"><a href="#">All About CSS</a></h2>
  <button id="menu-toggle" aria-expanded="false">☰ Menu</button>
  <nav id="main-menu" aria-label="Main navigation">
    <ul>
      <li><a href="#">Home</a></li>
      <li><a href="#">Articles</a></li>
      <li><a href="#">About</a></li>
      <li><a href="#">FAQ</a></li>
    </ul>
  </nav>
</header>

How to get canonical IANA timezone ids in javascript

Getting all canonical IANA ids

While I fully intend to support all IANA time zone ids in my application, when users select a timezone, I want the options to only contain currently canonical zones.

How would I get a list containing only canonical IANA time zone id’s in javascript?

Preferably, I would like a way to achieve this using javascript apis (Intl/Temporal – even if they are not widely available yet), but I’m also interested in how this might be achieved with luxon, dayjs, or other packages.

I am aware of Intl.supportedValuesOf('timeZone'). However, this returns a list of primary time zone identifiers, which

uniquely identify the time zone [and] usually refer to a geographic area anchored by a city…

and as such are a concept distinct from canonical ids, as described in this note:

Note that the attribution of primary identifiers preserves the country code: for example, the IANA database records Atlantic/Reykjavik as an alias for Africa/Abidjan, but because they correspond to different countries (Iceland and Côte d’Ivoire, respectively), they are treated as distinct primary identifiers.

I am also aware of the tzdb package. However, this returns too narrow of a list. It’s rules for grouping are

  • if the time zones are in the same country
  • if the [DST and non-DST] offsets are the same

which, for example, means that America/Indiana/Indianapolis is grouped under America/New_York, despite being a distinct canonical id.

Getting canonical id for a given valid IANA id

How would I resolve an IANA time zone id to its canonical version (follow link if it is an alias, or return itself if it is already canonical)?

If I could figure this out, I could map the result of Intl.supportedValuesOf('timeZone') and take the distinct values.

This is also necessary to check time zone ids for semantic equality – America/Indianapolis should be considered equivalent to America/Indiana/Indianapolis, but since string equality obviously will fail, I need a way to resolve links.

My research thus far found this approach

function getCanonicalTimezone(ianaId) {
  try {
    const formatter = new Intl.DateTimeFormat('en', { 
      timeZone: ianaId 
    });
    return formatter.resolvedOptions().timeZone;
  } catch (error) {
    throw new Error(`Invalid timezone: ${ianaId}`);
  }
}

However, this doesn’t resolve aliases in every case. For example, Africa/Accra resolves to itself, even though it is an alias of Africa/Abidjan.

Bonus question / rant

Why does Temporal invent this new concept of primary time zone identifiers?

I understand that sometimes an alias is scoped to a country different than the id to which it links (as in the example I quoted), but then if they were intended to be treated differently, why would IANA have created the link relationship? Temporal is very subtly diverging from IANA in their terminology (and therefore behavior), and is setting up all but those who carefully read the documentation to be confused (and I can tell you from my experience over the last few weeks that llms are not up to speed on the distinction). Creating a new standard also has x-framework implications – e.g. in my case our api is not javascript so its lists will not match the front end if the front end is not following IANA standards.

What are the technical consequences of using (click) instead of (ngSubmit) in Angular forms? [closed]

I’m working with Angular forms and I’ve seen different approaches to form submission. Some developers use a (click) handler on a button to handle form submission, while others rely on (ngSubmit) on the element.
Here are two examples:

Common Angular approach:

<form (ngSubmit)="onSubmit()" #form="ngForm">
  <input name="example" ngModel required>
  <button type="submit">Submit</button>
</form>

Alternative approach:

<form>
  <input name="example" ngModel required>
  <button (click)="onSubmit()">Submit</button>
</form>

My Questions

  • Are there technical differences between using (click)=”onSubmit()” and (ngSubmit)=”onSubmit()”?

  • Can using (click) lead to unexpected behaviors?

Thanks!

Is it bad practice to replace (ngSubmit) with a (click) handler in an Angular form?

I’m working on an Angular application and I’ve seen some developers avoid using (ngSubmit) on forms. Instead, they just add a (click) handler to the submit button, like this:

<form>
  <!-- form fields -->
  <button (click)="onSubmit()">Submit</button>
</form>

instead of a more typical approach :

<form (ngSubmit)="onSubmit()" #form="ngForm">
  <!-- form fields -->
  <button type="submit">Submit</button>
</form>

My questions:

  • Is it considered bad practice to use a (click) handler instead of (ngSubmit) on a form?

  • What are the downsides of doing so (validation, accessibility, native behavior, etc.)?

  • Are there any valid scenarios where using (click) instead of (ngSubmit) makes sense?

It works either way, but I feel like using (click) might bypass Angular’s built-in validation and form behavior. I’d love to get some expert input on this.

Thanks!