diff --git a/.dojorc b/.dojorc index dd52091b78..3be68850fa 100644 --- a/.dojorc +++ b/.dojorc @@ -7,7 +7,6 @@ "src/checkbox", "src/combobox", "src/dialog", - "src/enhanced-text-input", "src/icon", "src/grid", "src/label", diff --git a/README.md b/README.md index 9a66a9c96f..99e9b1237d 100644 --- a/README.md +++ b/README.md @@ -67,8 +67,6 @@ Live examples of current widgets are available in the [widget showcase](https:// [ComboBox](src/combobox/README.md) -[EnhancedTextInput](src/enhanced-text-input/README.md) - [Label](src/label/README.md) [Listbox](src/listbox/README.md) diff --git a/src/common/example/example.css b/src/common/example/example.css index 81ce78fb00..28d0fe03f6 100644 --- a/src/common/example/example.css +++ b/src/common/example/example.css @@ -18,7 +18,6 @@ @import '../../slider/styles/slider.m.css'; @import '../../theme/slider.m.css'; @import '../../split-pane/styles/split-pane.m.css'; -@import '../../theme/enhanced-text-input.m.css'; @import '../../theme/split-pane.m.css'; @import '../../theme/tab-controller.m.css'; @import '../../theme/text-area.m.css'; diff --git a/src/common/example/index.ts b/src/common/example/index.ts index a4686cfa43..b747bae53c 100644 --- a/src/common/example/index.ts +++ b/src/common/example/index.ts @@ -29,7 +29,6 @@ const modules = [ 'tab-controller', 'text-area', 'text-input', - 'enhanced-text-input', 'time-picker', 'title-pane', 'toolbar', diff --git a/src/common/tests/unit/all.ts b/src/common/tests/unit/all.ts index e9afd7c58b..0f2bca7735 100644 --- a/src/common/tests/unit/all.ts +++ b/src/common/tests/unit/all.ts @@ -7,7 +7,6 @@ import '../../../calendar/tests/unit/DatePicker'; import '../../../checkbox/tests/unit/Checkbox'; import '../../../combobox/tests/unit/ComboBox'; import '../../../dialog/tests/unit/Dialog'; -import '../../../enhanced-text-input/tests/unit/EnhancedTextInput'; import '../../../global-event/tests/unit/GlobalEvent'; import '../../../grid/tests/unit/all'; import '../../../icon/tests/unit/Icon'; diff --git a/src/enhanced-text-input/README.md b/src/enhanced-text-input/README.md deleted file mode 100644 index 0e80054cdd..0000000000 --- a/src/enhanced-text-input/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# @dojo/widgets/enhanced-text-input widget - -Dojo's `EnhancedTextInput` widget extends `TextInput` to provide optional addons before or after the input, similar to those in [bootstrap](https://v4-alpha.getbootstrap.com/components/input-group/#basic-example). - - -## Features - -- Allows any number of addons to be inserted before or after the input -- Includes all TextInput functionality - -### Accessibility Features - -The `EnhancedTextInput` does not add any built-in accessibility features above what is available in `TextInput`. If this widget is used, it should be done with attention to what instructional text is needed to convey the information visually implied by the addon or addons. This can be done in the label text, and optionally supplemented by custom descriptive text and `aria-describedby`. - -After careful consideration we decided not to automatically point the input's `aria-describedby` attribute to the `id` or `id`s of the addons, as Bootstrap does in some of their examples. While this may be a helpful technique in a few select cases, many of the common addon text snippets -- e.g. `@` or `.00` -- were either insufficient to describe the user input required, or even potentially more confusing than nothing at all. Input addons rely so heavily on visual context to convey meaning that any programmatic attempt to generate good descriptive text will likely fail too often to be useful. - -## Example Usage - -```typescript -// Basic usage -w(EnhancedTextInput, { - addonBefore: [ '@' ], - describedBy: 'twitter-desc', - label: 'Twitter Username', - placeholder: 'username', - value: this.state.firstName, - onChange: (event: TypedTargetEvent) => { - this.setState({ firstName: event.target.value }); - }, -}), -v('span', { - id: 'twitter-desc' -}, [ 'Not including the "@" symbol' ]); - -// Advanced usage -w(EnhancedTextInput, { - addonBefore: [ '$' ], - addonAfter: [ '.00' ], - label: 'Price, rounded to the nearest dollar', - type: 'number', - value: this.state.price, - onChange: (event: TypedTargetEvent) => { - this.setState({ price: event.target.value }); - } -}); -``` - -## Theming - -The following CSS classes are available on the `EnhancedTextInput` widget for use with custom themes: - -- `addon`: Applied to the span that contains the addon -- `addonBefore`: Applied to addons that come before the input -- `addonAfter`: Applied to addons positioned after the input -- `inputWrapper`: Applied to the immediate parent of the `` -- `input`: Applied to the `` element diff --git a/src/enhanced-text-input/example/index.ts b/src/enhanced-text-input/example/index.ts deleted file mode 100644 index 5060595cb0..0000000000 --- a/src/enhanced-text-input/example/index.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { WidgetBase } from '@dojo/framework/widget-core/WidgetBase'; -import { v, w } from '@dojo/framework/widget-core/d'; -import EnhancedTextInput from '../../enhanced-text-input/index'; - -export default class App extends WidgetBase { - private _value1: string | undefined; - private _value2: string | undefined; - - render() { - return v('div', { - styles: { maxWidth: '256px' } - }, [ - v('h2', {}, ['Text Input Examples']), - v('div', { id: 'example-text' }, [ - v('h3', {}, ['String label']), - w(EnhancedTextInput, { - key: 't1', - addonBefore: [ '@' ], - aria: { - describedBy: 'twitter-desc' - }, - label: 'Twitter Username', - type: 'text', - placeholder: 'username', - value: this._value1, - onChange: (value: string) => { - this._value1 = value; - this.invalidate(); - } - }), - v('span', { - id: 'twitter-desc' - }, [ 'Not including the "@" symbol' ]), - v('br'), - w(EnhancedTextInput, { - key: 't2', - addonBefore: [ '$' ], - addonAfter: [ '.00' ], - label: 'Price, rounded to the nearest dollar', - type: 'number', - value: this._value2, - onChange: (value: string) => { - this._value2 = value; - this.invalidate(); - } - }) - ]) - ]); - } -} diff --git a/src/enhanced-text-input/index.ts b/src/enhanced-text-input/index.ts deleted file mode 100644 index 8ba0cf957c..0000000000 --- a/src/enhanced-text-input/index.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { DNode } from '@dojo/framework/widget-core/interfaces'; -import { theme } from '@dojo/framework/widget-core/mixins/Themed'; -import { v } from '@dojo/framework/widget-core/d'; - -import { TextInputBase, TextInputProperties } from '../text-input/index'; -import * as css from '../theme/enhanced-text-input.m.css'; -import { customElement } from '@dojo/framework/widget-core/decorators/customElement'; - -export interface EnhancedTextInputProperties extends TextInputProperties { - addonAfter?: DNode[]; - addonBefore?: DNode[]; -} - -@theme(css) -@customElement({ - tag: 'dojo-enhanced-text-input', - properties: [ - 'theme', - 'classes', - 'aria', - 'extraClasses', - 'addonAfter', - 'addonBefore', - 'labelAfter', - 'labelHidden', - 'disabled', - 'readOnly', - 'valid', - 'customValidator' - ], - attributes: [ - 'widgetId', - 'label', - 'placeholder', - 'controls', - 'type', - 'minLength', - 'maxLength', - 'value', - 'name', - 'helperText' - ], - events: [ - 'onBlur', - 'onChange', - 'onClick', - 'onFocus', - 'onInput', - 'onKeyDown', - 'onKeyPress', - 'onKeyUp', - 'onMouseDown', - 'onMouseUp', - 'onTouchCancel', - 'onTouchEnd', - 'onTouchStart', - 'onValidate' - ] -}) -export default class EnhancedTextInput extends TextInputBase { - protected renderAddon(addon: DNode, before = false): DNode { - return v('span', { - classes: this.theme([css.addon, before ? css.addonBefore : css.addonAfter]) - }, [ addon ]); - } - - protected renderInputWrapper(): DNode { - let { - addonAfter = [], - addonBefore = [] - } = this.properties; - - return v('div', { classes: this.theme(css.inputWrapper) }, [ - v('div', { classes: this.theme(css.inputWrapperInner) }, [ - ...addonBefore.map((addon: DNode) => this.renderAddon(addon, true)), - this.renderInput(), - ...addonAfter.map((addon: DNode) => this.renderAddon(addon)) - ]), - this.renderHelperText() - ]); - } -} diff --git a/src/enhanced-text-input/tests/unit/EnhancedTextInput.ts b/src/enhanced-text-input/tests/unit/EnhancedTextInput.ts deleted file mode 100644 index e4cc615790..0000000000 --- a/src/enhanced-text-input/tests/unit/EnhancedTextInput.ts +++ /dev/null @@ -1,308 +0,0 @@ -const { registerSuite } = intern.getInterface('object'); -const { assert } = intern.getPlugin('chai'); - -import * as sinon from 'sinon'; - -import { v, w } from '@dojo/framework/widget-core/d'; -import Focus from '@dojo/framework/widget-core/meta/Focus'; - -import EnhancedTextInput from '../../index'; -import Label from '../../../label/index'; -import * as css from '../../../theme/enhanced-text-input.m.css'; -import * as textInputCss from '../../../theme/text-input.m.css'; -import { VNodeProperties } from '@dojo/framework/widget-core/interfaces'; -import { createHarness, compareId, compareForId, MockMetaMixin, noop, stubEvent } from '../../../common/tests/support/test-helpers'; - -const harness = createHarness([ compareId, compareForId ]); - -interface States { - disabled?: boolean; - readOnly?: boolean; - required?: boolean; -} - -interface ExpectedOptions { - inputOverrides?: VNodeProperties; - addonBefore?: boolean; - addonAfter?: boolean; - label?: boolean; - states?: States; - focused?: boolean; - invalid?: boolean; - helperText?: string; -} - -const expected = (options: ExpectedOptions = {}) => { - const { - inputOverrides = {}, - addonBefore = false, - addonAfter = false, - label = false, - states = {}, - focused = false, - invalid, - helperText - } = options; - const { readOnly, disabled, required } = states; - const children = [ - v('input', { - 'aria-invalid': invalid ? 'true' : null, - autocomplete: undefined, - classes: css.input, - disabled, - id: '', - key: 'input', - maxlength: null, - minlength: null, - name: undefined, - placeholder: undefined, - pattern: undefined, - readOnly, - 'aria-readonly': readOnly ? 'true' : null, - required, - type: 'text', - value: undefined, - focus: noop, - onblur: noop, - onchange: noop, - onclick: noop, - onfocus: noop, - oninput: noop, - onkeydown: noop, - onkeypress: noop, - onkeyup: noop, - onmousedown: noop, - onmouseup: noop, - ontouchstart: noop, - ontouchend: noop, - ontouchcancel: noop, - ...inputOverrides - }) - ]; - if (addonBefore) { - children.unshift(v('span', { - classes: [css.addon, css.addonBefore] - }, [ 'foo' ])); - } - if (addonAfter) { - children.push(v('span', { - classes: [css.addon, css.addonAfter] - }, [ 'bar' ])); - } - - return v('div', { - key: 'root', - classes: [ - textInputCss.root, - disabled ? textInputCss.disabled : null, - focused ? textInputCss.focused : null, - invalid ? textInputCss.invalid : null, - invalid === false ? textInputCss.valid : null, - readOnly ? textInputCss.readonly : null, - required ? textInputCss.required : null - ] - }, [ - label ? w(Label, { - theme: undefined, - classes: undefined, - disabled, - focused, - hidden: false, - invalid, - readOnly, - required, - forId: '' - }, [ 'foo' ]) : null, - v('div', { classes: css.inputWrapper }, [ - v('div', { classes: css.inputWrapperInner }, children), - helperText ? v('div', { - key: 'helperTextWrapper', - classes: [ - css.helperTextWrapper, - invalid ? css.invalid : null - ] - }, [ - v('div', { - key: 'helperText', - classes: css.helperText, - title: helperText - }, [helperText]) - ]) : null - ]) - ]); -}; - -registerSuite('EnhancedTextInput', { - - tests: { - 'addon before'() { - const h = harness(() => w(EnhancedTextInput, { addonBefore: [ 'foo' ]})); - h.expect(() => expected({ addonBefore: true })); - }, - - 'addon after'() { - const h = harness(() => w(EnhancedTextInput, { addonAfter: [ 'bar' ]})); - h.expect(() => expected({ addonAfter: true })); - }, - - 'addons before and after'() { - const h = harness(() => w(EnhancedTextInput, { - addonBefore: [ 'foo' ], - addonAfter: [ 'bar' ] - })); - h.expect(() => expected({ addonAfter: true, addonBefore: true })); - }, - - 'preserves TextInput functionality': { - 'default properties'() { - const h = harness(() => w(EnhancedTextInput, {})); - h.expect(expected); - }, - - 'custom properties'() { - const h = harness(() => w(EnhancedTextInput, { - aria: { - controls: 'foo', - describedBy: 'bar' - }, - widgetId: 'textinputId', - maxLength: 50, - minLength: 10, - name: 'baz', - placeholder: 'qux', - type: 'email', - value: 'hello world' - })); - - h.expect(() => expected({ - inputOverrides: { - 'aria-controls': 'foo', - 'aria-describedby': 'bar', - id: 'textinputId', - maxlength: '50', - minlength: '10', - name: 'baz', - placeholder: 'qux', - type: 'email', - value: 'hello world' - } - })); - }, - - 'label'() { - const h = harness(() => w(EnhancedTextInput, { - label: 'foo' - })); - h.expect(() => expected({ label: true })); - }, - - 'state classes'() { - let states: States = { - disabled: true, - readOnly: true, - required: true - }; - const h = harness(() => w(EnhancedTextInput, states)); - h.expect(() => expected({ - states - })); - states = { - disabled: false, - readOnly: false, - required: false - }; - h.expect(() => expected({ states })); - }, - - 'state classes on label'() { - let states: States = { - disabled: true, - readOnly: true, - required: true - }; - const h = harness(() => w(EnhancedTextInput, { label: 'foo', ...states })); - h.expect(() => expected({ label: true, states })); - }, - - 'focused class'() { - const mockMeta = sinon.stub(); - const mockFocusGet = sinon.stub().returns({ - active: false, - containsFocus: true - }); - mockMeta.withArgs(Focus).returns({ - get: mockFocusGet - }); - const h = harness(() => w(MockMetaMixin(EnhancedTextInput, mockMeta), {})); - h.expect(() => expected({ focused: true })); - }, - - 'helperText'() { - const h = harness(() => w(EnhancedTextInput, { - label: 'foo', - helperText: 'test' - })); - h.expect(() => expected({ label: true, helperText: 'test' })); - }, - - events() { - const onBlur = sinon.stub(); - const onChange = sinon.stub(); - const onClick = sinon.stub(); - const onFocus = sinon.stub(); - const onInput = sinon.stub(); - const onKeyDown = sinon.stub(); - const onKeyPress = sinon.stub(); - const onKeyUp = sinon.stub(); - const onMouseDown = sinon.stub(); - const onMouseUp = sinon.stub(); - const onTouchStart = sinon.stub(); - const onTouchEnd = sinon.stub(); - const onTouchCancel = sinon.stub(); - - const h = harness(() => w(EnhancedTextInput, { - onBlur, - onChange, - onClick, - onFocus, - onInput, - onKeyDown, - onKeyPress, - onKeyUp, - onMouseDown, - onMouseUp, - onTouchStart, - onTouchEnd, - onTouchCancel - })); - - h.trigger('@input', 'onblur', stubEvent); - assert.isTrue(onBlur.called, 'onBlur called'); - h.trigger('@input', 'onchange', stubEvent); - assert.isTrue(onChange.called, 'onChange called'); - h.trigger('@input', 'onclick', stubEvent); - assert.isTrue(onClick.called, 'onClick called'); - h.trigger('@input', 'onfocus', stubEvent); - assert.isTrue(onFocus.called, 'onFocus called'); - h.trigger('@input', 'oninput', stubEvent); - assert.isTrue(onInput.called, 'onInput called'); - h.trigger('@input', 'onkeydown', stubEvent); - assert.isTrue(onKeyDown.called, 'onKeyDown called'); - h.trigger('@input', 'onkeypress', stubEvent); - assert.isTrue(onKeyPress.called, 'onKeyPress called'); - h.trigger('@input', 'onkeyup', stubEvent); - assert.isTrue(onKeyUp.called, 'onKeyUp called'); - h.trigger('@input', 'onmousedown', stubEvent); - assert.isTrue(onMouseDown.called, 'onMouseDown called'); - h.trigger('@input', 'onmouseup', stubEvent); - assert.isTrue(onMouseUp.called, 'onMouseUp called'); - h.trigger('@input', 'ontouchstart', stubEvent); - assert.isTrue(onTouchStart.called, 'onTouchStart called'); - h.trigger('@input', 'ontouchend', stubEvent); - assert.isTrue(onTouchEnd.called, 'onTouchEnd called'); - h.trigger('@input', 'ontouchcancel', stubEvent); - assert.isTrue(onTouchCancel.called, 'onTouchCancel called'); - } - } - } -}); diff --git a/src/text-input/README.md b/src/text-input/README.md index f8f392acfa..80d0a0d207 100644 --- a/src/text-input/README.md +++ b/src/text-input/README.md @@ -8,6 +8,7 @@ Dojo's `TextInput` widget provides a basic text input widget with an optional la - Allows specification of input type (e.g. `text`, `email`, `number`, etc) - Correctly handles a11y attributes - Associates a visible or invisible but accessible `