Premium support for our pure JavaScript UI components


Post by mamorris »

Hi,

We've found an issue where events don't render correctly if the store data is changed whilst the scheduler isn't visible. We have reproduced this in the Salesforce environment, please see the following video:

https://drive.google.com/file/d/1fqXCypfY9VXjuHmH_6xNljDiIH5i8xTh/view?usp=sharing

The code in this scenario is very vanilla, there are no customisations. The scheduler is preloaded with some data in the front-end, and the button sends an event to the scheduler via Lightning Messaging Service. The event contains additional data which is then added to the scheduler event store. This is reproducible on the latest versions of Firefox and Chrome.

Thanks


Post by Maxim Gorkovsky »

Hello.
I can see a problem in the video. Could you please provide more info about this setup? You told you're using basic config from our doc and then send some data to the scheduler. Please share a code snippet that you use to do that. Also, the data that you pass to the scheduler, is that a regular JS objects or instances of salesforce records?


Post by mamorris »

Hi Maxim,

This is the config that we load the scheduler with

	DEFAULT_SCHEDULER_CONFIG = Object.freeze({
		enableDeleteKey: false,
		createEventOnDblClick: false,
		eventStyle: 'border',
		zoomOnTimeAxisDoubleClick: false,
		zoomOnMouseWheel: true,
		startDate: new Date(2017, 0, 1, 6),
		endDate: new Date(2017, 0, 1, 20),
		viewPreset: 'hourAndDay'
	});

DEFAULT_SCHEDULER_COLUMN_CONFIG = Object.freeze({
	enableCellContextMenu: false,
	enableHeaderContextMenu: false,
	showEventCount: false,
	showImage: true,
	sortable: false,
	type: 'resourceInfo'
});

We create the scheduler first

const { id } = createWidget('SchedulerPro', config);

Then add data to it

			scheduler.events = events;
			scheduler.resources = resources;

This is what the data looks like - it's pure front-end

const resources = [
	{ id: 'r1', name: 'Franke' },
	{ id: 'r2', name: 'India' },
	{ id: 'r3', name: 'Rimas' },
	{ id: 'r4', name: 'Stuart' },
	{ id: 'r5', name: 'Tim' }
];

const events = [
	{
		resourceId: 'r1',
		startDate: new Date(2017, 0, 1, 10),
		endDate: new Date(2017, 0, 1, 12),
		name: 'Click me'
	},
	{
		resourceId: 'r2',
		startDate: new Date(2017, 0, 1, 12),
		endDate: new Date(2017, 0, 1, 13),
		name: 'Drag me'
	},
	{
		resourceId: 'r3',
		startDate: new Date(2017, 0, 1, 14),
		endDate: new Date(2017, 0, 1, 16),
		name: 'Double click me'
	},
	{
		resourceId: 'r4',
		startDate: new Date(2017, 0, 1, 8),
		endDate: new Date(2017, 0, 1, 11),
		name: 'Right click me',
		eventColor: 'red'
	},
	{
		resourceId: 'r5',
		startDate: new Date(2017, 0, 1, 15),
		endDate: new Date(2017, 0, 1, 17),
		name: 'Resize me'
	},
	{
		resourceId: 'r3',
		startDate: new Date(2017, 0, 1, 16),
		endDate: new Date(2017, 0, 1, 18),
		name: 'Important meeting'
	},
	{
		resourceId: 'r4',
		startDate: new Date(2017, 0, 1, 6),
		endDate: new Date(2017, 0, 1, 8),
		name: 'Sports event'
	},
	{
		resourceId: 'r5',
		startDate: new Date(2017, 0, 1, 9),
		endDate: new Date(2017, 0, 1, 12),
		name: "Dad's birthday",
		eventStyle: 'line',
		eventColor: 'blue'
	}
];

export function getSchedulerData() {
	return {
		resources: resources,
		events: events
	};
}

When the button is clicked, the data is cleared, like this:

scheduler.eventStore.removeAll();
			scheduler.resourceStore.removeAll();

and then the same amount of data is added again

const resources = [
	{ id: 'r6', name: 'Simone' },
	{ id: 'r7', name: 'Eric' },
	{ id: 'r8', name: 'Charlotte' },
	{ id: 'r9', name: 'Omni' },
	{ id: 'r10', name: 'Neville' },
	{ id: 'r11', name: 'Derek' }
];

