Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 3, 2026

The width prop currently conflates two concepts: named sizes and default constraints. This refactor separates concerns: width defines constraints and defaults, while resizable.width holds the current controlled value.

API Changes

Before (proposed in PR #7348 but never shipped):

// Ambiguous: is width the default or current value?
const [width, setWidth] = useState(350)
<PageLayout.Pane 
  width={width}
  resizable={{persist: setWidth}}
/>

After:

// Clear separation: width = constraints, resizable.width = current
const [currentWidth, setCurrentWidth] = useState(350)
<PageLayout.Pane 
  width={{min: '256px', default: '296px', max: '600px'}}
  resizable={{
    width: currentWidth,  // current controlled value
    persist: setCurrentWidth
  }}
/>

Implementation

  • Types: Extended PersistConfig with optional width?: number for current controlled value
  • Hook logic: Priority order is now resizable.widthlocalStoragewidth.default
  • State sync: When resizable.width changes or is removed, state updates appropriately
  • Backward compatible: Existing code without resizable.width works unchanged
  • Simplified types: Removed number from PaneWidthValue union type (was never shipped, so no deprecation needed)

Changelog

New

  • resizable.width?: number - Optional property to control current pane width

Changed

  • width prop now defines constraints only (named sizes or CustomWidthOptions)
  • usePaneWidth initialization prioritizes resizable.width when provided

Removed

  • number type from PaneWidthValue (was never part of a public release)
  • isNumericWidth helper function (no longer needed)

Rollout strategy

  • Minor release
  • Patch release
  • Major release; if selected, include a written rollout or migration plan
  • None; if selected, include a brief description as to why

Fully backward compatible. Existing APIs continue to work without modification.

Testing & Reviewing

  • Review new tests covering resizable.width initialization, updates, and removal
  • Check stories demonstrating the new controlled width pattern
  • Verify existing examples still work without modification
  • All 90 PageLayout tests pass (73 usePaneWidth + 17 PageLayout)

Merge checklist

Original prompt

Summary

Refactor the PageLayout.Pane API in PR #7348 to separate "current width" from "default width" by moving the current/controlled width value into the resizable configuration object.

Current State

The width prop currently accepts three things:

  1. Named sizes ('small' | 'medium' | 'large')
  2. A number (current pixel width)
  3. CustomWidthOptions with min, default, max

This creates ambiguity - when passing width={350}, it's unclear if that's the default or the current controlled value.

Desired Change

Separate the concerns:

  • width prop - Should define constraints only: default value, min, max
  • resizable config - Should include the current controlled width value

New API Shape

// width = constraints (default, min, max)
<PageLayout.Pane 
  width={{
    default: 'medium',  // or a number like 350
    min: 256,
    max: 600
  }}
  // resizable = current controlled state + persistence
  resizable={{
    width: currentWidth,  // the live/current value
    save: (w) => setCurrentWidth(w)
  }}
/>

// If resizable.width is not provided, use width.default (or the default from named size)
<PageLayout.Pane 
  width="medium"
  resizable={{save: (w) => setCurrentWidth(w)}}
/>

Tasks

  1. Update the resizable type to include an optional width property for the current controlled value
  2. Update the width prop types to focus on constraints (remove number as a direct value, keep it only in CustomWidthOptions.default)
  3. Update usePaneWidth hook to read current width from resizable.width when provided, falling back to the default from width prop
  4. Update all related unit tests to match the new API
  5. Update stories to demonstrate the new API pattern
  6. Update JSDoc/documentation to clarify the separation of concerns

Acceptance Criteria

  • resizable.width is the source of truth for current width when provided
  • width prop defines defaults and constraints only
  • All existing tests updated to pass with new API
  • Clear separation between "current value" and "default/constraints"

This pull request was created from Copilot chat.


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@changeset-bot
Copy link

changeset-bot bot commented Jan 3, 2026

⚠️ No Changeset found

Latest commit: 80df7ec

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copilot AI and others added 3 commits January 3, 2026 18:22
…idth

- Update ResizableConfig type to include optional width property for current controlled value
- Update PaneWidthValue JSDoc to clarify it defines constraints/defaults only
- Update usePaneWidth hook to prioritize resizable.width over default width
- Add logic to sync state when resizable.width changes or is removed
- Add 6 new tests for resizable.width controlled behavior
- Update stories to demonstrate new API pattern with resizable.width
- Add new story showing controlled width with new API

Co-authored-by: mattcosta7 <[email protected]>
- Improve deprecation notice for direct number width values with migration guidance
- Simplify state sync logic by extracting boolean flags for better readability
- Add clearer comments explaining when state updates occur

Co-authored-by: mattcosta7 <[email protected]>
Copilot AI changed the title [WIP] Refactor PageLayout.Pane API to separate width concerns Separate current width from default width in PageLayout.Pane resizable API Jan 3, 2026
Copilot AI requested a review from mattcosta7 January 3, 2026 18:34
@github-actions github-actions bot added the integration-tests: recommended This change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm label Jan 3, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Jan 3, 2026

👋 Hi, this pull request contains changes to the source code that github/github-ui depends on. If you are GitHub staff, test these changes with github/github-ui using the integration workflow. Or, apply the integration-tests: skipped manually label to skip these checks.

* - Number: explicit pixel width (uses `minWidth` prop and viewport-based max)
* - Custom object: `{min: string, default: string, max: string}`
* - Number: explicit pixel width (uses `minWidth` prop and viewport-based max)
* **Deprecated**: Direct number values are deprecated. Use `CustomWidthOptions` with `default` property
Copy link
Contributor

Choose a reason for hiding this comment

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

@copilot

We don't need to deprecate we can just remove these since we never used them right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right! Removed the number type entirely since it was never shipped. This simplifies the API and removes the deprecation notice. The changes include:

  • Removed number from PaneWidthValue union type
  • Removed isNumericWidth helper function
  • Cleaned up JSDoc comments
  • Simplified CSS variable logic in PageLayout.tsx

Commit: 80df7ec

As suggested by @mattcosta7, removing the number type entirely instead of deprecating it since it was never part of a public release. This simplifies the API and removes unnecessary complexity.

- Remove number from PaneWidthValue union type
- Remove isNumericWidth helper function
- Clean up related JSDoc comments
- Simplify CSS variable assignment in PageLayout.tsx

Co-authored-by: mattcosta7 <[email protected]>
Copilot AI requested a review from mattcosta7 January 3, 2026 18:55
@mattcosta7 mattcosta7 marked this pull request as ready for review January 3, 2026 19:11
@mattcosta7 mattcosta7 requested a review from a team as a code owner January 3, 2026 19:11
@mattcosta7 mattcosta7 requested a review from joshblack January 3, 2026 19:11
@mattcosta7 mattcosta7 merged commit 3115cac into make-primer-react-pagelayout-accept-persister Jan 3, 2026
45 of 46 checks passed
@mattcosta7 mattcosta7 deleted the copilot/refactor-page-layout-pane-api branch January 3, 2026 19:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

integration-tests: recommended This change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants