Portal: add reusable 12-hour TimeInput component
TimeInput displays and accepts AM/PM times, offers the 5-minute options as suggestions, and allows typing any minute 00-59; its value model is a 24h HH:MM string. Adds isomorphic parseTimeInput/hmToMinutes/fiveMinuteOptions helpers and unit + component tests for them.
This commit is contained in:
@@ -3,8 +3,11 @@ import {
|
||||
blockDuration,
|
||||
blockSegments,
|
||||
findOverlap,
|
||||
fiveMinuteOptions,
|
||||
formatTime,
|
||||
hmToMinutes,
|
||||
isOvernight,
|
||||
parseTimeInput,
|
||||
snapMinutes,
|
||||
toHm,
|
||||
validateBlock,
|
||||
@@ -44,6 +47,52 @@ describe('toHm', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('hmToMinutes', () => {
|
||||
it('parses 24-hour HH:MM strings', () => {
|
||||
expect(hmToMinutes('00:00')).toBe(0);
|
||||
expect(hmToMinutes('07:05')).toBe(7 * 60 + 5);
|
||||
expect(hmToMinutes('23:59')).toBe(23 * 60 + 59);
|
||||
});
|
||||
|
||||
it('rejects malformed or out-of-range values', () => {
|
||||
expect(hmToMinutes('7:05')).toBeNull();
|
||||
expect(hmToMinutes('24:00')).toBeNull();
|
||||
expect(hmToMinutes('12:60')).toBeNull();
|
||||
expect(hmToMinutes('')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseTimeInput', () => {
|
||||
it('parses tolerant 12-hour input', () => {
|
||||
expect(parseTimeInput('7:05 pm')).toEqual({ ok: true, minutes: 19 * 60 + 5 });
|
||||
expect(parseTimeInput('7:05PM')).toEqual({ ok: true, minutes: 19 * 60 + 5 });
|
||||
expect(parseTimeInput('7pm')).toEqual({ ok: true, minutes: 19 * 60 });
|
||||
expect(parseTimeInput('7:7')).toEqual({ ok: true, minutes: 7 * 60 + 7 });
|
||||
expect(parseTimeInput('12:00 am')).toEqual({ ok: true, minutes: 0 });
|
||||
expect(parseTimeInput('12:30 PM')).toEqual({ ok: true, minutes: 12 * 60 + 30 });
|
||||
expect(parseTimeInput('11:59 pm')).toEqual({ ok: true, minutes: 23 * 60 + 59 });
|
||||
expect(parseTimeInput('0:30')).toEqual({ ok: true, minutes: 30 });
|
||||
});
|
||||
|
||||
it('rejects ambiguous or out-of-range input', () => {
|
||||
expect(parseTimeInput('13:00 PM').ok).toBe(false);
|
||||
expect(parseTimeInput('7:70').ok).toBe(false);
|
||||
expect(parseTimeInput('25:00').ok).toBe(false);
|
||||
expect(parseTimeInput('').ok).toBe(false);
|
||||
expect(parseTimeInput('noon').ok).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fiveMinuteOptions', () => {
|
||||
it('lists every 5-minute step across the day', () => {
|
||||
const options = fiveMinuteOptions();
|
||||
expect(options[0]).toBe(0);
|
||||
expect(options[1]).toBe(5);
|
||||
expect(options[options.length - 1]).toBe(23 * 60 + 55);
|
||||
expect(options).toHaveLength((24 * 60) / 5);
|
||||
});
|
||||
});
|
||||
|
||||
describe('snapMinutes', () => {
|
||||
it('rounds to the nearest 15 minutes and clamps to the day', () => {
|
||||
expect(snapMinutes(0)).toBe(0);
|
||||
|
||||
Reference in New Issue
Block a user