Skip to content

Commit 6ec495c

Browse files
Revert "[scheduler] Add basic UI to edit the monthly/weekly recurrence rule of an event (mui#20317)"
This reverts commit 48381b0.
1 parent 277b71e commit 6ec495c

File tree

8 files changed

+14
-310
lines changed

8 files changed

+14
-310
lines changed

packages/x-scheduler-headless/src/scheduler-selectors/schedulerRecurringEventSelectors.ts

Lines changed: 1 addition & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,10 @@ import { createSelector, createSelectorMemoized } from '@base-ui-components/util
22
import {
33
RecurringEventPresetKey,
44
RecurringEventRecurrenceRule,
5-
RecurringEventWeekDayCode,
65
SchedulerProcessedDate,
7-
SchedulerValidDate,
86
} from '../models';
97
import { SchedulerState as State } from '../utils/SchedulerStore/SchedulerStore.types';
10-
import {
11-
computeMonthlyOrdinal,
12-
getWeekDayCode,
13-
serializeRRule,
14-
} from '../utils/recurring-event-utils';
8+
import { getWeekDayCode, serializeRRule } from '../utils/recurring-event-utils';
159

1610
export const schedulerRecurringEventSelectors = {
1711
/**
@@ -135,41 +129,4 @@ export const schedulerRecurringEventSelectors = {
135129
return serializeRRule(adapter, rruleA) === serializeRRule(adapter, rruleB);
136130
},
137131
),
138-
/**
139-
* Returns the 7 week days with code and date, starting at startOfWeek(visibleDate).
140-
*/
141-
weeklyDays: createSelectorMemoized(
142-
(state: State) => state.adapter,
143-
(state: State) => state.visibleDate,
144-
(adapter, visibleDate): { code: RecurringEventWeekDayCode; date: SchedulerValidDate }[] => {
145-
const start = adapter.startOfWeek(visibleDate);
146-
return Array.from({ length: 7 }, (_, i) => {
147-
const date = adapter.addDays(start, i);
148-
return { code: getWeekDayCode(adapter, date), date };
149-
});
150-
},
151-
),
152-
153-
/**
154-
* Returns month reference for the given occurrence: dayOfMonth, weekday code and ordinal.
155-
*/
156-
monthlyReference: createSelectorMemoized(
157-
(state: State) => state.adapter,
158-
(
159-
adapter,
160-
date: SchedulerProcessedDate,
161-
): {
162-
dayOfMonth: number;
163-
code: RecurringEventWeekDayCode;
164-
ord: number;
165-
date: SchedulerValidDate;
166-
} => {
167-
return {
168-
dayOfMonth: adapter.getDate(date.value),
169-
code: getWeekDayCode(adapter, date.value),
170-
ord: computeMonthlyOrdinal(adapter, date.value),
171-
date: date.value,
172-
};
173-
},
174-
),
175132
};

