Skip to content
This repository was archived by the owner on Aug 5, 2026. It is now read-only.

[CAPY-2410][BpkDrawerV2] Add drawer v2 component - #4917

Draft
Guillermo Wu (guillewu) wants to merge 17 commits into
mainfrom
CAPY-2410/bpk-sheet-drawer
Draft

[CAPY-2410][BpkDrawerV2] Add drawer v2 component#4917
Guillermo Wu (guillewu) wants to merge 17 commits into
mainfrom
CAPY-2410/bpk-sheet-drawer

Conversation

@guillewu

@guillewu Guillermo Wu (guillewu) commented Jul 21, 2026

Copy link
Copy Markdown

JIRA Link: https://skyscanner.atlassian.net/browse/CAPY-2410

Summary

Adds a new BpkDrawerV2 component that wraps Ark UI's drawer primitives in Backpack naming, styling, and documentation conventions.

The component is exported from bpk-component-drawer as a named export alongside the existing default BpkDrawer. It exposes a namespace API with Root, RootProvider, Trigger, SwipeArea, Backdrop, Content, Header, Title, Description, Body, Grabber, GrabberIndicator, and CloseTrigger parts. It supports modal drawers, side drawers, snap points, persistent non-modal snap drawers, draggable grabbers, and the Backpack close button.

Styles are colocated per part, following the newer multi-part Ark wrapper pattern used by BpkModalV3.

What changed

  • Added BpkDrawerV2 under bpk-component-drawer with README guidance, stories, unit tests, and accessibility tests.
  • Added Backpack SCSS modules for drawer content, backdrop, header, title, body, grabber, and swipe area.
  • Added snap-point stories, including a persistent 10%/90% example where the background remains interactive.
  • Added keyboard-accessible grabber behavior in the persistent snap-point example.
  • Added Ark Drawer parts to the ESLint allowlist for Backpack-owned styling on Ark primitives.

Test plan

  • stylelint 'packages/backpack-web/src/bpk-component-drawer/src/BpkDrawerV2/**/*.scss'
  • eslint packages/backpack-web/src/bpk-component-drawer/src/BpkDrawerV2 packages/backpack-web/src/bpk-component-drawer/index.ts --ext .ts,.tsx
  • tsc --build packages/backpack-web/tsconfig.lib.json --pretty false
  • TZ=Etc/UTC npx jest packages/backpack-web/src/bpk-component-sheet-drawer --runInBand before the rename
  • CI=1 npx nx run backpack-storybook-host:build-storybook -- -o /private/tmp/bpk-sheet-drawer-storybook before the rename

Note: after merging latest main, local Jest cannot start until dependencies are refreshed because the stale local node_modules is missing @babel/preset-flow. CI runs from a clean install.


Remember to include the following changes:

  • Ensure the PR title includes the name of the component you are changing so it's clear in the release notes for consumers of the changes in the version e.g [Clover-123][BpkButton] Updating the colour
  • README.md (If you have created a new component)
  • Component README.md
  • Tests
  • Accessibility tests
    • The following checks were performed:
      • Ability to navigate using a keyboard only
      • Zoom functionality (Deque University explanation):
        • The page SHOULD be functional AND readable when only the text is magnified to 200% of its initial size
        • Pages must reflow as zoom increases up to 400% so that content continues to be presented in only one column i.e. Content MUST NOT require scrolling in two directions (both vertically and horizontally)
      • Ability to navigate using a screen reader only
  • Storybook examples created/updated
  • For breaking changes or deprecating components/properties, migration guides added to the description of the PR. If the guide has large changes, consider creating a new Markdown page inside the component's docs folder and link it here

@guillewu Guillermo Wu (guillewu) added the minor Non breaking change label Jul 21, 2026
@skyscanner-backpack-bot

Copy link
Copy Markdown
Contributor

Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser.

@skyscanner-backpack-bot

Copy link
Copy Markdown
Contributor

Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser.

@skyscanner-backpack-bot

skyscanner-backpack-bot Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor
Warnings
⚠️

Package source files (e.g. packages/package-name/src/Component.js) were updated, but snapshots weren't. Have you checked that the tests still pass?

Browser support

