[CAPY-2410][BpkDrawerV2] Add drawer v2 component - #4917
[CAPY-2410][BpkDrawerV2] Add drawer v2 component#4917Guillermo Wu (guillewu) wants to merge 17 commits into
Conversation
|
Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser. |
f196d04 to
0fa3b76
Compare
|
Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser. |
0fa3b76 to
d3e7ce9
Compare
|
Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser. |
There was a problem hiding this comment.
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
BpkSheetDrawernamespace 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.
…rawer # Conflicts: # .eslintrc
|
Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser. |
|
Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser. |
|
Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser. |
There was a problem hiding this comment.
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
onClickinto theasChildclose 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 viaBpkDrawerV2.CloseTrigger. Prefer makingBpkCloseButtonaccept an optionalonClick(or composing handlers) so the close behavior can be injected byDrawer.CloseTriggerwithout 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.ResizeObservermock 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 localbeforeAllhelper imported by both test files).
window.ResizeObserver =
window.ResizeObserver ||
jest.fn().mockImplementation(() => ({
disconnect: jest.fn(),
observe: jest.fn(),
unobserve: jest.fn(),
}));
|
Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser. |
|
Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser. |
|
Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser. |
|
Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser. |
|
Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser. |
|
Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser. |
|
Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser. |
|
Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser. |
|
Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser. |
|
Visit https://backpack.github.io/storybook-prs/4917 to see this build running in a browser. |
a7a17dd to
cd9e312
Compare
| }; | ||
|
|
||
| export default BpkCloseButton; No newline at end of file | ||
| export default BpkCloseButton; |
There was a problem hiding this comment.
revert, not needed
| it('has no accessibility violations when open', async () => { | ||
| const { container } = render( | ||
| <BpkDrawerV2.Root open onOpenChange={jest.fn()}> | ||
| <BpkDrawerV2.Backdrop /> |
There was a problem hiding this comment.
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)} |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
JIRA Link: https://skyscanner.atlassian.net/browse/CAPY-2410
Summary
Adds a new
BpkDrawerV2component that wraps Ark UI's drawer primitives in Backpack naming, styling, and documentation conventions.The component is exported from
bpk-component-draweras a named export alongside the existing defaultBpkDrawer. It exposes a namespace API withRoot,RootProvider,Trigger,SwipeArea,Backdrop,Content,Header,Title,Description,Body,Grabber,GrabberIndicator, andCloseTriggerparts. 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
BpkDrawerV2underbpk-component-drawerwith README guidance, stories, unit tests, and accessibility tests.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,.tsxtsc --build packages/backpack-web/tsconfig.lib.json --pretty falseTZ=Etc/UTC npx jest packages/backpack-web/src/bpk-component-sheet-drawer --runInBandbefore the renameCI=1 npx nx run backpack-storybook-host:build-storybook -- -o /private/tmp/bpk-sheet-drawer-storybookbefore the renameNote: after merging latest
main, local Jest cannot start until dependencies are refreshed because the stale localnode_modulesis missing@babel/preset-flow. CI runs from a clean install.Remember to include the following changes:
[Clover-123][BpkButton] Updating the colourREADME.md(If you have created a new component)README.md