Our pure JavaScript Scheduler component


Post by thorthor »

Currently I am debugging with a weird behavior that I can not fully replicate in a test case, but I will try to describe what is going on.



In the react application I am working on, we have the scheduler wrapped as a react component that receives events and resources and props. Every time the events or resources update, the bryntum engine gets updated similarly to your react example.
this.schedulerEngine.resources = props.resources;
this.schedulerEngine.eventStore.data = props.events;

Now, every time that events update, the row gets raised in height for the value of a single event.

This is the initial state.
initial-state.png
initial-state.png (63.32 KiB) Viewed 3062 times
And this is the state after new events get loaded.
updated-state.png
updated-state.png (57.56 KiB) Viewed 3062 times
Those events are constructed outside of the BryntumScheduler component. They are subclasses of EventModel and they get initialized before render. Resources are of type tree and they are subclasses of the ResourceModel. An important thing to possibly stand out is that loaded events can be the same, just constructed again on render and they will still produce this bug.

Below is a pseudo typescript example of what is actually going on outside BryntumScheduler.

class FooResource extends ResourceModel {
    static get fields() {
        return [{ name: "name", dataSource: "foo" }];
    }

    constructor(resource: RawResource) {
        super(resource);
    }
}

class FooEvent extends EventModel {
    static get fields() {
        return [
            {
                name: "resourceId",
                dataSource: "foo.bar.id",
            },
            {
                name: "startDate",
                dataSource: "computedDateFrom",
            },
            {
                name: "endDate",
                dataSource: "computedDateTo",
            },
        ];
    }

    constructor(event: RawEvent) {
        super(event);
    }
}

export class MyScheduler extends React.Component {
    public render() {
        const resources = this.createResources(this.props.rawResources);
        const events = this.createEvents(this.props.rawEvents);

        return (
            <div>
                <BryntumScheduler
                    resources={resources}
                    events={events}
                    columns={this.createColumns()}
                    features={{ tree: true }}
                />
            </div>
        );
    }

    private createColumns() {
        return [
            {
                field: "name",
                text: "text",
                type: "tree",
                expandedFolderIconCls: null,
                collapsedFolderIconCls: null,
                leafIconCls: null,
                width: "15%",
                id: "my-id",
            },
        ];
    }

    private createResources(rawResources) {
        return rawResources.map((resource) => {
            return new FooResource(resource);
        });
    }

    private createEvents(rawEvents) {
        return rawEvents.map((event) => {
            return new FooEvent(event);
        });
    }
}

And this is the actual BryntumScheduler component.
import * as React from "react";
import { Scheduler } from "bryntum-scheduler/scheduler.umd.js";
import { BryntumSchedulerProps } from "./interfaces/BryntumSchedulerProps";
import "bryntum-scheduler/scheduler.stockholm.css";
import autobind from "autobind-decorator";

export class BryntumScheduler extends React.Component<BryntumSchedulerProps> {
    public static defaultProps: Partial<BryntumSchedulerProps> = {
        autoWidthColumnsWithId: [],
        extendTimelineDayCount: 3,
        zoomOnMouseWheel: true,
    };

    public el: HTMLElement | null;
    private schedulerEngine: Scheduler;

    public componentWillUnmount() {
        this.schedulerEngine.destroy();
    }

    // tslint:disable-next-line:newspaper-order
    public componentDidMount(): void {
        if (this.el === null) {
            throw new Error(
                "Container div is null, can not instantiate scheduler",
            );
        }

        this.schedulerEngine = new Scheduler({
            height: this.props.height,
            // rowHeight: 60,
            // barMargin: 0,
            appendTo: this.el,
            columns: this.props.columns,
            events: this.props.events,
            resources: this.props.resources,
            // startDate: this.props.startDate,
            // endDate: this.props.endDate,
            // emptyText: SchedulerMessage.EmptyRows,
            // viewPreset: ViewPreset.DayAndWeek,
            features: this.props.features,
            autoAdjustTimeAxis: false,
            zoomOnMouseWheel: this.props.zoomOnMouseWheel,
            zoomOnTimeAxisDoubleClick: false,
            // minZoomLevel: 9,
            // maxZoomLevel: 10,
            // callOnFunctions: true,
        });
    }

    public render() {
        return <div ref={(el) => (this.el = el)} />;
    }

    public componentDidUpdate(prevProps: BryntumSchedulerProps) {
        this.updateEngineProperties(prevProps);
    }

    @autobind
    private updateEngineProperties(prevProps: BryntumSchedulerProps) {
        this.schedulerEngine.resources = this.props.resources;
        this.schedulerEngine.eventStore.data = this.props.events;

        if (prevProps.startDate !== this.props.startDate) {
            this.schedulerEngine.startDate = this.props.startDate;
        }

        if (prevProps.endDate !== this.props.endDate) {
            this.schedulerEngine.endDate = this.props.endDate;
        }
    }
}


I've constructed a simple test case that actually does not reproduce the aforementioned error, but creates a similar one.

Steps for installation:
1. extract scheduler-2.0.2 to vendor directory
2. npm install


Steps to reproduce:
1. before zooming click on update events
2. zoom out timeline

The bug that occurs should show only new events that are rendered in the scheduler, but instead it shows filled rows with unclickable events. If if zoom out first and them click on update events that but is not present. With this example I tried to replicate our case, but did not get the bug that I described on the start of this post. Also, if I remove the viewPreset key from options config, events accumulate if I don't zoom out before update, but get reset to correct state after the zooming out.

Could you tell what is wrong with the examples I gave you? Am I maybe constructing resource and event models wrong?

Post by pmiklashevich »

Hello,

So you're reporting about 2 issues as far as I get it.
simple test case that actually does not reproduce the aforementioned error
If a testcase doesn't reproduce the issue, it's not a testcase :)
Please provide something runnable we can look at, or instructions of how to reproduce with one of our samples.

About the second issue.
Steps to reproduce:
1. before zooming click on update events
2. zoom out timeline

The bug that occurs should show only new events that are rendered in the scheduler, but instead it shows filled rows with unclickable events. If if zoom out first and them click on update events that but is not present. With this example I tried to replicate our case, but did not get the bug that I described on the start of this post. Also, if I remove the viewPreset key from options config, events accumulate if I don't zoom out before update, but get reset to correct state after the zooming out.
Could you please provide more details and clear steps on what I should do, what I should pay attention to, and what the expected result is?

Please also mention what browser you're using.

Your test case is runnable and events look good when I zoom in/out.

Cheers,
Pavel

Pavlo Miklashevych
Sr. Frontend Developer


Post by thorthor »

Hello Pavel,

thank you for the reply. I agree that the concept of test case relies on its reproducibility therefore I tried to improve it with functional examples. :D

Some additional information about the test:
- link to repo: https://github.com/renatoruk/bryntum-scheduler-react-test

Browsers:

OS: High Sierra 10.13.6.
Tested on both chrome and firefox - bug is present on both browsers.

Chrome: Version 74.0.3729.169 (Official Build) (64-bit)
Firefox: 67.0

Windows 10 (in browserstack)
Chrome 74

Tested only on chrome for windows, but bug is still reproducible.


Steps to reproduce:
1. After installation run “npm run start”
2. open browser on localhost:3000
3. first, before any other activity press update events (that will create a new dataset of FooEvents and pass it into BryntumScheduler component)
4. try to zoom out of the timeline



I’ve recorded two sample videos, each showing a different scenario of the outcome.
Video 1 (https://youtu.be/TajgXFhxJZ8) - Bryntum-React-bug shows a scenario I’ve written out in “steps to reproduce”
Video 2 (https://youtu.be/HnEFrHZdk8o) - Bryntum-React-no-bug shows how to prevent the bug - by first zooming in or out in the timeline.


Unfortunately, the bug we have in the main application is not reproducible with this example, but I will try to address some concerns regarding those issues.



Is it valid flow to construct superclasses from EventModels multiple times with same ID-s? I haven’t seen any example that has specifically this case:


class MyEvent extends EventModel {}
class MyResource extends ResourceModel {}

// arbitrary collection length
const events = [new MyEvent({
id: 1,
name: "name"
})];
const resources = [new MyResource({
id: 1,
name: "res-name"
})];

const engine = new Scheduler({
    ...
    resources: resources,
    events: events,
});


// and then later after external update
const newEvents= [new MyEvent({
id: 1,
name: "name"
})];
const newResources = [new MyResource({
id: 1,
name: "res-name"
})];

engine.eventStore.data = newEvents;
engine.resources = newResources;
Can MyEvent and MyResource be passed directly to scheduler as in the written example or they must be injected in some other way?

Best,
Renato

Post by mats »

Try with Scheduler 2.0.3 that we just released?

Post by thorthor »

Hello Mats,

actually for the test case the issue is fixed!

Unfortunately, in the first case I mentioned the height is still accumulated on every eventStore update.
Events are clickable and draggable, but row height keeps getting incremented on every store replacement.

I will try to investigate further on Monday to replicate the specific issue. Please let me know if you have any guides on how to fix this or you've seen some behaviour similar to this.

Thank you for your help

Post by thorthor »

Ok, so I've managed to get a reproducible test case in the latest commit -> https://github.com/renatoruk/bryntum-scheduler-react-test

The expected behavior and steps are following:

1. run the app
2. click on update events (which will always generate resources and events with same ID-s and instantiate FooResources and FooEvents and pass them to scheduler)
3. the row height gets accumulated with events even though the store should be replaced

Screen Shot 2019-05-27 at 15.49.31.png
Screen Shot 2019-05-27 at 15.49.31.png (73.41 KiB) Viewed 3032 times
Can you tell what's happening? Is maybe FooEvent the culprit with resourceId field that gets referenced from bar.id?

Post by mats »

App not runnable:
./src/App.tsx
Module not found: Can't resolve 'bryntum-scheduler/scheduler.stockholm.css' in '/Library/WebServer/Documents/react/src'
Please upload something we can run/debug?

Post by thorthor »

Instructions for installation:

1. clone repository
2. create vendor folder in the root directory
3. paste scheduler-vanilla-2.0.3.zip in the vendor directory
4. extract scheduler to that folder
5. run npm install in the project's root directory
6. run "npm run start" after installation

Post by thorthor »

Hello mats, do you need any additional information for this issue?

I can reproduce the issue in the browser with the example I gave you, can you give me some insight in debugging the issue?

Thank you,
Renato

Post by pmiklashevich »

Hello Renato,

Following your instructions I'm able to see what you described. Investigation will take some time, so I'll try to get back to you till the end of this week with an update. Stay tuned.

Cheers,
Pavel

Pavlo Miklashevych
Sr. Frontend Developer


Post Reply