diff --git a/.yarn/versions/a54ad5a9.yml b/.yarn/versions/a54ad5a9.yml
new file mode 100644
index 000000000..29e8a6728
--- /dev/null
+++ b/.yarn/versions/a54ad5a9.yml
@@ -0,0 +1,6 @@
+releases:
+ "@radix-ui/react-scroll-area": minor
+
+declined:
+ - primitives
+ - ssr-testing
diff --git a/packages/react/scroll-area/src/ScrollArea.stories.tsx b/packages/react/scroll-area/src/ScrollArea.stories.tsx
index 3f4b0bea3..0fc1311af 100644
--- a/packages/react/scroll-area/src/ScrollArea.stories.tsx
+++ b/packages/react/scroll-area/src/ScrollArea.stories.tsx
@@ -307,6 +307,94 @@ export const Chromatic = () => (
);
Chromatic.parameters = { chromatic: { disable: false } };
+export const ChromaticEllipsis = () => (
+ <>
+
Ellipsis at viewport width
+
+ {Array.from({ length: 10 }).map((_, index) => (
+
+ ))}
+
+
+ Ellipsis at content width
+
+ {Array.from({ length: 10 }).map((_, index) => (
+
+ ))}
+
+ >
+);
+ChromaticEllipsis.parameters = { chromatic: { disable: false } };
+
+const COPY_SHORT = `
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sit amet eros iaculis,
+ bibendum tellus ac, lobortis odio. Aliquam bibendum elit est, in iaculis est commodo id.
+ Donec pulvinar est libero. Proin consectetur pellentesque molestie.
+`;
+
+export const ChromaticFillParentHeight = () => (
+ <>
+ Parent has fixed height, short content
+
+
+ {COPY_SHORT}
+
+
+ {COPY_SHORT}
+
+
+
+ Parent has fixed height, tall content
+
+
+ {COPY_SHORT}
+
+
+
+
+
+
+ Parent has max height
+
+
+ {COPY_SHORT}
+
+
+
+
+
+
+ Parent has auto height
+
+
+ {COPY_SHORT}
+
+
+
+
+
+
+
+ >
+);
+ChromaticFillParentHeight.parameters = { chromatic: { disable: false } };
+
const DYNAMIC_CONTENT_DELAY = 2000;
export const ChromaticDynamicContentBeforeLoaded = () => {
diff --git a/packages/react/scroll-area/src/ScrollArea.tsx b/packages/react/scroll-area/src/ScrollArea.tsx
index 051379375..19db52f62 100644
--- a/packages/react/scroll-area/src/ScrollArea.tsx
+++ b/packages/react/scroll-area/src/ScrollArea.tsx
@@ -142,22 +142,39 @@ interface ScrollAreaViewportProps extends PrimitiveDivProps {
const ScrollAreaViewport = React.forwardRef(
(props: ScopedProps, forwardedRef) => {
- const { __scopeScrollArea, children, nonce, ...viewportProps } = props;
+ const { __scopeScrollArea, children, asChild, nonce, ...viewportProps } = props;
const context = useScrollAreaContext(VIEWPORT_NAME, __scopeScrollArea);
const ref = React.useRef(null);
const composedRefs = useComposedRefs(forwardedRef, ref, context.onViewportChange);
return (
<>
- {/* Hide scrollbars cross-browser and enable momentum scroll for touch devices */}
- {/**
- * `display: table` ensures our content div will match the size of its children in both
- * horizontal and vertical axis so we can determine if scroll width/height changed and
- * recalculate thumb sizes. This doesn't account for children with *percentage*
- * widths that change. We'll wait to see what use-cases consumers come up with there
- * before trying to resolve it.
- */}
-
- {children}
-
+ {getSubtree({ asChild, children }, (children) => (
+
+ {children}
+
+ ))}
>
);
@@ -1010,6 +1033,26 @@ function useResizeObserver(element: HTMLElement | null, onResize: () => void) {
}, [element, handleResize]);
}
+/**
+ * This is a helper function that is used when a component supports `asChild`
+ * using the `Slot` component but its implementation contains nested DOM elements.
+ *
+ * Using it ensures if a consumer uses the `asChild` prop, the elements are in
+ * correct order in the DOM, adopting the intended consumer `children`.
+ */
+function getSubtree(
+ options: { asChild: boolean | undefined; children: React.ReactNode },
+ content: React.ReactNode | ((children: React.ReactNode) => React.ReactNode)
+) {
+ const { asChild, children } = options;
+ if (!asChild) return typeof content === 'function' ? content(children) : content;
+
+ const firstChild = React.Children.only(children) as React.ReactElement;
+ return React.cloneElement(firstChild, {
+ children: typeof content === 'function' ? content(firstChild.props.children) : content,
+ });
+}
+
/* -----------------------------------------------------------------------------------------------*/
const Root = ScrollArea;
diff --git a/ssr-testing/app/scroll-area/page.tsx b/ssr-testing/app/scroll-area/page.tsx
index ff5d77893..9eecc51e0 100644
--- a/ssr-testing/app/scroll-area/page.tsx
+++ b/ssr-testing/app/scroll-area/page.tsx
@@ -9,27 +9,57 @@ import {
export default function Page() {
return (
-
-
-
-
+
+
+
+
+
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
);
}