diff --git a/packages/lib/src/HalstackContext.stories.tsx b/packages/lib/src/HalstackContext.stories.tsx new file mode 100644 index 0000000000..9354e19e22 --- /dev/null +++ b/packages/lib/src/HalstackContext.stories.tsx @@ -0,0 +1,141 @@ +import ExampleContainer from "./../.storybook/components/ExampleContainer"; +import Title from "./../.storybook/components/Title"; +import { Meta, StoryObj } from "@storybook/react"; +import { HalstackProvider } from "./HalstackContext"; +import { useState } from "react"; +import DxcButton from "./button/Button"; +import DxcDateInput from "./date-input/DateInput"; +import DxcFlex from "./flex/Flex"; +import DxcSelect from "./select/Select"; +import DxcDialog from "./dialog/Dialog"; +import DxcInset from "./inset/Inset"; +import DxcAlert from "./alert/Alert"; + +export default { + title: "HalstackContext", + component: HalstackProvider, +} as Meta; + +const Provider = () => { + const [isDialogVisible, setDialogVisible] = useState(false); + const [isAlertVisible, setAlertVisible] = useState(false); + const handleClickDialog = () => { + setDialogVisible(!isDialogVisible); + }; + const handleClickAlert = () => { + setAlertVisible(!isAlertVisible); + }; + const [newTheme, setNewTheme] = useState>({ + "--color-primary-50": "#d3f0b4", + "--color-primary-100": "#a2df5e", + "--color-primary-200": "#77c81f", + "--color-primary-300": "#68ad1b", + "--color-primary-400": "#579317", + "--color-primary-500": "#487813", + "--color-primary-600": "#39600f", + "--color-primary-700": "#2b470b", + "--color-primary-800": "#1c2f07", + "--color-primary-900": "#0d1503", + "--color-secondary-50": "#fff9d6", + "--color-secondary-100": "#ffed99", + "--color-secondary-200": "#ffe066", + "--color-secondary-300": "#e6c84d", + "--color-secondary-400": "#ccad33", + "--color-secondary-500": "#b39426", + "--color-secondary-600": "#8f741f", + "--color-secondary-700": "#6b5517", + "--color-secondary-800": "#47370f", + "--color-secondary-900": "#241b08", + "--color-alpha-800-a": "#9a2257cc", + }); + const options = [ + { label: "Option 01", value: "1" }, + { label: "Option 02", value: "2" }, + { label: "Option 03", value: "3" }, + { label: "Option 04", value: "4" }, + ]; + return ( + <> + + + <HalstackProvider opinionatedTheme={newTheme}> + <DxcFlex gap="var(--spacing-padding-l)" direction="column" alignItems="baseline"> + <DxcButton + label="Primary" + semantic="default" + onClick={() => + setNewTheme({ + "--color-primary-50": "#ffd6e7", + "--color-primary-100": "#ff99c2", + "--color-primary-200": "#ff66a3", + "--color-primary-300": "#e05584", + "--color-primary-400": "#c5446d", + "--color-primary-500": "#a83659", + "--color-primary-600": "#872b47", + "--color-primary-700": "#661f35", + "--color-primary-800": "#441423", + "--color-primary-900": "#220a12", + "--color-brown-50": "#f3e6db", + "--color-secondary-100": "#e2c7a9", + "--color-secondary-200": "#d1a577", + "--color-secondary-300": "#b88252", + "--color-secondary-400": "#99673f", + "--color-secondary-500": "#7a5232", + "--color-secondary-600": "#5c3f26", + "--color-secondary-700": "#3e2b19", + "--color-secondary-800": "#21170d", + "--color-secondary-900": "#100b06", + "--color-alpha-800-a": "#fabadacc", + }) + } + size={{ height: "small" }} + /> + <DxcButton + label="Show Dialog" + semantic="default" + mode="secondary" + size={{ height: "small" }} + onClick={handleClickDialog} + /> + {isDialogVisible && ( + <DxcDialog onCloseClick={handleClickDialog}> + <DxcInset space="var(--spacing-padding-l)"> + <DxcButton label="Primary" semantic="default" mode="tertiary" size={{ height: "small" }} /> + <DxcButton label="Primary" semantic="info" size={{ height: "small" }} /> + <DxcButton label="Primary" semantic="info" mode="secondary" size={{ height: "small" }} /> + <DxcDateInput /> + <DxcSelect options={options} /> + </DxcInset> + </DxcDialog> + )} + <DxcButton + label="Alert visibility" + semantic="default" + mode="tertiary" + size={{ height: "small" }} + onClick={handleClickAlert} + /> + <DxcButton label="Primary" semantic="info" size={{ height: "small" }} /> + <DxcButton label="Primary" semantic="info" mode="secondary" size={{ height: "small" }} /> + <DxcDateInput /> + <DxcSelect options={options} /> + + {isAlertVisible && ( + <DxcAlert + title="Information" + mode="modal" + message={{ text: "Your document has been auto-saved.", onClose: handleClickAlert }} + /> + )} + </DxcFlex> + </HalstackProvider> + </ExampleContainer> + </> + ); +}; + +type Story = StoryObj<typeof HalstackProvider>; + +export const Chromatic: Story = { + render: Provider, +}; diff --git a/packages/lib/src/HalstackContext.tsx b/packages/lib/src/HalstackContext.tsx index eb021c4405..f1f53e54d9 100644 --- a/packages/lib/src/HalstackContext.tsx +++ b/packages/lib/src/HalstackContext.tsx @@ -1,4 +1,7 @@ import { createContext, ReactNode, useMemo } from "react"; +import styled from "@emotion/styled"; +import { css } from "@emotion/react"; +import { coreTokens, aliasTokens } from "./styles/tokens"; import { TranslatedLabels, defaultTranslatedComponentLabels } from "./common/variables"; /** @@ -8,7 +11,6 @@ import { TranslatedLabels, defaultTranslatedComponentLabels } from "./common/var export type DeepPartial<T> = { [P in keyof T]?: Partial<T[P]>; }; - const HalstackLanguageContext = createContext<TranslatedLabels>(defaultTranslatedComponentLabels); const parseLabels = (labels: DeepPartial<TranslatedLabels>): TranslatedLabels => { @@ -27,18 +29,55 @@ const parseLabels = (labels: DeepPartial<TranslatedLabels>): TranslatedLabels => }); return parsedLabels; }; +type ThemeType = Record<string, string | number>; type HalstackProviderPropsType = { labels?: DeepPartial<TranslatedLabels>; children: ReactNode; + opinionatedTheme?: ThemeType; }; -const HalstackProvider = ({ labels, children }: HalstackProviderPropsType): JSX.Element => { + +const HalstackThemed = styled.div<{ coreTheme?: ThemeType }>` + ${(props) => { + if (props.coreTheme) + return css` + ${Object.keys(props.coreTheme).length + ? Object.entries(props.coreTheme).map(([key, val]) => `${key}: ${val};`) + : coreTokens} + ${aliasTokens} + `; + else { + return css` + ${coreTokens} + ${aliasTokens} + `; + } + }} +`; + +const createCoreTheme = (opinionatedTheme: ThemeType | undefined = {}) => { + const newTheme: ThemeType = {}; + Object.entries(coreTokens).forEach(([key, value]) => { + newTheme[key] = opinionatedTheme[key] ?? value; + }); + return newTheme; +}; + +const HalstackProvider = ({ labels, children, opinionatedTheme }: HalstackProviderPropsType): JSX.Element => { const parsedLabels = useMemo(() => (labels ? parseLabels(labels) : null), [labels]); + const parsedCoreTheme = useMemo(() => { + const theme = createCoreTheme(opinionatedTheme); + return theme; + }, [opinionatedTheme]); - return parsedLabels ? ( - <HalstackLanguageContext.Provider value={parsedLabels}>{children}</HalstackLanguageContext.Provider> - ) : ( - <>{children}</> + return ( + <HalstackThemed coreTheme={parsedCoreTheme}> + {parsedLabels ? ( + <HalstackLanguageContext.Provider value={parsedLabels}>{children}</HalstackLanguageContext.Provider> + ) : ( + children + )} + </HalstackThemed> ); }; diff --git a/packages/lib/src/alert/ModalAlertWrapper.tsx b/packages/lib/src/alert/ModalAlertWrapper.tsx index 5456dd03a0..31002edb3f 100644 --- a/packages/lib/src/alert/ModalAlertWrapper.tsx +++ b/packages/lib/src/alert/ModalAlertWrapper.tsx @@ -1,5 +1,5 @@ import { createPortal } from "react-dom"; -import { useEffect } from "react"; +import { useEffect, useId, useState } from "react"; import { Global, css } from "@emotion/react"; import styled from "@emotion/styled"; import { responsiveSizes } from "../common/variables"; @@ -46,6 +46,9 @@ const ModalAlertContainer = styled.div` `; const ModalAlertWrapper = ({ condition, onClose, children }: ModalAlertWrapperProps) => { + const id = useId(); + const [portalContainer, setPortalContainer] = useState<HTMLElement | null>(null); + useEffect(() => { if (condition) { const handleModalAlertKeydown = (event: KeyboardEvent) => { @@ -61,18 +64,24 @@ const ModalAlertWrapper = ({ condition, onClose, children }: ModalAlertWrapperPr } }, [condition, onClose]); + useEffect(() => { + setPortalContainer(document.getElementById(`dialog-${id}-portal`)); + }, []); + return condition ? ( <> <BodyStyle /> - {createPortal( - <Modal> - <Overlay onClick={onClose} /> - <ModalAlertContainer> - <FocusLock>{children}</FocusLock> - </ModalAlertContainer> - </Modal>, - document.body - )} + <div id={`dialog-${id}-portal`} style={{ position: "absolute" }} /> + {portalContainer && + createPortal( + <Modal> + <Overlay onClick={onClose} /> + <ModalAlertContainer> + <FocusLock>{children}</FocusLock> + </ModalAlertContainer> + </Modal>, + portalContainer + )} </> ) : ( children diff --git a/packages/lib/src/date-input/DateInput.tsx b/packages/lib/src/date-input/DateInput.tsx index 8175d0b490..4fb9e426b0 100644 --- a/packages/lib/src/date-input/DateInput.tsx +++ b/packages/lib/src/date-input/DateInput.tsx @@ -286,58 +286,63 @@ const DxcDateInput = forwardRef<RefType, DateInputPropsType>( }, [isOpen, disabled, calendarId]); return ( - <DateInputContainer margin={margin} size={size} ref={ref}> - {label && ( - <Label - htmlFor={dateRef.current?.getElementsByTagName("input")[0]?.id} - disabled={disabled} - hasHelperText={!!helperText} - > - {label}{" "} - {optional && <OptionalLabel disabled={disabled}>{translatedLabels.formFields.optionalLabel}</OptionalLabel>} - </Label> - )} - {helperText && <HelperText disabled={disabled}>{helperText}</HelperText>} - <Popover.Root open={isOpen}> - <Popover.Trigger asChild aria-controls={undefined}> - <DxcTextInput - name={name} - defaultValue={defaultValue} - value={value ?? innerValue} - placeholder={placeholder ? format.toUpperCase() : undefined} - action={{ - onClick: openCalendar, - icon: "filled_calendar_today", - title: "Select date", - }} - clearable={clearable} + <> + <DateInputContainer margin={margin} size={size} ref={ref}> + {label && ( + <Label + htmlFor={dateRef.current?.getElementsByTagName("input")[0]?.id} disabled={disabled} - readOnly={readOnly} - optional={optional} - onChange={handleOnChange} - onBlur={handleOnBlur} - error={error} - autocomplete={autocomplete} - size={size} - tabIndex={tabIndex} - ref={dateRef} - ariaLabel={ariaLabel} - /> - </Popover.Trigger> - <Popover.Portal> - <StyledPopoverContent - sideOffset={sideOffset} - align="end" - aria-modal - onBlur={handleDatePickerOnBlur} - onKeyDown={handleDatePickerEscKeydown} - ref={popoverContentRef} + hasHelperText={!!helperText} > - <DatePicker id={calendarId} onDateSelect={handleCalendarOnClick} date={dayjsDate} /> - </StyledPopoverContent> - </Popover.Portal> - </Popover.Root> - </DateInputContainer> + {label}{" "} + {optional && ( + <OptionalLabel disabled={disabled}>{translatedLabels.formFields.optionalLabel}</OptionalLabel> + )} + </Label> + )} + {helperText && <HelperText disabled={disabled}>{helperText}</HelperText>} + <Popover.Root open={isOpen}> + <Popover.Trigger asChild aria-controls={undefined}> + <DxcTextInput + name={name} + defaultValue={defaultValue} + value={value ?? innerValue} + placeholder={placeholder ? format.toUpperCase() : undefined} + action={{ + onClick: openCalendar, + icon: "filled_calendar_today", + title: "Select date", + }} + clearable={clearable} + disabled={disabled} + readOnly={readOnly} + optional={optional} + onChange={handleOnChange} + onBlur={handleOnBlur} + error={error} + autocomplete={autocomplete} + size={size} + tabIndex={tabIndex} + ref={dateRef} + 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> + </Popover.Root> + </DateInputContainer> + <div id={`${calendarId}-portal`} style={{ position: "absolute" }} /> + </> ); } ); diff --git a/packages/lib/src/dialog/Dialog.stories.tsx b/packages/lib/src/dialog/Dialog.stories.tsx index 94323ddfe0..4be4ad64f1 100644 --- a/packages/lib/src/dialog/Dialog.stories.tsx +++ b/packages/lib/src/dialog/Dialog.stories.tsx @@ -15,11 +15,24 @@ import DxcSelect from "../select/Select"; import DxcDateInput from "../date-input/DateInput"; import DxcDropdown from "../dropdown/Dropdown"; import DxcTooltip from "../tooltip/Tooltip"; +import disabledRules from "../../test/accessibility/rules/specific/dialog/disabledRules"; +import preview from "../../.storybook/preview"; export default { title: "Dialog", component: DxcDialog, parameters: { + a11y: { + config: { + rules: [ + ...disabledRules.map((ruleId) => ({ + id: ruleId, + reviewOnFail: true, + })), + ...(preview?.parameters?.a11y?.config?.rules || []), + ], + }, + }, viewport: { viewports: INITIAL_VIEWPORTS, }, diff --git a/packages/lib/src/dialog/Dialog.tsx b/packages/lib/src/dialog/Dialog.tsx index 720b6717f1..6a4b5d3c17 100644 --- a/packages/lib/src/dialog/Dialog.tsx +++ b/packages/lib/src/dialog/Dialog.tsx @@ -1,4 +1,4 @@ -import { useContext, useEffect } from "react"; +import { useContext, useEffect, useId, useState } from "react"; import { createPortal } from "react-dom"; import styled from "@emotion/styled"; import { responsiveSizes } from "../common/variables"; @@ -66,7 +66,13 @@ const DxcDialog = ({ tabIndex = 0, disableFocusLock = false, }: DialogPropsType): JSX.Element => { + const id = useId(); const translatedLabels = useContext(HalstackLanguageContext); + const [portalContainer, setPortalContainer] = useState<HTMLElement | null>(null); + + useEffect(() => { + setPortalContainer(document.getElementById(`dialog-${id}-portal`)); + }, []); useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { @@ -85,43 +91,45 @@ const DxcDialog = ({ return ( <> <BodyStyle /> - {createPortal( - <DialogContainer> - {overlay && <Overlay onClick={onBackgroundClick} />} - <Dialog aria-label="Dialog" aria-modal={overlay} closable={closable} role="dialog"> - {!disableFocusLock ? ( - <FocusLock> - {children} - {closable && ( - <CloseIconActionContainer> - <DxcActionIcon - icon="close" - onClick={onCloseClick} - tabIndex={tabIndex} - title={translatedLabels.dialog.closeIconAriaLabel} - /> - </CloseIconActionContainer> - )} - </FocusLock> - ) : ( - <> - {children} - {closable && ( - <CloseIconActionContainer> - <DxcActionIcon - icon="close" - onClick={onCloseClick} - tabIndex={tabIndex} - title={translatedLabels.dialog.closeIconAriaLabel} - /> - </CloseIconActionContainer> - )} - </> - )} - </Dialog> - </DialogContainer>, - document.body - )} + <div id={`dialog-${id}-portal`} style={{ position: "absolute" }} /> + {portalContainer && + createPortal( + <DialogContainer> + {overlay && <Overlay onClick={onBackgroundClick} />} + <Dialog aria-label="Dialog" aria-modal={overlay} closable={closable} role="dialog"> + {!disableFocusLock ? ( + <FocusLock> + {children} + {closable && ( + <CloseIconActionContainer> + <DxcActionIcon + icon="close" + onClick={onCloseClick} + tabIndex={tabIndex} + title={translatedLabels.dialog.closeIconAriaLabel} + /> + </CloseIconActionContainer> + )} + </FocusLock> + ) : ( + <> + {children} + {closable && ( + <CloseIconActionContainer> + <DxcActionIcon + icon="close" + onClick={onCloseClick} + tabIndex={tabIndex} + title={translatedLabels.dialog.closeIconAriaLabel} + /> + </CloseIconActionContainer> + )} + </> + )} + </Dialog> + </DialogContainer>, + portalContainer + )} </> ); }; diff --git a/packages/lib/src/dropdown/Dropdown.stories.tsx b/packages/lib/src/dropdown/Dropdown.stories.tsx index 6a02758f6f..de8a5f1f32 100644 --- a/packages/lib/src/dropdown/Dropdown.stories.tsx +++ b/packages/lib/src/dropdown/Dropdown.stories.tsx @@ -1,7 +1,9 @@ import { Meta, StoryObj } from "@storybook/react"; import { userEvent, within } from "@storybook/test"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; +import preview from "../../.storybook/preview"; import Title from "../../.storybook/components/Title"; +import disabledRules from "../../test/accessibility/rules/specific/dropdown/disabledRules"; import DxcDropdown from "./Dropdown"; import DropdownMenu from "./DropdownMenu"; import { Option } from "./types"; @@ -9,6 +11,19 @@ import { Option } from "./types"; export default { title: "Dropdown", component: DxcDropdown, + parameters: { + a11y: { + config: { + rules: [ + ...disabledRules.map((ruleId) => ({ + id: ruleId, + reviewOnFail: true, + })), + ...(preview?.parameters?.a11y?.config?.rules || []), + ], + }, + }, + }, } as Meta<typeof DxcDropdown>; const iconSVG = ( diff --git a/packages/lib/src/dropdown/Dropdown.tsx b/packages/lib/src/dropdown/Dropdown.tsx index 905b419b8b..f2b10b12b1 100644 --- a/packages/lib/src/dropdown/Dropdown.tsx +++ b/packages/lib/src/dropdown/Dropdown.tsx @@ -251,71 +251,74 @@ const DxcDropdown = ({ }, [visualFocusIndex]); return ( - <DropdownContainer - onMouseEnter={!disabled && expandOnHover ? handleOnOpenMenu : undefined} - onMouseLeave={!disabled && expandOnHover ? handleOnCloseMenu : undefined} - onBlur={!disabled ? handleOnBlur : undefined} - margin={margin} - size={size} - > - <Popover.Root open={isOpen}> - <Tooltip label={title}> - <Popover.Trigger asChild type={undefined}> - <DropdownTrigger - onClick={handleTriggerOnClick} - onKeyDown={handleTriggerOnKeyDown} - onBlur={(event) => { - event.stopPropagation(); - }} - disabled={disabled} - label={label} - margin={margin} - size={size} - id={triggerId} - aria-haspopup="true" - aria-controls={isOpen ? menuId : undefined} - aria-expanded={isOpen ? true : undefined} - aria-label="Show options" - tabIndex={tabIndex} - ref={triggerRef} - > - <DropdownTriggerContent iconPosition={iconPosition}> - {icon && ( - <DropdownTriggerIcon - disabled={disabled} - role={typeof icon === "string" ? undefined : "img"} - aria-hidden - > - {typeof icon === "string" ? <DxcIcon icon={icon} /> : icon} - </DropdownTriggerIcon> + <> + <DropdownContainer + onMouseEnter={!disabled && expandOnHover ? handleOnOpenMenu : undefined} + onMouseLeave={!disabled && expandOnHover ? handleOnCloseMenu : undefined} + onBlur={!disabled ? handleOnBlur : undefined} + margin={margin} + size={size} + > + <Popover.Root open={isOpen}> + <Tooltip label={title}> + <Popover.Trigger asChild type={undefined}> + <DropdownTrigger + onClick={handleTriggerOnClick} + onKeyDown={handleTriggerOnKeyDown} + onBlur={(event) => { + event.stopPropagation(); + }} + disabled={disabled} + label={label} + margin={margin} + size={size} + id={triggerId} + aria-haspopup="true" + aria-controls={isOpen ? menuId : undefined} + aria-expanded={isOpen ? true : undefined} + aria-label="Show options" + tabIndex={tabIndex} + ref={triggerRef} + > + <DropdownTriggerContent iconPosition={iconPosition}> + {icon && ( + <DropdownTriggerIcon + disabled={disabled} + role={typeof icon === "string" ? undefined : "img"} + aria-hidden + > + {typeof icon === "string" ? <DxcIcon icon={icon} /> : icon} + </DropdownTriggerIcon> + )} + {label && <DropdownTriggerLabel>{label}</DropdownTriggerLabel>} + </DropdownTriggerContent> + {!caretHidden && ( + <CaretIcon disabled={disabled}> + <DxcIcon icon={isOpen ? "keyboard_arrow_up" : "keyboard_arrow_down"} /> + </CaretIcon> )} - {label && <DropdownTriggerLabel>{label}</DropdownTriggerLabel>} - </DropdownTriggerContent> - {!caretHidden && ( - <CaretIcon disabled={disabled}> - <DxcIcon icon={isOpen ? "keyboard_arrow_up" : "keyboard_arrow_down"} /> - </CaretIcon> - )} - </DropdownTrigger> - </Popover.Trigger> - </Tooltip> - <Popover.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> - </Popover.Root> - </DropdownContainer> + </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> + </Popover.Root> + </DropdownContainer> + <div id={`${id}-portal`} style={{ position: "absolute" }} /> + </> ); }; diff --git a/packages/lib/src/resultset-table/ResultsetTable.stories.tsx b/packages/lib/src/resultset-table/ResultsetTable.stories.tsx index e7e00d1c41..b4f347d27d 100644 --- a/packages/lib/src/resultset-table/ResultsetTable.stories.tsx +++ b/packages/lib/src/resultset-table/ResultsetTable.stories.tsx @@ -695,7 +695,7 @@ const ResultsetTableLast = () => ( ); const ResultsetActionsCellDropdown = () => ( - <ExampleContainer> + <ExampleContainer expanded> <Title title="Dropdown Action" theme="light" level={4} /> <DxcResultsetTable columns={columns} rows={rowsIcon} itemsPerPage={2} /> </ExampleContainer> diff --git a/packages/lib/src/select/Select.tsx b/packages/lib/src/select/Select.tsx index 2dd32f93b6..8d31c160f6 100644 --- a/packages/lib/src/select/Select.tsx +++ b/packages/lib/src/select/Select.tsx @@ -478,158 +478,161 @@ const DxcSelect = forwardRef<RefType, SelectPropsType>( ); return ( - <SelectContainer margin={margin} ref={ref} size={size}> - {label && ( - <Label - disabled={disabled} - hasMargin={!helperText} - id={labelId} - onClick={() => { - selectRef?.current?.focus(); - }} - > - {label} {optional && <span>{translatedLabels.formFields.optionalLabel}</span>} - </Label> - )} - {helperText && ( - <HelperText disabled={disabled} hasMargin> - {helperText} - </HelperText> - )} - <Popover.Root open={isOpen}> - <Popover.Trigger asChild type={undefined}> - <Select - aria-activedescendant={visualFocusIndex >= 0 ? `option-${visualFocusIndex}` : undefined} - aria-controls={isOpen ? listboxId : undefined} - aria-disabled={disabled} - aria-errormessage={error ? errorId : undefined} - aria-expanded={isOpen} - aria-haspopup="listbox" - aria-invalid={!!error} - aria-label={label ? undefined : ariaLabel} - aria-labelledby={label ? labelId : undefined} - aria-required={!disabled && !optional} + <> + <SelectContainer margin={margin} ref={ref} size={size}> + {label && ( + <Label disabled={disabled} - error={!!error} - id={selectInputId} - onBlur={handleOnBlur} - onClick={handleOnClick} - onFocus={handleOnFocus} - onKeyDown={handleOnKeyDown} - ref={selectRef} - role="combobox" - tabIndex={disabled ? -1 : tabIndex} + hasMargin={!helperText} + id={labelId} + onClick={() => { + selectRef?.current?.focus(); + }} > - {multiple && Array.isArray(selectedOption) && selectedOption.length > 0 && ( - <SelectionIndicator disabled={disabled}> - <SelectionNumber disabled={disabled}>{selectedOption.length}</SelectionNumber> - <Tooltip label={translatedLabels.select.actionClearSelectionTitle}> - <ClearOptionsAction - aria-label={translatedLabels.select.actionClearSelectionTitle} - disabled={disabled} - onClick={handleClearOptionsActionOnClick} - onMouseDown={(event) => { - // Avoid input to lose focus when pressed - event.preventDefault(); - }} - tabIndex={-1} - > - <DxcIcon icon="clear" /> - </ClearOptionsAction> - </Tooltip> - </SelectionIndicator> - )} - <TooltipWrapper condition={hasTooltip} label={getSelectedOptionLabel(placeholder, selectedOption)}> - <SearchableValueContainer> - <input - disabled={disabled} - name={name} - type="hidden" - value={ - multiple - ? (Array.isArray(value) ? value : Array.isArray(innerValue) ? innerValue : []).join(",") - : (value ?? innerValue) - } - /> - {searchable && ( - <SearchInput - aria-labelledby={label ? labelId : undefined} - autoComplete="nope" - autoCorrect="nope" + {label} {optional && <span>{translatedLabels.formFields.optionalLabel}</span>} + </Label> + )} + {helperText && ( + <HelperText disabled={disabled} hasMargin> + {helperText} + </HelperText> + )} + <Popover.Root open={isOpen}> + <Popover.Trigger asChild type={undefined}> + <Select + aria-activedescendant={visualFocusIndex >= 0 ? `option-${visualFocusIndex}` : undefined} + aria-controls={isOpen ? listboxId : undefined} + aria-disabled={disabled} + aria-errormessage={error ? errorId : undefined} + aria-expanded={isOpen} + aria-haspopup="listbox" + aria-invalid={!!error} + aria-label={label ? undefined : ariaLabel} + aria-labelledby={label ? labelId : undefined} + aria-required={!disabled && !optional} + disabled={disabled} + error={!!error} + id={selectInputId} + onBlur={handleOnBlur} + onClick={handleOnClick} + onFocus={handleOnFocus} + onKeyDown={handleOnKeyDown} + ref={selectRef} + role="combobox" + tabIndex={disabled ? -1 : tabIndex} + > + {multiple && Array.isArray(selectedOption) && selectedOption.length > 0 && ( + <SelectionIndicator disabled={disabled}> + <SelectionNumber disabled={disabled}>{selectedOption.length}</SelectionNumber> + <Tooltip label={translatedLabels.select.actionClearSelectionTitle}> + <ClearOptionsAction + aria-label={translatedLabels.select.actionClearSelectionTitle} + disabled={disabled} + onClick={handleClearOptionsActionOnClick} + onMouseDown={(event) => { + // Avoid input to lose focus when pressed + event.preventDefault(); + }} + tabIndex={-1} + > + <DxcIcon icon="clear" /> + </ClearOptionsAction> + </Tooltip> + </SelectionIndicator> + )} + <TooltipWrapper condition={hasTooltip} label={getSelectedOptionLabel(placeholder, selectedOption)}> + <SearchableValueContainer> + <input disabled={disabled} - onChange={handleSearchIOnChange} - ref={selectSearchInputRef} - size={1} - value={searchValue} - /> - )} - {(!searchable || searchValue === "") && ( - <SelectedOption - atBackground={ - (multiple ? (value ?? innerValue).length === 0 : !(value ?? innerValue)) || - (searchable && isOpen) + name={name} + type="hidden" + value={ + multiple + ? (Array.isArray(value) ? value : Array.isArray(innerValue) ? innerValue : []).join(",") + : (value ?? innerValue) } - disabled={disabled} - onMouseEnter={handleOnMouseEnter} - > - {getSelectedOptionLabel(placeholder, selectedOption)} - </SelectedOption> - )} - </SearchableValueContainer> - </TooltipWrapper> - <DxcFlex alignItems="center"> - {searchable && searchValue.length > 0 && ( - <Tooltip label={translatedLabels.select.actionClearSelectionTitle}> - <DxcActionIcon - icon="clear" - onClick={handleClearSearchActionOnClick} - tabIndex={-1} - title={translatedLabels.select.actionClearSearchTitle} /> - </Tooltip> - )} - <DxcIcon icon={isOpen ? "keyboard_arrow_up" : "keyboard_arrow_down"} /> - </DxcFlex> - </Select> - </Popover.Trigger> - <Popover.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> - </Popover.Root> - {!disabled && typeof error === "string" && <ErrorMessage error={error} id={errorId} />} - </SelectContainer> + {searchable && ( + <SearchInput + aria-labelledby={label ? labelId : undefined} + autoComplete="nope" + autoCorrect="nope" + disabled={disabled} + onChange={handleSearchIOnChange} + ref={selectSearchInputRef} + size={1} + value={searchValue} + /> + )} + {(!searchable || searchValue === "") && ( + <SelectedOption + atBackground={ + (multiple ? (value ?? innerValue).length === 0 : !(value ?? innerValue)) || + (searchable && isOpen) + } + disabled={disabled} + onMouseEnter={handleOnMouseEnter} + > + {getSelectedOptionLabel(placeholder, selectedOption)} + </SelectedOption> + )} + </SearchableValueContainer> + </TooltipWrapper> + <DxcFlex alignItems="center"> + {searchable && searchValue.length > 0 && ( + <Tooltip label={translatedLabels.select.actionClearSelectionTitle}> + <DxcActionIcon + icon="clear" + onClick={handleClearSearchActionOnClick} + tabIndex={-1} + title={translatedLabels.select.actionClearSearchTitle} + /> + </Tooltip> + )} + <DxcIcon icon={isOpen ? "keyboard_arrow_up" : "keyboard_arrow_down"} /> + </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> + </Popover.Root> + {!disabled && typeof error === "string" && <ErrorMessage error={error} id={errorId} />} + </SelectContainer> + <div id={`${id}-portal`} style={{ position: "absolute" }} /> + </> ); } ); diff --git a/packages/lib/src/styles/tables/tablesStyles.tsx b/packages/lib/src/styles/tables/tablesStyles.tsx index e6d4eaa8ce..3f6a28490f 100644 --- a/packages/lib/src/styles/tables/tablesStyles.tsx +++ b/packages/lib/src/styles/tables/tablesStyles.tsx @@ -5,7 +5,7 @@ import { calculateWidth } from "../../table/utils"; import { spaces } from "../../common/variables"; export const TableContainer = styled.div<{ margin: TablePropsType["margin"] }>` - height: 100%; + max-height: 100%; width: ${({ margin }) => calculateWidth(margin)}; margin: ${({ margin }) => (margin && typeof margin !== "object" ? spaces[margin] : "0px")}; margin-top: ${({ margin }) => (margin && typeof margin === "object" && margin.top ? spaces[margin.top] : "")}; diff --git a/packages/lib/src/styles/tokens.tsx b/packages/lib/src/styles/tokens.tsx new file mode 100644 index 0000000000..d9ff8dfe91 --- /dev/null +++ b/packages/lib/src/styles/tokens.tsx @@ -0,0 +1,365 @@ +export const coreTokens: Record<string, string | number> = { + /**************/ + /** POSITION **/ + /**************/ + + /* Application Layout */ + "--z-app-layout-header": 100, + "--z-app-layout-sidenav": 110, + + /* Header */ + "--z-header-overlay": 200, + "--z-header-menu": 210, + + /* UI components */ + "--z-date-input": 300, + "--z-dropdown": 310, + "--z-textinput": 320, + "--z-select": 330, + + /* Modals and overlays */ + "--z-spinner-overlay": 400, + "--z-progressbar-overlay": 410, + "--z-dialog": 420, + "--z-alert": 430, + + /* Notifications */ + "--z-toast": 500, + + /* Tooltip (topmost) */ + "--z-tooltip": 600, + + /************/ + /** TOKENS **/ + /************/ + "--color-absolutes-black": "#000000", + "--color-absolutes-white": "#ffffff", + "--color-alpha-100-a": "#ebebeb1a", + "--color-alpha-200-a": "#dedede33", + "--color-alpha-300-a": "#d1d1d14d", + "--color-alpha-400-a": "#b5b5b566", + "--color-alpha-500-a": "#9a9a9a80", + "--color-alpha-600-a": "#79797999", + "--color-alpha-700-a": "#5b5b5bb2", + "--color-alpha-800-a": "#494949cc", + "--color-alpha-900-a": "#333333e5", + "--color-primary-50": "#fcfbfe", + "--color-primary-100": "#f0e8fa", + "--color-primary-200": "#e7d9f6", + "--color-primary-300": "#ddc9f3", + "--color-primary-400": "#c8a7eb", + "--color-primary-500": "#b487e4", + "--color-primary-600": "#9363c8", + "--color-primary-700": "#6f4b97", + "--color-primary-800": "#5a3c7a", + "--color-primary-900": "#3e2a55", + "--color-secondary-50": "#f8fcff", + "--color-secondary-100": "#d9efff", + "--color-secondary-200": "#bce3ff", + "--color-secondary-300": "#a0d7ff", + "--color-secondary-400": "#61bdff", + "--color-secondary-500": "#30a1f1", + "--color-secondary-600": "#267fbf", + "--color-secondary-700": "#1d6091", + "--color-secondary-800": "#174e74", + "--color-secondary-900": "#103651", + "--color-tertiary-50": "#fefbef", + "--color-tertiary-100": "#fcedae", + "--color-tertiary-200": "#f9de6e", + "--color-tertiary-300": "#f5cd2b", + "--color-tertiary-400": "#d4b226", + "--color-tertiary-500": "#b6981f", + "--color-tertiary-600": "#8f7818", + "--color-tertiary-700": "#6c5a12", + "--color-tertiary-800": "#57490f", + "--color-tertiary-900": "#3d3309", + "--color-success-50": "#f3fcf5", + "--color-success-100": "#d1f5db", + "--color-success-200": "#acecbe", + "--color-success-300": "#87e3a0", + "--color-success-400": "#53cb75", + "--color-success-500": "#47ae64", + "--color-success-600": "#39884f", + "--color-success-700": "#2a673b", + "--color-success-800": "#225230", + "--color-success-900": "#183921", + "--color-warning-50": "#fffbf6", + "--color-warning-100": "#fde9d2", + "--color-warning-200": "#fbd9b3", + "--color-warning-300": "#f9c78f", + "--color-warning-400": "#f5a344", + "--color-warning-500": "#d58a35", + "--color-warning-600": "#a76d2b", + "--color-warning-700": "#7f5121", + "--color-warning-800": "#66421a", + "--color-warning-900": "#3d3309", + "--color-error-50": "#fff7f6", + "--color-error-100": "#ffe6e4", + "--color-error-200": "#ffd3d0", + "--color-error-300": "#ffc1bd", + "--color-error-400": "#ff9896", + "--color-error-500": "#ff696f", + "--color-error-600": "#e33248", + "--color-error-700": "#a92c37", + "--color-error-800": "#87262d", + "--color-error-900": "#5b1f21", + "--color-neutral-50": "#fbfbfb", + "--color-neutral-100": "#ebebeb", + "--color-neutral-200": "#dedede", + "--color-neutral-300": "#d1d1d1", + "--color-neutral-400": "#b5b5b5", + "--color-neutral-500": "#9a9a9a", + "--color-neutral-600": "#797979", + "--color-neutral-700": "#5b5b5b", + "--color-neutral-800": "#494949", + "--color-neutral-900": "#333333", + "--dimensions-0": "0px", + "--dimensions-1": "1px", + "--dimensions-2": "2px", + "--dimensions-4": "4px", + "--dimensions-8": "8px", + "--dimensions-12": "12px", + "--dimensions-16": "16px", + "--dimensions-20": "20px", + "--dimensions-24": "24px", + "--dimensions-28": "28px", + "--dimensions-32": "32px", + "--dimensions-36": "36px", + "--dimensions-40": "40px", + "--dimensions-44": "44px", + "--dimensions-48": "48px", + "--dimensions-56": "56px", + "--dimensions-64": "64px", + "--dimensions-72": "72px", + "--dimensions-80": "80px", + "--dimensions-96": "96px", + "--font-size-12": "12px", + "--font-size-14": "14px", + "--font-size-16": "16px", + "--font-size-18": "18px", + "--font-size-20": "20px", + "--font-size-24": "24px", + "--font-size-32": "32px", + "--font-size-40": "40px", + "--font-size-48": "48px", + "--font-size-60": "60px", + "--font-weight-light": "300", + "--font-weight-regular": "400", + "--font-weight-semibold": "600", + "--font-weight-bold": "700", + "--font-family-mono": "Source Code Pro, mono", + "--font-family-sans": "Open Sans, sans-serif", + "--font-style-lightitalic": "light italic", + "--font-style-normal": "normal", + "--line-style-dashed": "dashed", + "--line-style-solid": "solid", +}; + +export const aliasTokens: Record<string, string | number> = { + "--border-color-error-light": "var(--color-error-300)", + "--border-color-error-medium": "var(--color-error-600)", + "--border-color-error-strong": "var(--color-error-700)", + "--border-color-error-stronger": "var(--color-error-800)", + "--border-color-neutral-bright": "var(--color-neutral-50)", + "--border-color-neutral-brighter": "var(--color-absolutes-white)", + "--border-color-neutral-dark": "var(--color-neutral-900)", + "--border-color-neutral-light": "var(--color-neutral-300)", + "--border-color-neutral-lighter": "var(--color-neutral-200)", + "--border-color-neutral-lightest": "var(--color-neutral-100)", + "--border-color-neutral-medium": "var(--color-neutral-400)", + "--border-color-neutral-strong": "var(--color-neutral-500)", + "--border-color-neutral-stronger": "var(--color-neutral-600)", + "--border-color-neutral-strongest": "var(--color-neutral-700)", + "--border-color-primary-light": "var(--color-primary-400)", + "--border-color-primary-lighter": "var(--color-primary-300)", + "--border-color-primary-medium": "var(--color-primary-500)", + "--border-color-primary-strong": "var(--color-primary-600)", + "--border-color-primary-stronger": "var(--color-primary-700)", + "--border-color-secondary-light": "var(--color-secondary-300)", + "--border-color-secondary-medium": "var(--color-secondary-500)", + "--border-color-secondary-strong": "var(--color-secondary-600)", + "--border-color-secondary-stronger": "var(--color-secondary-700)", + "--border-color-secondary-strongest": "var(--color-secondary-800)", + "--border-color-success-light": "var(--color-success-300)", + "--border-color-success-medium": "var(--color-success-600)", + "--border-color-warning-light": "var(--color-warning-300)", + "--border-color-warning-medium": "var(--color-warning-500)", + "--color-bg-overlay-dark": "var(--color-alpha-800-a)", + "--color-bg-yellow-light": "var(--color-tertiary-100)", + "--color-bg-alpha-light": "var(--color-alpha-300-a)", + "--color-bg-alpha-medium": "var(--color-alpha-800-a)", + "--color-bg-alpha-strong": "var(--color-alpha-900-a)", + "--color-bg-error-light": "var(--color-error-200)", + "--color-bg-error-lighter": "var(--color-error-100)", + "--color-bg-error-lightest": "var(--color-error-50)", + "--color-bg-error-medium": "var(--color-error-300)", + "--color-bg-error-strong": "var(--color-error-600)", + "--color-bg-error-stronger": "var(--color-error-700)", + "--color-bg-error-strongest": "var(--color-error-800)", + "--color-bg-neutral-light": "var(--color-neutral-100)", + "--color-bg-neutral-lighter": "var(--color-neutral-50)", + "--color-bg-neutral-lightest": "var(--color-absolutes-white)", + "--color-bg-neutral-medium": "var(--color-neutral-200)", + "--color-bg-neutral-strong": "var(--color-neutral-300)", + "--color-bg-neutral-stronger": "var(--color-neutral-800)", + "--color-bg-neutral-strongest": "var(--color-neutral-900)", + "--color-bg-primary-light": "var(--color-primary-200)", + "--color-bg-primary-lighter": "var(--color-primary-100)", + "--color-bg-primary-lightest": "var(--color-primary-50)", + "--color-bg-primary-medium": "var(--color-primary-300)", + "--color-bg-primary-strong": "var(--color-primary-700)", + "--color-bg-primary-stronger": "var(--color-primary-800)", + "--color-bg-primary-strongest": "var(--color-primary-900)", + "--color-bg-secondary-light": "var(--color-secondary-200)", + "--color-bg-secondary-lighter": "var(--color-secondary-100)", + "--color-bg-secondary-lightest": "var(--color-secondary-50)", + "--color-bg-secondary-medium": "var(--color-secondary-300)", + "--color-bg-secondary-strong": "var(--color-secondary-600)", + "--color-bg-secondary-stronger": "var(--color-secondary-700)", + "--color-bg-secondary-strongest": "var(--color-secondary-800)", + "--color-bg-success-light": "var(--color-success-200)", + "--color-bg-success-lighter": "var(--color-success-100)", + "--color-bg-success-lightest": "var(--color-success-50)", + "--color-bg-success-medium": "var(--color-success-300)", + "--color-bg-success-strong": "var(--color-success-600)", + "--color-bg-success-stronger": "var(--color-success-700)", + "--color-bg-success-strongest": "var(--color-success-800)", + "--color-bg-warning-light": "var(--color-warning-200)", + "--color-bg-warning-lighter": "var(--color-warning-100)", + "--color-bg-warning-lightest": "var(--color-warning-50)", + "--color-bg-warning-medium": "var(--color-warning-300)", + "--color-bg-warning-strong": "var(--color-warning-500)", + "--color-bg-warning-stronger": "var(--color-warning-600)", + "--color-bg-warning-strongest": "var(--color-warning-700)", + "--color-fg-error-light": "var(--color-error-300)", + "--color-fg-error-lighter": "var(--color-error-200)", + "--color-fg-error-medium": "var(--color-error-600)", + "--color-fg-error-strong": "var(--color-error-700)", + "--color-fg-error-stronger": "var(--color-error-800)", + "--color-fg-neutral-bright": "var(--color-absolutes-white)", + "--color-fg-neutral-dark": "var(--color-neutral-900)", + "--color-fg-neutral-light": "var(--color-neutral-400)", + "--color-fg-neutral-lighter": "var(--color-neutral-200)", + "--color-fg-neutral-lightest": "var(--color-neutral-100)", + "--color-fg-neutral-medium": "var(--color-neutral-500)", + "--color-fg-neutral-strong": "var(--color-neutral-600)", + "--color-fg-neutral-stronger": "var(--color-neutral-700)", + "--color-fg-neutral-strongest": "var(--color-neutral-800)", + "--color-fg-neutral-yellow-dark": "var(--color-tertiary-800)", + "--color-fg-primary-light": "var(--color-primary-300)", + "--color-fg-primary-lighter": "var(--color-primary-100)", + "--color-fg-primary-medium": "var(--color-primary-400)", + "--color-fg-primary-strong": "var(--color-primary-700)", + "--color-fg-primary-stronger": "var(--color-primary-800)", + "--color-fg-primary-strongest": "var(--color-primary-900)", + "--color-fg-secondary-light": "var(--color-secondary-500)", + "--color-fg-secondary-lighter": "var(--color-secondary-300)", + "--color-fg-secondary-medium": "var(--color-secondary-600)", + "--color-fg-secondary-strong": "var(--color-secondary-700)", + "--color-fg-secondary-stronger": "var(--color-secondary-800)", + "--color-fg-secondary-strongest": "var(--color-secondary-900)", + "--color-fg-success-light": "var(--color-success-300)", + "--color-fg-success-lighter": "var(--color-success-200)", + "--color-fg-success-medium": "var(--color-success-600)", + "--color-fg-success-strong": "var(--color-success-700)", + "--color-fg-success-stronger": "var(--color-success-800)", + "--color-fg-warning-light": "var(--color-warning-300)", + "--color-fg-warning-medium": "var(--color-warning-500)", + "--color-fg-warning-strong": "var(--color-warning-600)", + "--color-fg-warning-stronger": "var(--color-warning-800)", + "--shadow-dark": "var(--color-alpha-400-a)", + "--shadow-light": "var(--color-alpha-300-a)", + "--border-radius-none": "var(--dimensions-0)", + "--border-radius-xs": "var(--dimensions-2)", + "--border-radius-s": "var(--dimensions-4)", + "--border-radius-m": "var(--dimensions-8)", + "--border-radius-l": "var(--dimensions-16)", + "--border-radius-xl": "var(--dimensions-24)", + "--border-width-none": "var(--dimensions-0)", + "--border-width-s": "var(--dimensions-1)", + "--border-width-m": "var(--dimensions-2)", + "--border-width-l": "var(--dimensions-4)", + "--height-xxxs": "var(--dimensions-12)", + "--height-xxs": "var(--dimensions-16)", + "--height-xs": "var(--dimensions-20)", + "--height-s": "var(--dimensions-24)", + "--height-m": "var(--dimensions-32)", + "--height-l": "var(--dimensions-36)", + "--height-xl": "var(--dimensions-40)", + "--height-xxl": "var(--dimensions-48)", + "--height-xxxl": "var(--dimensions-56)", + "--shadow-high-spread": "var(--dimensions-0)", + "--shadow-high-x-position": "var(--dimensions-0)", + "--shadow-high-blur": "var(--dimensions-24)", + "--shadow-high-y-position": "var(--dimensions-24)", + "--shadow-higher-spread": "var(--dimensions-0)", + "--shadow-higher-x-position": "var(--dimensions-0)", + "--shadow-higher-blur": "var(--dimensions-48)", + "--shadow-higher-y-position": "var(--dimensions-48)", + "--shadow-low-spread": "var(--dimensions-0)", + "--shadow-low-x-position": "var(--dimensions-0)", + "--shadow-low-blur": "var(--dimensions-2)", + "--shadow-low-y-position": "var(--dimensions-2)", + "--shadow-mid-spread": "var(--dimensions-0)", + "--shadow-mid-x-position": "var(--dimensions-0)", + "--shadow-mid-blur": "var(--dimensions-12)", + "--shadow-mid-y-position": "var(--dimensions-12)", + "--spacing-gap-none": "var(--dimensions-0)", + "--spacing-gap-xxs": "var(--dimensions-2)", + "--spacing-gap-xs": "var(--dimensions-4)", + "--spacing-gap-s": "var(--dimensions-8)", + "--spacing-gap-m": "var(--dimensions-12)", + "--spacing-gap-ml": "var(--dimensions-16)", + "--spacing-gap-l": "var(--dimensions-24)", + "--spacing-gap-xl": "var(--dimensions-32)", + "--spacing-gap-xxl": "var(--dimensions-48)", + "--spacing-padding-none": "var(--dimensions-0)", + "--spacing-padding-xxxs": "var(--dimensions-2)", + "--spacing-padding-xxs": "var(--dimensions-4)", + "--spacing-padding-xs": "var(--dimensions-8)", + "--spacing-padding-s": "var(--dimensions-12)", + "--spacing-padding-m": "var(--dimensions-16)", + "--spacing-padding-ml": "var(--dimensions-20)", + "--spacing-padding-l": "var(--dimensions-24)", + "--spacing-padding-xl": "var(--dimensions-32)", + "--spacing-padding-xxl": "var(--dimensions-40)", + "--typography-body-xs": "var(--font-size-12)", + "--typography-body-s": "var(--font-size-14)", + "--typography-body-m": "var(--font-size-16)", + "--typography-body-l": "var(--font-size-18)", + "--typography-body-xl": "var(--font-size-20)", + "--typography-body-xxl": "var(--font-size-24)", + "--typography-body-regular": "var(--font-weight-regular)", + "--typography-heading-xs": "var(--font-size-12)", + "--typography-heading-s": "var(--font-size-16)", + "--typography-heading-m": "var(--font-size-20)", + "--typography-heading-l": "var(--font-size-24)", + "--typography-heading-xl": "var(--font-size-32)", + "--typography-heading-xxl": "var(--font-size-40)", + "--typography-heading-light": "var(--font-weight-light)", + "--typography-heading-regular": "var(--font-weight-regular)", + "--typography-heading-semibold": "var(--font-weight-semibold)", + "--typography-helper-text-s": "var(--font-size-12)", + "--typography-helper-text-m": "var(--font-size-14)", + "--typography-helper-text-l": "var(--font-size-16)", + "--typography-helper-text-light": "var(--font-weight-light)", + "--typography-helper-text-regular": "var(--font-weight-regular)", + "--typography-helper-text-semibold": "var(--font-weight-semibold)", + "--typography-label-s": "var(--font-size-12)", + "--typography-label-m": "var(--font-size-14)", + "--typography-label-l": "var(--font-size-16)", + "--typography-label-xl": "var(--font-size-20)", + "--typography-label-regular": "var(--font-weight-regular)", + "--typography-label-semibold": "var(--font-weight-semibold)", + "--typography-link-m": "var(--font-size-14)", + "--typography-link-regular": "var(--font-weight-regular)", + "--typography-title-s": "var(--font-size-14)", + "--typography-title-m": "var(--font-size-16)", + "--typography-title-l": "var(--font-size-20)", + "--typography-title-xl": "var(--font-size-24)", + "--typography-title-bold": "var(--font-weight-bold)", + "--border-style-default": "var(--line-style-solid)", + "--border-style-outline": "var(--line-style-dashed)", + "--typography-font-family": "var(--font-family-sans)", + "--typography-helper-text-italic": "var(--font-style-lightitalic)", +}; diff --git a/packages/lib/src/styles/variables.css b/packages/lib/src/styles/variables.css index 685c6887e2..d88b33dc9f 100644 --- a/packages/lib/src/styles/variables.css +++ b/packages/lib/src/styles/variables.css @@ -12,15 +12,15 @@ --z-header-menu: 210; /* UI components */ - --z-dialog: 300; - --z-date-input: 310; - --z-dropdown: 320; - --z-textinput: 330; - --z-select: 340; + --z-date-input: 300; + --z-dropdown: 310; + --z-textinput: 320; + --z-select: 330; /* Modals and overlays */ - --z-spinner-overlay: 400; - --z-progressbar-overlay: 410; + --z-dialog: 400; + --z-spinner-overlay: 410; + --z-progressbar-overlay: 420; --z-alert: 430; /* Notifications */ @@ -45,76 +45,76 @@ --color-alpha-700-a: #5b5b5bb2; --color-alpha-800-a: #494949cc; --color-alpha-900-a: #333333e5; - --color-blue-50: #f8fcff; - --color-blue-100: #d9efff; - --color-blue-200: #bce3ff; - --color-blue-300: #a0d7ff; - --color-blue-400: #61bdff; - --color-blue-500: #30a1f1; - --color-blue-600: #267fbf; - --color-blue-700: #1d6091; - --color-blue-800: #174e74; - --color-blue-900: #103651; - --color-green-50: #f3fcf5; - --color-green-100: #d1f5db; - --color-green-200: #acecbe; - --color-green-300: #87e3a0; - --color-green-400: #53cb75; - --color-green-500: #47ae64; - --color-green-600: #39884f; - --color-green-700: #2a673b; - --color-green-800: #225230; - --color-green-900: #183921; - --color-grey-50: #fbfbfb; - --color-grey-100: #ebebeb; - --color-grey-200: #dedede; - --color-grey-300: #d1d1d1; - --color-grey-400: #b5b5b5; - --color-grey-500: #9a9a9a; - --color-grey-600: #797979; - --color-grey-700: #5b5b5b; - --color-grey-800: #494949; - --color-grey-900: #333333; - --color-orange-50: #fffbf6; - --color-orange-100: #fde9d2; - --color-orange-200: #fbd9b3; - --color-orange-300: #f9c78f; - --color-orange-400: #f5a344; - --color-orange-500: #d58a35; - --color-orange-600: #a76d2b; - --color-orange-700: #7f5121; - --color-orange-800: #66421a; - --color-orange-900: #3d3309; - --color-purple-50: #fcfbfe; - --color-purple-100: #f0e8fa; - --color-purple-200: #e7d9f6; - --color-purple-300: #ddc9f3; - --color-purple-400: #c8a7eb; - --color-purple-500: #b487e4; - --color-purple-600: #9363c8; - --color-purple-700: #6f4b97; - --color-purple-800: #5a3c7a; - --color-purple-900: #3e2a55; - --color-red-50: #fff7f6; - --color-red-100: #ffe6e4; - --color-red-200: #ffd3d0; - --color-red-300: #ffc1bd; - --color-red-400: #ff9896; - --color-red-500: #ff696f; - --color-red-600: #e33248; - --color-red-700: #a92c37; - --color-red-800: #87262d; - --color-red-900: #5b1f21; - --color-yellow-50: #fefbef; - --color-yellow-100: #fcedae; - --color-yellow-200: #f9de6e; - --color-yellow-300: #f5cd2b; - --color-yellow-400: #d4b226; - --color-yellow-500: #b6981f; - --color-yellow-600: #8f7818; - --color-yellow-700: #6c5a12; - --color-yellow-800: #57490f; - --color-yellow-900: #3d3309; + --color-primary-50: #fcfbfe; + --color-primary-100: #f0e8fa; + --color-primary-200: #e7d9f6; + --color-primary-300: #ddc9f3; + --color-primary-400: #c8a7eb; + --color-primary-500: #b487e4; + --color-primary-600: #9363c8; + --color-primary-700: #6f4b97; + --color-primary-800: #5a3c7a; + --color-primary-900: #3e2a55; + --color-secondary-50: #f8fcff; + --color-secondary-100: #d9efff; + --color-secondary-200: #bce3ff; + --color-secondary-300: #a0d7ff; + --color-secondary-400: #61bdff; + --color-secondary-500: #30a1f1; + --color-secondary-600: #267fbf; + --color-secondary-700: #1d6091; + --color-secondary-800: #174e74; + --color-secondary-900: #103651; + --color-tertiary-50: #fefbef; + --color-tertiary-100: #fcedae; + --color-tertiary-200: #f9de6e; + --color-tertiary-300: #f5cd2b; + --color-tertiary-400: #d4b226; + --color-tertiary-500: #b6981f; + --color-tertiary-600: #8f7818; + --color-tertiary-700: #6c5a12; + --color-tertiary-800: #57490f; + --color-tertiary-900: #3d3309; + --color-success-50: #f3fcf5; + --color-success-100: #d1f5db; + --color-success-200: #acecbe; + --color-success-300: #87e3a0; + --color-success-400: #53cb75; + --color-success-500: #47ae64; + --color-success-600: #39884f; + --color-success-700: #2a673b; + --color-success-800: #225230; + --color-success-900: #183921; + --color-warning-50: #fffbf6; + --color-warning-100: #fde9d2; + --color-warning-200: #fbd9b3; + --color-warning-300: #f9c78f; + --color-warning-400: #f5a344; + --color-warning-500: #d58a35; + --color-warning-600: #a76d2b; + --color-warning-700: #7f5121; + --color-warning-800: #66421a; + --color-warning-900: #3d3309; + --color-error-50: #fff7f6; + --color-error-100: #ffe6e4; + --color-error-200: #ffd3d0; + --color-error-300: #ffc1bd; + --color-error-400: #ff9896; + --color-error-500: #ff696f; + --color-error-600: #e33248; + --color-error-700: #a92c37; + --color-error-800: #87262d; + --color-error-900: #5b1f21; + --color-neutral-50: #fbfbfb; + --color-neutral-100: #ebebeb; + --color-neutral-200: #dedede; + --color-neutral-300: #d1d1d1; + --color-neutral-400: #b5b5b5; + --color-neutral-500: #9a9a9a; + --color-neutral-600: #797979; + --color-neutral-700: #5b5b5b; + --color-neutral-800: #494949; + --color-neutral-900: #333333; --dimensions-0: 0px; --dimensions-1: 1px; --dimensions-2: 2px; @@ -157,117 +157,117 @@ --line-style-solid: solid; /* Alias tokens */ - --border-color-error-light: var(--color-red-300); - --border-color-error-medium: var(--color-red-600); - --border-color-error-strong: var(--color-red-700); - --border-color-error-stronger: var(--color-red-800); - --border-color-neutral-bright: var(--color-grey-50); + --border-color-error-light: var(--color-error-300); + --border-color-error-medium: var(--color-error-600); + --border-color-error-strong: var(--color-error-700); + --border-color-error-stronger: var(--color-error-800); + --border-color-neutral-bright: var(--color-neutral-50); --border-color-neutral-brighter: var(--color-absolutes-white); - --border-color-neutral-dark: var(--color-grey-900); - --border-color-neutral-light: var(--color-grey-300); - --border-color-neutral-lighter: var(--color-grey-200); - --border-color-neutral-lightest: var(--color-grey-100); - --border-color-neutral-medium: var(--color-grey-400); - --border-color-neutral-strong: var(--color-grey-500); - --border-color-neutral-stronger: var(--color-grey-600); - --border-color-neutral-strongest: var(--color-grey-700); - --border-color-primary-light: var(--color-purple-400); - --border-color-primary-lighter: var(--color-purple-300); - --border-color-primary-medium: var(--color-purple-500); - --border-color-primary-strong: var(--color-purple-600); - --border-color-primary-stronger: var(--color-purple-700); - --border-color-secondary-light: var(--color-blue-300); - --border-color-secondary-medium: var(--color-blue-500); - --border-color-secondary-strong: var(--color-blue-600); - --border-color-secondary-stronger: var(--color-blue-700); - --border-color-secondary-strongest: var(--color-blue-800); - --border-color-success-light: var(--color-green-300); - --border-color-success-medium: var(--color-green-600); - --border-color-warning-light: var(--color-orange-300); - --border-color-warning-medium: var(--color-orange-500); + --border-color-neutral-dark: var(--color-neutral-900); + --border-color-neutral-light: var(--color-neutral-300); + --border-color-neutral-lighter: var(--color-neutral-200); + --border-color-neutral-lightest: var(--color-neutral-100); + --border-color-neutral-medium: var(--color-neutral-400); + --border-color-neutral-strong: var(--color-neutral-500); + --border-color-neutral-stronger: var(--color-neutral-600); + --border-color-neutral-strongest: var(--color-neutral-700); + --border-color-primary-light: var(--color-primary-400); + --border-color-primary-lighter: var(--color-primary-300); + --border-color-primary-medium: var(--color-primary-500); + --border-color-primary-strong: var(--color-primary-600); + --border-color-primary-stronger: var(--color-primary-700); + --border-color-secondary-light: var(--color-secondary-300); + --border-color-secondary-medium: var(--color-secondary-500); + --border-color-secondary-strong: var(--color-secondary-600); + --border-color-secondary-stronger: var(--color-secondary-700); + --border-color-secondary-strongest: var(--color-secondary-800); + --border-color-success-light: var(--color-success-300); + --border-color-success-medium: var(--color-success-600); + --border-color-warning-light: var(--color-warning-300); + --border-color-warning-medium: var(--color-warning-500); --color-bg-overlay-dark: var(--color-alpha-800-a); - --color-bg-yellow-light: var(--color-yellow-100); + --color-bg-yellow-light: var(--color-tertiary-100); --color-bg-alpha-light: var(--color-alpha-300-a); --color-bg-alpha-medium: var(--color-alpha-800-a); --color-bg-alpha-strong: var(--color-alpha-900-a); - --color-bg-error-light: var(--color-red-200); - --color-bg-error-lighter: var(--color-red-100); - --color-bg-error-lightest: var(--color-red-50); - --color-bg-error-medium: var(--color-red-300); - --color-bg-error-strong: var(--color-red-600); - --color-bg-error-stronger: var(--color-red-700); - --color-bg-error-strongest: var(--color-red-800); - --color-bg-neutral-light: var(--color-grey-100); - --color-bg-neutral-lighter: var(--color-grey-50); + --color-bg-error-light: var(--color-error-200); + --color-bg-error-lighter: var(--color-error-100); + --color-bg-error-lightest: var(--color-error-50); + --color-bg-error-medium: var(--color-error-300); + --color-bg-error-strong: var(--color-error-600); + --color-bg-error-stronger: var(--color-error-700); + --color-bg-error-strongest: var(--color-error-800); + --color-bg-neutral-light: var(--color-neutral-100); + --color-bg-neutral-lighter: var(--color-neutral-50); --color-bg-neutral-lightest: var(--color-absolutes-white); - --color-bg-neutral-medium: var(--color-grey-200); - --color-bg-neutral-strong: var(--color-grey-300); - --color-bg-neutral-stronger: var(--color-grey-800); - --color-bg-neutral-strongest: var(--color-grey-900); - --color-bg-primary-light: var(--color-purple-200); - --color-bg-primary-lighter: var(--color-purple-100); - --color-bg-primary-lightest: var(--color-purple-50); - --color-bg-primary-medium: var(--color-purple-300); - --color-bg-primary-strong: var(--color-purple-700); - --color-bg-primary-stronger: var(--color-purple-800); - --color-bg-primary-strongest: var(--color-purple-900); - --color-bg-secondary-light: var(--color-blue-200); - --color-bg-secondary-lighter: var(--color-blue-100); - --color-bg-secondary-lightest: var(--color-blue-50); - --color-bg-secondary-medium: var(--color-blue-300); - --color-bg-secondary-strong: var(--color-blue-600); - --color-bg-secondary-stronger: var(--color-blue-700); - --color-bg-secondary-strongest: var(--color-blue-800); - --color-bg-success-light: var(--color-green-200); - --color-bg-success-lighter: var(--color-green-100); - --color-bg-success-lightest: var(--color-green-50); - --color-bg-success-medium: var(--color-green-300); - --color-bg-success-strong: var(--color-green-600); - --color-bg-success-stronger: var(--color-green-700); - --color-bg-success-strongest: var(--color-green-800); - --color-bg-warning-light: var(--color-orange-200); - --color-bg-warning-lighter: var(--color-orange-100); - --color-bg-warning-lightest: var(--color-orange-50); - --color-bg-warning-medium: var(--color-orange-300); - --color-bg-warning-strong: var(--color-orange-500); - --color-bg-warning-stronger: var(--color-orange-600); - --color-bg-warning-strongest: var(--color-orange-700); - --color-fg-error-light: var(--color-red-300); - --color-fg-error-lighter: var(--color-red-200); - --color-fg-error-medium: var(--color-red-600); - --color-fg-error-strong: var(--color-red-700); - --color-fg-error-stronger: var(--color-red-800); + --color-bg-neutral-medium: var(--color-neutral-200); + --color-bg-neutral-strong: var(--color-neutral-300); + --color-bg-neutral-stronger: var(--color-neutral-800); + --color-bg-neutral-strongest: var(--color-neutral-900); + --color-bg-primary-light: var(--color-primary-200); + --color-bg-primary-lighter: var(--color-primary-100); + --color-bg-primary-lightest: var(--color-primary-50); + --color-bg-primary-medium: var(--color-primary-300); + --color-bg-primary-strong: var(--color-primary-700); + --color-bg-primary-stronger: var(--color-primary-800); + --color-bg-primary-strongest: var(--color-primary-900); + --color-bg-secondary-light: var(--color-secondary-200); + --color-bg-secondary-lighter: var(--color-secondary-100); + --color-bg-secondary-lightest: var(--color-secondary-50); + --color-bg-secondary-medium: var(--color-secondary-300); + --color-bg-secondary-strong: var(--color-secondary-600); + --color-bg-secondary-stronger: var(--color-secondary-700); + --color-bg-secondary-strongest: var(--color-secondary-800); + --color-bg-success-light: var(--color-success-200); + --color-bg-success-lighter: var(--color-success-100); + --color-bg-success-lightest: var(--color-success-50); + --color-bg-success-medium: var(--color-success-300); + --color-bg-success-strong: var(--color-success-600); + --color-bg-success-stronger: var(--color-success-700); + --color-bg-success-strongest: var(--color-success-800); + --color-bg-warning-light: var(--color-warning-200); + --color-bg-warning-lighter: var(--color-warning-100); + --color-bg-warning-lightest: var(--color-warning-50); + --color-bg-warning-medium: var(--color-warning-300); + --color-bg-warning-strong: var(--color-warning-500); + --color-bg-warning-stronger: var(--color-warning-600); + --color-bg-warning-strongest: var(--color-warning-700); + --color-fg-error-light: var(--color-error-300); + --color-fg-error-lighter: var(--color-error-200); + --color-fg-error-medium: var(--color-error-600); + --color-fg-error-strong: var(--color-error-700); + --color-fg-error-stronger: var(--color-error-800); --color-fg-neutral-bright: var(--color-absolutes-white); - --color-fg-neutral-dark: var(--color-grey-900); - --color-fg-neutral-light: var(--color-grey-400); - --color-fg-neutral-lighter: var(--color-grey-200); - --color-fg-neutral-lightest: var(--color-grey-100); - --color-fg-neutral-medium: var(--color-grey-500); - --color-fg-neutral-strong: var(--color-grey-600); - --color-fg-neutral-stronger: var(--color-grey-700); - --color-fg-neutral-strongest: var(--color-grey-800); - --color-fg-neutral-yellow-dark: var(--color-yellow-800); - --color-fg-primary-light: var(--color-purple-300); - --color-fg-primary-lighter: var(--color-purple-100); - --color-fg-primary-medium: var(--color-purple-400); - --color-fg-primary-strong: var(--color-purple-700); - --color-fg-primary-stronger: var(--color-purple-800); - --color-fg-primary-strongest: var(--color-purple-900); - --color-fg-secondary-light: var(--color-blue-500); - --color-fg-secondary-lighter: var(--color-blue-300); - --color-fg-secondary-medium: var(--color-blue-600); - --color-fg-secondary-strong: var(--color-blue-700); - --color-fg-secondary-stronger: var(--color-blue-800); - --color-fg-secondary-strongest: var(--color-blue-900); - --color-fg-success-light: var(--color-green-300); - --color-fg-success-lighter: var(--color-green-200); - --color-fg-success-medium: var(--color-green-600); - --color-fg-success-strong: var(--color-green-700); - --color-fg-success-stronger: var(--color-green-800); - --color-fg-warning-light: var(--color-orange-300); - --color-fg-warning-medium: var(--color-orange-500); - --color-fg-warning-strong: var(--color-orange-600); - --color-fg-warning-stronger: var(--color-orange-800); + --color-fg-neutral-dark: var(--color-neutral-900); + --color-fg-neutral-light: var(--color-neutral-400); + --color-fg-neutral-lighter: var(--color-neutral-200); + --color-fg-neutral-lightest: var(--color-neutral-100); + --color-fg-neutral-medium: var(--color-neutral-500); + --color-fg-neutral-strong: var(--color-neutral-600); + --color-fg-neutral-stronger: var(--color-neutral-700); + --color-fg-neutral-strongest: var(--color-neutral-800); + --color-fg-neutral-yellow-dark: var(--color-tertiary-800); + --color-fg-primary-light: var(--color-primary-300); + --color-fg-primary-lighter: var(--color-primary-100); + --color-fg-primary-medium: var(--color-primary-400); + --color-fg-primary-strong: var(--color-primary-700); + --color-fg-primary-stronger: var(--color-primary-800); + --color-fg-primary-strongest: var(--color-primary-900); + --color-fg-secondary-light: var(--color-secondary-500); + --color-fg-secondary-lighter: var(--color-secondary-300); + --color-fg-secondary-medium: var(--color-secondary-600); + --color-fg-secondary-strong: var(--color-secondary-700); + --color-fg-secondary-stronger: var(--color-secondary-800); + --color-fg-secondary-strongest: var(--color-secondary-900); + --color-fg-success-light: var(--color-success-300); + --color-fg-success-lighter: var(--color-success-200); + --color-fg-success-medium: var(--color-success-600); + --color-fg-success-strong: var(--color-success-700); + --color-fg-success-stronger: var(--color-success-800); + --color-fg-warning-light: var(--color-warning-300); + --color-fg-warning-medium: var(--color-warning-500); + --color-fg-warning-strong: var(--color-warning-600); + --color-fg-warning-stronger: var(--color-warning-800); --border-radius-none: var(--dimensions-0); --border-radius-xs: var(--dimensions-2); --border-radius-s: var(--dimensions-4); diff --git a/packages/lib/src/table/Table.stories.tsx b/packages/lib/src/table/Table.stories.tsx index fc7fdf0181..d3bccfdba7 100644 --- a/packages/lib/src/table/Table.stories.tsx +++ b/packages/lib/src/table/Table.stories.tsx @@ -565,7 +565,7 @@ const Table = () => ( ); const ActionsCellDropdown = () => ( - <ExampleContainer> + <ExampleContainer expanded> <Title title="Dropdown Action" theme="light" level={4} /> <DxcTable> <tr> diff --git a/packages/lib/src/text-input/TextInput.stories.tsx b/packages/lib/src/text-input/TextInput.stories.tsx index 88c61e4d0c..ba2d9f3758 100644 --- a/packages/lib/src/text-input/TextInput.stories.tsx +++ b/packages/lib/src/text-input/TextInput.stories.tsx @@ -1,14 +1,29 @@ import { Meta, StoryObj } from "@storybook/react"; import { userEvent, within } from "@storybook/test"; +import disabledRules from "../../test/accessibility/rules/specific/table/disabledRules"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import DxcFlex from "../flex/Flex"; import Suggestions from "./Suggestions"; import DxcTextInput from "./TextInput"; +import preview from "../../.storybook/preview"; export default { title: "Text Input", component: DxcTextInput, + parameters: { + a11y: { + config: { + rules: [ + ...disabledRules.map((ruleId) => ({ + id: ruleId, + reviewOnFail: true, + })), + ...(preview?.parameters?.a11y?.config?.rules || []), + ], + }, + }, + }, } as Meta<typeof DxcTextInput>; const action = { diff --git a/packages/lib/src/text-input/TextInput.tsx b/packages/lib/src/text-input/TextInput.tsx index 579f765957..2e2a5d2c9a 100644 --- a/packages/lib/src/text-input/TextInput.tsx +++ b/packages/lib/src/text-input/TextInput.tsx @@ -165,7 +165,7 @@ const DxcTextInput = forwardRef<RefType, TextInputPropsType>( > {children} </Popover.Trigger> - <Popover.Portal> + <Popover.Portal container={document.getElementById(`${inputId}-portal`)}> <Popover.Content aria-label="Suggestions" onCloseAutoFocus={(event) => { @@ -483,118 +483,121 @@ const DxcTextInput = forwardRef<RefType, TextInputPropsType>( }, [value, innerValue, suggestions, numberInputContext]); return ( - <TextInputContainer margin={margin} ref={ref} size={size}> - {label && ( - <Label disabled={disabled} hasMargin={!helperText} htmlFor={inputId}> - {label} {optional && <span>{translatedLabels.formFields.optionalLabel}</span>} - </Label> - )} - {helperText && ( - <HelperText disabled={disabled} hasMargin> - {helperText} - </HelperText> - )} - <AutosuggestWrapper condition={hasSuggestions(suggestions)} wrapper={autosuggestWrapperFunction}> - <TextInput - disabled={disabled} - error={!!error} - onClick={handleInputContainerOnClick} - onMouseDown={handleInputContainerOnMouseDown} - readOnly={readOnly} - ref={inputContainerRef} - > - {prefix && ( - <Addon disabled={disabled} type="prefix"> - {prefix} - </Addon> - )} - <Input - alignment={alignment} - aria-activedescendant={ - hasSuggestions(suggestions) && isOpen && visualFocusIndex !== -1 - ? `suggestion-${visualFocusIndex}` - : undefined - } - aria-autocomplete={hasSuggestions(suggestions) ? "list" : undefined} - aria-controls={hasSuggestions(suggestions) ? autosuggestId : undefined} - aria-errormessage={error ? errorId : undefined} - aria-expanded={hasSuggestions(suggestions) ? isOpen : undefined} - aria-haspopup={hasSuggestions(suggestions) ? "listbox" : undefined} - aria-invalid={!!error} - aria-label={label ? undefined : ariaLabel} - aria-required={!disabled && !optional} - autoComplete={autocomplete === "off" ? "nope" : autocomplete} + <> + <TextInputContainer margin={margin} ref={ref} size={size}> + {label && ( + <Label disabled={disabled} hasMargin={!helperText} htmlFor={inputId}> + {label} {optional && <span>{translatedLabels.formFields.optionalLabel}</span>} + </Label> + )} + {helperText && ( + <HelperText disabled={disabled} hasMargin> + {helperText} + </HelperText> + )} + <AutosuggestWrapper condition={hasSuggestions(suggestions)} wrapper={autosuggestWrapperFunction}> + <TextInput disabled={disabled} - id={inputId} - name={name} - onBlur={handleInputOnBlur} - onChange={handleInputOnChange} - onFocus={!readOnly ? openSuggestions : undefined} - onKeyDown={!readOnly ? handleInputOnKeyDown : undefined} - onMouseDown={(event) => { - event.stopPropagation(); - }} - onWheel={numberInputContext?.typeNumber === "number" ? handleNumberInputWheel : undefined} - placeholder={placeholder} - pattern={pattern} + error={!!error} + onClick={handleInputContainerOnClick} + onMouseDown={handleInputContainerOnMouseDown} readOnly={readOnly} - ref={inputRef} - role={hasSuggestions(suggestions) ? "combobox" : undefined} - maxLength={maxLength} - minLength={minLength} - tabIndex={tabIndex} - type="text" - value={value ?? innerValue} - /> - <DxcFlex> - {!disabled && !readOnly && clearable && (value ?? innerValue).length > 0 && ( - <DxcActionIcon - icon="close" - onClick={handleClearActionOnClick} - tabIndex={tabIndex} - title={translatedLabels.textInput.clearFieldActionTitle} - /> + ref={inputContainerRef} + > + {prefix && ( + <Addon disabled={disabled} type="prefix"> + {prefix} + </Addon> )} - {numberInputContext?.typeNumber === "number" && numberInputContext?.showControls && ( - <> + <Input + alignment={alignment} + aria-activedescendant={ + hasSuggestions(suggestions) && isOpen && visualFocusIndex !== -1 + ? `suggestion-${visualFocusIndex}` + : undefined + } + aria-autocomplete={hasSuggestions(suggestions) ? "list" : undefined} + aria-controls={hasSuggestions(suggestions) ? autosuggestId : undefined} + aria-errormessage={error ? errorId : undefined} + aria-expanded={hasSuggestions(suggestions) ? isOpen : undefined} + aria-haspopup={hasSuggestions(suggestions) ? "listbox" : undefined} + aria-invalid={!!error} + aria-label={label ? undefined : ariaLabel} + aria-required={!disabled && !optional} + autoComplete={autocomplete === "off" ? "nope" : autocomplete} + disabled={disabled} + id={inputId} + name={name} + onBlur={handleInputOnBlur} + onChange={handleInputOnChange} + onFocus={!readOnly ? openSuggestions : undefined} + onKeyDown={!readOnly ? handleInputOnKeyDown : undefined} + onMouseDown={(event) => { + event.stopPropagation(); + }} + onWheel={numberInputContext?.typeNumber === "number" ? handleNumberInputWheel : undefined} + placeholder={placeholder} + pattern={pattern} + readOnly={readOnly} + ref={inputRef} + role={hasSuggestions(suggestions) ? "combobox" : undefined} + maxLength={maxLength} + minLength={minLength} + tabIndex={tabIndex} + type="text" + value={value ?? innerValue} + /> + <DxcFlex> + {!disabled && !readOnly && clearable && (value ?? innerValue).length > 0 && ( <DxcActionIcon - disabled={disabled} - icon="remove" - onClick={!readOnly ? handleDecrementActionOnClick : undefined} - ref={actionRef} + icon="close" + onClick={handleClearActionOnClick} tabIndex={tabIndex} - title={translatedLabels.numberInput.decrementValueTitle} + title={translatedLabels.textInput.clearFieldActionTitle} /> + )} + {numberInputContext?.typeNumber === "number" && numberInputContext?.showControls && ( + <> + <DxcActionIcon + disabled={disabled} + icon="remove" + onClick={!readOnly ? handleDecrementActionOnClick : undefined} + ref={actionRef} + tabIndex={tabIndex} + title={translatedLabels.numberInput.decrementValueTitle} + /> + <DxcActionIcon + disabled={disabled} + icon="add" + onClick={!readOnly ? handleIncrementActionOnClick : undefined} + ref={actionRef} + tabIndex={tabIndex} + title={translatedLabels.numberInput.incrementValueTitle} + /> + </> + )} + {action && ( <DxcActionIcon disabled={disabled} - icon="add" - onClick={!readOnly ? handleIncrementActionOnClick : undefined} + icon={action.icon} + onClick={!readOnly ? action.onClick : undefined} ref={actionRef} tabIndex={tabIndex} - title={translatedLabels.numberInput.incrementValueTitle} + title={action.title ?? ""} /> - </> - )} - {action && ( - <DxcActionIcon - disabled={disabled} - icon={action.icon} - onClick={!readOnly ? action.onClick : undefined} - ref={actionRef} - tabIndex={tabIndex} - title={action.title ?? ""} - /> + )} + </DxcFlex> + {suffix && ( + <Addon disabled={disabled} type="suffix"> + {suffix} + </Addon> )} - </DxcFlex> - {suffix && ( - <Addon disabled={disabled} type="suffix"> - {suffix} - </Addon> - )} - </TextInput> - </AutosuggestWrapper> - {!disabled && typeof error === "string" && <ErrorMessage error={error} id={errorId} />} - </TextInputContainer> + </TextInput> + </AutosuggestWrapper> + {!disabled && typeof error === "string" && <ErrorMessage error={error} id={errorId} />} + </TextInputContainer> + <div id={`${inputId}-portal`} style={{ position: "absolute" }} /> + </> ); } ); diff --git a/packages/lib/src/toast/ToastsQueue.tsx b/packages/lib/src/toast/ToastsQueue.tsx index 859cfe5486..be3337ded5 100644 --- a/packages/lib/src/toast/ToastsQueue.tsx +++ b/packages/lib/src/toast/ToastsQueue.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useMemo, useState } from "react"; +import { useCallback, useEffect, useId, useMemo, useState } from "react"; import { createPortal } from "react-dom"; import styled from "@emotion/styled"; import DxcToast from "./Toast"; @@ -29,6 +29,7 @@ export default function DxcToastsQueue({ children, duration = 3000 }: ToastsQueu const [toasts, setToasts] = useState<QueuedToast[]>([]); const [isMounted, setIsMounted] = useState(false); // Next.js SSR mounting issue const adjustedDuration = useMemo(() => (duration > 5000 ? 5000 : duration < 3000 ? 3000 : duration), [duration]); + const id = useId(); const remove = useCallback((id: string) => { setToasts((prevToasts) => prevToasts.filter((toast) => toast.id !== id)); @@ -49,6 +50,7 @@ export default function DxcToastsQueue({ children, duration = 3000 }: ToastsQueu return ( <ToastContext.Provider value={add}> + <div id={`toasts-${id}-portal`} style={{ position: "absolute" }} /> {isMounted && createPortal( <ToastsQueue> @@ -63,7 +65,7 @@ export default function DxcToastsQueue({ children, duration = 3000 }: ToastsQueu /> ))} </ToastsQueue>, - document.body + document.getElementById(`toasts-${id}-portal`) || document.body )} {children} </ToastContext.Provider> diff --git a/packages/lib/test/accessibility/rules/specific/date-input/disabledRules.ts b/packages/lib/test/accessibility/rules/specific/date-input/disabledRules.ts index 728ef130ba..86658b004e 100644 --- a/packages/lib/test/accessibility/rules/specific/date-input/disabledRules.ts +++ b/packages/lib/test/accessibility/rules/specific/date-input/disabledRules.ts @@ -3,9 +3,11 @@ * */ const disabledRules = [ - // TODO: Remove when the false positive is fixed + // TODO: Remove when the false positives are fixed // Disable aria allowed rule to prevent false positive from gridcell role not being allowed in buttons "aria-allowed-role", + // Disable aria dialog name rule to prevent false positive from dialog role not having an accessible name + "aria-dialog-name", ]; export default disabledRules; diff --git a/packages/lib/test/accessibility/rules/specific/dialog/disabledRules.ts b/packages/lib/test/accessibility/rules/specific/dialog/disabledRules.ts new file mode 100644 index 0000000000..da2567170d --- /dev/null +++ b/packages/lib/test/accessibility/rules/specific/dialog/disabledRules.ts @@ -0,0 +1,11 @@ +/** + * Array of accessibility rule IDs to be disabled in both Jest and Storybook for the dialog component. + * + */ +const disabledRules = [ + // TODO: Remove when the false positives are fixed + // Disable aria dialog name rule to prevent false positive from dialog role not having an accessible name + "aria-dialog-name", +]; + +export default disabledRules; diff --git a/packages/lib/test/accessibility/rules/specific/dropdown/disabledRules.ts b/packages/lib/test/accessibility/rules/specific/dropdown/disabledRules.ts new file mode 100644 index 0000000000..c97f03bed5 --- /dev/null +++ b/packages/lib/test/accessibility/rules/specific/dropdown/disabledRules.ts @@ -0,0 +1,11 @@ +/** + * Array of accessibility rule IDs to be disabled in both Jest and Storybook for the dropdown component. + * + */ +const disabledRules = [ + // TODO: Find a better solution + // Disable scrollable region focusable rule to prevent errors from having scrollable dropdowns with no focusable elements + "scrollable-region-focusable", +]; + +export default disabledRules; diff --git a/packages/lib/test/accessibility/rules/specific/select/disabledRules.ts b/packages/lib/test/accessibility/rules/specific/select/disabledRules.ts index 42852ed8a3..df8d72fd0b 100644 --- a/packages/lib/test/accessibility/rules/specific/select/disabledRules.ts +++ b/packages/lib/test/accessibility/rules/specific/select/disabledRules.ts @@ -6,6 +6,7 @@ const disabledRules = [ // TODO: Work on nested interaction with the DxcCheckbox component to prevent these issues "nested-interactive", "scrollable-region-focusable", + "aria-required-children", ]; export default disabledRules; diff --git a/packages/lib/test/accessibility/rules/specific/text-input/disabledRules.ts b/packages/lib/test/accessibility/rules/specific/text-input/disabledRules.ts new file mode 100644 index 0000000000..23f31cf804 --- /dev/null +++ b/packages/lib/test/accessibility/rules/specific/text-input/disabledRules.ts @@ -0,0 +1,11 @@ +/** + * Array of accessibility rule IDs to be disabled in both Jest and Storybook for the text input component. + * + */ +const disabledRules = [ + // TODO: Find a better solution + // Disable scrollable region focusable rule to prevent errors from having scrollable suggestions with no focusable elements + "scrollable-region-focusable", +]; + +export default disabledRules;