Files
web/apps/scandic-web/components/TempDesignSystem/DeprecatedSelect/index.tsx
Anton Gunnarsson 75a377b59e Merged in chore/sw-3145-move-body (pull request #2505)
chore(SW-3145): Move Body component to design-system

* Move Body component to design-system


Approved-by: Joakim Jäderberg
2025-07-03 08:04:36 +00:00

141 lines
3.9 KiB
TypeScript

"use client"
import { useState } from "react"
import {
Button,
type Key,
ListBox,
ListBoxItem,
Popover,
Select as ReactAriaSelect,
SelectValue,
} from "react-aria-components"
import Body from "@scandic-hotels/design-system/Body"
import { Label } from "@scandic-hotels/design-system/Label"
import useSetOverflowVisibleOnRA from "@/hooks/useSetOverflowVisibleOnRA"
import SelectChevron from "../Form/SelectChevron"
import styles from "./select.module.css"
import type {
SelectPortalContainer,
SelectPortalContainerArgs,
SelectProps,
} from "./select"
export default function Select({
className = "",
"aria-label": ariaLabel,
defaultSelectedKey,
items,
label,
name,
onSelect,
disabled,
required = false,
tabIndex,
value,
maxHeight,
showRadioButton = false,
discreet = false,
isNestedInModal = false,
optionsIcon,
}: SelectProps) {
const [rootDiv, setRootDiv] = useState<SelectPortalContainer>(undefined)
const setOverflowVisible = useSetOverflowVisibleOnRA(isNestedInModal)
function setRef(node: SelectPortalContainerArgs) {
if (node) {
setRootDiv(node)
}
}
function handleOnSelect(key: Key) {
onSelect(key)
}
let chevronProps = {}
if (discreet) {
chevronProps = { color: "baseButtonTextOnFillNormal" }
} else if (disabled) {
chevronProps = { color: "disabled" }
}
return (
<div className={`${styles.container} ${className}`} ref={setRef}>
<ReactAriaSelect
aria-label={ariaLabel}
className={`${styles.select} ${discreet ? styles.discreet : ""} select-container`}
defaultSelectedKey={defaultSelectedKey}
name={name}
onSelectionChange={handleOnSelect}
selectedKey={value as Key}
onOpenChange={setOverflowVisible}
isDisabled={disabled}
>
<Body asChild fontOnly>
<Button
className={`${styles.input} select-button`}
data-testid={name}
>
<SelectValue tabIndex={tabIndex}>
{({ selectedText }) => (
<>
<Label
required={required}
size={discreet ? "discreet" : "regular"}
>
{label}
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
{discreet && `:`}
</Label>
{selectedText && (
<Body className={optionsIcon ? styles.iconLabel : ""}>
{optionsIcon ? optionsIcon : null}
{selectedText}
</Body>
)}
</>
)}
</SelectValue>
<SelectChevron {...chevronProps} />
</Button>
</Body>
<Body asChild fontOnly>
<Popover
className={styles.popover}
placement="bottom"
shouldFlip={false}
/**
* react-aria uses portals to render Popover in body
* unless otherwise specified. We need it to be contained
* by this component to both access css variables assigned
* on the container as well as to not overflow it at any time.
*/
UNSTABLE_portalContainer={rootDiv}
maxHeight={maxHeight}
>
<ListBox className={styles.listBox}>
{items.map((item) => (
<ListBoxItem
aria-label={item.label}
className={`${styles.listBoxItem} ${showRadioButton && styles.showRadioButton} ${optionsIcon && styles.iconLabel}`}
id={item.value}
key={`${item.value}_${item.label}`}
data-testid={item.label}
>
{optionsIcon ? optionsIcon : null}
{item.label}
</ListBoxItem>
))}
</ListBox>
</Popover>
</Body>
</ReactAriaSelect>
</div>
)
}