From b036750e002b4e026bc6d622d8d534b5a5441007 Mon Sep 17 00:00:00 2001 From: Ken Sin Date: Mon, 19 Dec 2016 14:55:28 -0800 Subject: [PATCH 1/2] Initial implementation of tf-input, tf-input-group, and tf-textarea --- README.md | 102 +++++++++++++++++- addon/components/tf-input-group/component.js | 29 +++++ addon/components/tf-input-group/template.hbs | 18 ++++ addon/components/tf-input/component.js | 18 ++++ .../tf-input/input-description/component.js | 27 +++++ .../tf-input/input-description/template.hbs | 1 + .../tf-input/input-label/component.js | 26 +++++ .../tf-input/input-label/template.hbs | 1 + addon/components/tf-textarea/component.js | 14 +++ addon/mixins/sizing-mixin.js | 38 +++++++ addon/mixins/validity-mixin.js | 22 ++++ app/components/tf-input-group.js | 1 + app/components/tf-input.js | 1 + app/components/tf-input/input-description.js | 1 + app/components/tf-input/input-label.js | 1 + app/components/tf-textarea.js | 1 + package.json | 11 +- .../components/tf-input-group-test.js | 46 ++++++++ tests/integration/components/tf-input-test.js | 37 +++++++ .../components/tf-textarea-test.js | 13 +++ 20 files changed, 402 insertions(+), 6 deletions(-) create mode 100644 addon/components/tf-input-group/component.js create mode 100644 addon/components/tf-input-group/template.hbs create mode 100644 addon/components/tf-input/component.js create mode 100644 addon/components/tf-input/input-description/component.js create mode 100644 addon/components/tf-input/input-description/template.hbs create mode 100644 addon/components/tf-input/input-label/component.js create mode 100644 addon/components/tf-input/input-label/template.hbs create mode 100644 addon/components/tf-textarea/component.js create mode 100644 addon/mixins/sizing-mixin.js create mode 100644 addon/mixins/validity-mixin.js create mode 100644 app/components/tf-input-group.js create mode 100644 app/components/tf-input.js create mode 100644 app/components/tf-input/input-description.js create mode 100644 app/components/tf-input/input-label.js create mode 100644 app/components/tf-textarea.js create mode 100644 tests/integration/components/tf-input-group-test.js create mode 100644 tests/integration/components/tf-input-test.js create mode 100644 tests/integration/components/tf-textarea-test.js diff --git a/README.md b/README.md index b417bd2..be45405 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,104 @@ -# ticketfly-fields +# Ticketfly Fields -This README outlines the details of collaborating on this Ember addon. +This Ember addon provides the field components of the Ticketfly UI library. The components provided include: + +* `tf-input` +* `tf-input-group` +* `tf-textarea` + +## Usage + +**Input Sizing** + +Template: +```hbs +{{tf-input size="large"}} +{{tf-input}} {{!-- size="default" by default --}} +{{tf-input size="small"}} +``` + +Resulting HTML: +```html + + + +``` + +**Input Block with Label & Description** + +Template: + +```hbs +{{#tf-input-group size="large" as |group|}} + {{group.label "First Name"}} + {{group.input}} + {{group.description "You must provide a First Name"}} +{{/tf-input-group}} +``` + +Resulting HTML: + +```html +
+ + +

You must provide a First Name

+
+``` + +The contextual components for `tf-input-group` support overriding the size of child components such as: + +```hbs +{{#tf-input-group size="large" as |group|}} + {{group.label "First Name" }} + {{group.input }} + {{group.description "You must provide a First Name" size="small"}} +{{/tf-input-group}} +``` + +**Input Validation States** + +Template: + +```hbs +{{tf-input should-validate=true is-valid=true}} +{{tf-input should-validate=true is-valid=false}} +{{tf-input should-validate=false is-valid=true}} + +{{#tf-input-group should-validate=true is-valid=false as |group|}} + {{group.label "First Name"}} + {{group.input}} + {{group.description "You must provide a First Name"}} +{{/tf-input-group}} +``` + +Resulting HTML: + +```html + + + + +
+ + +

You must provide a First Name

+
+``` + +**Text Area** + +Template: + +```hbs +{{tf-textarea}} +``` + +Resulting HTML: + +```html + +``` ## Installation diff --git a/addon/components/tf-input-group/component.js b/addon/components/tf-input-group/component.js new file mode 100644 index 0000000..c396137 --- /dev/null +++ b/addon/components/tf-input-group/component.js @@ -0,0 +1,29 @@ +/** + @module ticketfly-fields + */ +import Ember from 'ember'; +import layout from './template'; +const { Component, guidFor, computed } = Ember; + +/** + @public + @class TfInputGroup + @extends Ember.Component + */ +export default Component.extend({ + layout, + + classNames: ['c-input-group'], + + inputGuid: computed({ + get() { + return `id-${guidFor(this)}`; + } + }), + + describedByGuid: computed({ + get() { + return `described-by-${guidFor(this)}`; + } + }) +}); \ No newline at end of file diff --git a/addon/components/tf-input-group/template.hbs b/addon/components/tf-input-group/template.hbs new file mode 100644 index 0000000..cbb24cc --- /dev/null +++ b/addon/components/tf-input-group/template.hbs @@ -0,0 +1,18 @@ +{{yield (hash + label=(component 'tf-input/input-label' + sizeStyle=sizeStyle + for=inputGuid + shouldValidate=shouldValidate + isValid=isValid) + input=(component 'tf-input' + sizeStyle=sizeStyle + id=inputGuid + aria-describedby=describedByGuid + shouldValidate=shouldValidate + isValid=isValid) + description=(component 'tf-input/input-description' + sizeStyle=sizeStyle + id=describedByGuid + shouldValidate=shouldValidate + isValid=isValid) +)}} \ No newline at end of file diff --git a/addon/components/tf-input/component.js b/addon/components/tf-input/component.js new file mode 100644 index 0000000..b4ea2fc --- /dev/null +++ b/addon/components/tf-input/component.js @@ -0,0 +1,18 @@ +/** + @module ticketfly-fields + */ +import Ember from 'ember'; +import SizingMixin from 'ticketfly-fields/mixins/sizing-mixin'; +import ValidityMixin from 'ticketfly-fields/mixins/validity-mixin'; +const { TextField } = Ember; + +/** + @public + @class TfInput + @extends Ember.TextField + */ +export default TextField.extend(SizingMixin, ValidityMixin, { + classNames: ['c-input'], + classNameBindings: ['sizing', 'validity'], + attributeBindings: ['aria-describedby'] +}); \ No newline at end of file diff --git a/addon/components/tf-input/input-description/component.js b/addon/components/tf-input/input-description/component.js new file mode 100644 index 0000000..9c5cb03 --- /dev/null +++ b/addon/components/tf-input/input-description/component.js @@ -0,0 +1,27 @@ +/** + @module ticketfly-fields + */ +import Ember from 'ember'; +import SizingMixin from 'ticketfly-fields/mixins/sizing-mixin'; +import ValidityMixin from 'ticketfly-fields/mixins/validity-mixin'; +import layout from './template'; +const { Component } = Ember; + +/** + @class TfInputDescription + @extends Ember.Computed + */ +const InputDescriptionComponent = Component.extend(SizingMixin, ValidityMixin, { + layout, + + tagName: 'p', + classNameBindings: ['sizing', 'validity'], + + utilityName: 'description' +}); + +InputDescriptionComponent.reopenClass({ + positionalParams: ['text'] +}); + +export default InputDescriptionComponent; \ No newline at end of file diff --git a/addon/components/tf-input/input-description/template.hbs b/addon/components/tf-input/input-description/template.hbs new file mode 100644 index 0000000..bcc8355 --- /dev/null +++ b/addon/components/tf-input/input-description/template.hbs @@ -0,0 +1 @@ +{{text}} \ No newline at end of file diff --git a/addon/components/tf-input/input-label/component.js b/addon/components/tf-input/input-label/component.js new file mode 100644 index 0000000..76e1c32 --- /dev/null +++ b/addon/components/tf-input/input-label/component.js @@ -0,0 +1,26 @@ +/** + @module ticketfly-fields + */ +import Ember from 'ember'; +import SizingMixin from 'ticketfly-fields/mixins/sizing-mixin'; +import ValidityMixin from 'ticketfly-fields/mixins/validity-mixin'; +import layout from './template'; +const { Component } = Ember; + +/** + @class TfInputLabel + @extends Ember.Computed + */ +const InputLabelComponent = Component.extend(SizingMixin, ValidityMixin, { + layout, + + tagName: 'label', + classNameBindings: ['sizing', 'validity'], + attributeBindings: ['for'] +}); + +InputLabelComponent.reopenClass({ + positionalParams: ['text'] +}); + +export default InputLabelComponent; \ No newline at end of file diff --git a/addon/components/tf-input/input-label/template.hbs b/addon/components/tf-input/input-label/template.hbs new file mode 100644 index 0000000..bcc8355 --- /dev/null +++ b/addon/components/tf-input/input-label/template.hbs @@ -0,0 +1 @@ +{{text}} \ No newline at end of file diff --git a/addon/components/tf-textarea/component.js b/addon/components/tf-textarea/component.js new file mode 100644 index 0000000..bc882f9 --- /dev/null +++ b/addon/components/tf-textarea/component.js @@ -0,0 +1,14 @@ +/** + @module ticketfly-fields + */ +import Ember from 'ember'; +const { TextArea } = Ember; + +/** + @public + @class TfTextarea + @extends Ember.TextArea + */ +export default TextArea.extend({ + classNames: ['c-textarea'] +}); \ No newline at end of file diff --git a/addon/mixins/sizing-mixin.js b/addon/mixins/sizing-mixin.js new file mode 100644 index 0000000..9103b71 --- /dev/null +++ b/addon/mixins/sizing-mixin.js @@ -0,0 +1,38 @@ +/** + * @module ticketfly-fields + */ +import Ember from 'ember'; +const { Mixin, computed, get } = Ember; + +/** + @class SizingMixin + @extends Ember.Mixin + */ +export default Mixin.create({ + sizing: computed('sizeStyle','utilityName', { + get() { + const sizeStyle = get(this, 'sizeStyle'); + if (sizeStyle) { + const utilityName = get(this, 'utilityName'); + return `u-${utilityName}-${sizeStyle}`; + } + } + }), + + /** + Override the utilityName property for a more unique sizing class name. + Defaults to the class' tagName + + Example + + If the tagName of the component is 'p', one can set the utilityName to change + the return value of sizing: + + ```javascript + get(this, 'sizing'); // 'u-p-large' + set(this, 'utilityName', 'description'); + get(this, 'sizing'); // 'u-description-large' + ``` + */ + utilityName: computed.oneWay('tagName') +}); \ No newline at end of file diff --git a/addon/mixins/validity-mixin.js b/addon/mixins/validity-mixin.js new file mode 100644 index 0000000..d0c5767 --- /dev/null +++ b/addon/mixins/validity-mixin.js @@ -0,0 +1,22 @@ +/** + * @module ticketfly-fields + */ +import Ember from 'ember'; +const { Mixin, computed, get } = Ember; + +/** + @class ValidityMixin + @extends Ember.Mixin + */ +export default Mixin.create({ + shouldValidate: false, + isValid: false, + + validity: computed('shouldValidate','isValid', { + get() { + if (get(this, 'shouldValidate')) { + return get(this, 'isValid') ? 'is-valid' : 'is-invalid'; + } + } + }) +}); \ No newline at end of file diff --git a/app/components/tf-input-group.js b/app/components/tf-input-group.js new file mode 100644 index 0000000..9194285 --- /dev/null +++ b/app/components/tf-input-group.js @@ -0,0 +1 @@ +export { default } from 'ticketfly-fields/components/tf-input-group/component'; diff --git a/app/components/tf-input.js b/app/components/tf-input.js new file mode 100644 index 0000000..281e0ba --- /dev/null +++ b/app/components/tf-input.js @@ -0,0 +1 @@ +export { default } from 'ticketfly-fields/components/tf-input/component'; diff --git a/app/components/tf-input/input-description.js b/app/components/tf-input/input-description.js new file mode 100644 index 0000000..481e2c9 --- /dev/null +++ b/app/components/tf-input/input-description.js @@ -0,0 +1 @@ +export { default } from 'ticketfly-fields/components/tf-input/input-description/component'; diff --git a/app/components/tf-input/input-label.js b/app/components/tf-input/input-label.js new file mode 100644 index 0000000..8be586c --- /dev/null +++ b/app/components/tf-input/input-label.js @@ -0,0 +1 @@ +export { default } from 'ticketfly-fields/components/tf-input/input-label/component'; diff --git a/app/components/tf-textarea.js b/app/components/tf-textarea.js new file mode 100644 index 0000000..a0fc1a1 --- /dev/null +++ b/app/components/tf-textarea.js @@ -0,0 +1 @@ +export { default } from 'ticketfly-fields/components/tf-textarea/component'; diff --git a/package.json b/package.json index c25b597..5f41b34 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,12 @@ { "name": "ticketfly-fields", "version": "0.0.0", - "description": "The default blueprint for ember-cli addons.", + "description": "The field components of the Ticketfly UI library.", "keywords": [ - "ember-addon" + "ember-addon", + "ticketfly", + "ticketfly-ui", + "fields" ], "license": "MIT", "author": "", @@ -18,7 +21,8 @@ "test": "ember try:each" }, "dependencies": { - "ember-cli-babel": "^5.1.7" + "ember-cli-babel": "^5.1.7", + "ember-cli-htmlbars": "^1.0.10" }, "devDependencies": { "broccoli-asset-rev": "^2.4.5", @@ -26,7 +30,6 @@ "ember-cli": "2.10.0", "ember-cli-app-version": "^2.0.0", "ember-cli-dependency-checker": "^1.3.0", - "ember-cli-htmlbars": "^1.0.10", "ember-cli-htmlbars-inline-precompile": "^0.3.3", "ember-cli-inject-live-reload": "^1.4.1", "ember-cli-jshint": "^2.0.1", diff --git a/tests/integration/components/tf-input-group-test.js b/tests/integration/components/tf-input-group-test.js new file mode 100644 index 0000000..b1b0d4c --- /dev/null +++ b/tests/integration/components/tf-input-group-test.js @@ -0,0 +1,46 @@ +import { moduleForComponent, test } from 'ember-qunit'; +import hbs from 'htmlbars-inline-precompile'; + +moduleForComponent('tf-input-group', 'Integration | Component | tf-input-group', { + integration: true +}); + +test('renders tf-input-group', function(assert) { + this.render(hbs` + {{#tf-input-group sizeStyle="large" as |group|}} + {{group.label "First Name"}} + {{group.input}} + {{group.description "You must provide a First Name"}} + {{/tf-input-group}} + `); + assert.equal(this.$('label').text(), 'First Name', "Label prints First Name"); + assert.equal(this.$('label').attr('for'), this.$('input').attr('id'), + "The label 'for' matches the input 'id'"); + assert.ok(this.$('input').hasClass('u-input-large'), "Input is large"); + assert.equal(this.$('p').text(), 'You must provide a First Name', "Description text shows"); + assert.equal(this.$('input').attr('aria-describedby'), this.$('p').attr('id'), + "The input aria-describedby matches the

id"); +}); + +test('validates input group', function(assert) { + this.set('shouldValidate', false); + this.set('isValid', true); + this.render(hbs` + {{#tf-input-group shouldValidate=shouldValidate isValid=isValid as |group|}} + {{group.label "First Name"}} + {{group.input}} + {{group.description "You must provide a First Name"}} + {{/tf-input-group}} + `); + assert.ok(!this.$('input, label, p').hasClass('is-valid') || !this.$('input, label, p').hasClass('is-invalid'), + 'No class for validity when shouldValidate is false'); + this.set('shouldValidate', true); + assert.ok(this.$('input').hasClass('is-valid'), 'Input is valid'); + assert.ok(this.$('label').hasClass('is-valid'), 'Label is valid'); + assert.ok(this.$('p').hasClass('is-valid'), '

is valid'); + this.set('isValid', false); + assert.ok(this.$('input').hasClass('is-invalid'), 'Input is invalid'); + assert.ok(this.$('label').hasClass('is-invalid'), 'Label is invalid'); + assert.ok(this.$('p').hasClass('is-invalid'), '

is invalid'); +}); + diff --git a/tests/integration/components/tf-input-test.js b/tests/integration/components/tf-input-test.js new file mode 100644 index 0000000..74839fb --- /dev/null +++ b/tests/integration/components/tf-input-test.js @@ -0,0 +1,37 @@ +import { moduleForComponent, test } from 'ember-qunit'; +import hbs from 'htmlbars-inline-precompile'; + +moduleForComponent('tf-input', 'Integration | Component | tf-input', { + integration: true +}); + +test('renders tf-input', function(assert) { + this.render(hbs` + {{tf-input}} + `); + assert.ok(this.$('input').length, 'Input is rendered'); +}); + +test('binds sizeStyle attribute to input sizing', function(assert) { + this.render(hbs` + {{tf-input sizeStyle=sizeStyle}} + `); + assert.ok(!this.$('input').hasClass('u-input-large') || !this.$('input').hasClass('u-input-small'), + 'No input sizing specified by default'); + this.set('sizeStyle', 'large'); + assert.ok(this.$('input').hasClass('u-input-large'), 'Input sizeStyle is large'); +}); + +test('validation states', function(assert) { + this.set('shouldValidate', false); + this.set('isValid', true); + this.render(hbs` + {{tf-input shouldValidate=shouldValidate isValid=isValid}} + `); + assert.ok(!this.$('input').hasClass('is-valid') || !this.$('input').hasClass('is-invalid'), + 'No class for validity when shouldValidate is false'); + this.set('shouldValidate', true); + assert.ok(this.$('input').hasClass('is-valid'), 'Input is valid'); + this.set('isValid', false); + assert.ok(this.$('input').hasClass('is-invalid'), 'Input is invalid'); +}); \ No newline at end of file diff --git a/tests/integration/components/tf-textarea-test.js b/tests/integration/components/tf-textarea-test.js new file mode 100644 index 0000000..439e65e --- /dev/null +++ b/tests/integration/components/tf-textarea-test.js @@ -0,0 +1,13 @@ +import { moduleForComponent, test } from 'ember-qunit'; +import hbs from 'htmlbars-inline-precompile'; + +moduleForComponent('tf-textarea', 'Integration | Component | tf-textarea', { + integration: true +}); + +test('renders tf-textarea', function(assert) { + this.render(hbs` + {{tf-textarea}} + `); + assert.ok(this.$('textarea').length, 'Textarea is rendered'); +}); \ No newline at end of file From a0a36f6f8f8f1b91df0fe017fac1d92309f6cd4d Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Mon, 19 Dec 2016 22:55:52 +0000 Subject: [PATCH 2/2] chore(package): update dependencies https://greenkeeper.io/ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5f41b34..c141593 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "ember-data": "^2.10.0", "ember-disable-prototype-extensions": "^1.1.0", "ember-export-application-global": "^1.0.5", - "ember-load-initializers": "^0.5.1", + "ember-load-initializers": "^0.6.3", "ember-resolver": "^2.0.3", "ember-welcome-page": "^1.0.3", "loader.js": "^4.0.10"