Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EZP-27577: Creates <ez-field-edit> #26

Merged
merged 1 commit into from Jul 12, 2017

Conversation

StephaneDiot
Copy link
Contributor

@StephaneDiot StephaneDiot commented Jul 5, 2017

JIRA: https://jira.ez.no/browse/EZP-27577

Description

Creates ez-field-edit custom element. Currently this component only handles fields inputs validity.

A bit of CSS for the validation part in ezsystems/hybrid-platform-ui#55

Tests

Demo and unit tested

@StephaneDiot StephaneDiot force-pushed the EZP-27577_Creates_ez-field-edit branch 4 times, most recently from a412b9a to 9fce55d Compare July 5, 2017 09:02
@StephaneDiot StephaneDiot requested review from dpobel and removed request for dpobel July 5, 2017 09:03
@StephaneDiot StephaneDiot changed the title EZP-27577: Creates <ez-field-edit> WIP - EZP-27577: Creates <ez-field-edit> Jul 5, 2017
@StephaneDiot StephaneDiot force-pushed the EZP-27577_Creates_ez-field-edit branch 2 times, most recently from 493e664 to 4c1694a Compare July 5, 2017 15:18
@StephaneDiot StephaneDiot changed the title WIP - EZP-27577: Creates <ez-field-edit> EZP-27577: Creates <ez-field-edit> Jul 5, 2017
@StephaneDiot StephaneDiot requested a review from dpobel July 5, 2017 15:22

<link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
<link rel="import" href="../ez-field-edit.html">
<style is="custom-style" include="demo-pages-shared-styles">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since custom-style and share styles are not included, is and include attributes should be removed

@@ -0,0 +1,94 @@
(function () {
/**
* `<ez-field-edit>` is supposed to wrap one or several HTML5 inputs. It tracks user input,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CS issue: stars should be aligned with the first star in the previous line (example https://github.com/ezsystems/hybrid-platform-ui-core-components/blob/master/js/ez-notification.js#L3) and there should be only one space between the star beginning the line and the text


static get properties() {
return {
'invalid': {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing doc


constructor() {
super();
this.addEventListener('input', this._checkValidity.bind(this));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should probably be delayed to connectedCallback (meaning you don't need to define a constructor function)

this.querySelectorAll('input, select, textarea').forEach((formElement) => {
valid = formElement.validity.valid;

this.querySelectorAll('label').forEach((labelElement) => {
Copy link
Contributor

@dpobel dpobel Jul 6, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's a logical issue here. The selected label can be attached to another input so in that case most likely, the class ez-invalid will be added to ez-field-edit. I think you iterate over formElement.labels (which means the label must have a for attribute, those are missing in your test)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that also means you should have a test with a fixture where an input have several labels

const parents2 = this._parents(node2);

for (let i = 0; i < parents1.length; i++) {
if (parents1[i] != parents2[i]) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have some doubts if this is really working if node1 and node2 have a different depth. You should improve the test various cases.

<template>
<ez-field-edit>
<div id="lowestCommonParent1">
<label>Field label</label>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

related to comment about search the common ancestor, those fixtures are too simple. Having the label and the input with the same parent node is clearly an over simplification

<ez-field-edit>
<div id="lowestCommonParent1">
<label>Field label</label>
<input id="input1" type="text" pattern="\d*"/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

id="input1" is already used above, make sure to have unique ids (and by the way, label should have a for attribute)

<template>
<form>
<ez-field-edit>
Simple input:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

put this string and Multiple inputs in a h1 or something like that, this would help using the demo


this.querySelectorAll('label').forEach((labelElement) => {
if (!valid) {
this._commonAncestor(labelElement, formElement).classList.add('ez-invalid');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should put the invalid class in a const to avoid repeating it and also, I'm not super fan of that class, it's maybe a tad too generic. ez-invalid-input maybe ?


describe('invalid lowest common parentNode between related input and label', function () {
it('should set `ez-invalid` class on the lowest common parentNode', function () {
elementWithMultipleInputs.querySelectorAll('input')[0].value = 'invalid value';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should avoid that because this relies on the order in the fixture. Here you should rather use:

elementWithMultipleInputs.querySelectorAll('#lowestCommonParent1 input').value = 'invalid value';

or given you use the #lowestCommonParent1 element below:

const ancestor = elementWithMultipleInputs.querySelector('#lowestCommonParent1');

ancestor.querySelector('input').value = 'invalid value';

@StephaneDiot StephaneDiot force-pushed the EZP-27577_Creates_ez-field-edit branch from e6ef30a to 86fbffe Compare July 7, 2017 08:50
@StephaneDiot
Copy link
Contributor Author

updated @dpobel

/**
* `<ez-field-edit>` is supposed to wrap one or several HTML5 inputs. It tracks user input,
* and if an input is invalid, sets its own `invalid` property to true. It will also add an
* `ez-invalid` class on the related label. This class will be removed once the input gets valid.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the class has changed, ez-invalid-input and it now adds the class on the first common label and input ancestor.

* `ez-invalid` class on the related label. This class will be removed once the input gets valid.
*
* Warning: each element inside <ez-field-edit> can't own more than one couple <input> + <label>
* Furthermore to connect a <label> to an <input> use `for`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the standard mechanism, no need to add that

/**
* Checks whether the inputs are valid or not and set the `invalid` property
* accordingly.
* Also set `ez-invalid-input` class on labels connected to invalid inputs.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not on labels

}

/**
* Returns the parentNodes of the given node untill <ez-field-edit>.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parent nodes

* @param {HTMLElement} node
* @return {Array}
*/
_parents(node) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's a bad name, _getAncestors maybe ?

<ez-field-edit>
<div id="lowestCommonParent1">
<label for="input2">Field label</label>
<a>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not some most appropriate usage of a, put a span or a div

<template>
<ez-field-edit>
<div>
<a>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above regarding a usage

*
* @polymerElement
* @demo demo/ez-field-edit.html
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing a space :)


it('should get valid', function () {
element.invalid = true;
element.querySelectorAll('input').value = 42;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this does not what you want to do here :) querySelectorAll returns a NodeList on which you add a value property with the value 42 ie you are not setting the input value :)

});
});