If this is a visual change, make sure you've tested it in multiple browsers.

Generated by 🚫 dangerJS against fee8203

@skyscanner-backpack-bot

Copy link
Copy Markdown
Contributor

Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Adds a new BpkSheetDrawer component that wraps Ark UI’s Drawer primitives using Backpack’s compound-component API, styling conventions, and documentation/testing patterns.

Changes:

  • Introduced BpkSheetDrawer namespace API (Root, Trigger, Content, Backdrop, etc.) plus hooks (useBpkSheetDrawer, useBpkSheetDrawerContext).
  • Added SCSS modules for the drawer parts (content, backdrop, header, title, body, grabber, swipe area) including RTL and reduced-motion handling.
  • Added Storybook stories plus unit and accessibility tests; updated ESLint allowlist to permit styling Ark Drawer parts.

Reviewed changes

Copilot reviewed 29 out of 29 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/backpack-web/src/bpk-component-sheet-drawer/src/useBpkSheetDrawerContext.ts Adds typed wrapper hook around Ark useDrawerContext.
packages/backpack-web/src/bpk-component-sheet-drawer/src/useBpkSheetDrawer.ts Adds typed wrapper hook around Ark useDrawer.
packages/backpack-web/src/bpk-component-sheet-drawer/src/accessibility-test.tsx Adds jest-axe accessibility coverage for open/description/side drawer variants.
packages/backpack-web/src/bpk-component-sheet-drawer/src/BpkSheetDrawerTrigger.tsx Adds Backpack Trigger wrapper over Drawer.Trigger with data attribute.
packages/backpack-web/src/bpk-component-sheet-drawer/src/BpkSheetDrawerTitle/BpkSheetDrawerTitle.tsx Adds Title part wrapper with Backpack styling hook-up.
packages/backpack-web/src/bpk-component-sheet-drawer/src/BpkSheetDrawerTitle/BpkSheetDrawerTitle.module.scss Adds typography styling for the title.
packages/backpack-web/src/bpk-component-sheet-drawer/src/BpkSheetDrawerSwipeArea.tsx Adds SwipeArea wrapper that applies Backpack class and data attribute.
packages/backpack-web/src/bpk-component-sheet-drawer/src/BpkSheetDrawerSwipeArea.module.scss Adds SwipeArea positioning/z-index styling.
packages/backpack-web/src/bpk-component-sheet-drawer/src/BpkSheetDrawerRootProvider.tsx Adds RootProvider wrapper for external state machine usage.
packages/backpack-web/src/bpk-component-sheet-drawer/src/BpkSheetDrawerRoot.tsx Adds Root wrapper for Ark Drawer root with Backpack data attribute.
packages/backpack-web/src/bpk-component-sheet-drawer/src/BpkSheetDrawerHeader/BpkSheetDrawerHeader.tsx Adds Header layout part with Backpack class.
packages/backpack-web/src/bpk-component-sheet-drawer/src/BpkSheetDrawerHeader/BpkSheetDrawerHeader.module.scss Adds header spacing/layout tokens.
packages/backpack-web/src/bpk-component-sheet-drawer/src/BpkSheetDrawerGrabber/BpkSheetDrawerGrabberIndicator.tsx Re-exports GrabberIndicator for namespace API.
packages/backpack-web/src/bpk-component-sheet-drawer/src/BpkSheetDrawerGrabber/BpkSheetDrawerGrabber.tsx Adds Grabber + default indicator and styles/data attributes.
packages/backpack-web/src/bpk-component-sheet-drawer/src/BpkSheetDrawerGrabber/BpkSheetDrawerGrabber.module.scss Adds grabber cursor/focus/indicator styling and dragging states.
packages/backpack-web/src/bpk-component-sheet-drawer/src/BpkSheetDrawerDescription.tsx Adds Description wrapper part exposing Ark Drawer.Description.
packages/backpack-web/src/bpk-component-sheet-drawer/src/BpkSheetDrawerContent/BpkSheetDrawerContent.tsx Adds Content wrapper (Positioner + Content) with draggable default.
packages/backpack-web/src/bpk-component-sheet-drawer/src/BpkSheetDrawerContent/BpkSheetDrawerContent.module.scss Adds core panel positioning, animations, RTL handling, reduced-motion handling.
packages/backpack-web/src/bpk-component-sheet-drawer/src/BpkSheetDrawerCloseTrigger/BpkSheetDrawerCloseTrigger.tsx Adds CloseTrigger implementation using BpkCloseButton inside Ark CloseTrigger.
packages/backpack-web/src/bpk-component-sheet-drawer/src/BpkSheetDrawerBody/BpkSheetDrawerBody.tsx Adds Body part with “no drag” marker.
packages/backpack-web/src/bpk-component-sheet-drawer/src/BpkSheetDrawerBody/BpkSheetDrawerBody.module.scss Adds body padding/scroll behavior.
packages/backpack-web/src/bpk-component-sheet-drawer/src/BpkSheetDrawerBackdrop/BpkSheetDrawerBackdrop.tsx Adds Backdrop part wrapper with Backpack class.
packages/backpack-web/src/bpk-component-sheet-drawer/src/BpkSheetDrawerBackdrop/BpkSheetDrawerBackdrop.module.scss Adds scrim styling + in/out animations and reduced-motion behavior.
packages/backpack-web/src/bpk-component-sheet-drawer/src/BpkSheetDrawer.tsx Defines the public namespace object API.
packages/backpack-web/src/bpk-component-sheet-drawer/src/BpkSheetDrawer.stories.tsx Adds Storybook stories including persistent snap points with keyboard grabber behavior.
packages/backpack-web/src/bpk-component-sheet-drawer/src/BpkSheetDrawer-test.tsx Adds unit tests for namespace exports, parts, interactions, and RootProvider/hooks.
packages/backpack-web/src/bpk-component-sheet-drawer/index.ts Exports component, hooks, and public prop/type aliases.
packages/backpack-web/src/bpk-component-sheet-drawer/README.md Adds component documentation, anatomy, and usage guidance.
.eslintrc Allowlists Ark Drawer parts for Backpack-owned styling.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@skyscanner-backpack-bot