packages/x-scheduler/src/internals/components/event-popover/EventPopover.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@ p {
389389
border: none;
390390
padding: 0;
391391
margin: 0;
392+
font-size: var(--font-size-2);
392393

393394
&[data-disabled] {
394395
color: var(--disabled-text-color);
@@ -408,7 +409,6 @@ p {
408409
display: flex;
409410
align-items: center;
410411
gap: var(--space-2);
411-
font-size: var(--font-size-2);
412412
}
413413

414414
.EventPopoverAfterTimesInputWrapper {

packages/x-scheduler/src/internals/components/event-popover/EventPopover.test.tsx

Lines changed: 0 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -942,124 +942,6 @@ describe('<EventPopoverContent />', () => {
942942
adapter.startOfDay(adapter.date('2025-07-20T00:00:00')),
943943
);
944944
});
945-
946-
it('should submit custom weekly with selected weekdays', async () => {
947-
const onEventsChange = spy();
948-
949-
const { user } = render(
950-
<EventCalendarProvider
951-
events={[DEFAULT_EVENT]}
952-
resources={resources}
953-
onEventsChange={onEventsChange}
954-
>
955-
<Popover.Root open>
956-
<EventPopoverContent {...defaultProps} />
957-
</Popover.Root>
958-
</EventCalendarProvider>,
959-
);
960-
961-
await user.click(screen.getByRole('tab', { name: /recurrence/i }));
962-
await user.click(screen.getByRole('combobox', { name: /recurrence/i }));
963-
await user.click(await screen.findByRole('option', { name: /custom/i }));
964-
965-
const repeatGroup = screen.getByRole('group', { name: /repeat/i });
966-
const freqCombo = within(repeatGroup).getByRole('combobox');
967-
await user.click(freqCombo);
968-
await user.click(await screen.findByRole('option', { name: /weeks/i }));
969-
970-
// Select Monday and Friday in the weekly day toggles
971-
await user.click(screen.getByRole('button', { name: /monday/i }));
972-
await user.click(screen.getByRole('button', { name: /friday/i }));
973-
974-
await user.click(screen.getByRole('button', { name: /save changes/i }));
975-
976-
expect(onEventsChange.calledOnce).to.equal(true);
977-
const updated = onEventsChange.firstCall.firstArg[0];
978-
979-
expect(updated.rrule).to.deep.equal({
980-
freq: 'WEEKLY',
981-
interval: 1,
982-
byDay: ['MO', 'FR'],
983-
byMonthDay: [],
984-
});
985-
});
986-
987-
it('should submit custom monthly with "day of month" option', async () => {
988-
const onEventsChange = spy();
989-
990-
const { user } = render(
991-
<EventCalendarProvider
992-
events={[DEFAULT_EVENT]}
993-
resources={resources}
994-
onEventsChange={onEventsChange}
995-
>
996-
<Popover.Root open>
997-
<EventPopoverContent {...defaultProps} />
998-
</Popover.Root>
999-
</EventCalendarProvider>,
1000-
);
1001-
1002-
await user.click(screen.getByRole('tab', { name: /recurrence/i }));
1003-
await user.click(screen.getByRole('combobox', { name: /recurrence/i }));
1004-
await user.click(await screen.findByRole('option', { name: /custom/i }));
1005-
1006-
const repeatGroup = screen.getByRole('group', { name: /repeat/i });
1007-
const freqCombo = within(repeatGroup).getByRole('combobox');
1008-
await user.click(freqCombo);
1009-
await user.click(await screen.findByRole('option', { name: /months/i }));
1010-
1011-
await user.click(screen.getByRole('button', { name: /day 26/i })); // DEFAULT_EVENT is 2025-05-26
1012-
1013-
await user.click(screen.getByRole('button', { name: /save changes/i }));
1014-
1015-
expect(onEventsChange.calledOnce).to.equal(true);
1016-
const updated = onEventsChange.firstCall.firstArg[0];
1017-
1018-
expect(updated.rrule).to.deep.equal({
1019-
freq: 'MONTHLY',
1020-
interval: 1,
1021-
byMonthDay: [26],
1022-
});
1023-
});
1024-
1025-
it('should submit custom monthly with "ordinal weekday" option', async () => {
1026-
const onEventsChange = spy();
1027-
1028-
const { user } = render(
1029-
<EventCalendarProvider
1030-
events={[DEFAULT_EVENT]}
1031-
resources={resources}
1032-
onEventsChange={onEventsChange}
1033-
>
1034-
<Popover.Root open>
1035-
<EventPopoverContent {...defaultProps} />
1036-
</Popover.Root>
1037-
</EventCalendarProvider>,
1038-
);
1039-
1040-
await user.click(screen.getByRole('tab', { name: /recurrence/i }));
1041-
await user.click(screen.getByRole('combobox', { name: /recurrence/i }));
1042-
await user.click(await screen.findByRole('option', { name: /custom/i }));
1043-
1044-
const repeatGroup = screen.getByRole('group', { name: /repeat/i });
1045-
const freqCombo = within(repeatGroup).getByRole('combobox');
1046-
await user.click(freqCombo);
1047-
await user.click(await screen.findByRole('option', { name: /months/i }));
1048-
1049-
// The DEFAULT_EVENT (2025-05-26 Mon) is the last Monday of the month ("-1MO")
1050-
await user.click(screen.getByRole('button', { name: /mon.*last week/i }));
1051-
1052-
await user.click(screen.getByRole('button', { name: /save changes/i }));
1053-
1054-
expect(onEventsChange.calledOnce).to.equal(true);
1055-
const updated = onEventsChange.firstCall.firstArg[0];
1056-
1057-
expect(updated.rrule).to.deep.equal({
1058-
freq: 'MONTHLY',
1059-
interval: 1,
1060-
byDay: ['-1MO'],
1061-
});
1062-
});
1063945
});
1064946
});
1065947

