Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/components/library/molecules/Dropdown/Dropdown.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@

.text {
flex: 1;
// The trigger is a fixed-height row, so a long selected label has to
// truncate — wrapping would spill out of the bottom of the control.
min-width: 0;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
text-align: left;
}

Expand Down Expand Up @@ -98,6 +104,12 @@
position: fixed;
right: auto;
z-index: 1500;
// `.menu` is `width: 100%`, which under `position: fixed` resolves against
// the viewport. Size to the longest option instead; the inline min-width
// keeps the menu at least as wide as the trigger (compact pill triggers
// would otherwise crush option labels into a one-word-per-line column) and
// the inline max-width keeps it inside the viewport.
width: max-content;
}

.optionWrapper {
Expand Down Expand Up @@ -138,6 +150,14 @@
}
}

// Shelf and library names can be a single long token ("Crime/Detective"), and
// the menu is `overflow: visible` so an unbroken one paints over whatever sits
// behind the dropdown. Break it instead.
.optionLabel {
min-width: 0;
overflow-wrap: anywhere;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overflow-wrap: anywhere is applied to .optionLabel, which is also used inside .subMenu for sub-option labels (Dropdown.tsx ~L210-215). But .subMenu sets white-space: nowrap (line 198), which is inherited by .optionLabel and takes precedence over overflow-wrap — white-space: nowrap suppresses all soft-wrap opportunities, including the emergency breaks overflow-wrap: anywhere would otherwise insert. So a long, unbreakable sub-option label would still overflow the sub-menu rather than wrap.

Not triggered by today's only sub-option consumer (Shelf.tsx's Private/Public privacy options are short), so this won't reproduce right now — but it means the fix is silently inert for sub-menus, which contradicts the stated intent ("Shelf and library names can be a single long token ... Break it instead") since sub-option labels can be arbitrary CMS-driven text too. Worth an explicit white-space: normal; (or removing nowrap from .subMenu and relying on min-width: 100% + overflow-wrap) so this doesn't resurface as a real bug once a long sub-option label ships.

Fix this →

}

.subArrow {
flex-shrink: 0;
transform: rotate(0deg);
Expand Down
18 changes: 15 additions & 3 deletions src/components/library/molecules/Dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,11 @@ export function Dropdown(props: DropdownProps): JSX.Element {
? {
top: menuPos.top,
left: menuPos.left,
width: menuPos.width,
// Never narrower than the trigger, never wider than the room
// left of the viewport's right edge, and capped so a long shelf
// name can't stretch the menu across the whole screen.
minWidth: menuPos.width,
maxWidth: `min(320px, calc(100vw - ${menuPos.left + 8}px))`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two edge cases in this calc(), both low-severity but worth noting:

  1. If the trigger sits close enough to the right edge that 100vw - (menuPos.left + 8) goes negative (e.g. a resize/scroll that leaves less than 8px of room), CSS clamps a negative max-width to 0, which would make the portaled menu effectively invisible rather than just tight against the edge.
  2. minWidth: menuPos.width (line 118) and this maxWidth are independent — per spec, min-width wins over max-width when they conflict. So if the trigger itself is wider than the room available (e.g. a very long selected shelf name has grown the compact pill before the ellipsis truncation kicks in, or the trigger just happens to sit near a narrow viewport edge), the menu can still be forced wider than the viewport allows, reproducing a version of the original overflow bug.

Neither is likely with the current compact "Move To" pill in a centered modal, so this may not be worth guarding for now — flagging in case the portal variant gets reused somewhere with a wider trigger or edge-anchored placement.

transform:
menuPos.placement === 'top' ? 'translateY(-100%)' : undefined,
}
Expand Down Expand Up @@ -167,7 +171,12 @@ export function Dropdown(props: DropdownProps): JSX.Element {
</div>
</>
) : (
<Text variant={TypographyVariant.TextBase}>{option.label}</Text>
<Text
variant={TypographyVariant.TextBase}
className={styles.optionLabel}
>
{option.label}
</Text>
)}
{hasSubOptions ? (
<ArrowIcon
Expand Down Expand Up @@ -198,7 +207,10 @@ export function Dropdown(props: DropdownProps): JSX.Element {
onKeyDown={activateOnKey(() => handleSubSelect(sub.value))}
aria-label={`Select ${sub.label}`}
>
<Text variant={TypographyVariant.TextBase}>
<Text
variant={TypographyVariant.TextBase}
className={styles.optionLabel}
>
{sub.label}
</Text>
{value === sub.value && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,9 @@
// the Shelf settings-trigger override). Compact pill, not a full-width field.
.moveToTrigger {
width: fit-content !important;
// The pill hugs its label, so a long shelf name would push it past the
// modal's edge; cap it and let the trigger's own ellipsis do the rest.
max-width: 100%;
padding: 7px 12px !important;
background: var(--white) !important;
border: 1px solid var(--beige) !important;
Expand Down
Loading