Copy link
Copy Markdown
Contributor

Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser.

@guillewu Guillermo Wu (guillewu) changed the title [CAPY-2410][BpkSheetDrawer] Add sheet drawer component [CAPY-2410][BpkDrawerV2] Add drawer v2 component Jul 31, 2026
@skyscanner-backpack-bot

Copy link
Copy Markdown
Contributor

Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser.

@skyscanner-backpack-bot

Copy link
Copy Markdown
Contributor

Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 29 out of 29 changed files in this pull request and generated 1 comment.

Suppressed comments (3)

packages/backpack-web/src/bpk-component-drawer/src/BpkDrawerV2/BpkDrawerV2Backdrop/BpkDrawerV2Backdrop.module.scss:28

  • Backdrop and drawer positioner/content are both using the same z-index (tokens.$bpk-zindex-modal). With identical z-index values, stacking depends on DOM order, so consumers could accidentally render the backdrop above the content (or otherwise get inconsistent layering). Set distinct z-index levels (e.g., backdrop lower than content/positioner) so layering is deterministic regardless of render order.
.bpk-drawer-v2__backdrop {
  position: fixed;
  z-index: tokens.$bpk-zindex-modal;
  background-color: tokens.$bpk-scrim-day;
  inset: 0;

packages/backpack-web/src/bpk-component-drawer/src/BpkDrawerV2/BpkDrawerV2CloseTrigger/BpkDrawerV2CloseTrigger.tsx:37

  • Passing a hard-coded no-op onClick into the asChild close trigger is risky because it can interfere with (or override) the close trigger’s injected click handler depending on how props are merged, and it prevents consumers from supplying their own click behavior via BpkDrawerV2.CloseTrigger. Prefer making BpkCloseButton accept an optional onClick (or composing handlers) so the close behavior can be injected by Drawer.CloseTrigger without needing a placeholder function.
  <Drawer.CloseTrigger asChild>
    <BpkCloseButton
      label={label}
      onClick={() => {}}
      {...getDataComponentAttribute('DrawerV2CloseTrigger')}
    />

packages/backpack-web/src/bpk-component-drawer/src/BpkDrawerV2/accessibility-test.tsx:30

  • This test file sets a global window.ResizeObserver mock at module scope, and the same pattern appears in other tests in this PR. To avoid duplication and reduce the chance of cross-test interference, move the ResizeObserver polyfill/mock into a shared Jest setup file (or a local beforeAll helper imported by both test files).
window.ResizeObserver =
  window.ResizeObserver ||
  jest.fn().mockImplementation(() => ({
    disconnect: jest.fn(),
    observe: jest.fn(),
    unobserve: jest.fn(),
  }));

@skyscanner-backpack-bot

Copy link
Copy Markdown
Contributor

Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser.

@skyscanner-backpack-bot

Copy link
Copy Markdown
Contributor

Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser.

@skyscanner-backpack-bot

Copy link
Copy Markdown
Contributor

Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser.

@skyscanner-backpack-bot

Copy link
Copy Markdown
Contributor

Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser.

@skyscanner-backpack-bot

Copy link
Copy Markdown
Contributor

Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser.

@skyscanner-backpack-bot

Copy link
Copy Markdown
Contributor

Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser.

@skyscanner-backpack-bot

Copy link
Copy Markdown
Contributor

Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser.

@skyscanner-backpack-bot

Copy link
Copy Markdown
Contributor

Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser.

@skyscanner-backpack-bot

Copy link
Copy Markdown
Contributor

Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser.

@skyscanner-backpack-bot

Copy link
Copy Markdown
Contributor

Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser.

};

