Skip to content

Commit 5edf72c

Browse files
authored
fix(phase-10): correct wrap boundary behavior and prep 2.1.2 (#166)
* chore(vscode): associate CSS files with Tailwind CSS language mode * style(docs): add brand color tokens and update header logo styling * fix(phase-10): correct wrap boundary behavior and prep 2.1.2
1 parent 699d6d9 commit 5edf72c

File tree

10 files changed

+102
-15
lines changed

10 files changed

+102
-15
lines changed

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,7 @@
2828
"[json]": {
2929
"editor.defaultFormatter": "vscode.json-language-features"
3030
},
31+
"files.associations": {
32+
"*.css": "tailwindcss"
33+
}
3134
}

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
# Change Log
22

3+
## 2.1.2
4+
5+
### Patch Changes
6+
7+
- Fix `wrap` boundary navigation semantics so:
8+
9+
- `wrap={false}` stops at the first/last photo.
10+
- `wrap={true}` loops across boundaries as documented.
11+
12+
Also adds behavior tests for keyboard navigation at boundaries and control visibility in both wrap modes.
13+
314
## 2.1.0
415

516
### Minor Changes

MODERNIZATION_PLAN_v2.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ Add decisions here as we align each phase:
488488
- [#37](https://github.com/pedropalau/react-bnb-gallery/issues/37): add explicit image alt fields (for example `alt` and `thumbnailAlt`) while keeping `caption` as the default fallback for backward compatibility.
489489
- Target scope: upcoming `v2.x.x` patch/minor delivery after `2.1.0` release execution.
490490
- [#42](https://github.com/pedropalau/react-bnb-gallery/issues/42): fix `wrap` boundary behavior so runtime navigation semantics match documented expectations.
491-
- Target scope: upcoming `v2.x.x` patch/minor delivery after `2.1.0` release execution.
491+
- Status: delivered in `2.1.2` (2026-02-26).
492492
- [#43](https://github.com/pedropalau/react-bnb-gallery/pull/43) concept: support `ReactNode` values for `caption` and `subcaption`.
493493
- Target scope: upcoming `v2.x.x` patch/minor delivery after `2.1.0` release execution.
494494
- Delivery note: reimplement with current architecture/types/tests/docs; do not revive legacy PR code directly.

docs/app/styles.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
@custom-variant dark (&:is(.dark *));
99

1010
:root {
11+
--brand: oklch(0.5769 0.2277 18.5);
12+
--brand-foreground: oklch(1 0 0);
1113
--background: oklch(1 0 0);
1214
--foreground: oklch(0.145 0 0);
1315
--card: oklch(1 0 0);
@@ -43,6 +45,8 @@
4345
}
4446

4547
.dark {
48+
--brand: oklch(0.5769 0.2277 18.5);
49+
--brand-foreground: oklch(1 0 0);
4650
--background: oklch(0.145 0 0);
4751
--foreground: oklch(0.985 0 0);
4852
--card: oklch(0.205 0 0);
@@ -83,6 +87,8 @@
8387
--breakpoint-3xl: 1600px;
8488
--breakpoint-4xl: 2000px;
8589

90+
--color-brand: var(--brand);
91+
--color-brand-foreground: var(--brand-foreground);
8692
--color-sidebar-ring: var(--sidebar-ring);
8793
--color-sidebar-border: var(--sidebar-border);
8894
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);

docs/components/logo.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,17 @@ export function Logo({
77
return (
88
<svg
99
xmlns="http://www.w3.org/2000/svg"
10-
className={cn('w-8 h-auto fill-primary', className)}
10+
className={cn('w-7 h-auto fill-current', className)}
1111
fill="none"
1212
viewBox="0 0 180 98"
1313
{...rest}
1414
>
1515
<path
16-
fill="#000"
1716
fillRule="evenodd"
1817
d="M89.683 0c9.83 0 17.534-.004 23.538.454 5.79.442 10.72 1.348 14.852 3.576l.397.221c4.595 2.607 7.056 6.296 8.164 10.974.843 3.555.92 7.845.926 12.447v42.203c-.006 4.601-.083 8.892-.926 12.447-1.108 4.678-3.569 8.366-8.164 10.974-4.212 2.39-9.272 3.34-15.249 3.797-6.004.458-13.708.454-23.538.454s-17.535.004-23.539-.454c-5.977-.456-11.036-1.407-15.248-3.796l-.001-.001C46.3 90.688 43.84 87 42.732 82.322c-.843-3.555-.92-7.846-.927-12.447V27.672c.006-4.602.084-8.892.927-12.447C43.84 10.547 46.3 6.858 50.896 4.25 55.108 1.861 60.167.91 66.144.454 72.148-.004 79.853 0 89.683 0m0 11.547c-10.017 0-17.2.003-22.661.42-5.488.418-8.475 1.217-10.429 2.326-1.57.89-2.193 1.767-2.626 3.594-.578 2.441-.615 5.865-.615 11.775v38.223c0 5.91.037 9.333.615 11.774.433 1.827 1.056 2.704 2.626 3.595l.38.204c1.958 1.003 4.904 1.73 10.049 2.122 5.461.417 12.644.42 22.661.42s17.199-.003 22.66-.42c5.488-.418 8.474-1.217 10.429-2.326 1.57-.891 2.194-1.767 2.627-3.594.578-2.441.615-5.865.615-11.775V29.662c0-5.91-.037-9.334-.615-11.775-.433-1.827-1.057-2.703-2.627-3.594-1.955-1.109-4.941-1.908-10.429-2.326-5.461-.417-12.643-.42-22.66-.42"
1918
clipRule="evenodd"
2019
/>
21-
<path
22-
fill="#000"
23-
d="M.035 24.257A5.774 5.774 0 0 1 6.4 19.144c4.924.538 9.22 1.547 12.895 3.705 4.583 2.69 6.982 6.46 8.062 11.173.827 3.61.905 7.976.911 12.704v4.094c-.006 4.728-.084 9.093-.911 12.704-1.046 4.566-3.331 8.245-7.639 10.916l-.423.256c-3.675 2.158-7.97 3.167-12.895 3.704a5.774 5.774 0 0 1-1.254-11.477c3.958-.432 6.347-1.125 7.986-2.006l.319-.179c1.54-.904 2.2-1.823 2.65-3.792.586-2.554.621-6.12.621-12.173 0-6.055-.035-9.62-.62-12.174-.451-1.969-1.112-2.889-2.651-3.792-1.647-.967-4.083-1.723-8.305-2.184a5.774 5.774 0 0 1-5.112-6.366M172.971 19.145a5.774 5.774 0 0 1 1.253 11.478c-4.221.46-6.657 1.217-8.304 2.184-1.539.903-2.201 1.823-2.652 3.792-.585 2.554-.619 6.119-.619 12.174s.034 9.618.619 12.173c.451 1.969 1.112 2.889 2.65 3.792 1.647.967 4.084 1.724 8.306 2.185a5.773 5.773 0 0 1-1.253 11.477c-4.925-.537-9.22-1.547-12.895-3.704-4.582-2.69-6.983-6.459-8.063-11.172-.827-3.61-.905-7.976-.911-12.704v-4.094c.006-4.728.084-9.093.911-12.704 1.08-4.713 3.48-8.483 8.062-11.172l.001-.001c3.675-2.157 7.97-3.167 12.895-3.704"
24-
/>
20+
<path d="M.035 24.257A5.774 5.774 0 0 1 6.4 19.144c4.924.538 9.22 1.547 12.895 3.705 4.583 2.69 6.982 6.46 8.062 11.173.827 3.61.905 7.976.911 12.704v4.094c-.006 4.728-.084 9.093-.911 12.704-1.046 4.566-3.331 8.245-7.639 10.916l-.423.256c-3.675 2.158-7.97 3.167-12.895 3.704a5.774 5.774 0 0 1-1.254-11.477c3.958-.432 6.347-1.125 7.986-2.006l.319-.179c1.54-.904 2.2-1.823 2.65-3.792.586-2.554.621-6.12.621-12.173 0-6.055-.035-9.62-.62-12.174-.451-1.969-1.112-2.889-2.651-3.792-1.647-.967-4.083-1.723-8.305-2.184a5.774 5.774 0 0 1-5.112-6.366M172.971 19.145a5.774 5.774 0 0 1 1.253 11.478c-4.221.46-6.657 1.217-8.304 2.184-1.539.903-2.201 1.823-2.652 3.792-.585 2.554-.619 6.119-.619 12.174s.034 9.618.619 12.173c.451 1.969 1.112 2.889 2.65 3.792 1.647.967 4.084 1.724 8.306 2.185a5.773 5.773 0 0 1-1.253 11.477c-4.925-.537-9.22-1.547-12.895-3.704-4.582-2.69-6.983-6.459-8.063-11.172-.827-3.61-.905-7.976-.911-12.704v-4.094c.006-4.728.084-9.093.911-12.704 1.08-4.713 3.48-8.483 8.062-11.172l.001-.001c3.675-2.157 7.97-3.167 12.895-3.704" />
2521
</svg>
2622
);
2723
}

docs/components/site-header.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export function SiteHeader({
2424
<div className="flex items-center">
2525
<Link
2626
href="/"
27-
className="inline-flex items-center justify-center hover:bg-accent w-12 h-10 rounded-md"
27+
className="inline-flex items-center justify-center hover:text-brand size-10"
2828
>
2929
<Logo />
3030
</Link>

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-bnb-gallery",
3-
"version": "2.1.1",
3+
"version": "2.1.2",
44
"description": "Accessible React lightbox and photo gallery component inspired by Airbnb’s image gallery.",
55
"author": "Pedro Palau <palauisaac@gmail.com>",
66
"homepage": "https://bnb-gallery.vercel.app",

src/components/gallery.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,14 @@ function getNormalizedActivePhotoIndex(
7171
}
7272

7373
/**
74-
* Computes control visibility when wrap mode is enabled.
74+
* Computes control visibility based on boundary and wrap mode.
7575
*/
7676
function getWrapControlState(
7777
activePhotoIndex: number,
7878
totalPhotos: number,
7979
wrap: boolean,
8080
) {
81-
if (!wrap || totalPhotos <= 1) {
81+
if (wrap || totalPhotos <= 1) {
8282
return {
8383
hidePrevButton: false,
8484
hideNextButton: false,
@@ -178,7 +178,7 @@ const Gallery = forwardRef<GalleryController, GalleryProps>(function Gallery(
178178
(isPrevDirection && activeIndex === 0) ||
179179
(isNextDirection && activeIndex === lastItemIndex);
180180

181-
if (isGoingToWrap && wrap) {
181+
if (isGoingToWrap && !wrap) {
182182
return activeIndex;
183183
}
184184

tests/components/Gallery.test.js

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ describe('Gallery', () => {
5252
).toBeInTheDocument();
5353
});
5454

55-
it('syncs wrap controls when activePhotoIndex prop changes', () => {
55+
it('keeps both controls visible when wrap is enabled', () => {
5656
const photoList = photos.slice(0, 2);
5757
const { container, rerender } = render(
5858
<Gallery
@@ -65,7 +65,7 @@ describe('Gallery', () => {
6565

6666
expect(
6767
container.querySelector('.gallery-control--prev'),
68-
).not.toBeInTheDocument();
68+
).toBeInTheDocument();
6969
expect(
7070
container.querySelector('.gallery-control--next'),
7171
).toBeInTheDocument();
@@ -79,6 +79,41 @@ describe('Gallery', () => {
7979
/>,
8080
);
8181

82+
expect(
83+
container.querySelector('.gallery-control--prev'),
84+
).toBeInTheDocument();
85+
expect(
86+
container.querySelector('.gallery-control--next'),
87+
).toBeInTheDocument();
88+
});
89+
90+
it('hides boundary controls when wrap is disabled', () => {
91+
const photoList = photos.slice(0, 2);
92+
const { container, rerender } = render(
93+
<Gallery
94+
photos={photoList}
95+
showThumbnails={false}
96+
wrap={false}
97+
activePhotoIndex={0}
98+
/>,
99+
);
100+
101+
expect(
102+
container.querySelector('.gallery-control--prev'),
103+
).not.toBeInTheDocument();
104+
expect(
105+
container.querySelector('.gallery-control--next'),
106+
).toBeInTheDocument();
107+
108+
rerender(
109+
<Gallery
110+
photos={photoList}
111+
showThumbnails={false}
112+
wrap={false}
113+
activePhotoIndex={1}
114+
/>,
115+
);
116+
82117
expect(
83118
container.querySelector('.gallery-control--prev'),
84119
).toBeInTheDocument();
@@ -89,7 +124,7 @@ describe('Gallery', () => {
89124

90125
it('renders accessible labels on navigation controls', () => {
91126
const { container } = render(
92-
<Gallery photos={photos.slice(0, 2)} showThumbnails={false} />,
127+
<Gallery photos={photos.slice(0, 2)} showThumbnails={false} wrap />,
93128
);
94129

95130
expect(container.querySelector('.gallery-control--prev')).toHaveAttribute(

tests/components/react-bnb-gallery.test.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,42 @@ describe('ReactBnbGallery', () => {
150150
).toHaveTextContent('2 / 3');
151151
});
152152

153+
it('stops at the last photo when wrap is disabled', () => {
154+
render(
155+
<ReactBnbGallery
156+
photos={photos.slice(0, 3)}
157+
show
158+
activePhotoIndex={2}
159+
wrap={false}
160+
/>,
161+
);
162+
163+
const modal = document.body.querySelector('.gallery-modal');
164+
fireEvent.keyDown(modal, { key: 'ArrowRight' });
165+
166+
expect(
167+
document.body.querySelector('.gallery-photo-counter'),
168+
).toHaveTextContent('3 / 3');
169+
});
170+
171+
it('wraps from last photo to first when wrap is enabled', () => {
172+
render(
173+
<ReactBnbGallery
174+
photos={photos.slice(0, 3)}
175+
show
176+
activePhotoIndex={2}
177+
wrap
178+
/>,
179+
);
180+
181+
const modal = document.body.querySelector('.gallery-modal');
182+
fireEvent.keyDown(modal, { key: 'ArrowRight' });
183+
184+
expect(
185+
document.body.querySelector('.gallery-photo-counter'),
186+
).toHaveTextContent('1 / 3');
187+
});
188+
153189
it('warns when photos is passed as a single string', () => {
154190
render(<ReactBnbGallery photos="https://example.com/photo.jpg" show />);
155191

0 commit comments

Comments
 (0)