Bug 1098251 - Allow a contact to be created with either a phone number or an email address. r=MattN

This commit is contained in:
Jared Wein 2014-11-14 10:58:57 -05:00
Родитель 888dbec214
Коммит fcd99471b4
3 изменённых файлов: 129 добавлений и 64 удалений

Просмотреть файл

@ -52,36 +52,33 @@ loop.contacts = (function(_, mozL10n) {
* for the contact. Both fields are optional.
* @param {object} contact
* The contact object to get the field from.
* @param {object} data
* An object that has a 'field' property. If the 'optional' field
* is defined and true, then the field will only be set if the
* 'value' is not empty or if the previous value was defined.
* @param {string} field
* The field within the contact to set.
* @param {string} value
* The value that the field should be set to.
*/
let setPreferred = function(contact, data) {
if (data.optional) {
// Don't clear the field if it doesn't exist.
if (!data.value &&
(!contact[data.field] || !contact[data.field].length)) {
return;
}
let setPreferred = function(contact, field, value) {
// Don't clear the field if it doesn't exist.
if (!value && (!contact[field] || !contact[field].length)) {
return;
}
if (!contact[data.field]) {
contact[data.field] = [];
if (!contact[field]) {
contact[field] = [];
}
if (!contact[data.field].length) {
contact[data.field][0] = {"value": data.value};
if (!contact[field].length) {
contact[field][0] = {"value": value};
return;
}
// Set the value in the preferred tuple and return.
for (let i in contact[data.field]) {
if (contact[data.field][i].pref) {
contact[data.field][i].value = data.value;
for (let i in contact[field]) {
if (contact[field][i].pref) {
contact[field][i].value = value;
return;
}
}
contact[data.field][0].value = data.value;
contact[field][0].value = value;
};
const ContactDropdown = React.createClass({displayName: 'ContactDropdown',
@ -563,8 +560,11 @@ loop.contacts = (function(_, mozL10n) {
pristine: false,
});
let emailInput = this.refs.email.getDOMNode();
let telInput = this.refs.tel.getDOMNode();
if (!this.refs.name.getDOMNode().checkValidity() ||
!this.refs.email.getDOMNode().checkValidity()) {
((emailInput.required || emailInput.value) && !emailInput.checkValidity()) ||
((telInput.required || telInput.value) && !telInput.checkValidity())) {
return;
}
@ -575,10 +575,8 @@ loop.contacts = (function(_, mozL10n) {
switch (this.props.mode) {
case "edit":
this.state.contact.name[0] = this.state.name.trim();
setPreferred(this.state.contact,
{field: "email", value: this.state.email.trim()});
setPreferred(this.state.contact,
{field: "tel", value: this.state.tel.trim(), optional: true});
setPreferred(this.state.contact, "email", this.state.email.trim());
setPreferred(this.state.contact, "tel", this.state.tel.trim());
contactsAPI.update(this.state.contact, err => {
if (err) {
throw err;
@ -622,7 +620,8 @@ loop.contacts = (function(_, mozL10n) {
render: function() {
let cx = React.addons.classSet;
// XXX do we need the ref="" attributes below?
let phoneOrEmailRequired = !this.state.email && !this.state.tel;
return (
React.DOM.div({className: "content-area contact-form"},
React.DOM.header(null, this.props.mode == "add"
@ -633,11 +632,11 @@ loop.contacts = (function(_, mozL10n) {
className: cx({pristine: this.state.pristine}),
valueLink: this.linkState("name")}),
React.DOM.label(null, mozL10n.get("edit_contact_email_label")),
React.DOM.input({ref: "email", required: true, type: "email",
React.DOM.input({ref: "email", type: "email", required: phoneOrEmailRequired,
className: cx({pristine: this.state.pristine}),
valueLink: this.linkState("email")}),
React.DOM.label(null, mozL10n.get("new_contact_phone_placeholder")),
React.DOM.input({ref: "tel", type: "tel",
React.DOM.input({ref: "tel", type: "tel", required: phoneOrEmailRequired,
className: cx({pristine: this.state.pristine}),
valueLink: this.linkState("tel")}),
ButtonGroup(null,

Просмотреть файл

@ -52,36 +52,33 @@ loop.contacts = (function(_, mozL10n) {
* for the contact. Both fields are optional.
* @param {object} contact
* The contact object to get the field from.
* @param {object} data
* An object that has a 'field' property. If the 'optional' field
* is defined and true, then the field will only be set if the
* 'value' is not empty or if the previous value was defined.
* @param {string} field
* The field within the contact to set.
* @param {string} value
* The value that the field should be set to.
*/
let setPreferred = function(contact, data) {
if (data.optional) {
// Don't clear the field if it doesn't exist.
if (!data.value &&
(!contact[data.field] || !contact[data.field].length)) {
return;
}
let setPreferred = function(contact, field, value) {
// Don't clear the field if it doesn't exist.
if (!value && (!contact[field] || !contact[field].length)) {
return;
}
if (!contact[data.field]) {
contact[data.field] = [];
if (!contact[field]) {
contact[field] = [];
}
if (!contact[data.field].length) {
contact[data.field][0] = {"value": data.value};
if (!contact[field].length) {
contact[field][0] = {"value": value};
return;
}
// Set the value in the preferred tuple and return.
for (let i in contact[data.field]) {
if (contact[data.field][i].pref) {
contact[data.field][i].value = data.value;
for (let i in contact[field]) {
if (contact[field][i].pref) {
contact[field][i].value = value;
return;
}
}
contact[data.field][0].value = data.value;
contact[field][0].value = value;
};
const ContactDropdown = React.createClass({
@ -563,8 +560,11 @@ loop.contacts = (function(_, mozL10n) {
pristine: false,
});
let emailInput = this.refs.email.getDOMNode();
let telInput = this.refs.tel.getDOMNode();
if (!this.refs.name.getDOMNode().checkValidity() ||
!this.refs.email.getDOMNode().checkValidity()) {
((emailInput.required || emailInput.value) && !emailInput.checkValidity()) ||
((telInput.required || telInput.value) && !telInput.checkValidity())) {
return;
}
@ -575,10 +575,8 @@ loop.contacts = (function(_, mozL10n) {
switch (this.props.mode) {
case "edit":
this.state.contact.name[0] = this.state.name.trim();
setPreferred(this.state.contact,
{field: "email", value: this.state.email.trim()});
setPreferred(this.state.contact,
{field: "tel", value: this.state.tel.trim(), optional: true});
setPreferred(this.state.contact, "email", this.state.email.trim());
setPreferred(this.state.contact, "tel", this.state.tel.trim());
contactsAPI.update(this.state.contact, err => {
if (err) {
throw err;
@ -622,7 +620,8 @@ loop.contacts = (function(_, mozL10n) {
render: function() {
let cx = React.addons.classSet;
// XXX do we need the ref="" attributes below?
let phoneOrEmailRequired = !this.state.email && !this.state.tel;
return (
<div className="content-area contact-form">
<header>{this.props.mode == "add"
@ -633,11 +632,11 @@ loop.contacts = (function(_, mozL10n) {
className={cx({pristine: this.state.pristine})}
valueLink={this.linkState("name")} />
<label>{mozL10n.get("edit_contact_email_label")}</label>
<input ref="email" required type="email"
<input ref="email" type="email" required={phoneOrEmailRequired}
className={cx({pristine: this.state.pristine})}
valueLink={this.linkState("email")} />
<label>{mozL10n.get("new_contact_phone_placeholder")}</label>
<input ref="tel" type="tel"
<input ref="tel" type="tel" required={phoneOrEmailRequired}
className={cx({pristine: this.state.pristine})}
valueLink={this.linkState("tel")} />
<ButtonGroup>

Просмотреть файл

@ -75,6 +75,80 @@ describe("loop.contacts", function() {
expect(addButton).to.not.equal(null);
expect(addButton.textContent).to.eql(fakeAddContactButtonText);
});
it("should have all fields required by default", function() {
var view = TestUtils.renderIntoDocument(
loop.contacts.ContactDetailsForm({mode: "add"}));
var nameInput = view.getDOMNode().querySelector("input[type='text']");
var telInput = view.getDOMNode().querySelector("input[type='tel']");
var emailInput = view.getDOMNode().querySelector("input[type='email']");
expect(nameInput.required).to.equal(true);
expect(emailInput.required).to.equal(true);
expect(telInput.required).to.equal(true);
});
it("should have email and tel required after a name is input", function() {
var view = TestUtils.renderIntoDocument(
loop.contacts.ContactDetailsForm({mode: "add"}));
var nameInput = view.getDOMNode().querySelector("input[type='text']");
TestUtils.Simulate.change(nameInput, {target: {value: "Jenny"}});
var telInput = view.getDOMNode().querySelector("input[type='tel']");
var emailInput = view.getDOMNode().querySelector("input[type='email']");
expect(nameInput.required).to.equal(true);
expect(emailInput.required).to.equal(true);
expect(telInput.required).to.equal(true);
});
it("should allow a contact with only a name and a phone number", function() {
var view = TestUtils.renderIntoDocument(
loop.contacts.ContactDetailsForm({mode: "add"}));
var nameInput = view.getDOMNode().querySelector("input[type='text']");
TestUtils.Simulate.change(nameInput, {target: {value: "Jenny"}});
var telInput = view.getDOMNode().querySelector("input[type='tel']");
TestUtils.Simulate.change(telInput, {target: {value: "867-5309"}});
var emailInput = view.getDOMNode().querySelector("input[type='email']");
expect(nameInput.checkValidity()).to.equal(true, "nameInput");
expect(emailInput.required).to.equal(false, "emailInput");
expect(telInput.checkValidity()).to.equal(true, "telInput");
});
it("should allow a contact with only a name and email", function() {
var view = TestUtils.renderIntoDocument(
loop.contacts.ContactDetailsForm({mode: "add"}));
var nameInput = view.getDOMNode().querySelector("input[type='text']");
TestUtils.Simulate.change(nameInput, {target: {value: "Example"}});
var emailInput = view.getDOMNode().querySelector("input[type='email']");
TestUtils.Simulate.change(emailInput, {target: {value: "test@example.com"}});
var telInput = view.getDOMNode().querySelector("input[type='tel']");
expect(nameInput.checkValidity()).to.equal(true);
expect(emailInput.checkValidity()).to.equal(true);
expect(telInput.required).to.equal(false);
});
it("should not allow a contact with only a name", function() {
var view = TestUtils.renderIntoDocument(
loop.contacts.ContactDetailsForm({mode: "add"}));
var nameInput = view.getDOMNode().querySelector("input[type='text']");
TestUtils.Simulate.change(nameInput, {target: {value: "Example"}});
var emailInput = view.getDOMNode().querySelector("input[type='email']");
var telInput = view.getDOMNode().querySelector("input[type='tel']");
expect(nameInput.checkValidity()).to.equal(true);
expect(emailInput.checkValidity()).to.equal(false);
expect(telInput.checkValidity()).to.equal(false);
});
it("should not allow a contact without name", function() {
var view = TestUtils.renderIntoDocument(
loop.contacts.ContactDetailsForm({mode: "add"}));
var nameInput = view.getDOMNode().querySelector("input[type='text']");
var emailInput = view.getDOMNode().querySelector("input[type='email']");
TestUtils.Simulate.change(emailInput, {target: {value: "test@example.com"}});
var telInput = view.getDOMNode().querySelector("input[type='tel']");
TestUtils.Simulate.change(telInput, {target: {value: "867-5309"}});
expect(nameInput.checkValidity()).to.equal(false);
expect(emailInput.checkValidity()).to.equal(true);
expect(telInput.checkValidity()).to.equal(true);
});
});
describe("edit mode", function() {
it("should render 'edit' header", function() {
@ -137,28 +211,21 @@ describe("loop.contacts", function() {
it("should not set the value on the object if the new value is empty," +
" it didn't exist before, and it is optional", function() {
var contact = {};
loop.contacts._setPreferred(contact, {field: "fakeField", value: "", optional: true});
loop.contacts._setPreferred(contact, "fakeField", "");
expect(contact).to.not.have.property("fakeField");
});
it("should clear the value on the object if the new value is empty," +
" it existed before, and it is optional", function() {
var contact = {fakeField: [{value: "foobar"}]};
loop.contacts._setPreferred(contact, {field: "fakeField", value: "", optional: true});
loop.contacts._setPreferred(contact, "fakeField", "");
expect(contact["fakeField"][0].value).to.eql("");
});
it("should set the value on the object if the new value is empty," +
" and it did not exist before", function() {
var contact = {};
loop.contacts._setPreferred(contact, {field: "fakeField", value: ""});
expect(contact).to.have.property("fakeField");
});
it("should set the value on the object if the new value is empty," +
" and it did not exist before", function() {
var contact = {fakeField: [{value: "foobar"}]};
loop.contacts._setPreferred(contact, {field: "fakeField", value: "barbaz"});
loop.contacts._setPreferred(contact, "fakeField", "barbaz");
expect(contact["fakeField"][0].value).to.eql("barbaz");
});