diff --git a/dom/html/test/forms/test_input_datetime_input_change_events.html b/dom/html/test/forms/test_input_datetime_input_change_events.html
index bb95e5033915..a7ef608303c8 100644
--- a/dom/html/test/forms/test_input_datetime_input_change_events.html
+++ b/dom/html/test/forms/test_input_datetime_input_change_events.html
@@ -45,11 +45,6 @@ SimpleTest.waitForFocus(function() {
function test() {
for (var i = 0; i < inputTypes.length; i++) {
var input = document.getElementById("input_" + inputTypes[i]);
- var inputRect = input.getBoundingClientRect();
-
- // Points over the input's reset button
- var resetButton_X = inputRect.width - 15;
- var resetButton_Y = inputRect.height / 2;
is(changeEvents[i], 0, "Number of change events should be 0 at start.");
is(inputEvents[i], 0, "Number of input events should be 0 at start.");
@@ -76,16 +71,6 @@ function test() {
is(input.value, expectedValues[i][1], "Check that value was set correctly (2).");
is(changeEvents[i], 3, "Change event should be dispatched (2).");
is(inputEvents[i], 3, "Input event should be dispatched (2).");
-
- // Reset button is desktop only.
- if (isDesktop) {
- // Test that change and input events are fired when clearing the value using
- // the reset button.
- synthesizeMouse(input, resetButton_X, resetButton_Y, {});
- is(input.value, "", "Check that value was set correctly (3).");
- is(changeEvents[i], 4, "Change event should be dispatched (3).");
- is(inputEvents[i], 4, "Input event should be dispatched (3).");
- }
}
}
diff --git a/dom/html/test/forms/test_input_datetime_reset_default_value_input_change_event.html b/dom/html/test/forms/test_input_datetime_reset_default_value_input_change_event.html
index 9e722c97329c..393de9fdeed9 100644
--- a/dom/html/test/forms/test_input_datetime_reset_default_value_input_change_event.html
+++ b/dom/html/test/forms/test_input_datetime_reset_default_value_input_change_event.html
@@ -51,13 +51,6 @@ var numberInputEvents = 0;
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {
- if (isDesktopUserAgent(navigator)) {
- test_reset_in_ui_triggers_change_and_input_event(
- "time", numberChangeEvents, numberInputEvents);
- test_reset_in_ui_triggers_change_and_input_event(
- "date", numberChangeEvents, numberInputEvents);
- }
-
test_reset_in_script_does_not_trigger_change_and_input_event(
"time2", numberChangeEvents, numberInputEvents);
test_reset_in_script_does_not_trigger_change_and_input_event(
@@ -71,30 +64,6 @@ SimpleTest.waitForFocus(function() {
SimpleTest.finish();
});
-function test_reset_in_ui_triggers_change_and_input_event(
- inputFieldIdSuffix, oldNumberChangeEvents, oldNumberInputEvents) {
- const inputFieldName = INPUT_FIELD_ID_PREFIX + inputFieldIdSuffix;
- var input = document.getElementById(inputFieldName);
-
- is(input.value, input.defaultValue,
- "Check " + inputFieldName + "'s default value is initialized correctly.");
- is(numberChangeEvents, oldNumberChangeEvents,
- "Check numberChangeEvents is initialized correctly for " + inputFieldName +
- ".");
- is(numberInputEvents, oldNumberInputEvents,
- "Check numberInputEvents is initialized correctly for " + inputFieldName +
- ".");
-
- simulateUserClicksResetButton(input);
-
- is(input.value, "",
- "Check " + inputFieldName + "'s value was set correctly.");
- is(numberChangeEvents, oldNumberChangeEvents + 1,
- "Change event should be dispatched for " + inputFieldName + ".");
- is(numberInputEvents, oldNumberInputEvents + 1,
- "Input event should be dispatched for " + inputFieldName + ".");
-}
-
function test_reset_in_script_does_not_trigger_change_and_input_event(
inputFieldIdSuffix, oldNumberChangeEvents, oldNumberInputEvents) {
const inputFieldName = INPUT_FIELD_ID_PREFIX + inputFieldIdSuffix;
diff --git a/dom/html/test/forms/test_input_datetime_tabindex.html b/dom/html/test/forms/test_input_datetime_tabindex.html
index 60dff9177d35..207a7a8a8ecd 100644
--- a/dom/html/test/forms/test_input_datetime_tabindex.html
+++ b/dom/html/test/forms/test_input_datetime_tabindex.html
@@ -58,7 +58,15 @@ function testTabindex(type) {
is(document.activeElement, input1,
"input element with tabindex=0 is focusable");
- let fieldCount = type == "datetime-local" ? 6 : 3;
+ // Time input does not include a Calendar button
+ let fieldCount;
+ if (type == "datetime-local") {
+ fieldCount = 7;
+ } else if (type == "date") {
+ fieldCount = 4;
+ } else {
+ fieldCount = 3;
+ };
// Advance through inner fields.
for (let i = 0; i < fieldCount - 1; ++i) {
diff --git a/dom/html/test/forms/utils.js b/dom/html/test/forms/utils.js
deleted file mode 100644
index fee762995ccf..000000000000
--- a/dom/html/test/forms/utils.js
+++ /dev/null
@@ -1,20 +0,0 @@
-/**
- * Simulate the user clicks the reset button of the given date or time element.
- *
- * @param inputElement A date or time input element of default size.
- */
-function simulateUserClicksResetButton(inputElement) {
- var inputRectangle = inputElement.getBoundingClientRect();
- const offsetX = inputRectangle.width - 15;
- const offsetY = inputRectangle.height / 2;
-
- synthesizeMouse(inputElement, offsetX, offsetY, {});
-}
-
-/**
- * @param navigator https://www.w3schools.com/jsref/obj_navigator.asp.
- * @return true, iff it's a desktop user agent.
- */
-function isDesktopUserAgent(navigator) {
- return !/Mobile|Tablet/.test(navigator.userAgent);
-}
diff --git a/toolkit/actors/DateTimePickerChild.jsm b/toolkit/actors/DateTimePickerChild.jsm
index 7a212c383155..8f0a4b668707 100644
--- a/toolkit/actors/DateTimePickerChild.jsm
+++ b/toolkit/actors/DateTimePickerChild.jsm
@@ -89,6 +89,10 @@ class DateTimePickerChild extends JSWindowActorChild {
receiveMessage(aMessage) {
switch (aMessage.name) {
case "FormDateTime:PickerClosed": {
+ if (!this._inputElement) {
+ return;
+ }
+
this.close();
break;
}
diff --git a/toolkit/content/tests/browser/browser_datetime_datepicker.js b/toolkit/content/tests/browser/browser_datetime_datepicker.js
index b2294a2b7e2b..0d60976c75e8 100644
--- a/toolkit/content/tests/browser/browser_datetime_datepicker.js
+++ b/toolkit/content/tests/browser/browser_datetime_datepicker.js
@@ -314,11 +314,15 @@ add_task(async function test_datepicker_reopen_state() {
Assert.equal(helper.panel.state, "closed", "Panel should be closed");
// Ensures the picker opens to the month of the input value
- await BrowserTestUtils.synthesizeMouseAtCenter(
- "input",
- {},
- gBrowser.selectedBrowser
- );
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
+ let input = content.document.querySelector("input");
+ function getCalendarButton(input) {
+ const shadowRoot = SpecialPowers.wrap(input).openOrClosedShadowRoot;
+ return shadowRoot.getElementById("calendar-button");
+ }
+ getCalendarButton(input).click();
+ });
+
await helper.waitForPickerReady();
Assert.equal(
diff --git a/toolkit/content/tests/browser/browser_datetime_datepicker_keynav.js b/toolkit/content/tests/browser/browser_datetime_datepicker_keynav.js
index a8236ef1aea9..e3cd271305a9 100644
--- a/toolkit/content/tests/browser/browser_datetime_datepicker_keynav.js
+++ b/toolkit/content/tests/browser/browser_datetime_datepicker_keynav.js
@@ -11,6 +11,27 @@ const DATE_FORMAT = new Intl.DateTimeFormat("en-US", {
timeZone: "UTC",
}).format;
+/**
+ * Helper function to check the value of a Calendar button's specific attribute
+ *
+ * @param {String} attr: The name of the attribute to be tested
+ * @param {String} val: Value that is expected to be assigned to the attribute
+ */
+async function testCalendarBtnAttribute(attr, val) {
+ let browser = helper.tab.linkedBrowser;
+
+ await SpecialPowers.spawn(browser, [attr, val], (attr, val) => {
+ const input = content.document.querySelector("input");
+ const shadowRoot = SpecialPowers.wrap(input).openOrClosedShadowRoot;
+ const calendarBtn = shadowRoot.getElementById("calendar-button");
+ Assert.equal(
+ calendarBtn.getAttribute(attr),
+ val,
+ `Calendar button has ${attr} attribute set to ${val}`
+ );
+ });
+}
+
let helper = new DateTimeTestHelper();
registerCleanupFunction(() => {
@@ -46,6 +67,8 @@ add_task(async function test_datepicker_keyboard_nav() {
"Panel should be closed after Escape from anywhere on the window"
);
+ await testCalendarBtnAttribute("aria-expanded", "false");
+
let ready = helper.waitForPickerReady();
// Ensure focus is on the input field
@@ -67,6 +90,8 @@ add_task(async function test_datepicker_keyboard_nav() {
await ready;
+ await testCalendarBtnAttribute("aria-expanded", "true");
+
Assert.equal(
helper.panel.state,
"open",
@@ -122,6 +147,8 @@ add_task(async function test_datepicker_keyboard_nav() {
await ready;
+ await testCalendarBtnAttribute("aria-expanded", "true");
+
Assert.equal(helper.panel.state, "open", "Panel should be opened on Space");
await BrowserTestUtils.waitForCondition(() => {
@@ -146,5 +173,7 @@ add_task(async function test_datepicker_keyboard_nav() {
"Panel should be closed on Escape"
);
+ await testCalendarBtnAttribute("aria-expanded", "false");
+
await helper.tearDown();
});
diff --git a/toolkit/content/tests/browser/browser_datetime_datepicker_markup.js b/toolkit/content/tests/browser/browser_datetime_datepicker_markup.js
index a2ac9d315a87..f3773a59dd85 100644
--- a/toolkit/content/tests/browser/browser_datetime_datepicker_markup.js
+++ b/toolkit/content/tests/browser/browser_datetime_datepicker_markup.js
@@ -17,6 +17,40 @@ const MONTH_YEAR = ".month-year",
SPINNER_MONTH = "#spinner-month",
SPINNER_YEAR = "#spinner-year";
+/**
+ * Helper function to check the value of a Calendar button's specific attribute
+ *
+ * @param {String} attr: The name of the attribute to be tested
+ * @param {String} val: Value that is expected to be assigned to the attribute.
+ * @param {Boolean} presenceOnly: If "true", test only the presence of the attribute
+ */
+async function testCalendarBtnAttribute(attr, val, presenceOnly = false) {
+ let browser = helper.tab.linkedBrowser;
+
+ await SpecialPowers.spawn(
+ browser,
+ [attr, val, presenceOnly],
+ (attr, val, presenceOnly) => {
+ const input = content.document.querySelector("input");
+ const shadowRoot = SpecialPowers.wrap(input).openOrClosedShadowRoot;
+ const calendarBtn = shadowRoot.getElementById("calendar-button");
+
+ if (presenceOnly) {
+ Assert.ok(
+ calendarBtn.hasAttribute(attr),
+ `Calendar button has ${attr} attribute`
+ );
+ } else {
+ Assert.equal(
+ calendarBtn.getAttribute(attr),
+ val,
+ `Calendar button has ${attr} attribute set to ${val}`
+ );
+ }
+ }
+ );
+}
+
let helper = new DateTimeTestHelper();
registerCleanupFunction(() => {
@@ -326,3 +360,121 @@ add_task(async function test_datepicker_markup_refresh() {
await helper.tearDown();
});
+
+/**
+ * Test that date input field has a Calendar button with an accessible markup
+ */
+add_task(async function test_calendar_button_markup_date() {
+ info(
+ "Test that type=date input field has a Calendar button with an accessible markup"
+ );
+
+ await helper.openPicker("data:text/html, ");
+ let browser = helper.tab.linkedBrowser;
+
+ Assert.equal(helper.panel.state, "open", "Panel is visible");
+
+ let closed = helper.promisePickerClosed();
+
+ await testCalendarBtnAttribute("aria-expanded", "true");
+ await testCalendarBtnAttribute("aria-label", null, true);
+ await testCalendarBtnAttribute("data-l10n-id", "datetime-calendar");
+
+ await SpecialPowers.spawn(browser, [], () => {
+ const input = content.document.querySelector("input");
+ const shadowRoot = SpecialPowers.wrap(input).openOrClosedShadowRoot;
+ const calendarBtn = shadowRoot.getElementById("calendar-button");
+
+ Assert.equal(calendarBtn.tagName, "BUTTON", "Calendar control is a button");
+ Assert.ok(
+ ContentTaskUtils.is_visible(calendarBtn),
+ "The Calendar button is visible"
+ );
+
+ calendarBtn.click();
+ });
+
+ await closed;
+
+ Assert.equal(
+ helper.panel.state,
+ "closed",
+ "Panel should be closed on click on the Calendar button"
+ );
+
+ await testCalendarBtnAttribute("aria-expanded", "false");
+
+ await helper.tearDown();
+});
+
+/**
+ * Test that datetime-local input field has a Calendar button
+ * with an accessible markup
+ */
+add_task(async function test_calendar_button_markup_datetime() {
+ info(
+ "Test that type=datetime-local input field has a Calendar button with an accessible markup"
+ );
+
+ await helper.openPicker("data:text/html, ");
+ let browser = helper.tab.linkedBrowser;
+
+ Assert.equal(helper.panel.state, "open", "Panel is visible");
+
+ let closed = helper.promisePickerClosed();
+
+ await testCalendarBtnAttribute("aria-expanded", "true");
+ await testCalendarBtnAttribute("aria-label", null, true);
+ await testCalendarBtnAttribute("data-l10n-id", "datetime-calendar");
+
+ await SpecialPowers.spawn(browser, [], () => {
+ const input = content.document.querySelector("input");
+ const shadowRoot = SpecialPowers.wrap(input).openOrClosedShadowRoot;
+ const calendarBtn = shadowRoot.getElementById("calendar-button");
+
+ Assert.equal(calendarBtn.tagName, "BUTTON", "Calendar control is a button");
+ Assert.ok(
+ ContentTaskUtils.is_visible(calendarBtn),
+ "The Calendar button is visible"
+ );
+
+ calendarBtn.click();
+ });
+
+ await closed;
+
+ Assert.equal(
+ helper.panel.state,
+ "closed",
+ "Panel should be closed on click on the Calendar button"
+ );
+
+ await testCalendarBtnAttribute("aria-expanded", "false");
+
+ await helper.tearDown();
+});
+
+/**
+ * Test that time input field does not include a Calendar button
+ */
+add_task(async function test_calendar_button_markup_time() {
+ info("Test that type=time input field does not include a Calendar button");
+
+ let testTab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "data:text/html, "
+ );
+
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
+ const input = content.document.querySelector("input");
+ const shadowRoot = SpecialPowers.wrap(input).openOrClosedShadowRoot;
+ const calendarBtn = shadowRoot.getElementById("calendar-button");
+
+ Assert.ok(
+ ContentTaskUtils.is_hidden(calendarBtn),
+ "The Calendar control within a type=time input field is programmatically hidden"
+ );
+ });
+
+ BrowserTestUtils.removeTab(testTab);
+});
diff --git a/toolkit/content/tests/browser/head.js b/toolkit/content/tests/browser/head.js
index 6944a5d5bced..9684d495e8bc 100644
--- a/toolkit/content/tests/browser/head.js
+++ b/toolkit/content/tests/browser/head.js
@@ -206,7 +206,11 @@ class DateTimeTestHelper {
});
if (openMethod === "click") {
- await BrowserTestUtils.synthesizeMouseAtCenter("input", {}, bc);
+ await SpecialPowers.spawn(bc, [], () => {
+ const input = content.document.querySelector("input");
+ const shadowRoot = SpecialPowers.wrap(input).openOrClosedShadowRoot;
+ shadowRoot.getElementById("calendar-button").click();
+ });
} else if (openMethod === "showPicker") {
await SpecialPowers.spawn(bc, [], function() {
content.document.notifyUserGestureActivation();
diff --git a/toolkit/content/widgets/datetimebox.css b/toolkit/content/widgets/datetimebox.css
index 65763b76ba65..b53cb59ce4ad 100644
--- a/toolkit/content/widgets/datetimebox.css
+++ b/toolkit/content/widgets/datetimebox.css
@@ -45,23 +45,58 @@
user-select: none;
}
-.datetime-reset-button {
+.datetime-calendar-button {
+ -moz-context-properties: fill;
color: inherit;
font-size: inherit;
fill: currentColor;
- opacity: .5;
+ opacity: .65;
background-color: transparent;
border: none;
+ border-radius: 0.2em;
flex: none;
- padding-inline: 2px;
- padding-block: 0;
+ margin-block: 0;
+ margin-inline: 0.075em 0.15em;
+ padding: 0 0.15em;
line-height: 1;
}
-.datetime-reset-button-svg {
+.datetime-calendar-button:focus-visible,
+.datetime-calendar-button:hover {
+ opacity: 1;
+ outline: 0.15em solid SelectedItem;
+}
+
+.datetime-calendar-button-svg {
pointer-events: none;
/* When using a very small font-size, we don't want the button to take extra
* space (which will affect the baseline of the form control) */
max-width: 1em;
max-height: 1em;
}
+
+@media (prefers-contrast) {
+ .datetime-calendar-button {
+ opacity: 1;
+ background-color: ButtonFace;
+ color: ButtonText;
+ }
+
+ .datetime-calendar-button:focus-visible,
+ .datetime-calendar-button:hover {
+ background-color: SelectedItem;
+ }
+
+ .datetime-calendar-button:focus-visible > .datetime-calendar-button-svg,
+ .datetime-calendar-button:hover > .datetime-calendar-button-svg {
+ background-color: SelectedItem;
+ -moz-context-properties: fill;
+ fill: SelectedItemText;
+ }
+
+ .datetime-calendar-button-svg {
+ background-color: ButtonFace;
+ -moz-context-properties: fill;
+ fill: ButtonText;
+ }
+}
diff --git a/toolkit/content/widgets/datetimebox.js b/toolkit/content/widgets/datetimebox.js
index 73651e0e9e6f..623eb4695e12 100644
--- a/toolkit/content/widgets/datetimebox.js
+++ b/toolkit/content/widgets/datetimebox.js
@@ -58,11 +58,7 @@ this.DateTimeBoxWidget = class {
}
teardown() {
- this.mResetButton.removeEventListener("mousedown", this, {
- mozSystemGroup: true,
- });
-
- this.mInputElement.removeEventListener("keypress", this, {
+ this.mInputElement.removeEventListener("keydown", this, {
capture: true,
mozSystemGroup: true,
});
@@ -77,6 +73,7 @@ this.DateTimeBoxWidget = class {
this.l10n.disconnectRoot(this.shadowRoot);
this.removeEditFields();
+ this.removeEventListenersToField(this.mCalendarButton);
this.mInputElement = null;
@@ -134,6 +131,7 @@ this.DateTimeBoxWidget = class {
this.generateContent();
this.mDateTimeBoxElement = this.shadowRoot.firstChild;
+ this.mCalendarButton = this.shadowRoot.getElementById("calendar-button");
this.mInputElement = this.element;
this.mLocales = this.window.getWebExposedLocales();
@@ -189,14 +187,8 @@ this.DateTimeBoxWidget = class {
this.mHourPageUpDownInterval = 3;
this.mMinSecPageUpDownInterval = 10;
- this.mResetButton = this.shadowRoot.getElementById("reset-button");
- this.mResetButton.style.visibility = "hidden";
- this.mResetButton.addEventListener("mousedown", this, {
- mozSystemGroup: true,
- });
-
this.mInputElement.addEventListener(
- "keypress",
+ "keydown",
this,
{
capture: true,
@@ -204,14 +196,6 @@ this.DateTimeBoxWidget = class {
},
false
);
- // This is to open the picker when input element is clicked (this
- // includes padding area).
- this.mInputElement.addEventListener(
- "click",
- this,
- { mozSystemGroup: true },
- false
- );
// Those events are dispatched to
with bubble set
// to false. They are trapped inside UA Widget Shadow DOM and are not
@@ -221,6 +205,7 @@ this.DateTimeBoxWidget = class {
});
this.buildEditFields();
+ this.buildCalendarBtn();
this.updateEditAttributes();
if (this.mInputElement.value) {
@@ -243,10 +228,9 @@ this.DateTimeBoxWidget = class {
-
-
@@ -374,12 +358,8 @@ this.DateTimeBoxWidget = class {
return field;
}
- updateResetButtonVisibility() {
- if (this.isAnyFieldAvailable(false) && !this.isRequired()) {
- this.mResetButton.style.visibility = "";
- } else {
- this.mResetButton.style.visibility = "hidden";
- }
+ updateCalendarButtonState(isExpanded) {
+ this.mCalendarButton.setAttribute("aria-expanded", isExpanded);
}
notifyInputElementValueChanged() {
@@ -423,6 +403,8 @@ this.DateTimeBoxWidget = class {
setPickerState(aIsOpen) {
this.log("picker is now " + (aIsOpen ? "opened" : "closed"));
this.mIsPickerOpen = aIsOpen;
+ // Calendar button's expanded state mirrors this.mIsPickerOpen
+ this.updateCalendarButtonState(this.mIsPickerOpen);
}
setFieldTabIndexAttribute(field) {
@@ -454,9 +436,10 @@ this.DateTimeBoxWidget = class {
this.setFieldTabIndexAttribute(child);
}
- this.mResetButton.disabled =
- this.mInputElement.disabled || this.mInputElement.readOnly;
- this.updateResetButtonVisibility();
+ this.mCalendarButton.hidden =
+ this.mInputElement.disabled ||
+ this.mInputElement.readOnly ||
+ this.mInputElement.type === "time";
}
isEmpty(aValue) {
@@ -480,7 +463,6 @@ this.DateTimeBoxWidget = class {
if (aField.classList.contains("numeric")) {
aField.setAttribute("typeBuffer", "");
}
- this.updateResetButtonVisibility();
}
openDateTimePicker() {
@@ -547,8 +529,8 @@ this.DateTimeBoxWidget = class {
this.setPickerState(aEvent.detail);
break;
}
- case "keypress": {
- this.onKeyPress(aEvent);
+ case "keydown": {
+ this.onKeyDown(aEvent);
break;
}
case "click": {
@@ -626,7 +608,7 @@ this.DateTimeBoxWidget = class {
);
}
- shouldOpenDateTimePickerOnKeyPress() {
+ shouldOpenDateTimePickerOnKeyDown() {
if (!this.mLastFocusedField) {
return true;
}
@@ -647,20 +629,34 @@ this.DateTimeBoxWidget = class {
return this.isTimeField(field);
}
- onKeyPress(aEvent) {
- this.log("onKeyPress key: " + aEvent.key);
+ onKeyDown(aEvent) {
+ this.log("onKeyDown key: " + aEvent.key);
switch (aEvent.key) {
- // Toggle the picker on space/enter, close on Escape.
+ // Toggle the picker on Space/Enter on Calendar button or Space on input,
+ // close on Escape anywhere.
+ case "Escape": {
+ if (this.mIsPickerOpen) {
+ this.closeDateTimePicker();
+ aEvent.preventDefault();
+ }
+ break;
+ }
case "Enter":
- case "Escape":
case " ": {
+ // always close, if opened
if (this.mIsPickerOpen) {
this.closeDateTimePicker();
} else if (
- aEvent.key != "Escape" &&
- aEvent.key != "Enter" &&
- this.shouldOpenDateTimePickerOnKeyPress()
+ // open on Space from anywhere within the input
+ aEvent.key == " " &&
+ this.shouldOpenDateTimePickerOnKeyDown()
+ ) {
+ this.openDateTimePicker();
+ } else if (
+ // open from the Calendar button on either keydown
+ aEvent.originalTarget == this.mCalendarButton &&
+ this.shouldOpenDateTimePickerOnKeyDown()
) {
this.openDateTimePicker();
} else {
@@ -698,12 +694,16 @@ this.DateTimeBoxWidget = class {
break;
}
default: {
- // printable characters
+ // digits and printable characters
+ const regex = new RegExp("Digit\\d");
+ const isDigit = regex.test(aEvent.code);
+
if (
- aEvent.keyCode == 0 &&
- !(aEvent.ctrlKey || aEvent.altKey || aEvent.metaKey)
+ isDigit ||
+ (aEvent.key == 0 &&
+ !(aEvent.ctrlKey || aEvent.altKey || aEvent.metaKey))
) {
- this.handleKeypress(aEvent);
+ this.handleKeydown(aEvent);
aEvent.preventDefault();
}
break;
@@ -723,13 +723,16 @@ this.DateTimeBoxWidget = class {
return;
}
- if (aEvent.originalTarget == this.mResetButton) {
- this.clearInputFields(false);
- } else if (
- !this.mIsPickerOpen &&
- this.shouldOpenDateTimePickerOnClick(aEvent.originalTarget)
- ) {
- this.openDateTimePicker();
+ // Toggle the picker on click on the Calendar button only
+ if (aEvent.originalTarget == this.mCalendarButton) {
+ if (
+ !this.mIsPickerOpen &&
+ this.shouldOpenDateTimePickerOnClick(aEvent.originalTarget)
+ ) {
+ this.openDateTimePicker();
+ } else {
+ this.closeDateTimePicker();
+ }
}
}
@@ -873,6 +876,18 @@ this.DateTimeBoxWidget = class {
});
}
+ buildCalendarBtn() {
+ this.addEventListenersToField(this.mCalendarButton);
+ // This is to open the picker when a Calendar button is clicked (this
+ // includes padding area).
+ this.mCalendarButton.addEventListener(
+ "click",
+ this,
+ { mozSystemGroup: true },
+ false
+ );
+ }
+
clearInputFields(aFromInputElement) {
this.log("clearInputFields");
@@ -1085,7 +1100,7 @@ this.DateTimeBoxWidget = class {
this.setInputValueFromFields();
}
- handleKeypress(aEvent) {
+ handleKeydown(aEvent) {
if (!this.isEditable()) {
return;
}
@@ -1228,7 +1243,6 @@ this.DateTimeBoxWidget = class {
aField.textContent = formatted;
aField.setAttribute("aria-valuetext", formatted);
- this.updateResetButtonVisibility();
}
isAnyFieldAvailable(aForPicker = false) {
@@ -1522,6 +1536,5 @@ this.DateTimeBoxWidget = class {
this.mDayPeriodField.textContent = aValue;
this.mDayPeriodField.setAttribute("value", aValue);
- this.updateResetButtonVisibility();
}
};
diff --git a/toolkit/locales/en-US/toolkit/global/datetimebox.ftl b/toolkit/locales/en-US/toolkit/global/datetimebox.ftl
index 2ad6e2a26723..6b25a65120f2 100644
--- a/toolkit/locales/en-US/toolkit/global/datetimebox.ftl
+++ b/toolkit/locales/en-US/toolkit/global/datetimebox.ftl
@@ -2,10 +2,6 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-# Date/time clear button
-datetime-reset =
- .aria-label = Clear
-
## Placeholders for date and time inputs
datetime-year-placeholder = yyyy
@@ -34,3 +30,12 @@ datetime-millisecond =
.aria-label = Milliseconds
datetime-dayperiod =
.aria-label = AM/PM
+
+## Calendar button for input type=date
+
+# This label is used by screenreaders and other assistive technology
+# to indicate the purpose of a toggle button inside of the
+# field that opens/closes a date picker calendar dialog
+
+datetime-calendar =
+ .aria-label = Calendar
diff --git a/xpcom/ds/StaticAtoms.py b/xpcom/ds/StaticAtoms.py
index ec3bab7b7ff4..7f03d0b3a8a5 100644
--- a/xpcom/ds/StaticAtoms.py
+++ b/xpcom/ds/StaticAtoms.py
@@ -310,7 +310,6 @@ STATIC_ATOMS = [
Atom("datetime", "datetime"),
Atom("datetime_local", "datetime-local"),
Atom("datetimeInputBoxWrapper", "datetime-input-box-wrapper"),
- Atom("datetimeResetButton", "datetime-reset-button"),
Atom("dd", "dd"),
Atom("decimal", "decimal"),
Atom("decimalFormat", "decimal-format"),