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'],