Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
52e5632
Add width: 100%; to TextInput to avoid malfunctioning when the size i…
PelayoFelgueroso Feb 19, 2026
1ec6ba2
Add position absolute to popover portal container to avoid flex or gr…
PelayoFelgueroso Feb 19, 2026
363eb9f
Merge pull request #2398 from dxc-technology/PelayoFelgueroso/textInp…
Jialecl Feb 20, 2026
927d807
Merge branch 'master' into PelayoFelgueroso/popover-portalContainer-p…
Jialecl Feb 20, 2026
97280df
Merge pull request #2401 from dxc-technology/PelayoFelgueroso/popover…
Jialecl Feb 20, 2026
80da331
Add fillParent prop to FileInput component
PelayoFelgueroso Feb 23, 2026
c28aefd
Changes based on comments
PelayoFelgueroso Feb 23, 2026
8b1300b
Fix based on comments
PelayoFelgueroso Feb 23, 2026
adfc848
Merge pull request #2403 from dxc-technology/PelayoFelgueroso/fileInp…
Jialecl Feb 23, 2026
0ed14ce
Making sure that the popover container is ready
Jialecl Feb 24, 2026
9565d87
Toast SSR fix refactored
Jialecl Feb 24, 2026
2e36834
Changed to the correct value
Jialecl Feb 24, 2026
688ec89
Fixing dateInput tests
Jialecl Feb 24, 2026
6033b22
Fixing select tests
Jialecl Feb 24, 2026
326ad62
Refactored element queries
Jialecl Feb 24, 2026
5457ff8
Fix vulnerabilities via npm audit fix
raquelarrojo Feb 24, 2026
a874796
Merge pull request #2405 from dxc-technology/rarrojolopez/vulnerabili…
PelayoFelgueroso Feb 24, 2026
2c1a621
Merge branch 'master' into jialecl/portal-check
PelayoFelgueroso Feb 24, 2026
91efed0
removed document.body as container for toasts
Jialecl Feb 25, 2026
7086be9
Merge branch 'jialecl/portal-check' of https://github.com/dxc-technol…
Jialecl Feb 25, 2026
2cd4e86
Merge pull request #2404 from dxc-technology/jialecl/portal-check
PelayoFelgueroso Feb 25, 2026
795c0a9
Merge branch 'master' of https://github.com/dxc-technology/halstack-r…
PelayoFelgueroso Feb 26, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,21 @@ const sections = [
<TableCode>false</TableCode>
</td>
</tr>
<tr>
<td>
<DxcFlex direction="column" gap="var(--spacing-gap-xs)" alignItems="baseline">
<StatusBadge status="new" />
size
</DxcFlex>
</td>
<td>
<TableCode>'medium' | 'fillParent'</TableCode>
</td>
<td>Size of the component.</td>
<td>
<TableCode>'medium'</TableCode>
</td>
</tr>
<tr>
<td>tabIndex</td>
<td>
Expand Down
673 changes: 333 additions & 340 deletions package-lock.json

Large diffs are not rendered by default.

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
28 changes: 28 additions & 0 deletions packages/lib/src/file-input/FileInput.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Meta, StoryObj } from "@storybook/react-vite";
import ExampleContainer from "../../.storybook/components/ExampleContainer";
import Title from "../../.storybook/components/Title";
import DxcFileInput from "./FileInput";
import DxcContainer from "../container/Container";

export default {
title: "File Input",
Expand Down Expand Up @@ -510,6 +511,33 @@ const FileInput = () => (
margin="xxlarge"
/>
</ExampleContainer>
<Title title="Sizes" theme="light" level={3} />
<ExampleContainer>
<Title title="fillParent size" theme="light" level={4} />
<DxcContainer width="200px">
<DxcFileInput
label="File input"
helperText="Please select files"
value={fileExample}
callbackFile={() => {}}
mode="filedrop"
size="fillParent"
/>
</DxcContainer>
</ExampleContainer>
<ExampleContainer>
<Title title="fillParent size" theme="light" level={4} />
<DxcContainer width="200px">
<DxcFileInput
label="File input"
helperText="Please select files"
value={fileExample}
callbackFile={() => {}}
mode="dropzone"
size="fillParent"
/>
</DxcContainer>
</ExampleContainer>
</>
);
// const EllipsisError = () => {
Expand Down
Loading