diff --git a/.changeset/hot-readers-promise.md b/.changeset/hot-readers-promise.md
new file mode 100644
index 00000000000..8416b7f284b
--- /dev/null
+++ b/.changeset/hot-readers-promise.md
@@ -0,0 +1,7 @@
+---
+'@primer/react': minor
+---
+
+Add option to specify custom widths for PageLayout.Pane via the `width` prop.
+
+
diff --git a/src/PageLayout/PageLayout.docs.json b/src/PageLayout/PageLayout.docs.json
index dfa273638c5..14cf798687e 100644
--- a/src/PageLayout/PageLayout.docs.json
+++ b/src/PageLayout/PageLayout.docs.json
@@ -148,9 +148,9 @@
},
{
"name": "width",
- "type": "| 'small' | 'medium' | 'large'",
+ "type": "| 'small' | 'medium' | 'large' | { min: string max: string default: string }",
"defaultValue": "'medium'",
- "description": "The width of the pane."
+ "description": "The width of the pane. If using custom widths, provide an object with keys 'min', 'max' and 'default'."
},
{
"name": "minWidth",
diff --git a/src/PageLayout/PageLayout.features.stories.tsx b/src/PageLayout/PageLayout.features.stories.tsx
index 9a168985c56..9f2ceebed5f 100644
--- a/src/PageLayout/PageLayout.features.stories.tsx
+++ b/src/PageLayout/PageLayout.features.stories.tsx
@@ -340,3 +340,20 @@ export const ScrollContainerWithinPageLayoutPane: Story = () => (
)
+
+export const CustomPaneWidths: Story = () => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+)
diff --git a/src/PageLayout/PageLayout.tsx b/src/PageLayout/PageLayout.tsx
index 48533afc22e..71371fc4233 100644
--- a/src/PageLayout/PageLayout.tsx
+++ b/src/PageLayout/PageLayout.tsx
@@ -466,6 +466,25 @@ Content.displayName = 'PageLayout.Content'
// ----------------------------------------------------------------------------
// PageLayout.Pane
+type Measurement = `${number}px`
+
+type CustomWidthOptions = {
+ min: Measurement
+ default: Measurement
+ max: Measurement
+}
+
+type PaneWidth = keyof typeof paneWidths
+
+const isCustomWidthOptions = (width: PaneWidth | CustomWidthOptions): width is CustomWidthOptions => {
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+ return (width as CustomWidthOptions).default !== undefined
+}
+
+const isPaneWidth = (width: PaneWidth | CustomWidthOptions): width is PaneWidth => {
+ return ['small', 'medium', 'large'].includes(width as PaneWidth)
+}
+
export type PageLayoutPaneProps = {
position?: keyof typeof panePositions | ResponsiveValue
/**
@@ -485,7 +504,7 @@ export type PageLayoutPaneProps = {
positionWhenNarrow?: 'inherit' | keyof typeof panePositions
'aria-labelledby'?: string
'aria-label'?: string
- width?: keyof typeof paneWidths
+ width?: PaneWidth | CustomWidthOptions
minWidth?: number
resizable?: boolean
widthStorageKey?: string
@@ -576,9 +595,18 @@ const Pane = React.forwardRef {
+ if (isPaneWidth(width)) {
+ return defaultPaneWidth[width]
+ } else if (isCustomWidthOptions(width)) {
+ return Number(width.default.split('px')[0])
+ }
+ return 0
+ }
+
const [paneWidth, setPaneWidth] = React.useState(() => {
if (!canUseDOM) {
- return defaultPaneWidth[width]
+ return getDefaultPaneWidth(width)
}
let storedWidth
@@ -589,7 +617,7 @@ const Pane = React.forwardRef {
@@ -733,7 +761,7 @@ const Pane = React.forwardRef updatePaneWidth(defaultPaneWidth[width])}
+ onDoubleClick={() => updatePaneWidth(getDefaultPaneWidth(width))}
/>
({
- '--pane-min-width': `${minWidth}px`,
+ '--pane-min-width': isCustomWidthOptions(width) ? width.min : `${minWidth}px`,
'--pane-max-width-diff': '511px',
- '--pane-max-width': `calc(100vw - var(--pane-max-width-diff))`,
+ '--pane-max-width': isCustomWidthOptions(width) ? width.max : `calc(100vw - var(--pane-max-width-diff))`,
width: resizable
? ['100%', null, 'clamp(var(--pane-min-width), var(--pane-width), var(--pane-max-width))']
- : paneWidths[width],
+ : isPaneWidth(width)
+ ? paneWidths[width]
+ : width.default,
padding: SPACING_MAP[padding],
overflow: [null, null, 'auto'],