Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
5619075
[scheduler-premium] Dependencies - Reject dependencies that would cre…
rita-codes Aug 20, 2026
19e0a62
Shorten the comments
rita-codes Aug 21, 2026
15e5b22
Apply review: duplicate-first guard order, lookup-based walk, sturdie…
rita-codes Aug 21, 2026
8ceba35
Merge branch 'master' into 22858-scheduler-dependencies-cycle-guard
rita-codes Aug 21, 2026
1e297cd
Run prettier
rita-codes Aug 21, 2026
b34b83c
Pin the walk termination on a pre-existing cycle
rita-codes Aug 21, 2026
571b281
Document the same-tick limitation on addDependency
rita-codes Aug 21, 2026
f737229
[scheduler-premium] Dependencies - Auto-scheduling engine (FS)
rita-codes Aug 20, 2026
8b2e370
Merge branch 'master' into 22858-scheduler-dependencies-cycle-guard
rita-codes Aug 21, 2026
fd3e468
Merge remote-tracking branch 'upstream/master' into 22857-scheduler-d…
rita-codes Aug 21, 2026
6629c74
Merge branch 'master' into 22858-scheduler-dependencies-cycle-guard
rita-codes Aug 24, 2026
b9d8728
Apply review: cached by-source grouping, branching cycle test, docs
rita-codes Aug 24, 2026
3031afe
Shorten the cache comment
rita-codes Aug 24, 2026
5e40684
Merge branch 'master' into 22858-scheduler-dependencies-cycle-guard
rita-codes Aug 24, 2026
50edf78
Merge branch 'master' into 22858-scheduler-dependencies-cycle-guard
rita-codes Aug 24, 2026
182ac4a
Merge branch '22858-scheduler-dependencies-cycle-guard' into 22857-sc…
rita-codes Aug 24, 2026
9eb611d
Merge remote-tracking branch 'upstream/master' into 22857-scheduler-d…
rita-codes Aug 24, 2026
7b85409
Merge remote-tracking branch 'upstream/master' into 22857-scheduler-d…
rita-codes Aug 27, 2026
a8691a0
Use explicit vitest imports in the new test files
rita-codes Aug 27, 2026
bb44bf1
Run prettier
rita-codes Aug 27, 2026
61835c0
Clamp a repositioned event dropped before its predecessor's end
rita-codes Aug 27, 2026
287e9ed
Merge branch 'master' of github.com:mui/mui-x into 22857-scheduler-de…
rita-codes Aug 28, 2026
0663f9a
Absorb the initial ResizeObserver batch inside the dependency test ha…
rita-codes Aug 28, 2026
1d19b69
Merge remote-tracking branch 'upstream/master' into 22857-scheduler-d…
rita-codes Aug 28, 2026
3a4416a
Merge branch 'master' into 22857-scheduler-dependencies-auto-scheduli…
rita-codes Aug 28, 2026
7a62bb3
Extend the observer absorb to drag steps and await the async harness
rita-codes Aug 28, 2026
7a10466
Apply review: real repositioning, store-mirrored seeding, sturdier en…
rita-codes Aug 28, 2026
0ed58c1
Merge remote-tracking branch 'origin/22857-scheduler-dependencies-aut…
rita-codes Aug 28, 2026
56c2edc
Merge remote-tracking branch 'upstream/master' into 22857-scheduler-d…
rita-codes Aug 28, 2026
24fa03d
Migrate the drag marker to the pragmatic v3 entry point
rita-codes Aug 28, 2026
471e491
Reject a batch whose cascade would move a read-only event
rita-codes Aug 28, 2026
1ab7da9
Merge branch 'master' into 22857-scheduler-dependencies-auto-scheduli…
rita-codes Aug 28, 2026
a6ab77e
Apply review round 3: data-zone entry resolution, observable veto
rita-codes Aug 31, 2026
0f01d7d
Merge remote-tracking branch 'upstream/master' into 22857-scheduler-d…
rita-codes Aug 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 187 additions & 0 deletions docs/data/scheduler/experiments/TimelineAutoScheduling.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import * as React from 'react';

import { eventTimelinePremiumClasses } from '@mui/x-scheduler-premium/event-timeline-premium';
import {
EventTimelinePremiumContent,
EventTimelinePremiumStyledContext,
} from '@mui/x-scheduler-premium/internals';
import { useEventTimelinePremium } from '@mui/x-scheduler-internals-premium/use-event-timeline-premium';

import { SchedulerStoreContext } from '@mui/x-scheduler-internals/use-scheduler-store-context';
import {
ErrorContainer,
EventEditingStyledContext,
SharedComponentsStyledContext,
EVENT_TIMELINE_DEFAULT_LOCALE_TEXT,
} from '@mui/x-scheduler/internals';

const resources = [
{ id: 'build', title: 'Build' },
{ id: 'release', title: 'Release' },
{ id: 'ops', title: 'Ops' },
];

// The dataset covers the cascade behaviors: a chain with slack between its links, a
// diamond reconverging on one successor, a read-only successor, and an all-day pair.
// Early hours so everything is inside the initial viewport without scrolling.
const initialEvents = [
// Chain with slack: plan → build → deploy.
{
id: 'plan',
title: 'Plan',
start: '2025-07-03T01:00:00',
end: '2025-07-03T02:00:00',
resource: 'build',
},
{
id: 'build',
title: 'Build',
start: '2025-07-03T03:00:00',
end: '2025-07-03T04:30:00',
resource: 'build',
},
{
id: 'deploy',
title: 'Deploy',
start: '2025-07-03T05:00:00',
end: '2025-07-03T06:00:00',
resource: 'build',
},
// Diamond: design → front / back → integrate.
{
id: 'design',
title: 'Design',
start: '2025-07-03T01:00:00',
end: '2025-07-03T02:00:00',
resource: 'release',
},
{
id: 'front',
title: 'Front-end',
start: '2025-07-03T02:30:00',
end: '2025-07-03T05:00:00',
resource: 'release',
},
{
id: 'back',
title: 'Back-end',
start: '2025-07-03T02:30:00',
end: '2025-07-03T03:30:00',
resource: 'ops',
},
{
id: 'integrate',
title: 'Integrate',
start: '2025-07-03T05:30:00',
end: '2025-07-03T06:30:00',
resource: 'release',
},
// A read-only successor never moves: a change that would need to move it is rejected.
{
id: 'setup',
title: 'Setup',
start: '2025-07-03T01:30:00',
end: '2025-07-03T02:30:00',
resource: 'ops',
},
{
id: 'audit',
title: 'Audit (read-only)',
start: '2025-07-03T04:00:00',
end: '2025-07-03T05:00:00',
resource: 'ops',
readOnly: true,
},
// All-day pair: the successor shifts by whole days and stays all-day.
{
id: 'prep-day',
title: 'Prep day',
start: '2025-07-03T00:00:00',
end: '2025-07-03T23:59:59.999',
allDay: true,
resource: 'ops',
},
{
id: 'launch-day',
title: 'Launch day',
start: '2025-07-04T00:00:00',
end: '2025-07-04T23:59:59.999',
allDay: true,
resource: 'ops',
},
];

const initialDependencies = [
{ id: 'd1', source: 'plan', target: 'build', type: 'FinishToStart' },
{ id: 'd2', source: 'build', target: 'deploy', type: 'FinishToStart' },
{ id: 'd3', source: 'design', target: 'front', type: 'FinishToStart' },
{ id: 'd4', source: 'design', target: 'back', type: 'FinishToStart' },
{ id: 'd5', source: 'front', target: 'integrate', type: 'FinishToStart' },
{ id: 'd6', source: 'back', target: 'integrate', type: 'FinishToStart' },
{ id: 'd7', source: 'setup', target: 'audit', type: 'FinishToStart' },
{ id: 'd8', source: 'prep-day', target: 'launch-day', type: 'FinishToStart' },
];

const styledContextValue = {
schedulerId: 'experiment-auto-scheduling',
classes: eventTimelinePremiumClasses,
localeText: EVENT_TIMELINE_DEFAULT_LOCALE_TEXT,
};
const sharedStyledContextValue = { classes: eventTimelinePremiumClasses };

export default function TimelineAutoScheduling() {
const [events, setEvents] = React.useState(initialEvents);
const [dependencies, setDependencies] = React.useState(initialDependencies);

// `dependencies` has no public API yet (#22857), so instead of `<EventTimelinePremium />`
// this experiment feeds the internal store parameters to the same hook the component
// uses and renders its content inside the same providers.
const parameters = {
events,
onEventsChange: setEvents,
resources,
dependencies,
onDependenciesChange: setDependencies,
defaultVisibleDate: new Date('2025-07-03T00:00:00'),
defaultPreset: 'dayAndHour',
areEventsDraggable: true,
areEventsResizable: true,
};
const store = useEventTimelinePremium(parameters);
// The context is typed on the base scheduler state and the store generic is
// invariant, so the premium store (extra state slices) needs a widened type.
const storeContextValue = store;

return (
/* Mimics the layout, font-size and box-sizing reset the `EventTimelinePremium`
root provides to the content (the row-height CSS resolves against them). */
<div
className="experiment-auto-scheduling-host"
style={{
height: 420,
width: '100%',
display: 'flex',
flexDirection: 'column',
fontSize: '0.875rem',
// Contains the ErrorContainer toasts, absolutely positioned bottom-right.
position: 'relative',
}}
>
<style>
{
'.experiment-auto-scheduling-host, .experiment-auto-scheduling-host * { box-sizing: border-box; }'
}
</style>
<SchedulerStoreContext.Provider value={storeContextValue}>
<EventTimelinePremiumStyledContext.Provider value={styledContextValue}>
<EventEditingStyledContext.Provider value={styledContextValue}>
<SharedComponentsStyledContext.Provider value={sharedStyledContextValue}>
<EventTimelinePremiumContent />
<ErrorContainer />
</SharedComponentsStyledContext.Provider>
</EventEditingStyledContext.Provider>
</EventTimelinePremiumStyledContext.Provider>
</SchedulerStoreContext.Provider>
</div>
);
}
195 changes: 195 additions & 0 deletions docs/data/scheduler/experiments/TimelineAutoScheduling.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
import * as React from 'react';
import { SchedulerEvent, SchedulerResource } from '@mui/x-scheduler/models';
import { eventTimelinePremiumClasses } from '@mui/x-scheduler-premium/event-timeline-premium';
import {
EventTimelinePremiumContent,
EventTimelinePremiumStyledContext,
} from '@mui/x-scheduler-premium/internals';
import {
useEventTimelinePremium,
EventTimelinePremiumStoreParameters,
} from '@mui/x-scheduler-internals-premium/use-event-timeline-premium';
import { SchedulerDependency } from '@mui/x-scheduler-internals-premium/models';
import { SchedulerStoreContext } from '@mui/x-scheduler-internals/use-scheduler-store-context';
import {
ErrorContainer,
EventEditingStyledContext,
SharedComponentsStyledContext,
EVENT_TIMELINE_DEFAULT_LOCALE_TEXT,
} from '@mui/x-scheduler/internals';

const resources: SchedulerResource[] = [
{ id: 'build', title: 'Build' },
{ id: 'release', title: 'Release' },
{ id: 'ops', title: 'Ops' },
];

// The dataset covers the cascade behaviors: a chain with slack between its links, a
// diamond reconverging on one successor, a read-only successor, and an all-day pair.
// Early hours so everything is inside the initial viewport without scrolling.
const initialEvents: SchedulerEvent[] = [
// Chain with slack: plan → build → deploy.
{
id: 'plan',
title: 'Plan',
start: '2025-07-03T01:00:00',
end: '2025-07-03T02:00:00',
resource: 'build',
},
{
id: 'build',
title: 'Build',
start: '2025-07-03T03:00:00',
end: '2025-07-03T04:30:00',
resource: 'build',
},
{
id: 'deploy',
title: 'Deploy',
start: '2025-07-03T05:00:00',
end: '2025-07-03T06:00:00',
resource: 'build',
},
// Diamond: design → front / back → integrate.
{
id: 'design',
title: 'Design',
start: '2025-07-03T01:00:00',
end: '2025-07-03T02:00:00',
resource: 'release',
},
{
id: 'front',
title: 'Front-end',
start: '2025-07-03T02:30:00',
end: '2025-07-03T05:00:00',
resource: 'release',
},
{
id: 'back',
title: 'Back-end',
start: '2025-07-03T02:30:00',
end: '2025-07-03T03:30:00',
resource: 'ops',
},
{
id: 'integrate',
title: 'Integrate',
start: '2025-07-03T05:30:00',
end: '2025-07-03T06:30:00',
resource: 'release',
},
// A read-only successor never moves: a change that would need to move it is rejected.
{
id: 'setup',
title: 'Setup',
start: '2025-07-03T01:30:00',
end: '2025-07-03T02:30:00',
resource: 'ops',
},
{
id: 'audit',
title: 'Audit (read-only)',
start: '2025-07-03T04:00:00',
end: '2025-07-03T05:00:00',
resource: 'ops',
readOnly: true,
},
// All-day pair: the successor shifts by whole days and stays all-day.
{
id: 'prep-day',
title: 'Prep day',
start: '2025-07-03T00:00:00',
end: '2025-07-03T23:59:59.999',
allDay: true,
resource: 'ops',
},
{
id: 'launch-day',
title: 'Launch day',
start: '2025-07-04T00:00:00',
end: '2025-07-04T23:59:59.999',
allDay: true,
resource: 'ops',
},
];

const initialDependencies: SchedulerDependency[] = [
{ id: 'd1', source: 'plan', target: 'build', type: 'FinishToStart' },
{ id: 'd2', source: 'build', target: 'deploy', type: 'FinishToStart' },
{ id: 'd3', source: 'design', target: 'front', type: 'FinishToStart' },
{ id: 'd4', source: 'design', target: 'back', type: 'FinishToStart' },
{ id: 'd5', source: 'front', target: 'integrate', type: 'FinishToStart' },
{ id: 'd6', source: 'back', target: 'integrate', type: 'FinishToStart' },
{ id: 'd7', source: 'setup', target: 'audit', type: 'FinishToStart' },
{ id: 'd8', source: 'prep-day', target: 'launch-day', type: 'FinishToStart' },
];

const styledContextValue = {
schedulerId: 'experiment-auto-scheduling',
classes: eventTimelinePremiumClasses,
localeText: EVENT_TIMELINE_DEFAULT_LOCALE_TEXT,
};
const sharedStyledContextValue = { classes: eventTimelinePremiumClasses };

export default function TimelineAutoScheduling() {
const [events, setEvents] = React.useState(initialEvents);
const [dependencies, setDependencies] = React.useState<
readonly SchedulerDependency[] | undefined
>(initialDependencies);

// `dependencies` has no public API yet (#22857), so instead of `<EventTimelinePremium />`
// this experiment feeds the internal store parameters to the same hook the component
// uses and renders its content inside the same providers.
const parameters: EventTimelinePremiumStoreParameters<
SchedulerEvent,
SchedulerResource
> = {
events,
onEventsChange: setEvents,
resources,
dependencies,
onDependenciesChange: setDependencies,
defaultVisibleDate: new Date('2025-07-03T00:00:00'),
defaultPreset: 'dayAndHour',
areEventsDraggable: true,
areEventsResizable: true,
};
const store = useEventTimelinePremium(parameters);
// The context is typed on the base scheduler state and the store generic is
// invariant, so the premium store (extra state slices) needs a widened type.
const storeContextValue = store as any;

return (
/* Mimics the layout, font-size and box-sizing reset the `EventTimelinePremium`
root provides to the content (the row-height CSS resolves against them). */
<div
className="experiment-auto-scheduling-host"
style={{
height: 420,
width: '100%',
display: 'flex',
flexDirection: 'column',
fontSize: '0.875rem',
// Contains the ErrorContainer toasts, absolutely positioned bottom-right.
position: 'relative',
}}
>
<style>
{
'.experiment-auto-scheduling-host, .experiment-auto-scheduling-host * { box-sizing: border-box; }'
}
</style>
<SchedulerStoreContext.Provider value={storeContextValue}>
<EventTimelinePremiumStyledContext.Provider value={styledContextValue}>
<EventEditingStyledContext.Provider value={styledContextValue}>
<SharedComponentsStyledContext.Provider value={sharedStyledContextValue}>
<EventTimelinePremiumContent />
<ErrorContainer />
</SharedComponentsStyledContext.Provider>
</EventEditingStyledContext.Provider>
</EventTimelinePremiumStyledContext.Provider>
</SchedulerStoreContext.Provider>
</div>
);
}
Loading
Loading