From 5848759d7758ff8917af4709243d57ffa83ccc79 Mon Sep 17 00:00:00 2001 From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> Date: Thu, 1 Oct 2026 15:11:54 -0400 Subject: [PATCH 1/5] feat(gallery): resolve breakpoints from config --- core/api.txt | 4 +- core/src/components.d.ts | 8 +- .../components/gallery/gallery-interface.ts | 15 ++-- core/src/components/gallery/gallery.spec.ts | 79 +++++++++++++++++++ core/src/components/gallery/gallery.tsx | 48 ++++++----- 5 files changed, 112 insertions(+), 42 deletions(-) diff --git a/core/api.txt b/core/api.txt index e9f5eb4d403..88d97384026 100644 --- a/core/api.txt +++ b/core/api.txt @@ -1048,7 +1048,7 @@ ion-footer,prop,theme,"ios" | "md" | "ionic",undefined,false,false ion-footer,prop,translucent,boolean,false,false,false ion-gallery,shadow -ion-gallery,prop,columns,GalleryBreakpoints | number | string,{ +ion-gallery,prop,columns,BreakpointMap | number | string,{ xs: 2, sm: 3, md: 4, @@ -1056,7 +1056,7 @@ ion-gallery,prop,columns,GalleryBreakpoints | number | string,{ xl: 8, xxl: 10, },false,false -ion-gallery,prop,gap,GalleryBreakpoints | number | string,'16px',false,false +ion-gallery,prop,gap,BreakpointMap | number | string,'16px',false,false ion-gallery,prop,layout,"masonry" | "uniform",'uniform',false,false ion-gallery,prop,mode,"ios" | "md",undefined,false,false ion-gallery,prop,order,"best-fit" | "sequential" | undefined,undefined,false,false diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 4b93daad7f8..26591296197 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -1503,12 +1503,12 @@ export namespace Components { } interface IonGallery { /** - * The number of columns to display. Can be set as a number or an object of breakpoint values (e.g. `{ xs: 2, sm: 3, md: 4 }`). + * The number of columns to display. Can be set as a number or an object of breakpoint values (e.g. `{ xs: 2, sm: 3, md: 4 }`). A breakpoint is matched against the gallery's own width rather than the width of the screen, so a narrow gallery on a wide screen resolves to a small breakpoint. The width each breakpoint activates at can be changed with the `screenBreakpoints` config. * @default { xs: 2, sm: 3, md: 4, lg: 6, xl: 8, xxl: 10, } */ "columns": GalleryColumns; /** - * The space between gallery items. Accepts valid CSS [length-percentage](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/length-percentage) values like `16px`, `1rem`, `20%`, math functions like `calc(10px + 20%)`, CSS variables like `var(--app-gallery-gap)`, or numbers (treated as pixel values). Can also be set as a breakpoint map (e.g. `{ xs: '8px', sm: '1rem', md: '24px' }`). Does not accept space-separated values or CSS keyword values like `inherit`, `auto`, etc. + * The space between gallery items. Accepts valid CSS [length-percentage](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/length-percentage) values like `16px`, `1rem`, `20%`, math functions like `calc(10px + 20%)`, CSS variables like `var(--app-gallery-gap)`, or numbers (treated as pixel values). Can also be set as a breakpoint map (e.g. `{ xs: '8px', sm: '1rem', md: '24px' }`). Does not accept space-separated values or CSS keyword values like `inherit`, `auto`, etc. A breakpoint is matched against the gallery's own width rather than the width of the screen, so a narrow gallery on a wide screen resolves to a small breakpoint. The width each breakpoint activates at can be changed with the `screenBreakpoints` config. * @default '16px' */ "gap": GalleryGap; @@ -7379,12 +7379,12 @@ declare namespace LocalJSX { } interface IonGallery { /** - * The number of columns to display. Can be set as a number or an object of breakpoint values (e.g. `{ xs: 2, sm: 3, md: 4 }`). + * The number of columns to display. Can be set as a number or an object of breakpoint values (e.g. `{ xs: 2, sm: 3, md: 4 }`). A breakpoint is matched against the gallery's own width rather than the width of the screen, so a narrow gallery on a wide screen resolves to a small breakpoint. The width each breakpoint activates at can be changed with the `screenBreakpoints` config. * @default { xs: 2, sm: 3, md: 4, lg: 6, xl: 8, xxl: 10, } */ "columns"?: GalleryColumns; /** - * The space between gallery items. Accepts valid CSS [length-percentage](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/length-percentage) values like `16px`, `1rem`, `20%`, math functions like `calc(10px + 20%)`, CSS variables like `var(--app-gallery-gap)`, or numbers (treated as pixel values). Can also be set as a breakpoint map (e.g. `{ xs: '8px', sm: '1rem', md: '24px' }`). Does not accept space-separated values or CSS keyword values like `inherit`, `auto`, etc. + * The space between gallery items. Accepts valid CSS [length-percentage](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/length-percentage) values like `16px`, `1rem`, `20%`, math functions like `calc(10px + 20%)`, CSS variables like `var(--app-gallery-gap)`, or numbers (treated as pixel values). Can also be set as a breakpoint map (e.g. `{ xs: '8px', sm: '1rem', md: '24px' }`). Does not accept space-separated values or CSS keyword values like `inherit`, `auto`, etc. A breakpoint is matched against the gallery's own width rather than the width of the screen, so a narrow gallery on a wide screen resolves to a small breakpoint. The width each breakpoint activates at can be changed with the `screenBreakpoints` config. * @default '16px' */ "gap"?: GalleryGap; diff --git a/core/src/components/gallery/gallery-interface.ts b/core/src/components/gallery/gallery-interface.ts index 6ce4417bfca..cd7f0d4141c 100644 --- a/core/src/components/gallery/gallery-interface.ts +++ b/core/src/components/gallery/gallery-interface.ts @@ -1,11 +1,6 @@ -export interface GalleryBreakpoints { - xs?: T; - sm?: T; - md?: T; - lg?: T; - xl?: T; - xxl?: T; -} +import type { BreakpointMap } from '@utils/breakpoints'; -export type GalleryColumns = GalleryBreakpoints | string | number; -export type GalleryGap = GalleryBreakpoints | string | number; +export type GalleryBreakpoints = BreakpointMap; + +export type GalleryColumns = string | number | GalleryBreakpoints; +export type GalleryGap = string | number | GalleryBreakpoints; diff --git a/core/src/components/gallery/gallery.spec.ts b/core/src/components/gallery/gallery.spec.ts index 5ed9c1e40b9..805924173f6 100644 --- a/core/src/components/gallery/gallery.spec.ts +++ b/core/src/components/gallery/gallery.spec.ts @@ -1,4 +1,6 @@ +import { config } from '@global/config'; import { newSpecPage } from '@stencil/core/testing'; +import { resetScreenBreakpoints } from '@utils/breakpoints'; import * as helpers from '@utils/helpers'; import * as logging from '@utils/logging'; @@ -1151,6 +1153,83 @@ describe('gallery', () => { }); }); }); + + /** + * The thresholds come from the `screenBreakpoints` config rather than a + * hardcoded map, so apps can override them. Each width below maps to a + * different breakpoint with the override than it does with the defaults, + * ensuring these tests fail if the config is ignored. + */ + describe('gallery: screenBreakpoints config', () => { + const OVERRIDE = { xs: 0, sm: 200, md: 400, lg: 600, xl: 800, xxl: 1000 }; + + const setScreenBreakpoints = (value: unknown) => { + config.set('screenBreakpoints', value as any); + resetScreenBreakpoints(); + }; + + beforeEach(() => { + setScreenBreakpoints(OVERRIDE); + }); + + afterEach(() => { + setScreenBreakpoints(undefined); + }); + + it('should resolve columns against the configured widths', () => { + const breakpoints = [ + // xs under both + { width: 150, expectedColumns: 3 }, + // sm under the override, xs by default + { width: 350, expectedColumns: 4 }, + // md under the override, xs by default + { width: 500, expectedColumns: 5 }, + // lg under the override, sm by default + { width: 650, expectedColumns: 7 }, + // xl under the override, md by default + { width: 850, expectedColumns: 9 }, + // xxl under the override, lg by default + { width: 1100, expectedColumns: 12 }, + ]; + + sharedGallery.columns = { xs: 3, sm: 4, md: 5, lg: 7, xl: 9, xxl: 12 }; + + breakpoints.forEach(({ width, expectedColumns }) => { + expect((sharedGallery as any).getColumnsForWidth(width)).toBe(expectedColumns); + }); + }); + + it('should resolve the gap against the configured widths', () => { + const breakpoints = [ + { width: 150, expectedGap: '2px' }, + { width: 350, expectedGap: '4px' }, + { width: 500, expectedGap: '8px' }, + { width: 650, expectedGap: '16px' }, + { width: 850, expectedGap: '32px' }, + { width: 1100, expectedGap: '64px' }, + ]; + + sharedGallery.gap = { xs: '2px', sm: '4px', md: '8px', lg: '16px', xl: '32px', xxl: '64px' }; + + breakpoints.forEach(({ width, expectedGap }) => { + expect((sharedGallery as any).getGapForWidth(width)).toBe(expectedGap); + }); + }); + + it('should resolve the default columns against the configured widths', () => { + // 500 is below the default md of 768, but at or above the configured 400 + expect((sharedGallery as any).getColumnsForWidth(500)).toBe(DEFAULT_COLUMNS['md']); + }); + + it('should fall back to the defaults when the config is removed', () => { + setScreenBreakpoints(undefined); + + sharedGallery.columns = { xs: 3, md: 5 }; + + // 500 resolves to xs again, since the default md is 768 + expect((sharedGallery as any).getColumnsForWidth(500)).toBe(3); + }); + }); }); describe('gallery: classes', () => { diff --git a/core/src/components/gallery/gallery.tsx b/core/src/components/gallery/gallery.tsx index 3b5b7168cea..a94f69607f3 100644 --- a/core/src/components/gallery/gallery.tsx +++ b/core/src/components/gallery/gallery.tsx @@ -1,5 +1,7 @@ import type { ComponentInterface } from '@stencil/core'; import { Component, Element, Host, Listen, Prop, Watch, h } from '@stencil/core'; +import type { ScreenBreakpoint } from '@utils/breakpoints'; +import { SCREEN_BREAKPOINT_NAMES, getScreenBreakpoints, isBreakpointMap } from '@utils/breakpoints'; import { isCssVariable, isValidLengthPercentage } from '@utils/css-value-validation'; import { raf } from '@utils/helpers'; import { printIonWarning } from '@utils/logging'; @@ -9,19 +11,6 @@ import { getIonTheme } from '../../global/ionic-global'; import { DEFAULT_COLUMNS, DEFAULT_GAP } from './gallery-constants'; import type { GalleryBreakpoints, GalleryColumns, GalleryGap } from './gallery-interface'; -// TODO(FW-7285): Replace with global breakpoints -const BREAKPOINTS = { - xs: 0, - sm: 576, - md: 768, - lg: 992, - xl: 1200, - xxl: 1400, -}; - -type GalleryBreakpoint = keyof typeof BREAKPOINTS; -const BREAKPOINT_ORDER: GalleryBreakpoint[] = ['xs', 'sm', 'md', 'lg', 'xl', 'xxl']; - /** * The tag of the component used to wrap each gallery item. */ @@ -74,6 +63,11 @@ export class Gallery implements ComponentInterface { /** * The number of columns to display. Can be set as a number or an object of * breakpoint values (e.g. `{ xs: 2, sm: 3, md: 4 }`). + * + * A breakpoint is matched against the gallery's own width rather than the + * width of the screen, so a narrow gallery on a wide screen resolves to a + * small breakpoint. The width each breakpoint activates at can be changed + * with the `screenBreakpoints` config. */ @Prop() columns: GalleryColumns = DEFAULT_COLUMNS; @@ -84,6 +78,11 @@ export class Gallery implements ComponentInterface { * values). Can also be set as a breakpoint map * (e.g. `{ xs: '8px', sm: '1rem', md: '24px' }`). Does not accept * space-separated values or CSS keyword values like `inherit`, `auto`, etc. + * + * A breakpoint is matched against the gallery's own width rather than the + * width of the screen, so a narrow gallery on a wide screen resolves to a + * small breakpoint. The width each breakpoint activates at can be changed + * with the `screenBreakpoints` config. */ @Prop() gap: GalleryGap = DEFAULT_GAP; @@ -267,13 +266,6 @@ export class Gallery implements ComponentInterface { return isValidCssLength ? normalizedGap : undefined; } - /** - * Check if the value is a breakpoint map object. - */ - private isBreakpointMap(value: unknown): value is GalleryBreakpoints { - return typeof value === 'object' && value !== null && !Array.isArray(value); - } - /** * Check if the breakpoint map has any invalid values for the provided * sanitizer. A breakpoint map is invalid when there are no valid breakpoint @@ -286,7 +278,7 @@ export class Gallery implements ComponentInterface { ) { let hasBreakpointEntry = false; - for (const breakpoint of BREAKPOINT_ORDER) { + for (const breakpoint of SCREEN_BREAKPOINT_NAMES) { const value = breakpointMap[breakpoint]; if (value !== undefined) { hasBreakpointEntry = true; @@ -302,23 +294,27 @@ export class Gallery implements ComponentInterface { /** * Resolve a responsive value from a breakpoint map. * Uses a breakpoint-specific default when custom values are missing/invalid. + * + * Note that the breakpoints are compared against the gallery's own width + * rather than the width of the screen. */ private resolveFromBreakpoints( width: number, breakpointMap: GalleryBreakpoints, sanitizeProvided: (value: string | number | undefined) => T | undefined, - getSanitizedDefault: (breakpoint: GalleryBreakpoint) => T | undefined + getSanitizedDefault: (breakpoint: ScreenBreakpoint) => T | undefined ) { + const breakpoints = getScreenBreakpoints(); let resolvedValue: T | undefined; - for (const bp of BREAKPOINT_ORDER) { + for (const bp of SCREEN_BREAKPOINT_NAMES) { const providedValue = breakpointMap[bp]; const sanitizedProvided = sanitizeProvided(providedValue); const sanitizedDefault = getSanitizedDefault(bp); const resolved = providedValue === undefined || sanitizedProvided === undefined ? sanitizedDefault : sanitizedProvided; - if (resolved !== undefined && width >= BREAKPOINTS[bp]) { + if (resolved !== undefined && width >= breakpoints[bp]) { resolvedValue = resolved; } } @@ -414,7 +410,7 @@ export class Gallery implements ComponentInterface { */ private getColumnsForWidth(width: number) { const { columns } = this; - const isBreakpointColumns = this.isBreakpointMap(columns); + const isBreakpointColumns = isBreakpointMap(columns); const hasInvalidBreakpointColumns = isBreakpointColumns && this.hasInvalidBreakpointMap(columns, (value) => this.sanitizeColumns(value)); @@ -440,7 +436,7 @@ export class Gallery implements ComponentInterface { const { gap } = this; const providedGap = gap ?? DEFAULT_GAP; - const isBreakpointGap = this.isBreakpointMap(providedGap); + const isBreakpointGap = isBreakpointMap(providedGap); const hasInvalidBreakpointGap = isBreakpointGap && this.hasInvalidBreakpointMap(providedGap, (value) => this.sanitizeGap(value)); const sanitizedGap = isBreakpointGap From 21b785c49432aa0e50598f3f56cd58dae29681bd Mon Sep 17 00:00:00 2001 From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> Date: Thu, 1 Oct 2026 19:06:22 -0400 Subject: [PATCH 2/5] test(gallery): add an e2e test for breakpoints --- .../gallery/test/breakpoints/gallery.e2e.ts | 42 +++ .../gallery/test/breakpoints/index.html | 292 ++++++++++++++++++ 2 files changed, 334 insertions(+) create mode 100644 core/src/components/gallery/test/breakpoints/gallery.e2e.ts create mode 100644 core/src/components/gallery/test/breakpoints/index.html diff --git a/core/src/components/gallery/test/breakpoints/gallery.e2e.ts b/core/src/components/gallery/test/breakpoints/gallery.e2e.ts new file mode 100644 index 00000000000..d3a26cf21fe --- /dev/null +++ b/core/src/components/gallery/test/breakpoints/gallery.e2e.ts @@ -0,0 +1,42 @@ +import { expect } from '@playwright/test'; +import { configs, test } from '@utils/test/playwright'; + +/** + * The test page overrides every screen breakpoint to well below its default + * (xs 0, sm 200, md 400, lg 600, xl 800, xxl 1000), so each width below + * resolves to a different breakpoint under the defaults than it does under the + * override. + * + * The gallery fills the viewport, so its own width tracks the window unless a + * case narrows it explicitly. + * + * This behavior does not vary across modes/directions. + */ +configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { + test.describe(title('gallery: breakpoints'), () => { + const columnsFor = (page: any) => + page.locator('#gallery').evaluate((el: HTMLElement) => el.style.getPropertyValue('--internal-gallery-columns')); + + test('should resolve columns against the configured breakpoints', async ({ page }) => { + // 500px is above the configured md (400) but below the default sm (576), + // so the defaults would resolve to xs and give 1 column + await page.setViewportSize({ width: 500, height: 800 }); + await page.goto('/src/components/gallery/test/breakpoints', config); + + expect(await columnsFor(page)).toBe('3'); + }); + + test('should resolve against its own width rather than the viewport', async ({ page }) => { + await page.setViewportSize({ width: 1600, height: 800 }); + await page.goto('/src/components/gallery/test/breakpoints', config); + + // Filling the viewport, the gallery is xxl and takes the default columns + expect(await columnsFor(page)).toBe('10'); + + // Narrowing only the gallery drops it to xs, without the viewport moving + await page.locator('#gallery-sizer').evaluate((el: HTMLElement) => (el.style.width = '150px')); + + await expect.poll(() => columnsFor(page)).toBe('1'); + }); + }); +}); diff --git a/core/src/components/gallery/test/breakpoints/index.html b/core/src/components/gallery/test/breakpoints/index.html new file mode 100644 index 00000000000..26d5dea55c3 --- /dev/null +++ b/core/src/components/gallery/test/breakpoints/index.html @@ -0,0 +1,292 @@ + + + + + Gallery - Breakpoints + + + + + + + + + + + + + +

+ Resize the window to step through the breakpoints, or drag the handle in the gallery's lower corner to narrow + the gallery on its own — it resolves against its own width, not the window's. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
BreakpointActivates atColumns
xs0px1
sm200px3
md400px3
lg600px6
xl800px8
xxl1000px10
+ +

+ Columns come from { xs: 1, md: 3 }. The breakpoints it does not set fall back to the gallery's + own defaults. +

+ + +
+ +
+ … + –px +
+
+ + + + From 085bb4baf0e5c82a8dc17599d72854a0bcf25ecb Mon Sep 17 00:00:00 2001 From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> Date: Tue, 6 Oct 2026 15:13:34 -0400 Subject: [PATCH 3/5] fix(gallery): fallback to xs when xs is larger than 0 and screen is smaller --- core/src/components/gallery/gallery.spec.ts | 35 +++++++++++++++++++++ core/src/components/gallery/gallery.tsx | 6 +++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/core/src/components/gallery/gallery.spec.ts b/core/src/components/gallery/gallery.spec.ts index 805924173f6..9c3950d830c 100644 --- a/core/src/components/gallery/gallery.spec.ts +++ b/core/src/components/gallery/gallery.spec.ts @@ -1176,6 +1176,41 @@ describe('gallery', () => { setScreenBreakpoints(undefined); }); + /** + * `xs` is no longer pinned to 0, so a gallery narrower than the configured + * `xs` matches no breakpoint and would otherwise resolve to `undefined`. + */ + it('should resolve to the xs value when narrower than the configured xs', () => { + setScreenBreakpoints({ xs: 400 }); + + sharedGallery.columns = { xs: 1, md: 3 }; + + expect((sharedGallery as any).getColumnsForWidth(300)).toBe(1); + }); + + it('should resolve to the xs default when narrower than the configured xs', () => { + setScreenBreakpoints({ xs: 400 }); + + expect((sharedGallery as any).getColumnsForWidth(300)).toBe(DEFAULT_COLUMNS['xs']); + }); + + /** + * An unresolved value lands in the custom property as the string + * "undefined". The stylesheet's `var(--internal-gallery-columns, 2)` + * fallback cannot catch that, because the property is set. + */ + it('should not write an unresolved value into the columns custom property', () => { + setScreenBreakpoints({ xs: 400 }); + + sharedGallery.columns = { xs: 1, md: 3 }; + + jest.spyOn(sharedGallery.el, 'getBoundingClientRect').mockReturnValue({ width: 300 } as DOMRect); + + (sharedGallery as any).updateResponsiveStyles(); + + expect(sharedGallery.el.style.getPropertyValue('--internal-gallery-columns')).toBe('1'); + }); + it('should resolve columns against the configured widths', () => { const breakpoints = [ // xs under both diff --git a/core/src/components/gallery/gallery.tsx b/core/src/components/gallery/gallery.tsx index a94f69607f3..c5403c00851 100644 --- a/core/src/components/gallery/gallery.tsx +++ b/core/src/components/gallery/gallery.tsx @@ -314,7 +314,11 @@ export class Gallery implements ComponentInterface { const resolved = providedValue === undefined || sanitizedProvided === undefined ? sanitizedDefault : sanitizedProvided; - if (resolved !== undefined && width >= breakpoints[bp]) { + /** + * `xs` is the default when the gallery is narrower than the configured + * `xs` breakpoint, since no other breakpoint can match. + */ + if (resolved !== undefined && (bp === 'xs' || width >= breakpoints[bp])) { resolvedValue = resolved; } } From d193d8131a3b299d9e50e317b0df8ac7a0b68d35 Mon Sep 17 00:00:00 2001 From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> Date: Tue, 6 Oct 2026 15:42:27 -0400 Subject: [PATCH 4/5] style(gallery): use proper type and update comments --- .../gallery/test/breakpoints/gallery.e2e.ts | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/core/src/components/gallery/test/breakpoints/gallery.e2e.ts b/core/src/components/gallery/test/breakpoints/gallery.e2e.ts index d3a26cf21fe..39d28f2be1b 100644 --- a/core/src/components/gallery/test/breakpoints/gallery.e2e.ts +++ b/core/src/components/gallery/test/breakpoints/gallery.e2e.ts @@ -1,25 +1,29 @@ import { expect } from '@playwright/test'; import { configs, test } from '@utils/test/playwright'; +import type { E2EPage } from '@utils/test/playwright'; /** - * The test page overrides every screen breakpoint to well below its default - * (xs 0, sm 200, md 400, lg 600, xl 800, xxl 1000), so each width below - * resolves to a different breakpoint under the defaults than it does under the - * override. + * The test page configures every breakpoint below its default + * (xs 0, sm 200, md 400, lg 600, xl 800, xxl 1000). * - * The gallery fills the viewport, so its own width tracks the window unless a - * case narrows it explicitly. + * Only the first test verifies that the configured breakpoints are honored: + * 500px resolves to md with the override but xs with the defaults. The second + * verifies that the gallery resolves against its own width rather than the + * viewport, which is why it also passes with the defaults, both widths + * resolving to the same breakpoint; the gallery fills the viewport by default, + * so its width tracks the window unless a test explicitly narrows it. * - * This behavior does not vary across modes/directions. + * This behavior is the same across modes and directions. */ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { test.describe(title('gallery: breakpoints'), () => { - const columnsFor = (page: any) => + const columnsFor = (page: E2EPage) => page.locator('#gallery').evaluate((el: HTMLElement) => el.style.getPropertyValue('--internal-gallery-columns')); test('should resolve columns against the configured breakpoints', async ({ page }) => { - // 500px is above the configured md (400) but below the default sm (576), - // so the defaults would resolve to xs and give 1 column + // 500px is above the configured md (400), so it resolves to md and the + // page's md value of 3. With the defaults it would be xs, since sm does + // not start until 576, and give 1 instead. await page.setViewportSize({ width: 500, height: 800 }); await page.goto('/src/components/gallery/test/breakpoints', config); @@ -30,10 +34,10 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { await page.setViewportSize({ width: 1600, height: 800 }); await page.goto('/src/components/gallery/test/breakpoints', config); - // Filling the viewport, the gallery is xxl and takes the default columns + // Filling the viewport, the gallery is xxl and takes the default columns. expect(await columnsFor(page)).toBe('10'); - // Narrowing only the gallery drops it to xs, without the viewport moving + // Narrowing only the gallery drops it to xs, without the viewport moving. await page.locator('#gallery-sizer').evaluate((el: HTMLElement) => (el.style.width = '150px')); await expect.poll(() => columnsFor(page)).toBe('1'); From b0264530be836a51d62ffdae988ab32491e5baa4 Mon Sep 17 00:00:00 2001 From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> Date: Tue, 6 Oct 2026 15:45:01 -0400 Subject: [PATCH 5/5] docs(gallery): update prop descriptions --- core/src/components.d.ts | 8 ++++---- core/src/components/gallery/gallery.tsx | 17 ++++++----------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 26591296197..9e43ab8c87d 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -1503,12 +1503,12 @@ export namespace Components { } interface IonGallery { /** - * The number of columns to display. Can be set as a number or an object of breakpoint values (e.g. `{ xs: 2, sm: 3, md: 4 }`). A breakpoint is matched against the gallery's own width rather than the width of the screen, so a narrow gallery on a wide screen resolves to a small breakpoint. The width each breakpoint activates at can be changed with the `screenBreakpoints` config. + * The number of columns to display. Can be set as a number or an object of breakpoint values (e.g. `{ xs: 2, sm: 3, md: 4 }`). Breakpoints are matched against the gallery's own width rather than the screen width. The width each breakpoint activates at can be changed with the `screenBreakpoints` config. * @default { xs: 2, sm: 3, md: 4, lg: 6, xl: 8, xxl: 10, } */ "columns": GalleryColumns; /** - * The space between gallery items. Accepts valid CSS [length-percentage](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/length-percentage) values like `16px`, `1rem`, `20%`, math functions like `calc(10px + 20%)`, CSS variables like `var(--app-gallery-gap)`, or numbers (treated as pixel values). Can also be set as a breakpoint map (e.g. `{ xs: '8px', sm: '1rem', md: '24px' }`). Does not accept space-separated values or CSS keyword values like `inherit`, `auto`, etc. A breakpoint is matched against the gallery's own width rather than the width of the screen, so a narrow gallery on a wide screen resolves to a small breakpoint. The width each breakpoint activates at can be changed with the `screenBreakpoints` config. + * The space between gallery items. Accepts valid CSS [length-percentage](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/length-percentage) values like `16px`, `1rem`, `20%`, math functions like `calc(10px + 20%)`, CSS variables like `var(--app-gallery-gap)`, or numbers (treated as pixel values). Can also be set as a breakpoint map (e.g. `{ xs: '8px', sm: '1rem', md: '24px' }`). Does not accept space-separated values or CSS keyword values like `inherit`, `auto`, etc. Breakpoints are matched against the gallery's own width rather than the screen width. The width each breakpoint activates at can be changed with the `screenBreakpoints` config. * @default '16px' */ "gap": GalleryGap; @@ -7379,12 +7379,12 @@ declare namespace LocalJSX { } interface IonGallery { /** - * The number of columns to display. Can be set as a number or an object of breakpoint values (e.g. `{ xs: 2, sm: 3, md: 4 }`). A breakpoint is matched against the gallery's own width rather than the width of the screen, so a narrow gallery on a wide screen resolves to a small breakpoint. The width each breakpoint activates at can be changed with the `screenBreakpoints` config. + * The number of columns to display. Can be set as a number or an object of breakpoint values (e.g. `{ xs: 2, sm: 3, md: 4 }`). Breakpoints are matched against the gallery's own width rather than the screen width. The width each breakpoint activates at can be changed with the `screenBreakpoints` config. * @default { xs: 2, sm: 3, md: 4, lg: 6, xl: 8, xxl: 10, } */ "columns"?: GalleryColumns; /** - * The space between gallery items. Accepts valid CSS [length-percentage](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/length-percentage) values like `16px`, `1rem`, `20%`, math functions like `calc(10px + 20%)`, CSS variables like `var(--app-gallery-gap)`, or numbers (treated as pixel values). Can also be set as a breakpoint map (e.g. `{ xs: '8px', sm: '1rem', md: '24px' }`). Does not accept space-separated values or CSS keyword values like `inherit`, `auto`, etc. A breakpoint is matched against the gallery's own width rather than the width of the screen, so a narrow gallery on a wide screen resolves to a small breakpoint. The width each breakpoint activates at can be changed with the `screenBreakpoints` config. + * The space between gallery items. Accepts valid CSS [length-percentage](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/length-percentage) values like `16px`, `1rem`, `20%`, math functions like `calc(10px + 20%)`, CSS variables like `var(--app-gallery-gap)`, or numbers (treated as pixel values). Can also be set as a breakpoint map (e.g. `{ xs: '8px', sm: '1rem', md: '24px' }`). Does not accept space-separated values or CSS keyword values like `inherit`, `auto`, etc. Breakpoints are matched against the gallery's own width rather than the screen width. The width each breakpoint activates at can be changed with the `screenBreakpoints` config. * @default '16px' */ "gap"?: GalleryGap; diff --git a/core/src/components/gallery/gallery.tsx b/core/src/components/gallery/gallery.tsx index c5403c00851..1d810c6c8d1 100644 --- a/core/src/components/gallery/gallery.tsx +++ b/core/src/components/gallery/gallery.tsx @@ -64,10 +64,9 @@ export class Gallery implements ComponentInterface { * The number of columns to display. Can be set as a number or an object of * breakpoint values (e.g. `{ xs: 2, sm: 3, md: 4 }`). * - * A breakpoint is matched against the gallery's own width rather than the - * width of the screen, so a narrow gallery on a wide screen resolves to a - * small breakpoint. The width each breakpoint activates at can be changed - * with the `screenBreakpoints` config. + * Breakpoints are matched against the gallery's own width rather than the + * screen width. The width each breakpoint activates at can be changed with + * the `screenBreakpoints` config. */ @Prop() columns: GalleryColumns = DEFAULT_COLUMNS; @@ -79,10 +78,9 @@ export class Gallery implements ComponentInterface { * (e.g. `{ xs: '8px', sm: '1rem', md: '24px' }`). Does not accept * space-separated values or CSS keyword values like `inherit`, `auto`, etc. * - * A breakpoint is matched against the gallery's own width rather than the - * width of the screen, so a narrow gallery on a wide screen resolves to a - * small breakpoint. The width each breakpoint activates at can be changed - * with the `screenBreakpoints` config. + * Breakpoints are matched against the gallery's own width rather than the + * screen width. The width each breakpoint activates at can be changed with + * the `screenBreakpoints` config. */ @Prop() gap: GalleryGap = DEFAULT_GAP; @@ -294,9 +292,6 @@ export class Gallery implements ComponentInterface { /** * Resolve a responsive value from a breakpoint map. * Uses a breakpoint-specific default when custom values are missing/invalid. - * - * Note that the breakpoints are compared against the gallery's own width - * rather than the width of the screen. */ private resolveFromBreakpoints( width: number,