diff --git a/addon/components/materialize-date-input.js b/addon/components/materialize-date-input.js new file mode 100644 index 00000000..3682684c --- /dev/null +++ b/addon/components/materialize-date-input.js @@ -0,0 +1,20 @@ +import MaterializeInput from './materialize-input'; +import layout from '../templates/components/materialize-date-input'; + +export default MaterializeInput.extend({ + layout: layout, + selectMonths: true, + numberOfYears: 15, + min: '', + max: '', + didInsertElement: function() { + this._super(); + var self = this; + this.$('.datepicker').pickadate({ + selectMonths: self.selectMonths, + selectYears: self.numberOfYears, + min: self.min, + max: self.max + }); + } +}); diff --git a/addon/components/materialize-input-field.js b/addon/components/materialize-input-field.js new file mode 100644 index 00000000..c68827f8 --- /dev/null +++ b/addon/components/materialize-input-field.js @@ -0,0 +1,28 @@ +import Ember from 'ember'; + +export default Ember.Component.extend({ + init: function() { + this._super(); + // bind validation errors + var propertyPath = this.get('valueBinding._label'); + if (Ember.isPresent(propertyPath)) { + Ember.Binding.from('targetObject.errors.' + propertyPath) + .to('errors') + .connect(this); + } + }, + bindAttributes: ['disabled', 'readonly'], + tagName: 'div', + classNames: ['input-field'], + validate: false, + id: Ember.computed(function() { + var elementId = this.get('elementId'); + return elementId + '-input'; + }), + didInsertElement: function() { + // pad the errors element when an icon is present + if (Ember.isPresent(this.get('icon'))) { + this.$('>span').css('padding-left', '3rem'); + } + } +}); diff --git a/addon/components/materialize-input.js b/addon/components/materialize-input.js index a881ea18..15b6b5e6 100644 --- a/addon/components/materialize-input.js +++ b/addon/components/materialize-input.js @@ -1,31 +1,15 @@ import Ember from 'ember'; +import MaterializeInputField from './materialize-input-field'; import layout from '../templates/components/materialize-input'; -export default Ember.Component.extend({ - init: function() { - this._super(); - // bind validation errors - var propertyPath = this.get('valueBinding._label'); - if (Ember.isPresent(propertyPath)) { - Ember.Binding.from('targetObject.errors.' + propertyPath) - .to('errors') - .connect(this); - } - }, +export default MaterializeInputField.extend({ layout: layout, - tagName: 'div', - classNames: ['input-field'], - validate: false, - id: Ember.computed(function() { - var elementId = this.get('elementId'); - return elementId + '-input'; - }), didInsertElement: function() { + this._super(); // make sure the label moves when a value is bound. var labelSelector = this.$('>label'); if (Ember.isPresent(this.get('value')) && !labelSelector.hasClass('active')) { labelSelector.addClass('active'); - labelSelector.trigger('validate'); } } }); diff --git a/addon/components/materialize-select.js b/addon/components/materialize-select.js new file mode 100644 index 00000000..d3dc385a --- /dev/null +++ b/addon/components/materialize-select.js @@ -0,0 +1,26 @@ +import Ember from 'ember'; +import MaterializeInputField from './materialize-input-field'; +import layout from '../templates/components/materialize-select'; + +export default MaterializeInputField.extend({ + layout: layout, + didInsertElement: function() { + this._super(); + this.$('select').material_select(); + }, + errorsDidChange: function() { + var self = this; + var inputSelector = this.$('input'); + // monitor the select's validity and copy the appropriate validation class to the materialize input element. + Ember.run.later(function() { + var isValid = self.$('select').hasClass('valid'); + if (isValid) { + inputSelector.removeClass('invalid'); + inputSelector.addClass('valid'); + } else { + inputSelector.removeClass('valid'); + inputSelector.addClass('invalid'); + } + }, 150); + }.observes('errors') +}); diff --git a/addon/components/materialize-textarea.js b/addon/components/materialize-textarea.js new file mode 100644 index 00000000..8c422c31 --- /dev/null +++ b/addon/components/materialize-textarea.js @@ -0,0 +1,15 @@ +import Ember from 'ember'; +import InputField from './materialize-input-field'; +import layout from '../templates/components/materialize-textarea'; + +export default InputField.extend({ + layout: layout, + didInsertElement: function() { + this._super(); + // make sure the label moves when a value is bound. + var labelSelector = this.$('>label'); + if (Ember.isPresent(this.get('value')) && !labelSelector.hasClass('active')) { + labelSelector.addClass('active'); + } + } +}); diff --git a/addon/templates/components/materialize-date-input.hbs b/addon/templates/components/materialize-date-input.hbs new file mode 100644 index 00000000..9ebf9057 --- /dev/null +++ b/addon/templates/components/materialize-date-input.hbs @@ -0,0 +1,11 @@ +{{#if icon}} + +{{/if}} + + + + {{#if errors}} {{errors.firstObject}} {{else}}   {{/if}} + + diff --git a/addon/templates/components/materialize-input.hbs b/addon/templates/components/materialize-input.hbs index f9a640d8..9e5717a9 100644 --- a/addon/templates/components/materialize-input.hbs +++ b/addon/templates/components/materialize-input.hbs @@ -1,7 +1,7 @@ {{#if icon}} {{/if}} -{{input id=id value=value classNameBindings="validate:validate: errors:invalid:valid" type="text" +{{input id=id value=value classNameBindings="validate:validate: errors.firstObject:invalid:valid" type="text" required=required pattern=pattern maxlength=maxlength readonly=readonly disabled=disabled autocomplete=autocomplete}} diff --git a/addon/templates/components/materialize-select.hbs b/addon/templates/components/materialize-select.hbs new file mode 100644 index 00000000..2dfde08e --- /dev/null +++ b/addon/templates/components/materialize-select.hbs @@ -0,0 +1,6 @@ + +{{view "select" id=id content=content optionValuePath=optionValuePath optionLabelPath=optionLabelPath prompt=prompt + value=value classNameBindings="validate:validate: errors:invalid:valid"}} + + {{#if errors}} {{errors.firstObject}} {{else}}   {{/if}} + diff --git a/addon/templates/components/materialize-textarea.hbs b/addon/templates/components/materialize-textarea.hbs new file mode 100644 index 00000000..cffbd5ee --- /dev/null +++ b/addon/templates/components/materialize-textarea.hbs @@ -0,0 +1,10 @@ +{{#if icon}} + +{{/if}} +{{textarea id=id value=value name=name required=required readonly=readonly disabled=disabled maxlength=maxlength + class="materialize-textarea"}} + + + {{#if errors}} {{errors.firstObject}} {{else}}   {{/if}} + + diff --git a/app/components/materialize-date-input.js b/app/components/materialize-date-input.js new file mode 100644 index 00000000..4e4997ff --- /dev/null +++ b/app/components/materialize-date-input.js @@ -0,0 +1,3 @@ +import materializeDateInput from 'ember-cli-materialize/components/materialize-date-input'; + +export default materializeDateInput; diff --git a/app/components/materialize-input-field.js b/app/components/materialize-input-field.js new file mode 100644 index 00000000..34e0279f --- /dev/null +++ b/app/components/materialize-input-field.js @@ -0,0 +1,3 @@ +import materializeInputField from 'ember-cli-materialize/components/materialize-input-field'; + +export default materializeInputField; diff --git a/app/components/materialize-select.js b/app/components/materialize-select.js new file mode 100644 index 00000000..3c0baa6c --- /dev/null +++ b/app/components/materialize-select.js @@ -0,0 +1,3 @@ +import materializeSelect from 'ember-cli-materialize/components/materialize-select'; + +export default materializeSelect; diff --git a/app/components/materialize-textarea.js b/app/components/materialize-textarea.js new file mode 100644 index 00000000..d4b1418e --- /dev/null +++ b/app/components/materialize-textarea.js @@ -0,0 +1,3 @@ +import materializeTextarea from 'ember-cli-materialize/components/materialize-textarea'; + +export default materializeTextarea; diff --git a/tests/dummy/app/controllers/input.js b/tests/dummy/app/controllers/input.js index f1df59db..d98978ee 100644 --- a/tests/dummy/app/controllers/input.js +++ b/tests/dummy/app/controllers/input.js @@ -1,15 +1,33 @@ import Ember from 'ember'; export default Ember.ObjectController.extend({ - + frameworks: [ + {id: 1, value: 'Materialize CSS'}, + {id: 2, value: 'Ember-CLI Materialize'} + ], + errors: Ember.Object.create({ + name: [], + framework: [] + }), nameDidChange: function() { - var errors = { name: [] }; + var errors = this.get('errors'); + var messages = []; if (!Ember.isPresent(this.get('name'))) { - errors.name = ['This field is required']; - } else { - errors.name = []; + messages = ['This field is required']; } + errors.set('name', messages); this.set('errors', errors); - }.observes('name') - + }.observes('name'), + frameworkDidChange: function() { + var self = this; + var errors = self.get('errors'); + Ember.run.later(function() { + var messages = []; + if (!Ember.isPresent(self.get('framework'))) { + messages = ['This field is required']; + } + errors.set('framework', messages); + self.set('errors', errors); + }, 100); + }.observes('framework') }); diff --git a/tests/dummy/app/templates/index.hbs b/tests/dummy/app/templates/index.hbs index 4a7ec0ff..9442cfb5 100644 --- a/tests/dummy/app/templates/index.hbs +++ b/tests/dummy/app/templates/index.hbs @@ -34,7 +34,7 @@ {{#link-to 'navbar' class="collection-item"}}Navbar1{{/link-to}} {{#link-to 'cards' class="collection-item"}}Cards1{{/link-to}} {{#link-to 'collapsible' class="collection-item"}}Collapsible1{{/link-to}} - {{#link-to 'input' class="collection-item"}}Input1{{/link-to}} + {{#link-to 'input' class="collection-item"}}Input4{{/link-to}} diff --git a/tests/dummy/app/templates/input.hbs b/tests/dummy/app/templates/input.hbs index 1d72523c..b6787b63 100644 --- a/tests/dummy/app/templates/input.hbs +++ b/tests/dummy/app/templates/input.hbs @@ -9,9 +9,12 @@
+
+

Input

+
-

The component supports the following options:

+
The component supports the following options:
  • label, default value null
  • icon, default value null
  • @@ -25,9 +28,8 @@
  • autocomplete, default value null
-
-
-

Input

+ +

{{materialize-input value="Hi Mom" label='Example'}} @@ -83,5 +85,96 @@ nameDidChange: function() {
+ +
+

Text Area

+
+
The component supports the following options:
+
    +
  • label, default value null
  • +
  • icon, default value null
  • +
  • value, default value null
  • +
  • name, default value null
  • +
  • required, default value false
  • +
  • readonly, default value false
  • +
  • disabled, default value false
  • +
  • maxlength, default value null
  • +
  • validate, default value false
  • +
+
+
+
+ + {{materialize-textarea label='Message' value=message icon="mdi-action-face-unlock"}} + +
+
+{{materialize-textarea label='Message' value="The textarea will auto-grow as you type." icon="mdi-action-face-unlock"}}
+
+
+
+
+ +
+

Select

+
+
The component supports the following options:
+
    +
  • label, default value null
  • +
  • icon, default value null
  • +
  • prompt, default value null
  • +
  • content, default value null
  • +
  • value, default value null
  • +
  • optionValuePath, default value null
  • +
  • optionLabelPath, default value null
  • +
  • validate, default value false
  • +
+
+
+
+ + {{materialize-select content=frameworks value=framework label="Framework" prompt="Please choose..." + optionLabelPath="content.value" optionValuePath="content"}} + +
+
+{{materialize-select content=frameworks value=framework label="Framework" prompt="Please choose..."
+  optionLabelPath="content.value" optionValuePath="content"}}
+
+
+
+
+ +
+

Date Input

+
+
The component supports the following options:
+
    +
  • label, default value null
  • +
  • icon, default value null
  • +
  • value, default value null
  • +
  • required, default value null
  • +
  • readonly, default value null
  • +
  • disabled, default value null
  • +
  • selectMonths, default value true
  • +
  • numberOfYears, default value 15
  • +
  • min, default value null
  • +
  • max, default value null
  • +
  • validate, default value false
  • +
+
+ +
+
+ {{materialize-date-input label='Birthday' icon="mdi-action-face-unlock"}} + +
+
+{{materialize-date-input label='Birthday' icon="mdi-action-face-unlock"}}
+
+
+
+
+
diff --git a/tests/unit/components/materialize-date-input-test.js b/tests/unit/components/materialize-date-input-test.js new file mode 100644 index 00000000..a7a39ac1 --- /dev/null +++ b/tests/unit/components/materialize-date-input-test.js @@ -0,0 +1,54 @@ +import { + moduleForComponent, + test +} from 'ember-qunit'; + +moduleForComponent('materialize-date-input', { + // specify the other units that are required for this test +}); + +test('it renders', function(assert) { + assert.expect(2); + + // creates the component instance + var component = this.subject(); + assert.equal(component._state, 'preRender'); + + // renders the component to the page + this.render(); + assert.equal(component._state, 'inDOM'); +}); + + +test('has class input-field', function(assert) { + var component = this.subject(); + this.render(); + assert.ok(component.$().hasClass('input-field')); +}); + +test('has a label', function(assert) { + var label = 'My Input'; + var component = this.subject({ label: label }); + this.render(); + assert.equal(component.$('>label').text(), label); +}); + +test('has a value', function(assert) { + var value = '15 January, 1974'; + var component = this.subject({ value: value }); + this.render(); + assert.equal(component.$('>input').val(), value); +}); + +test('label is active with value', function(assert) { + var component = this.subject({ value: '15 January, 1974' }); + this.render(); + assert.ok(component.$('>label').hasClass('active')); +}); + +test('has an icon', function(assert) { + var icon = 'mdi-action-face-unlock'; + var component = this.subject({ icon: icon }); + this.render(); + assert.ok(component.$('>i').hasClass(icon)); +}); diff --git a/tests/unit/components/materialize-input-field-test.js b/tests/unit/components/materialize-input-field-test.js new file mode 100644 index 00000000..58eeaeed --- /dev/null +++ b/tests/unit/components/materialize-input-field-test.js @@ -0,0 +1,56 @@ +import { + moduleForComponent, + test +} from 'ember-qunit'; + +moduleForComponent('materialize-input-field', { + // specify the other units that are required for this test + // needs: ['component:foo', 'helper:bar'] +}); + +test('it renders', function(assert) { + assert.expect(2); + + // creates the component instance + var component = this.subject(); + assert.equal(component._state, 'preRender'); + + // renders the component to the page + this.render(); + assert.equal(component._state, 'inDOM'); +}); + +test('has class input-field', function(assert) { + var component = this.subject(); + this.render(); + assert.ok(component.$().hasClass('input-field')); +}); + + +test('has class input-field', function(assert) { + var component = this.subject(); + this.render(); + assert.ok(component.$().hasClass('input-field')); +}); + +test('has a label', function(assert) { + var label = 'My Input'; + var component = this.subject({ label: label }); + this.render(); + assert.equal(component.get('label'), label); +}); + +test('has a value', function(assert) { + var value = 'My Input Value'; + var component = this.subject({ value: value }); + this.render(); + assert.equal(component.get('value'), value); +}); + +test('has an icon', function(assert) { + var icon = 'mdi-action-face-unlock'; + var component = this.subject({ icon: icon }); + this.render(); + assert.equal(component.get('icon'), icon); +}); + diff --git a/tests/unit/components/materialize-input-test.js b/tests/unit/components/materialize-input-test.js index dabb1031..1ce2d65d 100644 --- a/tests/unit/components/materialize-input-test.js +++ b/tests/unit/components/materialize-input-test.js @@ -3,9 +3,7 @@ import { test } from 'ember-qunit'; -moduleForComponent('materialize-input', { - // specify the other units that are required for this test - // needs: ['component:foo', 'helper:bar'] +moduleForComponent('materialize-input', 'MaterializeInput', { }); test('it renders', function(assert) { diff --git a/tests/unit/components/materialize-select-test.js b/tests/unit/components/materialize-select-test.js new file mode 100644 index 00000000..f7c6f5f7 --- /dev/null +++ b/tests/unit/components/materialize-select-test.js @@ -0,0 +1,45 @@ +import { + moduleForComponent, + test +} from 'ember-qunit'; + +moduleForComponent('materialize-select', { + // specify the other units that are required for this test +}); + +test('it renders', function(assert) { + assert.expect(2); + + // creates the component instance + var component = this.subject(); + assert.equal(component._state, 'preRender'); + + // renders the component to the page + this.render(); + assert.equal(component._state, 'inDOM'); +}); + +test('has class input-field', function(assert) { + var component = this.subject(); + this.render(); + assert.ok(component.$().hasClass('input-field')); +}); + +test('has a label', function(assert) { + var label = 'My Input'; + var component = this.subject({ label: label }); + this.render(); + assert.equal(component.$('>label').text(), label); +}); + +test('label is active with value', function(assert) { + var value = {id: 1, name: 'My Input Value'}; + var component = this.subject({ + content: [value], + value: value, + optionLabelPath: 'content.name', + optionValuePath: 'content.id' + }); + this.render(); + assert.ok(component.$('>label').hasClass('active')); +}); diff --git a/tests/unit/components/materialize-textarea-test.js b/tests/unit/components/materialize-textarea-test.js new file mode 100644 index 00000000..54d3d325 --- /dev/null +++ b/tests/unit/components/materialize-textarea-test.js @@ -0,0 +1,54 @@ +import { + moduleForComponent, + test +} from 'ember-qunit'; + +moduleForComponent('materialize-textarea', { + // specify the other units that are required for this test +}); + +test('it renders', function(assert) { + assert.expect(2); + + // creates the component instance + var component = this.subject(); + assert.equal(component._state, 'preRender'); + + // renders the component to the page + this.render(); + assert.equal(component._state, 'inDOM'); +}); + +test('has class input-field', function(assert) { + var component = this.subject(); + this.render(); + assert.ok(component.$().hasClass('input-field')); +}); + +test('has a label', function(assert) { + var label = 'My Input'; + var component = this.subject({ label: label }); + this.render(); + assert.equal(component.$('>label').text(), label); +}); + +test('has a value', function(assert) { + var value = 'My Input Value'; + var component = this.subject({ value: value }); + this.render(); + assert.equal(component.get('value'), value); +}); + +test('label is active with value', function(assert) { + var component = this.subject({ value: 'some text' }); + this.render(); + assert.ok(component.$('>label').hasClass('active')); +}); + +test('has an icon', function(assert) { + var icon = 'mdi-action-face-unlock'; + var component = this.subject({ icon: icon }); + this.render(); + assert.ok(component.$('>i').hasClass(icon)); +}); +