packages/x-scheduler/src/internals/components/event-popover/FormContent.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,9 @@ export function FormContent(props: FormContentProps) {
143143
if (controlled.recurrenceSelection === null) {
144144
rruleToSubmit = undefined;
145145
} else if (controlled.recurrenceSelection === 'custom') {
146-
rruleToSubmit = controlled.rruleDraft;
146+
rruleToSubmit = {
147+
...controlled.rruleDraft,
148+
};
147149
} else {
148150
rruleToSubmit = recurrencePresets[controlled.recurrenceSelection];
149151
}

packages/x-scheduler/src/internals/components/event-popover/RecurrenceTab.tsx

Lines changed: 9 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,16 @@ import { Input } from '@base-ui-components/react/input';
77
import { Select } from '@base-ui-components/react/select';
88
import { RadioGroup } from '@base-ui-components/react/radio-group';
99
import { Radio } from '@base-ui-components/react/radio';
10+
import { Separator } from '@base-ui-components/react/separator';
1011
import { ChevronDown } from 'lucide-react';
11-
import { Toggle } from '@base-ui-components/react/toggle';
12-
import { ToggleGroup } from '@base-ui-components/react/toggle-group';
1312
import {
1413
SchedulerEventOccurrence,
1514
RecurringEventFrequency,
1615
RecurringEventPresetKey,
17-
RecurringEventByDayValue,
18-
RecurringEventWeekDayCode,
1916
} from '@mui/x-scheduler-headless/models';
2017
import { useSchedulerStoreContext } from '@mui/x-scheduler-headless/use-scheduler-store-context';
2118
import { useAdapter } from '@mui/x-scheduler-headless/use-adapter';
22-
import {
23-
schedulerEventSelectors,
24-
schedulerRecurringEventSelectors,
25-
} from '@mui/x-scheduler-headless/scheduler-selectors';
19+
import { schedulerEventSelectors } from '@mui/x-scheduler-headless/scheduler-selectors';
2620
import { Tabs } from '@base-ui-components/react/tabs';
2721
import { useTranslations } from '../../utils/TranslationsContext';
2822
import { ControlledValue, EndsSelection, getEndsSelectionFromRRule } from './utils';
@@ -48,12 +42,6 @@ export function RecurrenceTab(props: RecurrenceTabProps) {
4842
occurrence.id,
4943
);
5044
const customDisabled = controlled.recurrenceSelection !== 'custom' || isPropertyReadOnly('rrule');
51-
const monthlyRef = useStore(
52-
store,
53-
schedulerRecurringEventSelectors.monthlyReference,
54-
occurrence.start,
55-
);
56-
const weeklyDays = useStore(store, schedulerRecurringEventSelectors.weeklyDays);
5745

5846
const handleRecurrenceSelectionChange = (value: RecurringEventPresetKey | null | 'custom') => {
5947
if (value === 'custom') {
@@ -145,30 +133,6 @@ export function RecurrenceTab(props: RecurrenceTabProps) {
145133
}));
146134
};
147135

148-
const handleChangeWeeklyDays = (next: RecurringEventWeekDayCode[]) => {
149-
setControlled((prev) => ({
150-
...prev,
151-
rruleDraft: {
152-
...prev.rruleDraft,
153-
byDay: next,
154-
},
155-
}));
156-
};
157-
158-
const handleChangeMonthlyGroup = (next: string[]) => {
159-
const nextKey = next[0];
160-
161-
setControlled((prev) => {
162-
if (nextKey === 'byDay') {
163-
const value = `${monthlyRef.ord}${monthlyRef.code}` as RecurringEventByDayValue;
164-
const { byMonthDay, ...rest } = prev.rruleDraft;
165-
return { ...prev, rruleDraft: { ...rest, byDay: [value] } };
166-
}
167-
const { byDay, ...rest } = prev.rruleDraft;
168-
return { ...prev, rruleDraft: { ...rest, byMonthDay: [monthlyRef.dayOfMonth] } };
169-
});
170-
};
171-
172136
const customEndsValue: 'never' | 'after' | 'until' = getEndsSelectionFromRRule(
173137
controlled.rruleDraft,
174138
);
@@ -219,46 +183,6 @@ export function RecurrenceTab(props: RecurrenceTabProps) {
219183
},
220184
];
221185

