{
this.referenceEl = el;
- connectFloatingUI(this, this.referenceEl, this.floatingEl);
+ connectFloatingUI(this);
this.resizeObserver.observe(el);
};
setFloatingEl = (el: HTMLDivElement): void => {
this.floatingEl = el;
- connectFloatingUI(this, this.referenceEl, this.floatingEl);
+ connectFloatingUI(this);
};
private keyDownHandler = (event: KeyboardEvent): void => {
diff --git a/packages/calcite-components/src/components/dropdown/resources.ts b/packages/calcite-components/src/components/dropdown/resources.ts
index 27b82a111a3..63f0a0e1544 100644
--- a/packages/calcite-components/src/components/dropdown/resources.ts
+++ b/packages/calcite-components/src/components/dropdown/resources.ts
@@ -1,3 +1,7 @@
export const SLOTS = {
dropdownTrigger: "trigger",
};
+
+export const CSS = {
+ wrapper: "calcite-dropdown-wrapper",
+};
diff --git a/packages/calcite-components/src/components/input-date-picker/input-date-picker.e2e.ts b/packages/calcite-components/src/components/input-date-picker/input-date-picker.e2e.ts
index fbe6b4b898a..21afe8e6547 100644
--- a/packages/calcite-components/src/components/input-date-picker/input-date-picker.e2e.ts
+++ b/packages/calcite-components/src/components/input-date-picker/input-date-picker.e2e.ts
@@ -158,13 +158,15 @@ describe("calcite-input-date-picker", () => {
const page = await newE2EPage();
await page.setContent("
");
const inputDatePicker = await page.find("calcite-input-date-picker");
- const input = await page.find("calcite-input-date-picker >>> calcite-input-text");
const changeEvent = await page.spyOnEvent("calciteInputDatePickerChange");
- await input.click();
+ const inputWrapper = await page.find(`calcite-input-date-picker >>> .${CSS.inputWrapper}`);
+ await inputWrapper.click();
await page.waitForChanges();
await page.waitForTimeout(animationDurationInMs);
+ expect(await inputDatePicker.getProperty("open")).toBe(true);
+
const calendar = await page.find(`calcite-input-date-picker >>> .${CSS.calendarWrapper}`);
expect(await calendar.isVisible()).toBe(true);
@@ -785,11 +787,11 @@ describe("calcite-input-date-picker", () => {
const calendar = await page.find(`#canReadOnly >>> .${CSS.menu}`);
expect(await page.evaluate(() => document.activeElement.id)).toBe("canReadOnly");
- expect(calendar).not.toHaveClass(CSS.menuActive);
+ expect(await calendar.isVisible()).toBe(false);
await component.click();
await page.waitForChanges();
- expect(calendar).not.toHaveClass(CSS.menuActive);
+ expect(await calendar.isVisible()).toBe(false);
await component.type("atención atención");
await page.waitForChanges();
@@ -1775,10 +1777,12 @@ describe("calcite-input-date-picker", () => {
expect(await calendar.isVisible()).toBe(false);
await inputDatePicker.click();
+ await page.waitForChanges();
expect(await calendar.isVisible()).toBe(true);
expect(await isElementFocused(page, "#input-date")).toBe(true);
await input.click();
+ await page.waitForChanges();
expect(await calendar.isVisible()).toBe(false);
expect(await isElementFocused(page, "#input")).toBe(true);
});
diff --git a/packages/calcite-components/src/components/input-date-picker/input-date-picker.scss b/packages/calcite-components/src/components/input-date-picker/input-date-picker.scss
index fddda1133a5..e50a5fb0545 100644
--- a/packages/calcite-components/src/components/input-date-picker/input-date-picker.scss
+++ b/packages/calcite-components/src/components/input-date-picker/input-date-picker.scss
@@ -115,20 +115,10 @@
.menu-container {
--calcite-floating-ui-z-index: theme("zIndex.dropdown");
@include floating-ui-container();
- @include floating-ui-wrapper();
- @apply invisible;
}
@include floating-ui-elem-anim(".menu-container");
-:host([open]) .menu-container {
- @include floating-ui-wrapper-active();
-}
-
-.menu-container--active {
- @include floating-ui-wrapper-active();
-}
-
.input .calcite-input__wrapper {
@apply mt-0;
}
diff --git a/packages/calcite-components/src/components/input-date-picker/input-date-picker.tsx b/packages/calcite-components/src/components/input-date-picker/input-date-picker.tsx
index fbabd1f6a1c..bd228dc2e76 100644
--- a/packages/calcite-components/src/components/input-date-picker/input-date-picker.tsx
+++ b/packages/calcite-components/src/components/input-date-picker/input-date-picker.tsx
@@ -33,6 +33,7 @@ import {
MenuPlacement,
OverlayPositioning,
reposition,
+ hideFloatingUI,
} from "../../utils/floating-ui";
import {
connectForm,
@@ -511,7 +512,7 @@ export class InputDatePicker
onToggleOpenCloseComponent(this);
}
- connectFloatingUI(this, this.referenceEl, this.floatingEl);
+ connectFloatingUI(this);
}
async componentWillLoad(): Promise
{
@@ -524,14 +525,14 @@ export class InputDatePicker
componentDidLoad(): void {
setComponentLoaded(this);
this.localizeInputValues();
- connectFloatingUI(this, this.referenceEl, this.floatingEl);
+ connectFloatingUI(this);
}
disconnectedCallback(): void {
deactivateFocusTrap(this);
disconnectLabel(this);
disconnectForm(this);
- disconnectFloatingUI(this, this.referenceEl, this.floatingEl);
+ disconnectFloatingUI(this);
disconnectLocalized(this);
disconnectMessages(this);
}
@@ -598,10 +599,7 @@ export class InputDatePicker
aria-label={messages.chooseDate}
aria-live="polite"
aria-modal="true"
- class={{
- [CSS.menu]: true,
- [CSS.menuActive]: this.open,
- }}
+ class={CSS.menu}
id={this.dialogId}
ref={this.setFloatingEl}
role="dialog"
@@ -780,9 +778,9 @@ export class InputDatePicker
private endInput: HTMLCalciteInputTextElement;
- private floatingEl: HTMLDivElement;
+ floatingEl: HTMLDivElement;
- private referenceEl: HTMLDivElement;
+ referenceEl: HTMLDivElement;
private startWrapper: HTMLDivElement;
@@ -805,7 +803,7 @@ export class InputDatePicker
? endWrapper || startWrapper
: startWrapper || endWrapper;
- requestAnimationFrame(() => connectFloatingUI(this, this.referenceEl, this.floatingEl));
+ requestAnimationFrame(() => connectFloatingUI(this));
}
private valueAsDateChangedExternally = false;
@@ -877,6 +875,7 @@ export class InputDatePicker
onClose(): void {
this.calciteInputDatePickerClose.emit();
+ hideFloatingUI(this);
deactivateFocusTrap(this);
this.focusOnOpen = false;
this.datePickerEl.reset();
@@ -986,7 +985,7 @@ export class InputDatePicker
setFloatingEl = (el: HTMLDivElement): void => {
this.floatingEl = el;
- connectFloatingUI(this, this.referenceEl, this.floatingEl);
+ connectFloatingUI(this);
};
setStartWrapper = (el: HTMLDivElement): void => {
diff --git a/packages/calcite-components/src/components/input-date-picker/resources.ts b/packages/calcite-components/src/components/input-date-picker/resources.ts
index d06ed9c58df..81783934745 100644
--- a/packages/calcite-components/src/components/input-date-picker/resources.ts
+++ b/packages/calcite-components/src/components/input-date-picker/resources.ts
@@ -14,7 +14,6 @@ export const CSS = {
inputWrapper: "input-wrapper",
input: "input",
menu: "menu-container",
- menuActive: "menu-container--active",
toggleIcon: "toggle-icon",
verticalChevronContainer: "vertical-chevron-container",
chevronIcon: "chevron-icon",
diff --git a/packages/calcite-components/src/components/input-time-picker/input-time-picker.e2e.ts b/packages/calcite-components/src/components/input-time-picker/input-time-picker.e2e.ts
index 1bbfd39b13f..c82f971d152 100644
--- a/packages/calcite-components/src/components/input-time-picker/input-time-picker.e2e.ts
+++ b/packages/calcite-components/src/components/input-time-picker/input-time-picker.e2e.ts
@@ -15,6 +15,7 @@ import {
import { getFocusedElementProp, skipAnimations, waitForAnimationFrame } from "../../tests/utils";
import { html } from "../../../support/formatting";
import { openClose } from "../../tests/commonTests";
+import { CSS as PopoverCSS } from "../../components/popover/resources";
async function getInputValue(page: E2EPage): Promise {
return page.evaluate(
@@ -868,7 +869,9 @@ describe("calcite-input-time-picker", () => {
next sibling
`,
);
await skipAnimations(page);
- const popover = await page.find("calcite-input-time-picker >>> calcite-popover");
+ const popoverPositionContainer = await page.find(
+ `calcite-input-time-picker >>> calcite-popover >>> .${PopoverCSS.positionContainer}`,
+ );
await page.keyboard.press("Tab");
expect(await getFocusedElementProp(page, "tagName")).toBe("CALCITE-INPUT-TIME-PICKER");
@@ -885,7 +888,7 @@ describe("calcite-input-time-picker", () => {
await page.keyboard.press("ArrowDown");
await page.waitForChanges();
- expect(await popover.isVisible()).toBe(true);
+ expect(await popoverPositionContainer.isVisible()).toBe(true);
expect(await getFocusedElementProp(page, "tagName", { shadow: true })).toBe("CALCITE-TIME-PICKER");
await page.keyboard.down("Shift");
@@ -899,7 +902,7 @@ describe("calcite-input-time-picker", () => {
await page.keyboard.press("Escape");
await page.waitForChanges();
- expect(await popover.isVisible()).toBe(false);
+ expect(await popoverPositionContainer.isVisible()).toBe(false);
expect(await getFocusedElementProp(page, "tagName")).toBe("CALCITE-INPUT-TIME-PICKER");
expect(await getFocusedElementProp(page, "tagName", { shadow: true })).toBe("CALCITE-INPUT-TEXT");
@@ -936,41 +939,41 @@ describe("calcite-input-time-picker", () => {
});
it("toggles the time picker when clicked", async () => {
- let popover = await page.find("calcite-input-time-picker >>> calcite-popover");
+ const popoverPositionContainer = await page.find(
+ `calcite-input-time-picker >>> calcite-popover >>> .${PopoverCSS.positionContainer}`,
+ );
- expect(await popover.isVisible()).toBe(false);
+ expect(await popoverPositionContainer.isVisible()).toBe(false);
await inputTimePicker.click();
await page.waitForChanges();
- popover = await page.find("calcite-input-time-picker >>> calcite-popover");
- expect(await popover.isVisible()).toBe(true);
+ expect(await popoverPositionContainer.isVisible()).toBe(true);
await inputTimePicker.click();
await page.waitForChanges();
- popover = await page.find("calcite-input-time-picker >>> calcite-popover");
- expect(await popover.isVisible()).toBe(false);
+ expect(await popoverPositionContainer.isVisible()).toBe(false);
});
it("toggles the time picker when using arrow down/escape key", async () => {
- let popover = await page.find("calcite-input-time-picker >>> calcite-popover");
+ const popoverPositionContainer = await page.find(
+ `calcite-input-time-picker >>> calcite-popover >>> .${PopoverCSS.positionContainer}`,
+ );
- expect(await popover.isVisible()).toBe(false);
+ expect(await popoverPositionContainer.isVisible()).toBe(false);
await inputTimePicker.callMethod("setFocus");
await page.waitForChanges();
await page.keyboard.press("ArrowDown");
await page.waitForChanges();
- popover = await page.find("calcite-input-time-picker >>> calcite-popover");
- expect(await popover.isVisible()).toBe(true);
+ expect(await popoverPositionContainer.isVisible()).toBe(true);
await page.keyboard.press("Escape");
await page.waitForChanges();
- popover = await page.find("calcite-input-time-picker >>> calcite-popover");
- expect(await popover.isVisible()).toBe(false);
+ expect(await popoverPositionContainer.isVisible()).toBe(false);
});
});
});
diff --git a/packages/calcite-components/src/components/input-time-picker/input-time-picker.tsx b/packages/calcite-components/src/components/input-time-picker/input-time-picker.tsx
index d4372d5178e..7ea41322c98 100644
--- a/packages/calcite-components/src/components/input-time-picker/input-time-picker.tsx
+++ b/packages/calcite-components/src/components/input-time-picker/input-time-picker.tsx
@@ -17,7 +17,7 @@ import localeData from "dayjs/esm/plugin/localeData";
import localizedFormat from "dayjs/esm/plugin/localizedFormat";
import preParsePostFormat from "dayjs/esm/plugin/preParsePostFormat";
import updateLocale from "dayjs/esm/plugin/updateLocale";
-import { FloatingUIComponent, LogicalPlacement, OverlayPositioning } from "../../utils/floating-ui";
+import { LogicalPlacement, OverlayPositioning } from "../../utils/floating-ui";
import {
connectForm,
disconnectForm,
@@ -151,7 +151,6 @@ interface DayjsTimeParts {
})
export class InputTimePicker
implements
- FloatingUIComponent,
FormComponent,
InteractiveComponent,
LabelableComponent,
diff --git a/packages/calcite-components/src/components/popover/popover.e2e.ts b/packages/calcite-components/src/components/popover/popover.e2e.ts
index 2975e571b29..45419aed913 100644
--- a/packages/calcite-components/src/components/popover/popover.e2e.ts
+++ b/packages/calcite-components/src/components/popover/popover.e2e.ts
@@ -16,8 +16,11 @@ import { FloatingCSS } from "../../utils/floating-ui";
import { CSS } from "./resources";
describe("calcite-popover", () => {
- describe("renders", () => {
- renders("calcite-popover", { visible: false, display: "block" });
+ describe("renders when closed", () => {
+ renders("calcite-popover", { display: "block" });
+ });
+
+ describe("renders when open", () => {
renders(`😄
`, {
display: "block",
});
@@ -36,11 +39,11 @@ describe("calcite-popover", () => {
await page.waitForChanges();
- const popover = await page.find(`calcite-popover`);
+ const positionContainer = await page.find(`calcite-popover >>> .${CSS.positionContainer}`);
await page.waitForChanges();
- const style = await popover.getComputedStyle();
+ const style = await positionContainer.getComputedStyle();
expect(style.zIndex).toBe("900");
});
@@ -117,9 +120,9 @@ describe("calcite-popover", () => {
html`
referenceElement
`,
);
- const element = await page.find("calcite-popover");
+ const positionContainer = await page.find(`calcite-popover >>> .${CSS.positionContainer}`);
- let computedStyle: CSSStyleDeclaration = await element.getComputedStyle();
+ let computedStyle: CSSStyleDeclaration = await positionContainer.getComputedStyle();
expect(computedStyle.transform).toBe("none");
@@ -129,7 +132,7 @@ describe("calcite-popover", () => {
});
await page.waitForChanges();
- computedStyle = await element.getComputedStyle();
+ computedStyle = await positionContainer.getComputedStyle();
expect(computedStyle.transform).not.toBe("none");
});
@@ -149,15 +152,15 @@ describe("calcite-popover", () => {
await page.waitForChanges();
- const popover = await page.find(`calcite-popover`);
+ const positionContainer = await page.find(`calcite-popover >>> .${CSS.positionContainer}`);
- expect(await popover.isVisible()).toBe(false);
+ expect(await positionContainer.isVisible()).toBe(false);
element.setProperty("open", true);
await page.waitForChanges();
- expect(await popover.isVisible()).toBe(true);
+ expect(await positionContainer.isVisible()).toBe(true);
});
it("should accept referenceElement as string id", async () => {
@@ -245,15 +248,15 @@ describe("calcite-popover", () => {
await page.waitForChanges();
- const popover = await page.find(`calcite-popover`);
+ const positionContainer = await page.find(`calcite-popover >>> .${CSS.positionContainer}`);
- expect(await popover.isVisible()).toBe(false);
+ expect(await positionContainer.isVisible()).toBe(false);
const ref = await page.find("#ref");
await ref.click();
- expect(await popover.isVisible()).toBe(true);
+ expect(await positionContainer.isVisible()).toBe(true);
});
it("should honor Enter key interaction", async () => {
@@ -264,24 +267,26 @@ describe("calcite-popover", () => {
referenceElement
`,
);
+ await skipAnimations(page);
+
await page.waitForChanges();
- const popover = await page.find(`calcite-popover`);
+ const positionContainer = await page.find(`calcite-popover >>> .${CSS.positionContainer}`);
const ref = await page.find("#ref");
- expect(await popover.isVisible()).toBe(false);
+ expect(await positionContainer.isVisible()).toBe(false);
await ref.focus();
await page.keyboard.press("Enter");
await page.waitForChanges();
- expect(await popover.isVisible()).toBe(true);
+ expect(await positionContainer.isVisible()).toBe(true);
await ref.focus();
await page.keyboard.press("Enter");
await page.waitForChanges();
- expect(await popover.isVisible()).toBe(false);
+ expect(await positionContainer.isVisible()).toBe(false);
});
it("should honor Space key interaction", async () => {
@@ -292,24 +297,26 @@ describe("calcite-popover", () => {
referenceElement
`,
);
+ await skipAnimations(page);
+
await page.waitForChanges();
- const popover = await page.find(`calcite-popover`);
+ const positionContainer = await page.find(`calcite-popover >>> .${CSS.positionContainer}`);
const ref = await page.find("#ref");
- expect(await popover.isVisible()).toBe(false);
+ expect(await positionContainer.isVisible()).toBe(false);
await ref.focus();
await page.keyboard.press(" ");
await page.waitForChanges();
- expect(await popover.isVisible()).toBe(true);
+ expect(await positionContainer.isVisible()).toBe(true);
await ref.focus();
await page.keyboard.press(" ");
await page.waitForChanges();
- expect(await popover.isVisible()).toBe(false);
+ expect(await positionContainer.isVisible()).toBe(false);
});
it("should open popovers", async () => {
@@ -375,10 +382,10 @@ describe("calcite-popover", () => {
expect(await scrollEl.getProperty("scrollTop")).toBe(0);
- const popover = await page.find("calcite-popover");
+ const positionContainer = await page.find(`calcite-popover >>> .${CSS.positionContainer}`);
- expect(await popover.isVisible()).toBe(true);
- expect((await popover.getComputedStyle()).pointerEvents).toBe("auto");
+ expect(await positionContainer.isVisible()).toBe(true);
+ expect((await positionContainer.getComputedStyle()).pointerEvents).toBe("auto");
await page.$eval("#scrollEl", async (scrollEl: HTMLDivElement) => {
scrollEl.scrollTo({ top: 300 });
@@ -386,8 +393,8 @@ describe("calcite-popover", () => {
await page.waitForChanges();
- expect(await popover.isVisible()).toBe(false);
- expect((await popover.getComputedStyle()).pointerEvents).toBe("none");
+ expect(await positionContainer.isVisible()).toBe(false);
+ expect((await positionContainer.getComputedStyle()).pointerEvents).toBe("none");
});
it("do not autoClose popovers when clicked outside", async () => {
@@ -502,10 +509,10 @@ describe("calcite-popover", () => {
expect(await scrollEl.getProperty("scrollTop")).toBe(0);
- const popover = await page.find("calcite-popover");
+ const positionContainer = await page.find(`calcite-popover >>> .${CSS.positionContainer}`);
- expect(await popover.isVisible()).toBe(true);
- expect((await popover.getComputedStyle()).pointerEvents).toBe("auto");
+ expect(await positionContainer.isVisible()).toBe(true);
+ expect((await positionContainer.getComputedStyle()).pointerEvents).toBe("auto");
await page.$eval("#scrollEl", async (scrollEl: HTMLDivElement) => {
scrollEl.scrollTo({ top: 300 });
@@ -513,8 +520,8 @@ describe("calcite-popover", () => {
await page.waitForChanges();
- expect(await popover.isVisible()).toBe(false);
- expect((await popover.getComputedStyle()).pointerEvents).toBe("none");
+ expect(await positionContainer.isVisible()).toBe(false);
+ expect((await positionContainer.getComputedStyle()).pointerEvents).toBe("none");
});
it("should not toggle popovers with triggerDisabled", async () => {
@@ -568,6 +575,7 @@ describe("calcite-popover", () => {
floatingUIOwner(
`contentreferenceElement
`,
"open",
+ { shadowSelector: `.${CSS.positionContainer}` },
);
});
@@ -625,12 +633,13 @@ describe("calcite-popover", () => {
);
await skipAnimations(page);
- const popover = await page.find(`calcite-popover`);
+ const positionContainer = await page.find(`calcite-popover >>> .${CSS.positionContainer}`);
const ref = await page.find("#ref");
- expect(await popover.isVisible()).toBe(true);
+ expect(await positionContainer.isVisible()).toBe(true);
await ref.click();
- expect(await popover.isVisible()).toBe(false);
+ await page.waitForChanges();
+ expect(await positionContainer.isVisible()).toBe(false);
await page.$eval("calcite-popover", (popoverEl: HTMLCalcitePopoverElement) => {
const transferEl = document.getElementById("transfer");
@@ -640,7 +649,7 @@ describe("calcite-popover", () => {
await page.waitForChanges();
await ref.click();
- expect(await popover.isVisible()).toBe(true);
+ expect(await positionContainer.isVisible()).toBe(true);
});
it("should close popovers with ESC key", async () => {
diff --git a/packages/calcite-components/src/components/popover/popover.scss b/packages/calcite-components/src/components/popover/popover.scss
index 518dc9aee1e..4e10aeb1970 100644
--- a/packages/calcite-components/src/components/popover/popover.scss
+++ b/packages/calcite-components/src/components/popover/popover.scss
@@ -11,10 +11,16 @@
*/
:host {
+ @apply relative block;
--calcite-floating-ui-z-index: var(--calcite-popover-z-index, theme("zIndex.popover"));
}
-@include floating-ui-host();
+.position-container {
+ @include floating-ui-container();
+}
+
+@include floating-ui-elem-anim(".position-container");
+
@include floating-ui-arrow();
:host([scale="s"]) {
@@ -49,7 +55,7 @@
pointer-events: initial;
}
-.calcite-floating-ui-anim {
+.position-container .calcite-floating-ui-anim {
@apply border
border-solid;
diff --git a/packages/calcite-components/src/components/popover/popover.stories.ts b/packages/calcite-components/src/components/popover/popover.stories.ts
index bb69f56f2a8..cecf74cb1b8 100644
--- a/packages/calcite-components/src/components/popover/popover.stories.ts
+++ b/packages/calcite-components/src/components/popover/popover.stories.ts
@@ -208,3 +208,9 @@ export const transparentBG_TestOnly = (): string => html`
`;
+
+export const closedShouldNotCauseScrollbars = (): string =>
+ html`
+ Popover
+
+
Button`;
diff --git a/packages/calcite-components/src/components/popover/popover.tsx b/packages/calcite-components/src/components/popover/popover.tsx
index 6a9ccf4fde4..20ef517a00b 100644
--- a/packages/calcite-components/src/components/popover/popover.tsx
+++ b/packages/calcite-components/src/components/popover/popover.tsx
@@ -21,6 +21,7 @@ import {
FloatingCSS,
FloatingLayout,
FloatingUIComponent,
+ hideFloatingUI,
LogicalPlacement,
OverlayPositioning,
ReferenceElement,
@@ -264,7 +265,7 @@ export class Popover
updateMessages(this, this.effectiveLocale);
}
- @State() effectiveReferenceElement: ReferenceElement;
+ @State() referenceEl: ReferenceElement;
@State() defaultMessages: PopoverMessages;
@@ -278,6 +279,8 @@ export class Popover
transitionEl: HTMLDivElement;
+ floatingEl: HTMLDivElement;
+
hasLoaded = false;
focusTrap: FocusTrap;
@@ -314,7 +317,7 @@ export class Popover
componentDidLoad(): void {
setComponentLoaded(this);
- if (this.referenceElement && !this.effectiveReferenceElement) {
+ if (this.referenceElement && !this.referenceEl) {
this.setUpReferenceElement();
}
@@ -329,7 +332,7 @@ export class Popover
this.removeReferences();
disconnectLocalized(this);
disconnectMessages(this);
- disconnectFloatingUI(this, this.effectiveReferenceElement, this.el);
+ disconnectFloatingUI(this);
deactivateFocusTrap(this);
}
@@ -365,8 +368,7 @@ export class Popover
@Method()
async reposition(delayed = false): Promise
{
const {
- el,
- effectiveReferenceElement,
+ referenceEl,
placement,
overlayPositioning,
flipDisabled,
@@ -374,12 +376,13 @@ export class Popover
offsetDistance,
offsetSkidding,
arrowEl,
+ floatingEl,
} = this;
return reposition(
this,
{
- floatingEl: el,
- referenceEl: effectiveReferenceElement,
+ floatingEl,
+ referenceEl: referenceEl,
overlayPositioning,
placement,
flipDisabled,
@@ -417,6 +420,11 @@ export class Popover
//
// --------------------------------------------------------------------------
+ private setFloatingEl = (el: HTMLDivElement): void => {
+ this.floatingEl = el;
+ requestAnimationFrame(() => this.setUpReferenceElement());
+ };
+
private setTransitionEl = (el: HTMLDivElement): void => {
this.transitionEl = el;
};
@@ -431,11 +439,11 @@ export class Popover
setUpReferenceElement = (warn = true): void => {
this.removeReferences();
- this.effectiveReferenceElement = this.getReferenceElement();
- connectFloatingUI(this, this.effectiveReferenceElement, this.el);
+ this.referenceEl = this.getReferenceElement();
+ connectFloatingUI(this);
- const { el, referenceElement, effectiveReferenceElement } = this;
- if (warn && referenceElement && !effectiveReferenceElement) {
+ const { el, referenceElement, referenceEl } = this;
+ if (warn && referenceElement && !referenceEl) {
console.warn(`${el.tagName}: reference-element id "${referenceElement}" was not found.`, {
el,
});
@@ -449,47 +457,47 @@ export class Popover
};
setExpandedAttr = (): void => {
- const { effectiveReferenceElement, open } = this;
+ const { referenceEl, open } = this;
- if (!effectiveReferenceElement) {
+ if (!referenceEl) {
return;
}
- if ("setAttribute" in effectiveReferenceElement) {
- effectiveReferenceElement.setAttribute(ARIA_EXPANDED, toAriaBoolean(open));
+ if ("setAttribute" in referenceEl) {
+ referenceEl.setAttribute(ARIA_EXPANDED, toAriaBoolean(open));
}
};
addReferences = (): void => {
- const { effectiveReferenceElement } = this;
+ const { referenceEl } = this;
- if (!effectiveReferenceElement) {
+ if (!referenceEl) {
return;
}
const id = this.getId();
- if ("setAttribute" in effectiveReferenceElement) {
- effectiveReferenceElement.setAttribute(ARIA_CONTROLS, id);
+ if ("setAttribute" in referenceEl) {
+ referenceEl.setAttribute(ARIA_CONTROLS, id);
}
- manager.registerElement(effectiveReferenceElement, this.el);
+ manager.registerElement(referenceEl, this.el);
this.setExpandedAttr();
};
removeReferences = (): void => {
- const { effectiveReferenceElement } = this;
+ const { referenceEl } = this;
- if (!effectiveReferenceElement) {
+ if (!referenceEl) {
return;
}
- if ("removeAttribute" in effectiveReferenceElement) {
- effectiveReferenceElement.removeAttribute(ARIA_CONTROLS);
- effectiveReferenceElement.removeAttribute(ARIA_EXPANDED);
+ if ("removeAttribute" in referenceEl) {
+ referenceEl.removeAttribute(ARIA_CONTROLS);
+ referenceEl.removeAttribute(ARIA_EXPANDED);
}
- manager.unregisterElement(effectiveReferenceElement);
+ manager.unregisterElement(referenceEl);
};
getReferenceElement(): ReferenceElement {
@@ -521,6 +529,7 @@ export class Popover
onClose(): void {
this.calcitePopoverClose.emit();
+ hideFloatingUI(this);
deactivateFocusTrap(this);
}
@@ -532,8 +541,7 @@ export class Popover
private clickOutsideDeactivates = (event: MouseEvent): boolean => {
const path = event.composedPath();
const isReferenceElementInPath =
- this.effectiveReferenceElement instanceof EventTarget &&
- path.includes(this.effectiveReferenceElement);
+ this.referenceEl instanceof EventTarget && path.includes(this.referenceEl);
const outsideClick = !path.includes(this.el);
const shouldCloseOnOutsideClick = this.autoClose && outsideClick;
@@ -586,9 +594,8 @@ export class Popover
}
render(): VNode {
- const { effectiveReferenceElement, heading, label, open, pointerDisabled, floatingLayout } =
- this;
- const displayed = effectiveReferenceElement && open;
+ const { referenceEl, heading, label, open, pointerDisabled, floatingLayout } = this;
+ const displayed = referenceEl && open;
const hidden = !displayed;
const arrowNode = !pointerDisabled ? (
@@ -599,30 +606,31 @@ export class Popover
aria-hidden={toAriaBoolean(hidden)}
aria-label={label}
aria-live="polite"
- calcite-hydrated-hidden={hidden}
id={this.getId()}
role="dialog"
>
-
- {arrowNode}
+
- {this.renderHeader()}
-
-
+ {arrowNode}
+
+ {this.renderHeader()}
+
+
+
+ {!heading ? this.renderCloseButton() : null}
- {!heading ? this.renderCloseButton() : null}
diff --git a/packages/calcite-components/src/components/popover/resources.ts b/packages/calcite-components/src/components/popover/resources.ts
index 1ca6900a22b..a8f76839846 100644
--- a/packages/calcite-components/src/components/popover/resources.ts
+++ b/packages/calcite-components/src/components/popover/resources.ts
@@ -1,4 +1,5 @@
export const CSS = {
+ positionContainer: "position-container",
container: "container",
imageContainer: "image-container",
closeButtonContainer: "close-button-container",
diff --git a/packages/calcite-components/src/components/shell/shell.stories.ts b/packages/calcite-components/src/components/shell/shell.stories.ts
index 2939c7f7b43..e323ebc7ab6 100644
--- a/packages/calcite-components/src/components/shell/shell.stories.ts
+++ b/packages/calcite-components/src/components/shell/shell.stories.ts
@@ -55,6 +55,7 @@ export default {
chromatic: {
delay: 1000,
},
+ layout: "fullscreen",
},
};
@@ -1849,8 +1850,8 @@ export const panelWithPopoverZIndex = (): string =>
-
- `;
+
`;
export const popoverZIndex = (): string =>
html`
@@ -1866,7 +1867,9 @@ export const popoverZIndex = (): string =>
- This is a popover
+ This is a popover
diff --git a/packages/calcite-components/src/components/split-button/split-button.e2e.ts b/packages/calcite-components/src/components/split-button/split-button.e2e.ts
index 9954b2eaf10..3b1593a0528 100644
--- a/packages/calcite-components/src/components/split-button/split-button.e2e.ts
+++ b/packages/calcite-components/src/components/split-button/split-button.e2e.ts
@@ -1,6 +1,7 @@
import { newE2EPage } from "@stencil/core/testing";
import { html } from "../../../support/formatting";
import { accessible, defaults, disabled, focusable, hidden, reflects, renders } from "../../tests/commonTests";
+import { CSS as DropdownCSS } from "../../components/dropdown/resources";
describe("calcite-split-button", () => {
describe("defaults", () => {
@@ -239,12 +240,12 @@ describe("calcite-split-button", () => {
Option 4
`);
- const group = await page.find("calcite-dropdown-group");
+ const positionContainer = await page.find(`calcite-split-button >>> calcite-dropdown >>> .${DropdownCSS.wrapper}`);
const secondary = await page.find(`calcite-split-button >>> calcite-button[split-child="secondary"]`);
const dropdownOpenEvent = page.waitForEvent("calciteDropdownOpen");
await secondary.click();
await dropdownOpenEvent;
- expect(await group.isVisible()).toBe(true);
+ expect(await positionContainer.isVisible()).toBe(true);
expect(await page.evaluate(() => document.activeElement.id)).toEqual("item-1");
await page.keyboard.press("ArrowDown");
await page.waitForChanges();
@@ -255,6 +256,6 @@ describe("calcite-split-button", () => {
const dropdownCloseEvent = page.waitForEvent("calciteDropdownClose");
await page.keyboard.press("Enter");
await dropdownCloseEvent;
- expect(await group.isVisible()).toBe(false);
+ expect(await positionContainer.isVisible()).toBe(false);
});
});
diff --git a/packages/calcite-components/src/components/tooltip/resources.ts b/packages/calcite-components/src/components/tooltip/resources.ts
index c8029f59b18..b7d0071e0e8 100644
--- a/packages/calcite-components/src/components/tooltip/resources.ts
+++ b/packages/calcite-components/src/components/tooltip/resources.ts
@@ -1,4 +1,5 @@
export const CSS = {
+ positionContainer: "position-container",
container: "container",
};
diff --git a/packages/calcite-components/src/components/tooltip/tooltip.e2e.ts b/packages/calcite-components/src/components/tooltip/tooltip.e2e.ts
index 4244dd691e4..05318c85401 100644
--- a/packages/calcite-components/src/components/tooltip/tooltip.e2e.ts
+++ b/packages/calcite-components/src/components/tooltip/tooltip.e2e.ts
@@ -1,8 +1,8 @@
import { E2EPage, newE2EPage } from "@stencil/core/testing";
-import { TOOLTIP_OPEN_DELAY_MS, TOOLTIP_CLOSE_DELAY_MS } from "../tooltip/resources";
+import { TOOLTIP_OPEN_DELAY_MS, TOOLTIP_CLOSE_DELAY_MS, CSS } from "../tooltip/resources";
import { accessible, defaults, floatingUIOwner, hidden, openClose, renders } from "../../tests/commonTests";
import { html } from "../../../support/formatting";
-import { getElementXY, GlobalTestProps } from "../../tests/utils";
+import { getElementXY, GlobalTestProps, skipAnimations } from "../../tests/utils";
interface PointerMoveOptions {
delay: number;
@@ -87,7 +87,7 @@ describe("calcite-tooltip", () => {
}
describe("renders", () => {
- renders(`calcite-tooltip`, { visible: false, display: "block" });
+ renders(`calcite-tooltip`, { display: "block" });
renders(`😄
`, {
display: "block",
});
@@ -95,13 +95,13 @@ describe("calcite-tooltip", () => {
describe("accessible when closed", () => {
accessible(
- `Hello World!Tooltip Reference
`,
+ `Hello World!Tooltip Reference
`,
);
});
describe("accessible when open", () => {
accessible(
- `Hello World!Tooltip Reference
`,
+ `Hello World!Tooltip Reference
`,
);
});
@@ -182,11 +182,11 @@ describe("calcite-tooltip", () => {
await page.waitForChanges();
- const tooltip = await page.find(`calcite-tooltip`);
+ const positionContainer = await page.find(`calcite-tooltip >>> .${CSS.positionContainer}`);
await page.waitForChanges();
- const style = await tooltip.getComputedStyle();
+ const style = await positionContainer.getComputedStyle();
expect(style.zIndex).toBe("901");
});
@@ -197,9 +197,9 @@ describe("calcite-tooltip", () => {
html`
referenceElement
`,
);
- const element = await page.find("calcite-tooltip");
+ const positionContainer = await page.find(`calcite-tooltip >>> .${CSS.positionContainer}`);
- let computedStyle: CSSStyleDeclaration = await element.getComputedStyle();
+ let computedStyle: CSSStyleDeclaration = await positionContainer.getComputedStyle();
expect(computedStyle.transform).toBe("none");
@@ -209,7 +209,7 @@ describe("calcite-tooltip", () => {
});
await page.waitForChanges();
- computedStyle = await element.getComputedStyle();
+ computedStyle = await positionContainer.getComputedStyle();
expect(computedStyle.transform).not.toBe("none");
});
@@ -229,15 +229,15 @@ describe("calcite-tooltip", () => {
await page.waitForChanges();
- const tooltip = await page.find(`calcite-tooltip`);
+ const positionContainer = await page.find(`calcite-tooltip >>> .${CSS.positionContainer}`);
- expect(await tooltip.isVisible()).toBe(false);
+ expect(await positionContainer.isVisible()).toBe(false);
element.setProperty("open", true);
await page.waitForChanges();
- expect(await tooltip.isVisible()).toBe(true);
+ expect(await positionContainer.isVisible()).toBe(true);
});
it("should accept referenceElement as string id", async () => {
@@ -249,11 +249,11 @@ describe("calcite-tooltip", () => {
await page.waitForChanges();
- const tooltip = await page.find(`calcite-tooltip`);
+ const positionContainer = await page.find(`calcite-tooltip >>> .${CSS.positionContainer}`);
await page.waitForChanges();
- expect(await tooltip.isVisible()).toBe(true);
+ expect(await positionContainer.isVisible()).toBe(true);
const element = await page.find("calcite-tooltip");
@@ -285,11 +285,11 @@ describe("calcite-tooltip", () => {
await page.waitForChanges();
- const tooltip = await page.find(`calcite-tooltip`);
+ const positionContainer = await page.find(`calcite-tooltip >>> .${CSS.positionContainer}`);
- expect(await tooltip.isVisible()).toBe(true);
+ expect(await positionContainer.isVisible()).toBe(true);
- const computedStyle = await tooltip.getComputedStyle();
+ const computedStyle = await positionContainer.getComputedStyle();
expect(computedStyle.transform).not.toBe("matrix(0, 0, 0, 0, 0, 0)");
});
@@ -303,9 +303,9 @@ describe("calcite-tooltip", () => {
await page.waitForChanges();
- const tooltip = await page.find(`calcite-tooltip`);
+ const positionContainer = await page.find(`calcite-tooltip >>> .${CSS.positionContainer}`);
- expect(await tooltip.isVisible()).toBe(false);
+ expect(await positionContainer.isVisible()).toBe(false);
const ref = await page.find("#ref");
@@ -313,7 +313,7 @@ describe("calcite-tooltip", () => {
await page.waitForTimeout(TOOLTIP_OPEN_DELAY_MS);
- expect(await tooltip.isVisible()).toBe(true);
+ expect(await positionContainer.isVisible()).toBe(true);
});
it("should honor hover interaction with span inside", async () => {
@@ -325,9 +325,9 @@ describe("calcite-tooltip", () => {
await page.waitForChanges();
- const tooltip = await page.find(`calcite-tooltip`);
+ const positionContainer = await page.find(`calcite-tooltip >>> .${CSS.positionContainer}`);
- expect(await tooltip.isVisible()).toBe(false);
+ expect(await positionContainer.isVisible()).toBe(false);
const ref = await page.find("#ref span");
@@ -335,7 +335,7 @@ describe("calcite-tooltip", () => {
await page.waitForTimeout(TOOLTIP_OPEN_DELAY_MS);
- expect(await tooltip.isVisible()).toBe(true);
+ expect(await positionContainer.isVisible()).toBe(true);
});
it("should honor text", async () => {
@@ -583,6 +583,7 @@ describe("calcite-tooltip", () => {
floatingUIOwner(
`contentreferenceElement
`,
"open",
+ { shadowSelector: `.${CSS.positionContainer}` },
);
});
@@ -710,18 +711,18 @@ describe("calcite-tooltip", () => {
await page.waitForChanges();
- const tooltip = await page.find(`calcite-tooltip`);
+ const positionContainer = await page.find(`calcite-tooltip >>> .${CSS.positionContainer}`);
const ref = await page.find("#ref");
- expect(await tooltip.isVisible()).toBe(false);
+ expect(await positionContainer.isVisible()).toBe(false);
await ref.focus();
await page.waitForChanges();
- expect(await tooltip.isVisible()).toBe(true);
+ expect(await positionContainer.isVisible()).toBe(true);
const testElement = await page.find("#test");
await testElement.focus();
await page.waitForChanges();
- expect(await tooltip.isVisible()).toBe(false);
+ expect(await positionContainer.isVisible()).toBe(false);
await page.$eval("calcite-tooltip", (tooltipEl: HTMLCalciteTooltipElement) => {
const transferEl = document.getElementById("transfer");
@@ -732,7 +733,7 @@ describe("calcite-tooltip", () => {
await ref.focus();
await page.waitForChanges();
- expect(await tooltip.isVisible()).toBe(true);
+ expect(await positionContainer.isVisible()).toBe(true);
});
describe("beforeOpen, open, beforeClose, close event emitting", () => {
@@ -875,9 +876,9 @@ describe("calcite-tooltip", () => {
const openEvent = await page.spyOnEvent("calciteTooltipOpen");
const container = await page.find(".container");
- const tooltip = await page.find(`calcite-tooltip`);
+ const positionContainer = await page.find(`calcite-tooltip >>> .${CSS.positionContainer}`);
- expect(await tooltip.isVisible()).toBe(false);
+ expect(await positionContainer.isVisible()).toBe(false);
await container.hover();
await page.waitForChanges();
@@ -888,7 +889,7 @@ describe("calcite-tooltip", () => {
await page.waitForTimeout(TOOLTIP_OPEN_DELAY_MS);
await page.waitForChanges();
- expect(await tooltip.isVisible()).toBe(true);
+ expect(await positionContainer.isVisible()).toBe(true);
expect(beforeOpenEvent).toHaveReceivedEventTimes(1);
expect(openEvent).toHaveReceivedEventTimes(1);
@@ -901,7 +902,7 @@ describe("calcite-tooltip", () => {
await page.waitForTimeout(TOOLTIP_CLOSE_DELAY_MS);
await page.waitForChanges();
- expect(await tooltip.isVisible()).not.toBe(true);
+ expect(await positionContainer.isVisible()).not.toBe(true);
expect(beforeOpenEvent).toHaveReceivedEventTimes(1);
expect(openEvent).toHaveReceivedEventTimes(1);
@@ -1146,15 +1147,18 @@ describe("calcite-tooltip", () => {
it("should work when clicking on a reference element first", async () => {
const page = await newE2EPage();
await page.setContent(pageContent);
+ await skipAnimations(page);
await page.waitForChanges();
const tooltip = await page.find("calcite-tooltip");
+ const positionContainer = await page.find(`calcite-tooltip >>> .${CSS.positionContainer}`);
const referenceElement = await page.find("#ref");
await referenceElement.click();
await page.waitForChanges();
expect(await tooltip.getProperty("open")).toBe(true);
- await tooltip.click();
+ await page.waitForTimeout(TOOLTIP_OPEN_DELAY_MS);
+ await positionContainer.click();
await page.waitForChanges();
expect(await tooltip.getProperty("open")).toBe(true);
@@ -1170,15 +1174,18 @@ describe("calcite-tooltip", () => {
it("should work when focusing on a reference element first", async () => {
const page = await newE2EPage();
await page.setContent(pageContent);
+ await skipAnimations(page);
await page.waitForChanges();
const tooltip = await page.find("calcite-tooltip");
+ const positionContainer = await page.find(`calcite-tooltip >>> .${CSS.positionContainer}`);
const referenceElement = await page.find("#ref");
await referenceElement.focus();
await page.waitForChanges();
expect(await tooltip.getProperty("open")).toBe(true);
- await tooltip.click();
+ await page.waitForTimeout(TOOLTIP_OPEN_DELAY_MS);
+ await positionContainer.click();
await page.waitForChanges();
expect(await tooltip.getProperty("open")).toBe(true);
diff --git a/packages/calcite-components/src/components/tooltip/tooltip.scss b/packages/calcite-components/src/components/tooltip/tooltip.scss
index b1ce06ce490..cb068052ceb 100644
--- a/packages/calcite-components/src/components/tooltip/tooltip.scss
+++ b/packages/calcite-components/src/components/tooltip/tooltip.scss
@@ -7,10 +7,16 @@
*/
:host {
+ @apply relative block;
--calcite-floating-ui-z-index: var(--calcite-tooltip-z-index, theme("zIndex.tooltip"));
}
-@include floating-ui-host();
+.position-container {
+ @include floating-ui-container();
+}
+
+@include floating-ui-elem-anim(".position-container");
+
@include floating-ui-arrow();
.container {
diff --git a/packages/calcite-components/src/components/tooltip/tooltip.tsx b/packages/calcite-components/src/components/tooltip/tooltip.tsx
index 46babba2918..dae0647f4a7 100644
--- a/packages/calcite-components/src/components/tooltip/tooltip.tsx
+++ b/packages/calcite-components/src/components/tooltip/tooltip.tsx
@@ -19,6 +19,7 @@ import {
FloatingCSS,
FloatingLayout,
FloatingUIComponent,
+ hideFloatingUI,
LogicalPlacement,
OverlayPositioning,
ReferenceElement,
@@ -138,7 +139,7 @@ export class Tooltip implements FloatingUIComponent, OpenCloseComponent {
@Element() el: HTMLCalciteTooltipElement;
- @State() effectiveReferenceElement: ReferenceElement;
+ @State() referenceEl: ReferenceElement;
@State() floatingLayout: FloatingLayout = "vertical";
@@ -150,6 +151,8 @@ export class Tooltip implements FloatingUIComponent, OpenCloseComponent {
transitionEl: HTMLDivElement;
+ floatingEl: HTMLDivElement;
+
// --------------------------------------------------------------------------
//
// Lifecycle
@@ -170,14 +173,14 @@ export class Tooltip implements FloatingUIComponent, OpenCloseComponent {
}
componentDidLoad(): void {
- if (this.referenceElement && !this.effectiveReferenceElement) {
+ if (this.referenceElement && !this.referenceEl) {
this.setUpReferenceElement();
}
}
disconnectedCallback(): void {
this.removeReferences();
- disconnectFloatingUI(this, this.effectiveReferenceElement, this.el);
+ disconnectFloatingUI(this);
}
//--------------------------------------------------------------------------
@@ -212,20 +215,20 @@ export class Tooltip implements FloatingUIComponent, OpenCloseComponent {
@Method()
async reposition(delayed = false): Promise {
const {
- el,
- effectiveReferenceElement,
+ referenceEl,
placement,
overlayPositioning,
offsetDistance,
offsetSkidding,
arrowEl,
+ floatingEl,
} = this;
return reposition(
this,
{
- floatingEl: el,
- referenceEl: effectiveReferenceElement,
+ floatingEl,
+ referenceEl: referenceEl,
overlayPositioning,
placement,
offsetDistance,
@@ -257,19 +260,25 @@ export class Tooltip implements FloatingUIComponent, OpenCloseComponent {
onClose(): void {
this.calciteTooltipClose.emit();
+ hideFloatingUI(this);
}
- private setTransitionEl = (el): void => {
+ private setFloatingEl = (el: HTMLDivElement): void => {
+ this.floatingEl = el;
+ requestAnimationFrame(() => this.setUpReferenceElement());
+ };
+
+ private setTransitionEl = (el: HTMLDivElement): void => {
this.transitionEl = el;
};
setUpReferenceElement = (warn = true): void => {
this.removeReferences();
- this.effectiveReferenceElement = getEffectiveReferenceElement(this.el);
- connectFloatingUI(this, this.effectiveReferenceElement, this.el);
+ this.referenceEl = getEffectiveReferenceElement(this.el);
+ connectFloatingUI(this);
- const { el, referenceElement, effectiveReferenceElement } = this;
- if (warn && referenceElement && !effectiveReferenceElement) {
+ const { el, referenceElement, referenceEl } = this;
+ if (warn && referenceElement && !referenceEl) {
console.warn(`${el.tagName}: reference-element id "${referenceElement}" was not found.`, {
el,
});
@@ -283,33 +292,33 @@ export class Tooltip implements FloatingUIComponent, OpenCloseComponent {
};
addReferences = (): void => {
- const { effectiveReferenceElement } = this;
+ const { referenceEl } = this;
- if (!effectiveReferenceElement) {
+ if (!referenceEl) {
return;
}
const id = this.getId();
- if ("setAttribute" in effectiveReferenceElement) {
- effectiveReferenceElement.setAttribute(ARIA_DESCRIBED_BY, id);
+ if ("setAttribute" in referenceEl) {
+ referenceEl.setAttribute(ARIA_DESCRIBED_BY, id);
}
- manager.registerElement(effectiveReferenceElement, this.el);
+ manager.registerElement(referenceEl, this.el);
};
removeReferences = (): void => {
- const { effectiveReferenceElement } = this;
+ const { referenceEl } = this;
- if (!effectiveReferenceElement) {
+ if (!referenceEl) {
return;
}
- if ("removeAttribute" in effectiveReferenceElement) {
- effectiveReferenceElement.removeAttribute(ARIA_DESCRIBED_BY);
+ if ("removeAttribute" in referenceEl) {
+ referenceEl.removeAttribute(ARIA_DESCRIBED_BY);
}
- manager.unregisterElement(effectiveReferenceElement);
+ manager.unregisterElement(referenceEl);
};
// --------------------------------------------------------------------------
@@ -319,8 +328,8 @@ export class Tooltip implements FloatingUIComponent, OpenCloseComponent {
// --------------------------------------------------------------------------
render(): VNode {
- const { effectiveReferenceElement, label, open, floatingLayout } = this;
- const displayed = effectiveReferenceElement && open;
+ const { referenceEl, label, open, floatingLayout } = this;
+ const displayed = referenceEl && open;
const hidden = !displayed;
return (
@@ -328,23 +337,24 @@ export class Tooltip implements FloatingUIComponent, OpenCloseComponent {
aria-hidden={toAriaBoolean(hidden)}
aria-label={label}
aria-live="polite"
- calcite-hydrated-hidden={hidden}
id={this.getId()}
role="tooltip"
>
-
-
(this.arrowEl = arrowEl)}
- />
-
-
+
+
+
(this.arrowEl = arrowEl)}
+ />
+
+
+
diff --git a/packages/calcite-components/src/utils/floating-ui.spec.ts b/packages/calcite-components/src/utils/floating-ui.spec.ts
index e6a8a953e06..38572183d23 100644
--- a/packages/calcite-components/src/utils/floating-ui.spec.ts
+++ b/packages/calcite-components/src/utils/floating-ui.spec.ts
@@ -45,6 +45,8 @@ function createFakeFloatingUiComponent(referenceEl: HTMLElement, floatingEl: HTM
type: "menu",
});
},
+ floatingEl,
+ referenceEl,
overlayPositioning: "absolute",
placement: "auto",
};
@@ -75,24 +77,36 @@ describe("repositioning", () => {
type: "popover",
};
- connectFloatingUI(fakeFloatingUiComponent, referenceEl, floatingEl);
+ connectFloatingUI(fakeFloatingUiComponent);
});
+ function assertClosedPositioning(floatingEl: HTMLElement): void {
+ expect(floatingEl.style.display).toBe("");
+ expect(floatingEl.style.pointerEvents).toBe("");
+ expect(floatingEl.style.position).toBe("");
+ expect(floatingEl.style.transform).toBe("");
+ expect(floatingEl.style.visibility).toBe("");
+ }
+
function assertPreOpenPositioning(floatingEl: HTMLElement): void {
+ expect(floatingEl.style.display).toBe("block");
+ expect(floatingEl.style.pointerEvents).toBe("");
+ expect(floatingEl.style.position).toBe("absolute");
expect(floatingEl.style.transform).toBe("");
- expect(floatingEl.style.top).toBe("");
- expect(floatingEl.style.left).toBe("");
+ expect(floatingEl.style.visibility).toBe("");
}
function assertOpenPositioning(floatingEl: HTMLElement): void {
+ expect(floatingEl.style.display).toBe("block");
+ expect(floatingEl.style.pointerEvents).toBe("none");
+ expect(floatingEl.style.position).not.toBe("");
expect(floatingEl.style.transform).not.toBe("");
- expect(floatingEl.style.top).toBe("0");
- expect(floatingEl.style.left).toBe("0");
+ expect(floatingEl.style.visibility).toBe("hidden");
}
it("repositions only for open components", async () => {
await reposition(fakeFloatingUiComponent, positionOptions);
- assertPreOpenPositioning(floatingEl);
+ assertClosedPositioning(floatingEl);
fakeFloatingUiComponent.open = true;
@@ -101,6 +115,8 @@ describe("repositioning", () => {
});
it("repositions immediately by default", async () => {
+ assertClosedPositioning(floatingEl);
+
fakeFloatingUiComponent.open = true;
reposition(fakeFloatingUiComponent, positionOptions);
@@ -112,6 +128,8 @@ describe("repositioning", () => {
});
it("can reposition after a delay", async () => {
+ assertClosedPositioning(floatingEl);
+
fakeFloatingUiComponent.open = true;
reposition(fakeFloatingUiComponent, positionOptions, true);
@@ -162,27 +180,27 @@ describe("connect/disconnect helpers", () => {
expect(floatingEl.style.visibility).toBe("");
expect(floatingEl.style.pointerEvents).toBe("");
- await connectFloatingUI(fakeFloatingUiComponent, referenceEl, floatingEl);
+ await connectFloatingUI(fakeFloatingUiComponent);
expect(autoUpdatingComponentMap.has(fakeFloatingUiComponent)).toBe(true);
expect(floatingEl.style.position).toBe("absolute");
expect(floatingEl.style.visibility).toBe("hidden");
expect(floatingEl.style.pointerEvents).toBe("none");
- disconnectFloatingUI(fakeFloatingUiComponent, referenceEl, floatingEl);
+ disconnectFloatingUI(fakeFloatingUiComponent);
expect(autoUpdatingComponentMap.has(fakeFloatingUiComponent)).toBe(false);
expect(floatingEl.style.position).toBe("absolute");
fakeFloatingUiComponent.overlayPositioning = "fixed";
- await connectFloatingUI(fakeFloatingUiComponent, referenceEl, floatingEl);
+ await connectFloatingUI(fakeFloatingUiComponent);
expect(autoUpdatingComponentMap.has(fakeFloatingUiComponent)).toBe(true);
expect(floatingEl.style.position).toBe("fixed");
expect(floatingEl.style.visibility).toBe("hidden");
expect(floatingEl.style.pointerEvents).toBe("none");
- disconnectFloatingUI(fakeFloatingUiComponent, referenceEl, floatingEl);
+ disconnectFloatingUI(fakeFloatingUiComponent);
expect(autoUpdatingComponentMap.has(fakeFloatingUiComponent)).toBe(false);
expect(floatingEl.style.position).toBe("fixed");
diff --git a/packages/calcite-components/src/utils/floating-ui.ts b/packages/calcite-components/src/utils/floating-ui.ts
index 43503148793..2c21fe46db1 100644
--- a/packages/calcite-components/src/utils/floating-ui.ts
+++ b/packages/calcite-components/src/utils/floating-ui.ts
@@ -100,7 +100,7 @@ export const positionFloatingUI =
},
): Promise
=> {
if (!referenceEl || !floatingEl) {
- return null;
+ return;
}
const isRTL = getElementDir(floatingEl) === "rtl";
@@ -153,15 +153,11 @@ export const positionFloatingUI =
floatingEl.setAttribute(placementDataAttribute, effectivePlacement);
- const { open } = component;
-
Object.assign(floatingEl.style, {
- visibility,
pointerEvents,
position,
- transform: open ? `translate(${roundByDPR(x)}px,${roundByDPR(y)}px)` : "",
- top: 0,
- left: 0,
+ transform: `translate(${roundByDPR(x)}px,${roundByDPR(y)}px)`,
+ visibility,
});
};
@@ -313,6 +309,16 @@ export interface FloatingUIComponent {
* See [FloatingArrow](https://github.com/Esri/calcite-design-system/blob/dev/src/components/functional/FloatingArrow.tsx)
*/
floatingLayout?: FloatingLayout;
+
+ /**
+ * The `floatingElement` containing the floating ui.
+ */
+ floatingEl: HTMLElement;
+
+ /**
+ * The `referenceElement` used to position the component according to its `placement` value.
+ */
+ referenceEl: ReferenceElement;
}
export type FloatingLayout = Extract;
@@ -440,10 +446,16 @@ export async function reposition(
return;
}
+ Object.assign(options.floatingEl.style, {
+ display: "block",
+ // initial positioning based on https://floating-ui.com/docs/computePosition#initial-layout
+ position: options.overlayPositioning ?? "absolute",
+ });
+
const trackedState = autoUpdatingComponentMap.get(component);
if (!trackedState) {
- return runAutoUpdate(component, options.referenceEl, options.floatingEl);
+ return runAutoUpdate(component);
}
const positionFunction = delayed ? getDebouncedReposition(component) : positionFloatingUI;
@@ -495,11 +507,9 @@ export const autoUpdatingComponentMap = new WeakMap>();
-async function runAutoUpdate(
- component: FloatingUIComponent,
- referenceEl: ReferenceElement,
- floatingEl: HTMLElement,
-): Promise {
+async function runAutoUpdate(component: FloatingUIComponent): Promise {
+ const { referenceEl, floatingEl } = component;
+
if (!floatingEl.isConnected) {
return;
}
@@ -537,50 +547,58 @@ async function runAutoUpdate(
}
/**
- * Helper to set up floating element interactions on connectedCallback.
+ * Helper to hide the floating element when the component is closed. This should be called within onClose() of an OpenCloseComponent.
*
* @param component - A floating-ui component.
- * @param referenceEl - The `referenceElement` used to position the component according to its `placement` value.
- * @param floatingEl - The `floatingElement` containing the floating ui.
*/
-export async function connectFloatingUI(
- component: FloatingUIComponent,
- referenceEl: ReferenceElement,
- floatingEl: HTMLElement,
-): Promise {
- if (!floatingEl || !referenceEl) {
+export function hideFloatingUI(component: FloatingUIComponent): void {
+ const { floatingEl } = component;
+
+ if (!floatingEl) {
return;
}
- disconnectFloatingUI(component, referenceEl, floatingEl);
-
Object.assign(floatingEl.style, {
- visibility: "hidden",
- pointerEvents: "none",
-
- // initial positioning based on https://floating-ui.com/docs/computePosition#initial-layout
- position: component.overlayPositioning,
+ display: "",
+ pointerEvents: "",
+ position: "",
+ transform: "",
+ visibility: "",
});
+}
+
+/**
+ * Helper to set up floating element interactions on connectedCallback.
+ *
+ * @param component - A floating-ui component.
+ * @returns {Promise}
+ */
+export async function connectFloatingUI(component: FloatingUIComponent): Promise {
+ const { floatingEl, referenceEl } = component;
+
+ hideFloatingUI(component);
+
+ if (!floatingEl || !referenceEl) {
+ return;
+ }
+
+ disconnectFloatingUI(component);
if (!component.open) {
return;
}
- return runAutoUpdate(component, referenceEl, floatingEl);
+ return runAutoUpdate(component);
}
/**
* Helper to tear down floating element interactions on disconnectedCallback.
*
* @param component - A floating-ui component.
- * @param referenceEl - The `referenceElement` used to position the component according to its `placement` value.
- * @param floatingEl - The `floatingElement` containing the floating ui.
*/
-export function disconnectFloatingUI(
- component: FloatingUIComponent,
- referenceEl: ReferenceElement,
- floatingEl: HTMLElement,
-): void {
+export function disconnectFloatingUI(component: FloatingUIComponent): void {
+ const { floatingEl, referenceEl } = component;
+
if (!floatingEl || !referenceEl) {
return;
}