Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ROU-4664: Prevent dom move when inside popup #870

Merged
merged 5 commits into from
Dec 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion dist/OutSystemsUI.css
Original file line number Diff line number Diff line change
Expand Up @@ -12699,7 +12699,7 @@ html[data-uieditorversion^="1"] .wizard-wrapper-item{
font:normal normal normal 20px/1 FontAwesome;
height:100%;
pointer-events:none;
position:absolute;
position:fixed;
right:16px;
top:0;
-webkit-transition:-webkit-transform 200ms ease-in-out;
Expand Down Expand Up @@ -12940,6 +12940,11 @@ html[data-uieditorversion^="1"] .wizard-wrapper-item{
color:var(--color-neutral-6);
pointer-events:none;
}
.osui-dropdown-serverside--is-inside-popup .osui-dropdown-serverside__balloon-wrapper{
top:calc(var(--osui-dropdown-ss-top) + var(--osui-dropdown-ss-input-height) + 4px);
position:fixed;
overflow:visible;
}
.osui-dropdown-serverside--not-valid .osui-dropdown-serverside__selected-values-wrapper{
border-color:var(--color-error);
}
Expand Down
5 changes: 5 additions & 0 deletions dist/OutSystemsUI.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ declare namespace OSFramework.OSUI.GlobalEnum {
MainContent = "main-content",
MenuLinks = "app-menu-links",
Placeholder = "ph",
Popup = "popup-dialog",
SkipContent = "skip-nav"
}
enum CSSSelectors {
Expand Down Expand Up @@ -319,6 +320,7 @@ declare namespace OSFramework.OSUI.GlobalEnum {
Prefix = "on",
Resize = "resize",
Scroll = "scroll",
ScrollEnd = "scrollend",
TouchEnd = "touchend",
TouchMove = "touchmove",
TouchStart = "touchstart",
Expand Down Expand Up @@ -1035,6 +1037,7 @@ declare namespace OSFramework.OSUI.Helper {
static GetElementById(id: string): HTMLElement;
static GetElementByUniqueId(uniqueId: string): HTMLElement;
static GetFocusableElements(element: HTMLElement): HTMLElement[];
static IsInsidePopupWidget(element: HTMLElement): boolean;
static Move(element: HTMLElement, target: HTMLElement): void;
static SetInputValue(inputElem: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement, value: string): void;
static TagSelector(element: HTMLElement, htmlTag: string): HTMLElement | undefined;
Expand Down Expand Up @@ -1934,6 +1937,7 @@ declare namespace OSFramework.OSUI.Patterns.Dropdown.ServerSide {
private _hasA11yEnabled;
private _intersectionObserver;
private _isBlocked;
private _isInsidePopup;
private _isOpen;
private _platformEventOnToggleCallback;
private _selectValuesWrapper;
Expand Down Expand Up @@ -2033,6 +2037,7 @@ declare namespace OSFramework.OSUI.Patterns.Dropdown.ServerSide.Enum {
BalloonWrapper = "osui-dropdown-serverside__balloon-wrapper",
ErrorMessage = "osui-dropdown-serverside-error-message",
IsDisabled = "osui-dropdown-serverside--is-disabled",
IsInsidePopup = "osui-dropdown-serverside--is-inside-popup",
IsOpened = "osui-dropdown-serverside--is-opened",
IsVisible = "osui-dropdown-serverside-visible",
NotValid = "osui-dropdown-serverside--not-valid",
Expand Down
55 changes: 44 additions & 11 deletions dist/OutSystemsUI.js
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ var OSFramework;
CssClassElements["MainContent"] = "main-content";
CssClassElements["MenuLinks"] = "app-menu-links";
CssClassElements["Placeholder"] = "ph";
CssClassElements["Popup"] = "popup-dialog";
CssClassElements["SkipContent"] = "skip-nav";
})(CssClassElements = GlobalEnum.CssClassElements || (GlobalEnum.CssClassElements = {}));
let CSSSelectors;
Expand Down Expand Up @@ -395,6 +396,7 @@ var OSFramework;
HTMLEvent["Prefix"] = "on";
HTMLEvent["Resize"] = "resize";
HTMLEvent["Scroll"] = "scroll";
HTMLEvent["ScrollEnd"] = "scrollend";
HTMLEvent["TouchEnd"] = "touchend";
HTMLEvent["TouchMove"] = "touchmove";
HTMLEvent["TouchStart"] = "touchstart";
Expand Down Expand Up @@ -2789,6 +2791,12 @@ var OSFramework;
const _filteredElements = Array.from(_focusableElems).filter((element) => element.getAttribute(OSUI.Constants.FocusTrapIgnoreAttr) !== 'true');
return [..._filteredElements];
}
static IsInsidePopupWidget(element) {
const _popup = document.querySelector(OSUI.Constants.Dot + OSUI.GlobalEnum.CssClassElements.Popup);
if (_popup && element) {
return _popup.contains(element);
}
}
static Move(element, target) {
if (element && target) {
target.appendChild(element);
Expand Down Expand Up @@ -2860,11 +2868,26 @@ var OSFramework;
}
}
static _scrollToInvalidInput(element, isSmooth, elementParentClass) {
const browser = OSFramework.OSUI.Helper.DeviceInfo.GetBrowser();
OutSystems.OSUI.Utils.ScrollToElement(element.id, isSmooth, 0, elementParentClass);
if (browser === OSUI.GlobalEnum.Browser.safari || OSFramework.OSUI.Helper.DeviceInfo.IsIos) {
if (isSmooth) {
console.warn('Due to the unsupported scrollend event on Safari/iOS, the smooth transition is disabled and the invalid input will be focused directly.');
}
element.focus();
}
else {
const activeScreenElement = Helper.Dom.ClassSelector(document.body, OSUI.GlobalEnum.CssClassElements.ActiveScreen);
const focusOnScrollEnd = () => {
element.focus();
activeScreenElement.removeEventListener(OSUI.GlobalEnum.HTMLEvent.ScrollEnd, focusOnScrollEnd);
};
activeScreenElement.addEventListener(OSUI.GlobalEnum.HTMLEvent.ScrollEnd, focusOnScrollEnd);
}
}
static _searchElementId(element, isSmooth, elementParentClass) {
const elementToSearch = element.parentElement;
if (elementToSearch.id !== '') {
if (elementToSearch.id !== OSUI.Constants.EmptyString) {
this._scrollToInvalidInput(elementToSearch, isSmooth, elementParentClass);
}
else {
Expand All @@ -2876,10 +2899,12 @@ var OSFramework;
errorCode: OutSystems.OSUI.ErrorCodes.Utilities.FailGetInvalidInput,
callback: () => {
let element = document.body;
if (elementId !== '') {
if (elementId !== OSUI.Constants.EmptyString) {
element = Helper.Dom.GetElementById(elementId);
}
this._checkInvalidInputs(element, isSmooth, elementParentClass);
Helper.AsyncInvocation(() => {
this._checkInvalidInputs(element, isSmooth, elementParentClass);
});
},
});
return result;
Expand Down Expand Up @@ -4462,12 +4487,6 @@ var OSFramework;
var BottomSheet;
(function (BottomSheet_1) {
class BottomSheet extends Patterns.AbstractPattern {
get gestureEventInstance() {
return this._gestureEventInstance;
}
get hasGestureEvents() {
return this._hasGestureEvents;
}
constructor(uniqueId, configs) {
super(uniqueId, new BottomSheet_1.BottomSheetConfig(configs));
this._isOpen = false;
Expand All @@ -4480,6 +4499,12 @@ var OSFramework;
},
};
}
get gestureEventInstance() {
return this._gestureEventInstance;
}
get hasGestureEvents() {
return this._hasGestureEvents;
}
_handleFocusBehavior() {
const opts = {
focusTargetElement: this._parentSelf,
Expand Down Expand Up @@ -5404,7 +5429,8 @@ var OSFramework;
throw new Error(`${OSUI.ErrorCodes.Dropdown.HasNoImplementation.code}: ${OSUI.ErrorCodes.Dropdown.HasNoImplementation.message}`);
}
_moveBallonElement() {
OSUI.Helper.Dom.Move(this._balloonWrapperElement, this._activeScreenElement);
const balloon = document.adoptNode(this._balloonWrapperElement);
this._activeScreenElement.appendChild(balloon);
}
_onBodyClick(_eventType, event) {
if (this._isOpen === false) {
Expand Down Expand Up @@ -5802,7 +5828,13 @@ var OSFramework;
this.setA11YProperties();
this._setUpEvents();
this._setCssClasses();
this._moveBallonElement();
this._isInsidePopup = OSUI.Helper.Dom.IsInsidePopupWidget(this.selfElement);
if (this._isInsidePopup) {
OSUI.Helper.Dom.Styles.AddClass(this.selfElement, ServerSide.Enum.CssClass.IsInsidePopup);
}
else {
this._moveBallonElement();
}
this._setBalloonCoordinates();
}
unsetCallbacks() {
Expand Down Expand Up @@ -6036,6 +6068,7 @@ var OSFramework;
CssClass["BalloonWrapper"] = "osui-dropdown-serverside__balloon-wrapper";
CssClass["ErrorMessage"] = "osui-dropdown-serverside-error-message";
CssClass["IsDisabled"] = "osui-dropdown-serverside--is-disabled";
CssClass["IsInsidePopup"] = "osui-dropdown-serverside--is-inside-popup";
CssClass["IsOpened"] = "osui-dropdown-serverside--is-opened";
CssClass["IsVisible"] = "osui-dropdown-serverside-visible";
CssClass["NotValid"] = "osui-dropdown-serverside--not-valid";
Expand Down
1 change: 1 addition & 0 deletions src/scripts/OSFramework/OSUI/GlobalEnum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ namespace OSFramework.OSUI.GlobalEnum {
MainContent = 'main-content',
MenuLinks = 'app-menu-links',
Placeholder = 'ph',
Popup = 'popup-dialog',
SkipContent = 'skip-nav',
}

Expand Down
23 changes: 23 additions & 0 deletions src/scripts/OSFramework/OSUI/Helper/Dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,29 @@ namespace OSFramework.OSUI.Helper {
return [..._filteredElements] as HTMLElement[];
}

/**
* Method to check if element is inside a Popup widget
*
* @static
* @param {HTMLElement} element
* @return {*} {boolean}
* @memberof Dom
*/
public static IsInsidePopupWidget(element: HTMLElement): boolean {
const _popup = document.querySelectorAll(Constants.Dot + GlobalEnum.CssClassElements.Popup);
let _isInsidePopup = false;

if (_popup.length > 0 && element) {
_popup.forEach((popup) => {
if (popup.contains(element)) {
_isInsidePopup = true;
}
});
}

return _isInsidePopup;
}

/**
* Moves a given HTML element to target position.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ namespace OSFramework.OSUI.Patterns.Dropdown.ServerSide {
private _intersectionObserver: IntersectionObserver;
// Store a Flag property that will control if the dropdown is blocked (like it's under closing animation)
private _isBlocked = false;
// Store if Dropdown is being used inside a popup widget
private _isInsidePopup: boolean;
// Store the Element State, by default is closed!
private _isOpen = false;
// Platform OnClose Callback
Expand Down Expand Up @@ -507,9 +509,8 @@ namespace OSFramework.OSUI.Patterns.Dropdown.ServerSide {
this._selfElementBoundingClientRect.x + this._selfElementBoundingClientRect.width &&
selfElement.y === this._selfElementBoundingClientRect.y)
)

// Store the new selElement coordinates
this._selfElementBoundingClientRect.x = selfElement.x;
// Store the new selElement coordinates
this._selfElementBoundingClientRect.x = selfElement.x;
this._selfElementBoundingClientRect.y = selfElement.y;

// Set Css inline variables
Expand Down Expand Up @@ -649,8 +650,8 @@ namespace OSFramework.OSUI.Patterns.Dropdown.ServerSide {
Event.DOMEvents.Listeners.Type.BodyOnClick,
this._eventOnBodyClick
);
if(Helper.DeviceInfo.IsPhone === false) {

if (Helper.DeviceInfo.IsPhone === false) {
// Add the ScreenScroll callback that will be used to update the balloon coodinates
Event.DOMEvents.Listeners.GlobalListenerManager.Instance.addHandler(
Event.DOMEvents.Listeners.Type.ScreenOnScroll,
Expand Down Expand Up @@ -730,7 +731,7 @@ namespace OSFramework.OSUI.Patterns.Dropdown.ServerSide {
this._eventOnBodyClick
);

if(Helper.DeviceInfo.IsPhone === false) {
if (Helper.DeviceInfo.IsPhone === false) {
Event.DOMEvents.Listeners.GlobalListenerManager.Instance.removeHandler(
Event.DOMEvents.Listeners.Type.ScreenOnScroll,
this._eventOnScreenScroll
Expand Down Expand Up @@ -971,8 +972,20 @@ namespace OSFramework.OSUI.Patterns.Dropdown.ServerSide {
this._setUpEvents();
// Add CSS classes
this._setCssClasses();
// Ensure that the Move only happens after HTML elements has been set!
this._moveBallonElement();

// Check if the dropdown is placed inside a Popup Widget
this._isInsidePopup = Helper.Dom.IsInsidePopupWidget(this.selfElement);

if (this._isInsidePopup) {
/* If it is inside, then do not perform the MoveElement and instead add a class to change some CSS properties.
This is done due to the changes in recat-dom recent version, where listeners are all placed on the reactContainer, making any widget with events to loose
its context when its moved inside a Popup, that is placed outside the reactContainer*/
Helper.Dom.Styles.AddClass(this.selfElement, Enum.CssClass.IsInsidePopup);
} else {
// Ensure that the Move only happens after HTML elements has been set!
this._moveBallonElement();
}

// Set the balloon coordinates
this._setBalloonCoordinates();
}
Expand Down Expand Up @@ -1275,4 +1288,4 @@ namespace OSFramework.OSUI.Patterns.Dropdown.ServerSide {
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ namespace OSFramework.OSUI.Patterns.Dropdown.ServerSide.Enum {
BalloonWrapper = 'osui-dropdown-serverside__balloon-wrapper',
ErrorMessage = 'osui-dropdown-serverside-error-message',
IsDisabled = 'osui-dropdown-serverside--is-disabled',
IsInsidePopup = 'osui-dropdown-serverside--is-inside-popup',
IsOpened = 'osui-dropdown-serverside--is-opened',
IsVisible = 'osui-dropdown-serverside-visible',
NotValid = 'osui-dropdown-serverside--not-valid',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,7 @@ $balloonMobileTopMargin: 5vh;
overflow: hidden;
// --osui-dropdown-ss-thresholdanimateval is the value used as a threshold to animate down the balloon
transform: translateY(calc(-1 * var(--osui-dropdown-ss-thresholdanimateval)));
transition:
opacity 250ms ease,
transform 300ms ease-in-out;
transition: opacity 250ms ease, transform 300ms ease-in-out;

// Service Studio Preview
& {
Expand Down Expand Up @@ -297,6 +295,19 @@ $balloonMobileTopMargin: 5vh;
}
}

// When inside Popup widget
&--is-inside-popup {
.osui-dropdown-serverside__balloon-wrapper {
top: calc(var(--osui-dropdown-ss-top) + var(--osui-dropdown-ss-input-height) + 4px);
position: fixed;
overflow: visible;

&.osui-dropdown-serverside--is-opened {
z-index: calc(var(--osui-popup-layer) + var(--layer-local-tier-1));
}
}
}

// When it's not valid
&--not-valid {
.osui-dropdown-serverside__selected-values-wrapper {
Expand Down Expand Up @@ -344,10 +355,10 @@ $balloonMobileTopMargin: 5vh;
}
}

// Inside Popup
body:has(.popup-dialog):has(.osui-dropdown-serverside--is-opened) {
.osui-dropdown-serverside__balloon-wrapper.osui-dropdown-serverside--is-opened {
z-index: calc(var(--osui-popup-layer) + var(--layer-local-tier-1));
// Inside Form & Popup
.form {
.osui-dropdown-serverside--is-inside-popup input[data-input] {
margin-bottom: 0;
}
}

Expand Down
Loading