From 3a04da1d0616fd1ed8d89f1d39d37770dd82a61f Mon Sep 17 00:00:00 2001 From: Will Ernest Date: Fri, 2 Feb 2024 08:54:18 -0800 Subject: [PATCH] fix(popover): update positioning logic to render within body (#1109) ## PR Checklist Please check if your PR fulfills the following requirements: - [X] Tests for the changes have been added (for bug fixes / features) - [ ] Docs have been added / updated (for bug fixes / features) - [ ] If applicable, have a visual design approval ## PR Type What kind of change does this PR introduce? - [X] Bugfix - [ ] Feature - [ ] Code style update (formatting, local variables) - [ ] Refactoring (no functional changes, no api changes) - [ ] Build related changes - [ ] CI related changes - [ ] Documentation content changes - [ ] Other... Please describe: ## What is the current behavior? ![datepicker popover position example](https://github.com/vmware-clarity/ng-clarity/assets/34519388/0d078063-c542-472c-a892-d5864eb61756) Issue Number: CDE-1508 ## What is the new behavior? ![datepicker positioning logic after example](https://github.com/vmware-clarity/ng-clarity/assets/34519388/61eecd6f-d708-468f-b6c9-8ab5c40c5db9) ## Does this PR introduce a breaking change? - [ ] Yes - [X] No ## Other information --- .../popover-position.service.spec.ts | 22 +++++++++++++++++++ .../providers/popover-position.service.ts | 13 +++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/projects/angular/src/utils/popover/providers/popover-position.service.spec.ts b/projects/angular/src/utils/popover/providers/popover-position.service.spec.ts index 65c9394606..95677d5bcf 100644 --- a/projects/angular/src/utils/popover/providers/popover-position.service.spec.ts +++ b/projects/angular/src/utils/popover/providers/popover-position.service.spec.ts @@ -394,6 +394,28 @@ export default function (): void { expect(handleVerticalAxisTwoViolationsSpy).not.toHaveBeenCalled(); expect(handleHorizontalAxisTwoViolationsSpy.calls.count()).toEqual(1); }); + + it('content is rendered within the the body (y >= 0) when AXIS is HORIZONTAL and there is a BOTTOM ViewportViolation', function (this: TestContext) { + const bottomLeftViolation: ClrPopoverPosition = { + axis: ClrAxis.VERTICAL, + side: ClrSide.AFTER, + anchor: ClrAlignment.END, + content: ClrAlignment.START, + }; + + // Set the popover content to be as big as the window + popoverContent.style.width = '25px'; + popoverContent.style.height = window.innerHeight + 'px'; + // Set the anchor element to render at the bottom of the body/window + this.eventService.anchorButtonRef.nativeElement.style.bottom = '100%'; + this.eventService.anchorButtonRef.nativeElement.style.position = 'absolute'; + this.positionService.position = bottomLeftViolation; + document.body.appendChild(popoverContent); + const result = this.positionService.alignContent(popoverContent); + + // Verify the yOffset is not a negative value (on the view this would translate to a negative top value). + expect(result.yOffset).toBeGreaterThanOrEqual(0); + }); }); describe('handles content realignment', function (this: TestContext) { diff --git a/projects/angular/src/utils/popover/providers/popover-position.service.ts b/projects/angular/src/utils/popover/providers/popover-position.service.ts index 520bf03041..3947171fa0 100644 --- a/projects/angular/src/utils/popover/providers/popover-position.service.ts +++ b/projects/angular/src/utils/popover/providers/popover-position.service.ts @@ -65,12 +65,12 @@ export class ClrPopoverPositionService { * * Note, more than 3 viewport violations and there isn't anything we can do to help. Also when there are two * violations, we can't help if the violations are TOP+BOTTOM || LEFT+RIGHT => There is no transformation we - * can make to the postion that will help. + * can make to the position that will help. * * Some examples: * There is only one error and Primary axis is VERTICAL * - this.handleVerticalAxisOneViolation has a switch that will use the error sum to apply the correct - * transform to the postion based on the reduction of visibilityViolations. + * transform to the position based on the reduction of visibilityViolations. * * There are two errors and Primary axis is HORIZONTAL * - handleHorizontalAxisTwoViolations has a switch that uses the error sum to apply both transforms needed to @@ -103,6 +103,15 @@ export class ClrPopoverPositionService { this.contentOffsets.yOffset += Math.abs(this.currentContentCoords.top); } + /** + * This detects the condition where the popover is flipped because it would violate the bottom of the viewport, but flipping it results in the + * popover rendering above the top of the body (y coordinate outside the body). In that event, it should be rendered within the body + * as much as possible, so this logic sets the top of popover to render touching the top of the body. + */ + if (this.contentOffsets.yOffset + this.currentAnchorCoords.y < 0) { + this.contentOffsets.yOffset = 0 - this.currentContentCoords.top; + } + return this.contentOffsets; }