const events = [
	{
		resourceId: 'r11',
		startDate: new Date(2017, 0, 1, 10),
		endDate: new Date(2017, 0, 1, 12),
		name: 'Click me'
	},
	{
		resourceId: 'r6',
		startDate: new Date(2017, 0, 1, 12),
		endDate: new Date(2017, 0, 1, 13),
		name: 'Drag me'
	},
	{
		resourceId: 'r7',
		startDate: new Date(2017, 0, 1, 14),
		endDate: new Date(2017, 0, 1, 16),
		name: 'Double click me'
	},
	{
		resourceId: 'r8',
		startDate: new Date(2017, 0, 1, 8),
		endDate: new Date(2017, 0, 1, 11),
		name: 'Right click me',
		eventColor: 'red'
	},
	{
		resourceId: 'r9',
		startDate: new Date(2017, 0, 1, 15),
		endDate: new Date(2017, 0, 1, 17),
		name: 'Resize me'
	},
	{
		resourceId: 'r10',
		startDate: new Date(2017, 0, 1, 16),
		endDate: new Date(2017, 0, 1, 18),
		name: 'Important meeting'
	},
	{
		resourceId: 'r9',
		startDate: new Date(2017, 0, 1, 6),
		endDate: new Date(2017, 0, 1, 8),
		name: 'Sports event'
	},
	{
		resourceId: 'r7',
		startDate: new Date(2017, 0, 1, 9),
		endDate: new Date(2017, 0, 1, 12),
		name: "Dad's birthday",
		eventStyle: 'line',
		eventColor: 'blue'
	}
];

export function getSchedulerAdditionalData() {
	return {
		resources: resources,
		events: events
	};
}

Hope that helps, thanks!


Post by alex.l »

Hi mamorris,

We are investigating this issue. We need a bit more time to check it. We will get back when we have news!

Thank you!

All the best,
Alex


Post by Maxim Gorkovsky »

Problem is reproduced, I opened ticket here: https://github.com/bryntum/support/issues/3422

Long story short, when LWC gets out of the view locker service reports its height as 0. And when you assign new events/resources in the next tab you force refresh which now thinks that scheduler height is 0. That's why you see a subset of events.

To fix this, you will have to refresh scheduler when it becomes visible again (when you navigate back to the tab). Unfortunately there's no public API to update the size, so options are:

  1. Render rows twice. This API is public and gets job done, although it renders events twice which is far from optimal
    scheduler.renderRows()
    scheduler.renderRows()
  2. Refer to the private API until we get you proper public one:
    scheduler.onHeightChange();
    scheduler.renderRows();
  3. Change column size. That eventually invokes same onHeightChange but through public api:
    scheduler.columns[0].width += 1;

Post by mamorris »

Hi Maxim,

Thanks for looking into this.
Unfortunately, I don't believe there is any way of knowing when tabs are switched on a lightning page in Salesforce. I think this is a platform limitation. If you know of a way then that would be very useful!

Until then we will have to wait for the bug fix.

Thanks.


Post by gbrdhvndi »

There is a way though...

import { LightningElement, wire } from 'lwc';
import { CurrentPageReference } from 'lightning/navigation';

export default class Bla extends LightningElement {
    @wire (CurrentPageReference)
    setCurrentPageReference(pageRef) {
        // TODO: react to page reference change
    }
}

Aleksei


Post by mamorris »

gbrdhvndi wrote: Fri Sep 17, 2021 12:54 am

There is a way though...

import { LightningElement, wire } from 'lwc';
import { CurrentPageReference } from 'lightning/navigation';

export default class Bla extends LightningElement {
    @wire (CurrentPageReference)
    setCurrentPageReference(pageRef) {
        // TODO: react to page reference change
    }
}

This doesn't work for switching tabs on a lightning page (e.g. switching between the Scheduler and Button tabs in the video linked in the original post). It fires once when the tab is first loaded, but not each time. I think it's used for switching lightning tabs, which is not the same as tabs on a lightning page.


Post by Maxim Gorkovsky »

For switching tabs you can try using onactive handler on the individual tab, as described in this demo: https://developer.salesforce.com/docs/component-library/bundle/lightning-tabset/documentation

This configuration (simplified) works perfectly for me:

// app.html
<template>
    <lightning-tabset class="tabs">
        <lightning-tab label="Item One" onactive={handleActiveOne}>
            <c-prodemo></c-prodemo>
        </lightning-tab>
        <lightning-tab label="Item Two" title="2nd tab extended title">
            <lightning-button label="Click me" title="Non-primary action" onclick={onButtonClick}></lightning-button>
        </lightning-tab>
    </lightning-tabset>
</template>

// app.js
export default class AppTest extends LightningElement {
    @wire(MessageContext) messageContext;

    handleActiveOne() {
         // Notify scheduler component about active tab
        publish(this.messageContext, testChannel, { refresh : true });
    }
}

// prodemo.js
export default class Prodemo extends LightningElement {
    @wire(MessageContext) messageContext;

    handleMessage(message) {
        const
            { scheduler } = this,
            { events, resources } = message;

        if (events && resources) {
            scheduler.eventStore.removeAll();
            scheduler.resourceStore.removeAll();

            scheduler.events = events;
            scheduler.resources = resources;
        }
        // Handle refresh flag and update scheduler
        else if (message.refresh) {
            scheduler.onHeightChange();
            scheduler.refresh();
        }
    }
}

Post by mamorris »

Hi Maxim,

It's not the LWC tabs either, it's lightning page tabs. As per this screenshot:

Screenshot 2021-09-21 at 11.41.27.png
Screenshot 2021-09-21 at 11.41.27.png (78.77 KiB) Viewed 1292 times

This is in the Lightning App Builder, accessed from the 'Edit Page' option from the settings cog (whilst on an object, home or app page).


Post Reply