Skip to content

Commit

Permalink
Fix DropDownEditor Popup position depending the "rtlEnabled" option v…
Browse files Browse the repository at this point in the history
…alue (T856114) (#11687)

* Fix Popup position of the DropDownEditor depending on "rtlEnabled" option value (T856114)

* refactoring
  • Loading branch information
DokaRus committed Jan 24, 2020
1 parent eae6fc8 commit d9a0fd7
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 54 deletions.
4 changes: 2 additions & 2 deletions js/core/utils/position.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const config = require('../config');
import config from '../config';

const getDefaultAlignment = function(isRtlEnabled) {
const rtlEnabled = isRtlEnabled || config().rtlEnabled;
const rtlEnabled = isRtlEnabled ?? config().rtlEnabled;

return rtlEnabled ? 'right' : 'left';
};
Expand Down
112 changes: 60 additions & 52 deletions js/ui/drop_down_editor/ui.drop_down_editor.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,26 @@
const $ = require('../../core/renderer');
const AsyncTemplateMixin = require('../shared/async_template_mixin');
const eventsEngine = require('../../events/core/events_engine');
const Guid = require('../../core/guid');
const registerComponent = require('../../core/component_registrator');
const commonUtils = require('../../core/utils/common');
const domUtils = require('../../core/utils/dom');
const focused = require('../widget/selectors').focused;
const each = require('../../core/utils/iterator').each;
const isDefined = require('../../core/utils/type').isDefined;
const extend = require('../../core/utils/extend').extend;
const getPublicElement = require('../../core/utils/dom').getPublicElement;
const errors = require('../widget/ui.errors');
const positionUtils = require('../../animation/position');
const getDefaultAlignment = require('../../core/utils/position').getDefaultAlignment;
const DropDownButton = require('./ui.drop_down_button').default;
const Widget = require('../widget/ui.widget');
const messageLocalization = require('../../localization/message');
const eventUtils = require('../../events/utils');
const TextBox = require('../text_box');
const clickEvent = require('../../events/click');
const devices = require('../../core/devices');
const FunctionTemplate = require('../../core/templates/function_template').FunctionTemplate;
const Popup = require('../popup');
import $ from '../../core/renderer';
import AsyncTemplateMixin from '../shared/async_template_mixin';
import eventsEngine from '../../events/core/events_engine';
import Guid from '../../core/guid';
import registerComponent from '../../core/component_registrator';
import { noop, splitPair } from '../../core/utils/common';
import { focused } from '../widget/selectors';
import { each } from '../../core/utils/iterator';
import { isDefined } from '../../core/utils/type';
import { extend } from '../../core/utils/extend';
import { getPublicElement } from '../../core/utils/dom';
import errors from '../widget/ui.errors';
import { setup as setupPosition } from '../../animation/position';
import { getDefaultAlignment } from '../../core/utils/position';
import DropDownButton from './ui.drop_down_button';
import Widget from '../widget/ui.widget';
import { format as formatMessage } from '../../localization/message';
import { addNamespace } from '../../events/utils';
import TextBox from '../text_box';
import clickEvent from '../../events/click';
import devices from '../../core/devices';
import { FunctionTemplate } from '../../core/templates/function_template';
import Popup from '../popup';

const DROP_DOWN_EDITOR_CLASS = 'dx-dropdowneditor';
const DROP_DOWN_EDITOR_INPUT_WRAPPER = 'dx-dropdowneditor-input-wrapper';
Expand Down Expand Up @@ -137,8 +136,8 @@ const DropDownEditor = TextBox.inherit({
dropDownOptions: {},
popupPosition: this._getDefaultPopupPosition(),
onPopupInitialized: null,
applyButtonText: messageLocalization.format('OK'),
cancelButtonText: messageLocalization.format('Cancel'),
applyButtonText: formatMessage('OK'),
cancelButtonText: formatMessage('Cancel'),
buttonsLocation: 'default',
showPopupTitle: false,
useHiddenSubmitElement: false
Expand Down Expand Up @@ -180,8 +179,8 @@ const DropDownEditor = TextBox.inherit({
});
},

_getDefaultPopupPosition: function() {
const position = getDefaultAlignment();
_getDefaultPopupPosition: function(isRtlEnabled) {
const position = getDefaultAlignment(isRtlEnabled);

return {
offset: { h: 0, v: -1 },
Expand Down Expand Up @@ -213,9 +212,17 @@ const DropDownEditor = TextBox.inherit({
this.callBase();
this._initVisibilityActions();
this._initPopupInitializedAction();
this._updatePopupPosition(this.option('rtlEnabled'));
this._options.cache('dropDownOptions', this.option('dropDownOptions'));
},

_updatePopupPosition: function(isRtlEnabled) {
const { my, at } = this._getDefaultPopupPosition(isRtlEnabled);
const currentPosition = this.option('popupPosition');

this.option('popupPosition', extend({}, currentPosition, { my, at }));
},

_initVisibilityActions: function() {
this._openAction = this._createActionByOption('onOpened', {
excludeValidators: ['disabled', 'readOnly']
Expand Down Expand Up @@ -340,7 +347,7 @@ const DropDownEditor = TextBox.inherit({

fieldTemplate.render({
model: data,
container: domUtils.getPublicElement($templateWrapper),
container: getPublicElement($templateWrapper),
onRendered: () => {
const $input = this._input();

Expand Down Expand Up @@ -374,24 +381,23 @@ const DropDownEditor = TextBox.inherit({
},

_renderOpenHandler: function() {
const that = this;
const $inputWrapper = that._inputWrapper();
const eventName = eventUtils.addNamespace(clickEvent.name, that.NAME);
const openOnFieldClick = that.option('openOnFieldClick');
const $inputWrapper = this._inputWrapper();
const eventName = addNamespace(clickEvent.name, this.NAME);
const openOnFieldClick = this.option('openOnFieldClick');

eventsEngine.off($inputWrapper, eventName);
eventsEngine.on($inputWrapper, eventName, that._getInputClickHandler(openOnFieldClick));
that.$element().toggleClass(DROP_DOWN_EDITOR_FIELD_CLICKABLE, openOnFieldClick);
eventsEngine.on($inputWrapper, eventName, this._getInputClickHandler(openOnFieldClick));
this.$element().toggleClass(DROP_DOWN_EDITOR_FIELD_CLICKABLE, openOnFieldClick);

if(openOnFieldClick) {
that._openOnFieldClickAction = that._createAction(that._openHandler.bind(that));
this._openOnFieldClickAction = this._createAction(this._openHandler.bind(this));
}
},

_attachFocusOutHandler: function() {
if(isIOs) {
this._detachFocusOutEvents();
eventsEngine.on(this._inputWrapper(), eventUtils.addNamespace('focusout', this.NAME), function(event) {
eventsEngine.on(this._inputWrapper(), addNamespace('focusout', this.NAME), (event) => {
const newTarget = event.relatedTarget;
const popupWrapper = this.content ? $(this.content()).closest('.' + DROP_DOWN_EDITOR_OVERLAY) : this._$popup;
if(newTarget && this.option('opened')) {
Expand All @@ -400,20 +406,18 @@ const DropDownEditor = TextBox.inherit({
this.close();
}
}
}.bind(this));
});
}
},

_detachFocusOutEvents: function() {
isIOs && eventsEngine.off(this._inputWrapper(), eventUtils.addNamespace('focusout', this.NAME));
isIOs && eventsEngine.off(this._inputWrapper(), addNamespace('focusout', this.NAME));
},

_getInputClickHandler: function(openOnFieldClick) {
const that = this;

return openOnFieldClick ?
function(e) { that._executeOpenAction(e); } :
function(e) { that._focusInput(); };
(e) => { this._executeOpenAction(e); } :
(e) => { this._focusInput(); };
},

_openHandler: function() {
Expand Down Expand Up @@ -503,7 +507,7 @@ const DropDownEditor = TextBox.inherit({
this.setAria('id', this._popupContentId, $popupContent);
},

_contentReadyHandler: commonUtils.noop,
_contentReadyHandler: noop,

_popupConfig: function() {

Expand Down Expand Up @@ -536,16 +540,16 @@ const DropDownEditor = TextBox.inherit({
return;
}

return (function(e) {
return (e) => {
this._popupInitializedAction({ popup: e.component });
}).bind(this);
};
},

_popupPositionedHandler: function(e) {
e.position && this._popup.overlayContent().toggleClass(DROP_DOWN_EDITOR_OVERLAY_FLIPPED, e.position.v.flip);
},

_popupShowingHandler: commonUtils.noop,
_popupShowingHandler: noop,

_popupHidingHandler: function() {
this.option('opened', false);
Expand All @@ -570,8 +574,8 @@ const DropDownEditor = TextBox.inherit({
let positionRequest = 'below';

if(this._popup && this._popup.option('visible')) {
const myTop = positionUtils.setup(this.$element()).top;
const popupTop = positionUtils.setup(this._popup.$content()).top;
const { top: myTop } = setupPosition(this.$element());
const { top: popupTop } = setupPosition(this._popup.$content());

positionRequest = (myTop + this.option('popupPosition').offset.v) > popupTop ? 'below' : 'above';
}
Expand All @@ -595,7 +599,7 @@ const DropDownEditor = TextBox.inherit({
$popupContent.empty();

contentTemplate.render({
container: domUtils.getPublicElement($popupContent),
container: getPublicElement($popupContent),
model: templateData
});
},
Expand Down Expand Up @@ -695,7 +699,7 @@ const DropDownEditor = TextBox.inherit({
const resultConfig = buttonsConfig;

if(buttonsLocation !== 'default') {
const position = commonUtils.splitPair(buttonsLocation);
const position = splitPair(buttonsLocation);

each(resultConfig, function(_, element) {
extend(element, {
Expand All @@ -718,7 +722,7 @@ const DropDownEditor = TextBox.inherit({
this.option('focusStateEnabled') && this.focus();
},

_updatePopupWidth: commonUtils.noop,
_updatePopupWidth: noop,

_popupOptionChanged: function(args) {
const options = Widget.getOptionsFromContainer(args);
Expand Down Expand Up @@ -807,6 +811,10 @@ const DropDownEditor = TextBox.inherit({

this._renderSubmitElement();
break;
case 'rtlEnabled':
this._updatePopupPosition(args.value);
this.callBase(args);
break;
default:
this.callBase(args);
}
Expand Down
31 changes: 31 additions & 0 deletions testing/tests/DevExpress.core/utils.position.tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import config from 'core/config';
import { getDefaultAlignment } from 'core/utils/position.js';

const { module: testModule, test } = QUnit;

testModule('getDefaultAlignment', function() {
test('getDefaultAlignment should return an alignment depending on the global "rtlEnabled" config or passed value', function(assert) {
const originalConfig = config();

try {
config({
rtlEnabled: false
});

assert.strictEqual(getDefaultAlignment(), 'left', '"isRtlEnabled" argument is undefined, global "rtlEnabled" config is false');
assert.strictEqual(getDefaultAlignment(true), 'right', '"isRtlEnabled" argument is true, global "rtlEnabled" config is false');
assert.strictEqual(getDefaultAlignment(false), 'left', '"isRtlEnabled" argument is false, global "rtlEnabled" config is false');

config({
rtlEnabled: true
});

assert.strictEqual(getDefaultAlignment(), 'right', '"isRtlEnabled" argument is undefined, global "rtlEnabled" config is true');
assert.strictEqual(getDefaultAlignment(true), 'right', '"isRtlEnabled" argument is true, global "rtlEnabled" config is true');
assert.strictEqual(getDefaultAlignment(false), 'left', '"isRtlEnabled" argument is false, global "rtlEnabled" config is true');

} finally {
config(originalConfig);
}
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,33 @@ module('aria accessibility', () => {
});
});


module('option change', function() {
const getStartDirection = (isRtlEnabled) => isRtlEnabled ? 'right' : 'left';

[false, true].forEach((rtlEnabled) => {
test(`after updating of the "rtlEnabled" option to "${rtlEnabled}" Popup should update its position`, function(assert) {
const dropDownEditor = $('#dropDownEditorLazy').dxDropDownEditor({ rtlEnabled }).dxDropDownEditor('instance');
const { my: initialMyPosition, at: initialAtPosition } = dropDownEditor.option('popupPosition');
const initialStartDirection = getStartDirection(rtlEnabled);

assert.strictEqual(initialAtPosition, `${initialStartDirection} bottom`, 'correct initial "at" position');
assert.strictEqual(initialMyPosition, `${initialStartDirection} top`, 'correct initial "my" position');

dropDownEditor.option('rtlEnabled', !rtlEnabled);

const { my: newMyPosition, at: newAtPosition } = dropDownEditor.option('popupPosition');
const newStartDirection = getStartDirection(!rtlEnabled);

assert.strictEqual(newAtPosition, `${newStartDirection} bottom`, 'correct new "at" position');
assert.strictEqual(newMyPosition, `${newStartDirection} top`, 'correct new "my" position');

dropDownEditor.option('rtlEnabled', rtlEnabled);

const { my: revertedMyPosition, at: revertedAtPosition } = dropDownEditor.option('popupPosition');

assert.strictEqual(revertedAtPosition, `${initialStartDirection} bottom`, 'correct initial "at" position');
assert.strictEqual(revertedMyPosition, `${initialStartDirection} top`, 'correct initial "my" position');
});
});
});

0 comments on commit d9a0fd7

Please sign in to comment.