Skip to content
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
117 changes: 62 additions & 55 deletions packages/lib/src/base-menu/GroupItem.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useContext, useId } from "react";
import { useContext, useEffect, useId, useState } from "react";
import DxcIcon from "../icon/Icon";
import SubMenu from "./SubMenu";
import ItemAction from "./ItemAction";
Expand All @@ -10,14 +10,17 @@ import BaseMenuContext from "./BaseMenuContext";

const GroupItem = ({ items, ...props }: GroupItemProps) => {
const groupMenuId = `group-menu-${useId()}`;

const NavigationTreeId = `sidenav-${useId()}`;
const navigationTreeId = `sidenav-${useId()}`;
const contextValue = useContext(BaseMenuContext) ?? {};
const { groupSelected, isOpen, toggleOpen, hasPopOver, isHorizontal } = useGroupItem(
items,
contextValue,
props.defaultOpen
);
const [portalContainer, setPortalContainer] = useState<HTMLElement | null>(null);
useEffect(() => {
setPortalContainer(document?.getElementById(`${navigationTreeId}-portal`));
}, []);

return hasPopOver ? (
<>
Expand All @@ -38,60 +41,64 @@ const GroupItem = ({ items, ...props }: GroupItemProps) => {
{...props}
/>
</Popover.Trigger>
<Popover.Portal container={document.getElementById(`${NavigationTreeId}-portal`)}>
<BaseMenuContext.Provider value={{ ...contextValue, displayGroupLines: false, hasPopOver: false }}>
<Popover.Content
aria-label="Group details"
onKeyDown={(event) => {
if (event.key === "Escape") {
toggleOpen();
}
}}
align="start"
side={isHorizontal ? "bottom" : "right"}
style={{
zIndex: "var(--z-contextualmenu)",
padding: "var(--spacing-padding-xs)",
boxShadow: "var(--shadow-100)",
backgroundColor: "var(--color-bg-neutral-lightest)",
borderRadius: "var(--border-radius-m)",
...(isHorizontal
? {}
: {
display: "flex",
flexDirection: "column",
gap: "var(--spacing-gap-xxs)",
}),
}}
sideOffset={isHorizontal ? 16 : 0}
onInteractOutside={() => toggleOpen()}
>
{!isHorizontal && props.depthLevel === 0 && (
<ItemAction
aria-controls={isOpen ? groupMenuId : undefined}
aria-expanded={isOpen ? true : undefined}
aria-pressed={groupSelected && !isOpen}
collapseIcon={isOpen ? <DxcIcon icon="filled_expand_less" /> : <DxcIcon icon="filled_expand_more" />}
onClick={() => toggleOpen()}
selected={groupSelected && !isOpen}
{...props}
icon={undefined}
/>
)}
<SubMenu id={groupMenuId} depthLevel={props.depthLevel} isPopOver={true}>
{items.map((item, index) => (
<MenuItem
item={item}
depthLevel={isHorizontal ? props.depthLevel : props.depthLevel + 1}
key={`${item.label}-${index}`}
{portalContainer && (
<Popover.Portal container={portalContainer}>
<BaseMenuContext.Provider value={{ ...contextValue, displayGroupLines: false, hasPopOver: false }}>
<Popover.Content
aria-label="Group details"
onKeyDown={(event) => {
if (event.key === "Escape") {
toggleOpen();
}
}}
align="start"
side={isHorizontal ? "bottom" : "right"}
style={{
zIndex: "var(--z-contextualmenu)",
padding: "var(--spacing-padding-xs)",
boxShadow: "var(--shadow-100)",
backgroundColor: "var(--color-bg-neutral-lightest)",
borderRadius: "var(--border-radius-m)",
...(isHorizontal
? {}
: {
display: "flex",
flexDirection: "column",
gap: "var(--spacing-gap-xxs)",
}),
}}
sideOffset={isHorizontal ? 16 : 0}
onInteractOutside={() => toggleOpen()}
>
{!isHorizontal && props.depthLevel === 0 && (
<ItemAction
aria-controls={isOpen ? groupMenuId : undefined}
aria-expanded={isOpen ? true : undefined}
aria-pressed={groupSelected && !isOpen}
collapseIcon={
isOpen ? <DxcIcon icon="filled_expand_less" /> : <DxcIcon icon="filled_expand_more" />
}
onClick={() => toggleOpen()}
selected={groupSelected && !isOpen}
{...props}
icon={undefined}
/>
))}
</SubMenu>
</Popover.Content>
</BaseMenuContext.Provider>
</Popover.Portal>
)}
<SubMenu id={groupMenuId} depthLevel={props.depthLevel} isPopOver={true}>
{items.map((item, index) => (
<MenuItem
item={item}
depthLevel={isHorizontal ? props.depthLevel : props.depthLevel + 1}
key={`${item.label}-${index}`}
/>
))}
</SubMenu>
</Popover.Content>
</BaseMenuContext.Provider>
</Popover.Portal>
)}
</Popover.Root>
<div id={`${NavigationTreeId}-portal`} style={{ position: "absolute" }} />
<div id={`${navigationTreeId}-portal`} style={{ position: "absolute" }} />
</>
) : (
<>
Expand Down
32 changes: 20 additions & 12 deletions packages/lib/src/date-input/DateInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ const DxcDateInput = forwardRef<RefType, DateInputPropsType>(
: null
);
const [sideOffset, setSideOffset] = useState(SIDEOFFSET);
const [portalContainer, setPortalContainer] = useState<HTMLElement | null>(null);

const translatedLabels = useContext(HalstackLanguageContext);
const dateRef = useRef<HTMLDivElement | null>(null);
const popoverContentRef = useRef<HTMLDivElement | null>(null);
Expand Down Expand Up @@ -258,6 +260,9 @@ const DxcDateInput = forwardRef<RefType, DateInputPropsType>(
closeCalendar();
}
};
useEffect(() => {
setPortalContainer(document?.getElementById(`${calendarId}-portal`));
}, []);

useEffect(() => {
window.addEventListener("scroll", adjustSideOffset);
Expand Down Expand Up @@ -327,20 +332,23 @@ const DxcDateInput = forwardRef<RefType, DateInputPropsType>(
ariaLabel={ariaLabel}
/>
</Popover.Trigger>
<Popover.Portal container={document.getElementById(`${calendarId}-portal`)}>
<StyledPopoverContent
sideOffset={sideOffset}
align="end"
aria-modal
onBlur={handleDatePickerOnBlur}
onKeyDown={handleDatePickerEscKeydown}
ref={popoverContentRef}
>
<DatePicker id={calendarId} onDateSelect={handleCalendarOnClick} date={dayjsDate} />
</StyledPopoverContent>
</Popover.Portal>
{portalContainer && (
<Popover.Portal container={portalContainer}>
<StyledPopoverContent
sideOffset={sideOffset}
align="end"
aria-modal
onBlur={handleDatePickerOnBlur}
onKeyDown={handleDatePickerEscKeydown}
ref={popoverContentRef}
>
<DatePicker id={calendarId} onDateSelect={handleCalendarOnClick} date={dayjsDate} />
</StyledPopoverContent>
</Popover.Portal>
)}
</Popover.Root>
</DateInputContainer>

<div id={`${calendarId}-portal`} style={{ position: "absolute" }} />
</>
);
Expand Down
39 changes: 23 additions & 16 deletions packages/lib/src/dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as Popover from "@radix-ui/react-popover";
import { FocusEvent, KeyboardEvent, useCallback, useId, useLayoutEffect, useRef, useState } from "react";
import { FocusEvent, KeyboardEvent, useCallback, useEffect, useId, useLayoutEffect, useRef, useState } from "react";
import styled from "@emotion/styled";
import { getMargin } from "../common/utils";
import { spaces } from "../common/variables";
Expand Down Expand Up @@ -131,6 +131,10 @@ const DxcDropdown = ({
const menuId = `menu-${id}`;
const [isOpen, changeIsOpen] = useState(false);
const [visualFocusIndex, setVisualFocusIndex] = useState(0);
const [portalContainer, setPortalContainer] = useState<HTMLElement | null>(null);
useEffect(() => {
setPortalContainer(document?.getElementById(`${id}-portal`));
}, []);

const triggerRef = useRef<HTMLButtonElement | null>(null);
const menuRef = useRef<HTMLUListElement | null>(null);
Expand Down Expand Up @@ -300,23 +304,26 @@ const DxcDropdown = ({
</DropdownTrigger>
</Popover.Trigger>
</Tooltip>
<Popover.Portal container={document.getElementById(`${id}-portal`)}>
<Popover.Content aria-label="Dropdown options" asChild sideOffset={1}>
<DropdownMenu
id={menuId}
dropdownTriggerId={triggerId}
options={options}
iconsPosition={optionsIconPosition}
visualFocusIndex={visualFocusIndex}
menuItemOnClick={handleMenuItemOnClick}
onKeyDown={handleMenuOnKeyDown}
styles={{ width }}
ref={menuRef}
/>
</Popover.Content>
</Popover.Portal>
{portalContainer && (
<Popover.Portal container={portalContainer}>
<Popover.Content aria-label="Dropdown options" asChild sideOffset={1}>
<DropdownMenu
id={menuId}
dropdownTriggerId={triggerId}
options={options}
iconsPosition={optionsIconPosition}
visualFocusIndex={visualFocusIndex}
menuItemOnClick={handleMenuItemOnClick}
onKeyDown={handleMenuOnKeyDown}
styles={{ width }}
ref={menuRef}
/>
</Popover.Content>
</Popover.Portal>
)}
</Popover.Root>
</DropdownContainer>

<div id={`${id}-portal`} style={{ position: "absolute" }} />
</>
);
Expand Down
78 changes: 43 additions & 35 deletions packages/lib/src/select/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
MouseEvent,
useCallback,
useContext,
useEffect,
useId,
useMemo,
useRef,
Expand Down Expand Up @@ -210,6 +211,10 @@ const DxcSelect = forwardRef<RefType, SelectPropsType>(
const [isOpen, changeIsOpen] = useState(false);
const [searchValue, setSearchValue] = useState("");
const [visualFocusIndex, changeVisualFocusIndex] = useState(-1);
const [portalContainer, setPortalContainer] = useState<HTMLElement | null>(null);
useEffect(() => {
setPortalContainer(document?.getElementById(`${id}-portal`));
}, []);

const selectRef = useRef<HTMLDivElement | null>(null);
const selectSearchInputRef = useRef<HTMLInputElement | null>(null);
Expand Down Expand Up @@ -596,44 +601,47 @@ const DxcSelect = forwardRef<RefType, SelectPropsType>(
</DxcFlex>
</Select>
</Popover.Trigger>
<Popover.Portal container={document.getElementById(`${id}-portal`)}>
<Popover.Content
aria-label="Select options"
onCloseAutoFocus={(event) => {
// Avoid select to lose focus when the list is closed
event.preventDefault();
}}
onOpenAutoFocus={(event) => {
// Avoid select to lose focus when the list is opened
event.preventDefault();
}}
sideOffset={4}
style={{ zIndex: "var(--z-dropdown)" }}
>
<Listbox
ariaLabelledBy={labelId}
currentValue={value ?? innerValue}
enableSelectAll={enableSelectAll}
handleOptionOnClick={handleOptionOnClick}
handleGroupOnClick={handleSelectAllGroup}
handleSelectAllOnClick={handleSelectAllOnClick}
virtualizedHeight={virtualizedHeight}
id={listboxId}
lastOptionIndex={lastOptionIndex}
multiple={multiple}
optional={optional}
optionalItem={optionalItem}
options={searchable ? filteredOptions : options}
searchable={searchable}
selectionType={selectionType}
styles={{ width }}
visualFocusIndex={visualFocusIndex}
/>
</Popover.Content>
</Popover.Portal>
{portalContainer && (
<Popover.Portal container={portalContainer}>
<Popover.Content
aria-label="Select options"
onCloseAutoFocus={(event) => {
// Avoid select to lose focus when the list is closed
event.preventDefault();
}}
onOpenAutoFocus={(event) => {
// Avoid select to lose focus when the list is opened
event.preventDefault();
}}
sideOffset={4}
style={{ zIndex: "var(--z-dropdown)" }}
>
<Listbox
ariaLabelledBy={labelId}
currentValue={value ?? innerValue}
enableSelectAll={enableSelectAll}
handleOptionOnClick={handleOptionOnClick}
handleGroupOnClick={handleSelectAllGroup}
handleSelectAllOnClick={handleSelectAllOnClick}
virtualizedHeight={virtualizedHeight}
id={listboxId}
lastOptionIndex={lastOptionIndex}
multiple={multiple}
optional={optional}
optionalItem={optionalItem}
options={searchable ? filteredOptions : options}
searchable={searchable}
selectionType={selectionType}
styles={{ width }}
visualFocusIndex={visualFocusIndex}
/>
</Popover.Content>
</Popover.Portal>
)}
</Popover.Root>
{!disabled && typeof error === "string" && <ErrorMessage error={error} id={errorId} />}
</SelectContainer>

<div id={`${id}-portal`} style={{ position: "absolute" }} />
</>
);
Expand Down
Loading