describe('invalid lowest common parentNode between related input and label', function () {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the test outline is not super clear, it seems like some cases are not tested or mixed.
I would write something like:

describe('`ez-invalid-input` class', function () {
    describe('unique input', function () {
        describe('with label', function () {
            it('should be set on lowest common ancestor');
            it('should be remove from lowest common ancestor');

            describe('label deeper than input', function () {
                it('should be set on lowest common ancestor');
                it('should be remove from lowest common ancestor');
            });

            describe('input deeper than label', function () {
                it('should be set on lowest common ancestor');
                it('should be remove from lowest common ancestor');
            });
        });

        describe('without label', function () {
             it('should be set on parent node');
             it('should be remove from parent node');
        });
    });

    describe('multiple inputs', function () {
        describe('with label', function () {
            it('should be set on lowest common ancestor');
            it('should be remove from lowest common ancestor');

            describe('label deeper than input', function () {
                it('should be set on lowest common ancestor');
                it('should be remove from lowest common ancestor');
            });

            describe('input deeper than label', function () {
                it('should be set on lowest common ancestor');
                it('should be remove from lowest common ancestor');
            });
        });

        describe('without label', function () {
             it('should be set on parent node');
             it('should be remove from parent node');
        });
    });
});

@StephaneDiot StephaneDiot force-pushed the EZP-27577_Creates_ez-field-edit branch from 5835978 to 2baa3cf Compare July 10, 2017 13:02

this.querySelectorAll('input, select, textarea').forEach((formElement) => {
const getLabelsForFormElement = (input) => {
const labels = input.id ? this.querySelectorAll(`label[for="${formElement.id}"]`) : [];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use input instead of formElement, that way this function can be defined outside of the forEach loop

toggleInvalidClass(formElement.parentNode, valid);
}
});
this.invalid = !!this.querySelectorAll('.ez-invalid-input').length || this.classList.contains('ez-invalid-input');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is quite complicated, given you loop over inputs, why not computing the validity:

let globalValidity = true;

this.querySelectorAll('input, select, textarea').forEach((formElement) => {
    // rest of the code
    globalValidity = (globalValidity && valid);
});
this.invalid = globalValidity;

}

/**
* Returns the parent nodes of the given node untill <ez-field-edit>.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

until

@StephaneDiot StephaneDiot force-pushed the EZP-27577_Creates_ez-field-edit branch from 0dd2835 to ef0fbe4 Compare July 12, 2017 12:13
@StephaneDiot StephaneDiot merged commit 2377cb2 into master Jul 12, 2017
@StephaneDiot StephaneDiot deleted the EZP-27577_Creates_ez-field-edit branch July 12, 2017 12:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
2 participants