mirror of
https://github.com/lexmount/moli.git
synced 2026-10-06 16:01:00 +00:00
fix(forms): round temporal input steps to millisecond precision
This commit is contained in:
@@ -209,11 +209,31 @@ pub fn input_step(input_type: InputType, step: Option<&str>) -> Option<f64> {
|
||||
Some(
|
||||
parse_finite_number(step)
|
||||
.filter(|value| *value > 0.0)
|
||||
.map(|value| value * input_step_scale(input_type))
|
||||
.map(|value| {
|
||||
let scaled = value * input_step_scale(input_type);
|
||||
if matches!(input_type, InputType::Time | InputType::DatetimeLocal) {
|
||||
temporal_step_milliseconds(step).unwrap_or_else(|| scaled.round().max(1.0))
|
||||
} else {
|
||||
scaled
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| default_input_step(input_type)),
|
||||
)
|
||||
}
|
||||
|
||||
fn temporal_step_milliseconds(step: &str) -> Option<f64> {
|
||||
let value = parse_decimal_number(step)?;
|
||||
let milliseconds = if value.scale <= 3 {
|
||||
value
|
||||
.coefficient
|
||||
.checked_mul(checked_pow10(3 - value.scale)?)?
|
||||
} else {
|
||||
let divisor = checked_pow10(value.scale - 3)?;
|
||||
value.coefficient / divisor + i128::from(value.coefficient % divisor >= divisor / 2)
|
||||
};
|
||||
Some(milliseconds.max(1) as f64)
|
||||
}
|
||||
|
||||
fn default_input_step(input_type: InputType) -> f64 {
|
||||
match input_type {
|
||||
InputType::Date => MS_PER_DAY,
|
||||
|
||||
@@ -439,6 +439,56 @@ fn meter_values_share_html_numeric_clamping_and_gauge_region_rules() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn temporal_steps_round_to_millisecond_precision_before_stepping() {
|
||||
for kind in [InputType::Time, InputType::DatetimeLocal] {
|
||||
for (step, expected) in [
|
||||
("0.9999", 1000.0),
|
||||
("1.0025", 1003.0),
|
||||
("2.0004", 2000.0),
|
||||
("2.0005", 2001.0),
|
||||
("0.0001", 1.0),
|
||||
("5e-4", 1.0),
|
||||
("59.9999", 60000.0),
|
||||
] {
|
||||
assert_eq!(
|
||||
input_step(kind, Some(step)),
|
||||
Some(expected),
|
||||
"{kind:?} step={step}"
|
||||
);
|
||||
}
|
||||
assert_eq!(input_step(kind, Some("any")), None);
|
||||
assert_eq!(input_step(kind, Some("0")), Some(60000.0));
|
||||
}
|
||||
assert_eq!(input_step(InputType::Number, Some("0.0001")), Some(0.0001));
|
||||
assert_eq!(input_step(InputType::Range, Some("0.9999")), Some(0.9999));
|
||||
for (kind, initial, expected) in [
|
||||
(InputType::Time, "00:00:01.003", "00:00:02.006"),
|
||||
(
|
||||
InputType::DatetimeLocal,
|
||||
"1970-01-01T00:00:01.003",
|
||||
"1970-01-01T00:00:02.006",
|
||||
),
|
||||
] {
|
||||
assert_eq!(
|
||||
step_input_value(
|
||||
InputStepState {
|
||||
input_type: kind,
|
||||
value: initial,
|
||||
min: None,
|
||||
max: None,
|
||||
step: Some("1.0025"),
|
||||
value_attribute: None,
|
||||
},
|
||||
InputStepDirection::Up,
|
||||
1.0
|
||||
)
|
||||
.unwrap(),
|
||||
InputStepOutcome::Set(expected.into())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn input_step_up_down_rules_are_shared() {
|
||||
let stepped = step_input_value(
|
||||
|
||||
@@ -6008,3 +6008,42 @@ fn disconnected_radio_groups_follow_tree_roots_and_form_owners() {
|
||||
"true|true:false|true|true:false|true|true|true:true|false:true"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn temporal_input_validation_and_stepping_use_rounded_millisecond_steps() {
|
||||
for (kind, step, initial, after_up) in [
|
||||
("time", "0.9999", "12:30:02", "12:30:03"),
|
||||
("time", "1.0025", "00:00:01.003", "00:00:02.006"),
|
||||
("time", "0.0001", "00:00:00.001", "00:00:00.002"),
|
||||
(
|
||||
"datetime-local",
|
||||
"59.9999",
|
||||
"2024-02-29T12:30",
|
||||
"2024-02-29T12:31",
|
||||
),
|
||||
(
|
||||
"datetime-local",
|
||||
"1.0025",
|
||||
"1970-01-01T00:00:01.003",
|
||||
"1970-01-01T00:00:02.006",
|
||||
),
|
||||
] {
|
||||
let mut vm = new_storage_test_vm("https://temporal-step.test/");
|
||||
let result=vm.eval(&format!(r#"
|
||||
(() => {{
|
||||
const input=document.createElement('input');
|
||||
input.type={kind:?};input.step={step:?};input.value={initial:?};
|
||||
const initialMismatch=input.validity.stepMismatch;
|
||||
input.stepUp();
|
||||
const afterUp=input.value, afterUpMismatch=input.validity.stepMismatch;
|
||||
input.stepDown();
|
||||
return JSON.stringify([initialMismatch,afterUp,afterUpMismatch,input.value,input.validity.stepMismatch]);
|
||||
}})()
|
||||
"#)).expect("ordinary temporal input probe");
|
||||
assert_eq!(
|
||||
result,
|
||||
format!("[false,{after_up:?},false,{initial:?},false]"),
|
||||
"{kind} step={step}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user