export default BpkCloseButton; No newline at end of file
export default BpkCloseButton;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

revert, not needed

it('has no accessibility violations when open', async () => {
const { container } = render(
<BpkDrawerV2.Root open onOpenChange={jest.fn()}>
<BpkDrawerV2.Backdrop />

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Claude remark:

No Portal around Backdrop and Content — drawer will get trapped by transformed ancestors

BpkDrawerV2Content.tsx renders <Drawer.Positioner><Drawer.Content>…</Drawer.Content></Drawer.Positioner> directly, and BpkDrawerV2Backdrop.tsx renders bare <Drawer.Backdrop>. Neither is wrapped in Ark's
.

Consequence: any ancestor with transform, filter, perspective, will-change, or contain: paint creates a containing block that traps position: fixed. This is common in card grids, animated wrappers, and virtualized lists. The drawer will then render clipped inside its ancestor instead of full-viewport, and it's a nightmare to debug from a consumer's side.

Fix: wrap in at the top of BpkDrawerV2Content (and BpkDrawerV2Backdrop, or in BpkDrawerV2Root) as BpkModalV3 and BpkContextMenu do. Owners will absolutely flag this.

Example here:

<Drawer.CloseTrigger asChild>
<BpkCloseButton
label={label}
onClick={() => drawer.setOpen(false)}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Claude:

BpkDrawerV2CloseTrigger fires close twice / overrides Ark's handler

BpkDrawerV2CloseTrigger/BpkDrawerV2CloseTrigger.tsx:817-828:
const drawer = useDrawerContext();
return (
<Drawer.CloseTrigger asChild>
<BpkCloseButton
label={label}
onClick={() => drawer.setOpen(false)}
{...getDataComponentAttribute('DrawerV2CloseTrigger')}
/>
</Drawer.CloseTrigger>
);

Drawer.CloseTrigger already closes on click — that is its entire purpose. The explicit onClick={() => drawer.setOpen(false)} on the child is either:

  • redundant (Ark merges → both fire, onOpenChange gets called twice)
  • or supersedes Ark's merged handler, depending on how asChild prop merging orders them

Either way it's wrong. Drop useDrawerContext() and the explicit onClick — just let <Drawer.CloseTrigger asChild> handle it. This will also remove the surface area for onOpenChange firing twice, which will
bite consumers doing analytics on drawer close.

const BpkDrawerV2Description = ({
children,
}: BpkDrawerV2DescriptionProps) => (
<Drawer.Description

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

No className, no test for it beyond the accessibility test. Consumers will get raw browser

(or whatever Ark defaults to) which typically doesn't match Backpack typography.

Either apply typography.bpk-body-default here or document that consumers must style the description themselves.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

minor Non breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants