diff --git a/packages/devextreme/js/ui/text_box/ui.text_editor.base.js b/packages/devextreme/js/ui/text_box/ui.text_editor.base.js index 08ac00fc563f..d9170fde3ecf 100644 --- a/packages/devextreme/js/ui/text_box/ui.text_editor.base.js +++ b/packages/devextreme/js/ui/text_box/ui.text_editor.base.js @@ -326,18 +326,23 @@ const TextEditorBase = Editor.inherit({ .css('minHeight', this.option('height') ? '0' : ''); }, - _getDefaultAttributes: function() { + _getPlaceholderAttr() { + const { ios, mac } = devices.real(); + const { placeholder } = this.option(); + + // WA to fix vAlign (T898735) + // https://bugs.webkit.org/show_bug.cgi?id=142968 + const value = placeholder || ((ios || mac) ? ' ' : null); + + return value; + }, + + _getDefaultAttributes() { const defaultAttributes = { - autocomplete: 'off' + autocomplete: 'off', + placeholder: this._getPlaceholderAttr(), }; - const { ios, mac } = devices.real(); - if(ios || mac) { - // WA to fix vAlign (T898735) - // https://bugs.webkit.org/show_bug.cgi?id=142968 - defaultAttributes.placeholder = ' '; - } - return defaultAttributes; }, @@ -484,14 +489,17 @@ const TextEditorBase = Editor.inherit({ }, _setFieldAria(force) { + const { 'aria-label': ariaLabel } = this.option('inputAttr'); + const labelId = this._label.getId(); const placeholderId = this._$placeholder?.attr('id'); - const value = [labelId, placeholderId].filter(Boolean).join(' '); + const value = ariaLabel ? undefined : [labelId, placeholderId].filter(Boolean).join(' '); if(value || force) { const aria = { 'labelledby': value || undefined, + label: ariaLabel, }; this.setAria(aria, this._getFieldElement()); } @@ -801,6 +809,7 @@ const TextEditorBase = Editor.inherit({ case 'placeholder': this._renderPlaceholder(); this._setFieldAria(true); + this._input().attr({ placeholder: this._getPlaceholderAttr() }); break; case 'label': this._label.updateText(value); diff --git a/packages/devextreme/scss/widgets/base/textEditor/_index.scss b/packages/devextreme/scss/widgets/base/textEditor/_index.scss index 824e03f0f1a9..ac56739b9881 100644 --- a/packages/devextreme/scss/widgets/base/textEditor/_index.scss +++ b/packages/devextreme/scss/widgets/base/textEditor/_index.scss @@ -83,6 +83,10 @@ &:-moz-ui-invalid { box-shadow: none; } + + &::placeholder { + color: transparent; + } } .dx-show-clear-button { diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/lookup.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/lookup.tests.js index 317d158e6756..8bf8e034d473 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/lookup.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/lookup.tests.js @@ -3394,7 +3394,6 @@ let helper; if(devices.real().deviceType === 'desktop') { QUnit.module('Aria accessibility', { beforeEach: function() { - this.isMac = devices.real().mac; helper = new ariaAccessibilityTestHelper({ createWidget: ($element, options) => new Lookup($element, options) }); @@ -3413,9 +3412,6 @@ if(devices.real().deviceType === 'desktop') { const $field = helper.$widget.find(`.${LOOKUP_FIELD_CLASS}`); const $list = $(`.${LIST_CLASS}`); const $input = helper.widget._popup.$content().find(`.${TEXTEDITOR_INPUT_CLASS}`); - const $placeholder = helper.widget._popup.$content().find(`.${PLACEHOLDER_CLASS}`); - - const placeholderId = $placeholder.attr('id'); const listAttributes = { id: helper.widget._listId, @@ -3458,13 +3454,9 @@ if(devices.real().deviceType === 'desktop') { tabindex: '0', role: 'textbox', 'aria-label': 'Search', - 'aria-labelledby': placeholderId, + placeholder: 'Search', }; - if(this.isMac) { - expectedAttributes.placeholder = ' '; - } - helper.checkAttributes($input, expectedAttributes, 'input'); } @@ -3501,13 +3493,9 @@ if(devices.real().deviceType === 'desktop') { spellcheck: 'false', role: 'textbox', 'aria-label': 'Search', - 'aria-labelledby': placeholderId, + placeholder: 'Search', }; - if(this.isMac) { - expectedAttributes.placeholder = ' '; - } - helper.checkAttributes($input, expectedAttributes, 'input'); } }); diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/selectBox.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/selectBox.tests.js index 9ab04b9e7c93..5254fef33462 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/selectBox.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/selectBox.tests.js @@ -5939,7 +5939,6 @@ let helper; if(devices.real().deviceType === 'desktop') { QUnit.module('Aria accessibility', { beforeEach: function() { - this.isMac = devices.real().mac; helper = new ariaAccessibilityTestHelper({ createWidget: ($element, options) => new SelectBox($element, options) }); @@ -5972,6 +5971,7 @@ if(devices.real().deviceType === 'desktop') { spellcheck: 'false', tabindex: '0', type: 'text', + placeholder: 'Select...', 'aria-autocomplete': 'list', 'aria-expanded': 'true', 'aria-haspopup': 'listbox', @@ -5984,9 +5984,6 @@ if(devices.real().deviceType === 'desktop') { if(!searchEnabled) { inputAttributes.readonly = ''; } - if(this.isMac) { - inputAttributes.placeholder = ' '; - } helper.checkAttributes(helper.widget._input(), inputAttributes, 'input'); helper.checkAttributes(helper.$widget, { 'aria-owns': helper.widget._popupContentId }, 'widget'); helper.checkAttributes(helper.widget._popup.$content(), { id: helper.widget._popupContentId }, 'popupContent'); @@ -6024,6 +6021,7 @@ if(devices.real().deviceType === 'desktop') { spellcheck: 'false', tabindex: '0', type: 'text', + placeholder: 'Select...', 'aria-autocomplete': 'list', 'aria-expanded': 'false', 'aria-haspopup': 'listbox', @@ -6034,10 +6032,6 @@ if(devices.real().deviceType === 'desktop') { inputAttributes.readonly = ''; } - if(this.isMac) { - inputAttributes.placeholder = ' '; - } - helper.checkAttributes(helper.$widget, { }, 'widget'); helper.checkAttributes(helper.widget._input(), inputAttributes, 'input'); diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/textEditorParts/common.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/textEditorParts/common.tests.js index 96088e5d5d7e..4ce46e589675 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/textEditorParts/common.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/textEditorParts/common.tests.js @@ -679,6 +679,17 @@ QUnit.module('aria-labelledby attribute', { assert.strictEqual(inputAttr, `${labelId} ${placeholderId}`); }); + QUnit.test('aria-labelledby should not be set if inputAttr containes aria-label', function(assert) { + this.textEditor.option({ + inputAttr: { 'aria-label': 'custom' }, + label: null, + }); + + const inputAttr = this.$input.attr('aria-labelledby'); + + assert.strictEqual(inputAttr, undefined); + }); + QUnit.test('aria-labelledby should be equal label id if label is specified and placeholder is not specified', function(assert) { this.textEditor.option({ placeholder: null }); @@ -716,6 +727,35 @@ QUnit.module('aria-labelledby attribute', { assert.strictEqual(inputAttr, undefined); }); + + QUnit.test('the placeholder attr should be equal custom placeholder', function(assert) { + this.textEditor.option({ + placeholder: 'custom', + label: null, + }); + + const placeholder = this.$input.attr('placeholder'); + + assert.strictEqual(placeholder, this.textEditor.option('placeholder')); + }); + + QUnit.test('the placeholder attr should be equal empty string if placeholder is null in ios or mac', function(assert) { + this.textEditor.option({ + placeholder: null, + label: null, + }); + + const { ios, mac } = devices.real(); + const placeholder = this.$input.attr('placeholder'); + + if(ios || mac) { + assert.strictEqual(placeholder, ' '); + + return; + } + + assert.strictEqual(placeholder, undefined); + }); }); QUnit.module('text option', moduleConfig, () => {