222-
const weeklyDayItems = React.useMemo(
223-
() =>
224-
weeklyDays.map(({ code, date }) => ({
225-
value: code,
226-
ariaLabel: adapter.format(date, 'weekday'),
227-
label: adapter.format(date, 'weekdayShort'),
228-
})),
229-
[adapter, weeklyDays],
230-
);
231-
232-
const monthlyItems = React.useMemo(() => {
233-
const ordinal = monthlyRef.ord;
234-
const dayOfMonthLabel = translations.recurrenceMonthlyDayOfMonthLabel?.(monthlyRef.dayOfMonth);
235-
const isLast = ordinal === -1;
236-
const weekdayShort = adapter.formatByString(monthlyRef.date, 'ccc');
237-
const weekAriaLabel = isLast
238-
? translations.recurrenceMonthlyLastWeekAriaLabel(weekday)
239-
: translations.recurrenceMonthlyWeekNumberAriaLabel?.(ordinal, weekday);
240-
const weekLabel = isLast
241-
? translations.recurrenceMonthlyLastWeekLabel(weekdayShort)
242-
: translations.recurrenceMonthlyWeekNumberLabel?.(ordinal, weekdayShort);
243-
244-
return [
245-
{
246-
value: 'byMonthDay',
247-
ariaLabel: dayOfMonthLabel,
248-
label: dayOfMonthLabel,
249-
},
250-
{
251-
value: 'byDay',
252-
ariaLabel: weekAriaLabel,
253-
label: weekLabel,
254-
},
255-
];
256-
}, [adapter, monthlyRef.date, monthlyRef.dayOfMonth, monthlyRef.ord, translations, weekday]);
257-
258-
const monthlyMode: 'byMonthDay' | 'byDay' = controlled.rruleDraft.byDay?.length
259-
? 'byDay'
260-
: 'byMonthDay';
261-
262186
return (
263187
<Tabs.Panel value="recurrence" keepMounted>
264188
<div className="EventPopoverMainContent">
@@ -311,6 +235,7 @@ export function RecurrenceTab(props: RecurrenceTabProps) {
311235
<Field.Root className="EventPopoverInputsRow">
312236
{translations.recurrenceEveryLabel}
313237
<Input
238+
name="interval"
314239
type="number"
315240
min={1}
316241
value={controlled.rruleDraft.interval}
@@ -350,40 +275,15 @@ export function RecurrenceTab(props: RecurrenceTabProps) {
350275
</Field.Root>
351276
</Fieldset.Root>
352277
{controlled.recurrenceSelection === 'custom' && controlled.rruleDraft.freq === 'WEEKLY' && (
353-
<Field.Root className="EventPopoverInputsRow">
354-
<Field.Label>{translations.recurrenceWeeklyMonthlySpecificInputsLabel}</Field.Label>
355-
<ToggleGroup
356-
className="ToggleGroup"
357-
multiple
358-
value={controlled.rruleDraft.byDay}
359-
onValueChange={handleChangeWeeklyDays}
360-
>
361-
{weeklyDayItems.map(({ value, ariaLabel, label }) => (
362-
<Toggle key={value} aria-label={ariaLabel} value={value} className="ToggleItem">
363-
{label}
364-
</Toggle>
365-
))}
366-
</ToggleGroup>
367-
</Field.Root>
278+
<p className="EventPopoverRecurrenceFieldset">TODO: Weekly Fields</p>
368279
)}
369280
{controlled.recurrenceSelection === 'custom' &&
370281
controlled.rruleDraft.freq === 'MONTHLY' && (
371-
<Field.Root className="EventPopoverInputsRow">
372-
<Field.Label>{translations.recurrenceWeeklyMonthlySpecificInputsLabel}</Field.Label>
373-
<ToggleGroup
374-
className="ToggleGroup"
375-
value={[monthlyMode]}
376-
onValueChange={handleChangeMonthlyGroup}
377-
>
378-
{monthlyItems.map(({ value, ariaLabel, label }) => (
379-
<Toggle key={value} aria-label={ariaLabel} value={value} className="ToggleItem">
380-
{label}
381-
</Toggle>
382-
))}
383-
</ToggleGroup>
384-
</Field.Root>
282+
<p className="EventPopoverRecurrenceFieldset">TODO: Monthly Fields</p>
385283
)}
386284

285+
<Separator className="EventPopoverSeparator" />
286+
387287
<Fieldset.Root
388288
className="EventPopoverRecurrenceFieldset"
389289
disabled={customDisabled}
@@ -427,6 +327,7 @@ export function RecurrenceTab(props: RecurrenceTabProps) {
427327
</Radio.Root>
428328
<div className="EventPopoverAfterTimesInputWrapper">
429329
<Input
330+
name="count"
430331
type="number"
431332
min={1}
432333
value={customEndsValue === 'after' ? (controlled.rruleDraft.count ?? 1) : 1}
@@ -452,6 +353,7 @@ export function RecurrenceTab(props: RecurrenceTabProps) {
452353
</span>
453354
</Radio.Root>
454355
<Input
356+
name="until"
455357
type="date"
456358
value={
457359
customEndsValue === 'until' && controlled.rruleDraft.until

0 commit comments

Comments
 (0)