Portal: 12-hour schedule times and dynamicAudio default on
Use TimeInput for Block start/end and show the day timeline axis with AM/PM, so no 24-hour time is displayed in the schedule UI. New Blocks now default dynamicAudio to true in the modal and server-side when the field is absent; existing stored Blocks are untouched.
This commit is contained in:
@@ -46,53 +46,50 @@ function submit() {
|
||||
return submitRef.current({ formData: new FormData(form), cancel: () => {} });
|
||||
}
|
||||
|
||||
async function typeTime(name: string, text: string) {
|
||||
const input = (await page.getByRole('combobox', { name }).element()) as HTMLInputElement;
|
||||
input.value = text;
|
||||
input.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
}
|
||||
|
||||
describe('BlockModal', () => {
|
||||
it('uses separate hour and minute selects for start and end', async () => {
|
||||
it('uses a 12-hour TimeInput for start and end', async () => {
|
||||
mount();
|
||||
|
||||
await expect.element(page.getByRole('combobox', { name: /^Start hour$/ })).toBeInTheDocument();
|
||||
await expect
|
||||
.element(page.getByRole('combobox', { name: /^Start minute$/ }))
|
||||
.toBeInTheDocument();
|
||||
await expect.element(page.getByRole('combobox', { name: /^End hour$/ })).toBeInTheDocument();
|
||||
await expect.element(page.getByRole('combobox', { name: /^End minute$/ })).toBeInTheDocument();
|
||||
await expect.element(page.getByRole('combobox', { name: 'Start time' })).toBeInTheDocument();
|
||||
await expect.element(page.getByRole('combobox', { name: 'End time' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('pre-fills the selects from the initial times', async () => {
|
||||
it('pre-fills the fields from the initial times', async () => {
|
||||
mount({ initialStartMinutes: 7 * 60 + 30, initialEndMinutes: 9 * 60 + 15 });
|
||||
|
||||
await expect.element(page.getByRole('combobox', { name: /^Start hour$/ })).toHaveValue('7');
|
||||
await expect.element(page.getByRole('combobox', { name: /^Start minute$/ })).toHaveValue('30');
|
||||
await expect.element(page.getByRole('combobox', { name: /^End hour$/ })).toHaveValue('9');
|
||||
await expect.element(page.getByRole('combobox', { name: /^End minute$/ })).toHaveValue('15');
|
||||
await expect.element(page.getByRole('combobox', { name: 'Start time' })).toHaveValue('7:30 AM');
|
||||
await expect.element(page.getByRole('combobox', { name: 'End time' })).toHaveValue('9:15 AM');
|
||||
});
|
||||
|
||||
it('uses a 12-hour clock with an AM/PM select for each time', async () => {
|
||||
it('shows a 12-hour clock with AM/PM', async () => {
|
||||
mount({ initialStartMinutes: 21 * 60, initialEndMinutes: 0 });
|
||||
|
||||
await expect.element(page.getByRole('combobox', { name: /^Start hour$/ })).toHaveValue('9');
|
||||
await expect.element(page.getByRole('combobox', { name: /^Start period$/ })).toHaveValue('PM');
|
||||
await expect.element(page.getByRole('combobox', { name: /^End hour$/ })).toHaveValue('12');
|
||||
await expect.element(page.getByRole('combobox', { name: /^End period$/ })).toHaveValue('AM');
|
||||
|
||||
// No 24-hour option exists any more.
|
||||
await expect
|
||||
.element(page.getByRole('combobox', { name: /^Start hour$/ }))
|
||||
.not.toHaveValue('21');
|
||||
await expect.element(page.getByRole('combobox', { name: 'Start time' })).toHaveValue('9:00 PM');
|
||||
await expect.element(page.getByRole('combobox', { name: 'End time' })).toHaveValue('12:00 AM');
|
||||
});
|
||||
|
||||
it('recomputes the time when the AM/PM switch changes', async () => {
|
||||
it('recomputes the time from typed input', async () => {
|
||||
mount({ initialStartMinutes: 9 * 60, initialEndMinutes: 10 * 60 });
|
||||
|
||||
const period = (await page
|
||||
.getByRole('combobox', { name: /^End period$/ })
|
||||
.element()) as HTMLSelectElement;
|
||||
period.value = 'PM';
|
||||
period.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
await typeTime('End time', '10:00 PM');
|
||||
|
||||
await expect.element(page.getByText('9:00 AM – 10:00 PM')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows validation feedback for unparseable input', async () => {
|
||||
mount();
|
||||
|
||||
await typeTime('Start time', '25:00');
|
||||
|
||||
await expect.element(page.getByRole('alert')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows the review times and selected proclamation titles', async () => {
|
||||
mount();
|
||||
|
||||
@@ -113,7 +110,6 @@ describe('BlockModal', () => {
|
||||
initialEndMinutes: 9 * 60
|
||||
});
|
||||
|
||||
await page.getByRole('combobox', { name: /^Start hour$/ }).element();
|
||||
submit();
|
||||
|
||||
await expect
|
||||
@@ -124,7 +120,6 @@ describe('BlockModal', () => {
|
||||
it('requires at least one proclamation', async () => {
|
||||
mount({ initialProclamationIds: [] });
|
||||
|
||||
await page.getByRole('combobox', { name: /^Start hour$/ }).element();
|
||||
submit();
|
||||
|
||||
await expect.element(page.getByText('Select at least one proclamation.')).toBeInTheDocument();
|
||||
@@ -133,7 +128,6 @@ describe('BlockModal', () => {
|
||||
it('does not show an error for a valid selection', async () => {
|
||||
mount();
|
||||
|
||||
await page.getByRole('combobox', { name: /^Start hour$/ }).element();
|
||||
const result = submit();
|
||||
|
||||
expect(typeof result).toBe('function');
|
||||
@@ -165,21 +159,32 @@ describe('BlockModal', () => {
|
||||
|
||||
const checkbox = page.getByRole('checkbox', { name: /Enable Dynamic Audio/ });
|
||||
await expect.element(checkbox).toBeInTheDocument();
|
||||
expect((checkbox.element() as HTMLInputElement).checked).toBe(false);
|
||||
|
||||
await expect
|
||||
.element(page.getByText(/Dynamic audio will be enabled for this block/))
|
||||
.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('defaults dynamic audio on for new blocks', async () => {
|
||||
mount();
|
||||
|
||||
const checkbox = page.getByRole('checkbox', { name: /Enable Dynamic Audio/ });
|
||||
expect((checkbox.element() as HTMLInputElement).checked).toBe(true);
|
||||
});
|
||||
|
||||
it('pre-checks dynamic audio when editing a block that has it enabled', async () => {
|
||||
mount({ initialDynamicAudio: true });
|
||||
|
||||
const checkbox = page.getByRole('checkbox', { name: /Enable Dynamic Audio/ });
|
||||
await expect.element(checkbox).toBeInTheDocument();
|
||||
expect((checkbox.element() as HTMLInputElement).checked).toBe(true);
|
||||
});
|
||||
|
||||
it('leaves dynamic audio off when editing a block that has it disabled', async () => {
|
||||
mount({ initialDynamicAudio: false });
|
||||
|
||||
const checkbox = page.getByRole('checkbox', { name: /Enable Dynamic Audio/ });
|
||||
expect((checkbox.element() as HTMLInputElement).checked).toBe(false);
|
||||
});
|
||||
|
||||
it('toggles the dynamic audio flag in the submitted payload', async () => {
|
||||
mount();
|
||||
|
||||
@@ -188,6 +193,6 @@ describe('BlockModal', () => {
|
||||
(checkbox.element() as HTMLInputElement).click();
|
||||
|
||||
const hidden = document.querySelector('input[name="dynamicAudio"]') as HTMLInputElement;
|
||||
await expect.poll(() => hidden.value).toBe('true');
|
||||
await expect.poll(() => hidden.value).toBe('false');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user