Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/editor/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

### Bug Fixes

- Device Preview: Keep tablet and mobile iframe widths inside their responsive breakpoints so media queries remain accurate at browser zoom levels.
- Document tools: Fix icon button focus styles to use the design system `outset-ring__focus` mixin ([#81115](https://github.com/WordPress/gutenberg/pull/81115)).
- `mediaUpload`: Add an `isTransportOnly` parameter, set by the `@wordpress/upload-media` queue, which owns progress tracking and save locking for its own items and uses this function only as its server transport. Fixes the progress snackbar showing "1 of 2" for a single HEIC upload in Safari ([#80369](https://github.com/WordPress/gutenberg/issues/80369)).

Expand Down
4 changes: 2 additions & 2 deletions packages/editor/src/store/test/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ describe( 'Post actions', () => {
} );

describe( 'setDeviceType', () => {
it( 'sets the canvas width using custom rem viewport settings', () => {
it( 'sets the canvas one pixel inside a custom rem viewport breakpoint', () => {
const registry = createRegistryWithStores();

registry.dispatch( blockEditorStore ).updateSettings( {
Expand All @@ -158,7 +158,7 @@ describe( 'Post actions', () => {

expect(
unlock( registry.select( editorStore ) ).getCanvasWidth()
).toBe( 1024 );
).toBe( 1023 );
expect( registry.select( editorStore ).getDeviceType() ).toBe(
'Tablet'
);
Expand Down
24 changes: 12 additions & 12 deletions packages/editor/src/store/test/private-selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -278,16 +278,16 @@ describe( 'getCanvasHeight', () => {
};
}

it( 'returns the portrait height at the mobile preset width', () => {
// Mobile aspect ratio is 8/5 (portrait): 480 * 8/5 = 768.
it( 'keeps the portrait aspect ratio at the inset mobile preview width', () => {
// Mobile aspect ratio is 8/5 (portrait): 479 * 8/5 = 766 (rounded).
setupRegistry();
expect( getCanvasHeight( { canvasWidth: 480 } ) ).toBe( 768 );
expect( getCanvasHeight( { canvasWidth: 479 } ) ).toBe( 766 );
} );

it( 'returns the portrait height at the tablet preset width', () => {
// Tablet aspect ratio is 4/3 (portrait): 782 * 4/3 = 1043.
it( 'keeps the portrait aspect ratio at the inset tablet preview width', () => {
// Tablet aspect ratio is 4/3 (portrait): 781 * 4/3 = 1041 (rounded).
setupRegistry();
expect( getCanvasHeight( { canvasWidth: 782 } ) ).toBe( 1043 );
expect( getCanvasHeight( { canvasWidth: 781 } ) ).toBe( 1041 );
} );

it( 'returns undefined for desktop (no aspect ratio applies)', () => {
Expand All @@ -297,12 +297,12 @@ describe( 'getCanvasHeight', () => {

it( 'returns undefined when zoom-out is active', () => {
setupRegistry( { isZoomOut: true } );
expect( getCanvasHeight( { canvasWidth: 480 } ) ).toBeUndefined();
expect( getCanvasHeight( { canvasWidth: 479 } ) ).toBeUndefined();
} );

it( 'returns undefined when the width is dragged within a device band but is not the preset', () => {
// 400 resolves to Mobile (at or below the 480 breakpoint) but is not the
// 480 preset, so the device height does not apply and the canvas fills.
// 479 preset, so the device height does not apply and the canvas fills.
setupRegistry();
expect( getCanvasHeight( { canvasWidth: 400 } ) ).toBeUndefined();
} );
Expand All @@ -313,12 +313,12 @@ describe( 'getCanvasHeight', () => {
} );

it( 'uses custom viewport breakpoints when provided', () => {
// Custom mobile preset 640: 640 * 8/5 = 1024.
// Custom mobile preset 639: 639 * 8/5 = 1022 (rounded).
setupRegistry( {
viewport: { mobile: '640px', tablet: '1024px' },
} );
expect( getCanvasHeight( { canvasWidth: 640 } ) ).toBe( 1024 );
// Custom tablet preset 1024: 1024 * 4/3 = 1365 (rounded).
expect( getCanvasHeight( { canvasWidth: 1024 } ) ).toBe( 1365 );
expect( getCanvasHeight( { canvasWidth: 639 } ) ).toBe( 1022 );
// Custom tablet preset 1023: 1023 * 4/3 = 1364.
expect( getCanvasHeight( { canvasWidth: 1023 } ) ).toBe( 1364 );
} );
} );
45 changes: 31 additions & 14 deletions packages/editor/src/utils/device-type.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const VIEWPORT_KEY_BY_DEVICE_TYPE = {
const DESKTOP_DEVICE_TYPE = 'Desktop';
const TABLET_DEVICE_TYPE = 'Tablet';
const MOBILE_DEVICE_TYPE = 'Mobile';
const DEVICE_PREVIEW_WIDTH_OFFSET = 1;

/**
* Maps a device preview type to its corresponding viewport style state. Used
Expand All @@ -43,24 +44,19 @@ export const VIEWPORT_STATE_BY_DEVICE_TYPE = {
*/
export function getDeviceTypeByCanvasWidth( canvasWidth, viewportSettings ) {
const width = getViewportBreakpointValueInPixels( canvasWidth );
const breakpoints = getViewportBreakpoints( viewportSettings );

// Mobile
if (
width &&
width <=
getViewportBreakpointValueInPixels(
getCanvasWidthByDeviceType( 'Mobile', viewportSettings )
)
width <= getViewportBreakpointValueInPixels( breakpoints.mobile )
) {
return MOBILE_DEVICE_TYPE;
}
// Tablet
if (
width &&
width <=
getViewportBreakpointValueInPixels(
getCanvasWidthByDeviceType( 'Tablet', viewportSettings )
)
width <= getViewportBreakpointValueInPixels( breakpoints.tablet )
) {
return TABLET_DEVICE_TYPE;
}
Expand All @@ -69,18 +65,39 @@ export function getDeviceTypeByCanvasWidth( canvasWidth, viewportSettings ) {
}

/**
* Get the canvas width by device type.
* Gets the canvas width for a device preview. The preview is inset from its
* breakpoint to avoid browser zoom rounding the iframe viewport outside the
* intended media query.
*
* @param {string} deviceType The device type.
* @param {Object} viewportSettings Optional viewport breakpoint settings.
* @return {number|undefined} The canvas width in pixels.
* @return {number|undefined} The device preview width in pixels.
*/
export function getCanvasWidthByDeviceType( deviceType, viewportSettings ) {
const viewportKey = VIEWPORT_KEY_BY_DEVICE_TYPE[ deviceType ];

if ( viewportKey ) {
return getViewportBreakpointValueInPixels(
getViewportBreakpoints( viewportSettings )[ viewportKey ]
);
if ( ! viewportKey ) {
return undefined;
}

const breakpoints = getViewportBreakpoints( viewportSettings );
const width = getViewportBreakpointValueInPixels(
breakpoints[ viewportKey ]
);

if ( width === undefined ) {
return undefined;
}

let lowerBreakpoint = 0;
if ( deviceType === TABLET_DEVICE_TYPE ) {
lowerBreakpoint =
getViewportBreakpointValueInPixels( breakpoints.mobile ) ?? 0;
}
const offset = Math.min(
DEVICE_PREVIEW_WIDTH_OFFSET,
( width - lowerBreakpoint ) / 2
);

return width - offset;
}
40 changes: 28 additions & 12 deletions packages/editor/src/utils/test/device-type.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,37 @@ import {
} from '../device-type';

describe( 'device type utilities', () => {
it( 'uses default viewport breakpoints when viewport settings are not provided', () => {
expect( getCanvasWidthByDeviceType( 'Mobile' ) ).toBe( 480 );
expect( getCanvasWidthByDeviceType( 'Tablet' ) ).toBe( 782 );

it( 'classifies widths using default viewport breakpoints when viewport settings are not provided', () => {
expect( getDeviceTypeByCanvasWidth( 480 ) ).toBe( 'Mobile' );
expect( getDeviceTypeByCanvasWidth( 481 ) ).toBe( 'Tablet' );
expect( getDeviceTypeByCanvasWidth( 782 ) ).toBe( 'Tablet' );
expect( getDeviceTypeByCanvasWidth( 783 ) ).toBe( 'Desktop' );
expect( getDeviceTypeByCanvasWidth( undefined ) ).toBe( 'Desktop' );
} );

it( 'uses default viewport breakpoints when viewport settings are provided', () => {
expect( getCanvasWidthByDeviceType( 'Mobile', {} ) ).toBe( 480 );
expect( getCanvasWidthByDeviceType( 'Tablet', {} ) ).toBe( 782 );
it( 'places default device preview widths one pixel inside their breakpoints', () => {
expect( getCanvasWidthByDeviceType( 'Mobile' ) ).toBe( 479 );
expect( getCanvasWidthByDeviceType( 'Tablet' ) ).toBe( 781 );
expect( getCanvasWidthByDeviceType( 'Desktop' ) ).toBeUndefined();
} );

it( 'keeps a tablet preview inside a tablet range narrower than one pixel', () => {
const viewportSettings = {
mobile: '480px',
tablet: '480.5px',
};
const previewWidth = getCanvasWidthByDeviceType(
'Tablet',
viewportSettings
);

expect( previewWidth ).toBe( 480.25 );
expect(
getDeviceTypeByCanvasWidth( previewWidth, viewportSettings )
).toBe( 'Tablet' );
} );

it( 'classifies widths using default viewport breakpoints when viewport settings are empty', () => {
expect( getDeviceTypeByCanvasWidth( '480px', {} ) ).toBe( 'Mobile' );
expect( getDeviceTypeByCanvasWidth( 782, {} ) ).toBe( 'Tablet' );
} );
Expand All @@ -33,10 +49,10 @@ describe( 'device type utilities', () => {
};

expect( getCanvasWidthByDeviceType( 'Mobile', viewportSettings ) ).toBe(
640
639
);
expect( getCanvasWidthByDeviceType( 'Tablet', viewportSettings ) ).toBe(
1024
1023
);

expect( getDeviceTypeByCanvasWidth( 640, viewportSettings ) ).toBe(
Expand All @@ -57,7 +73,7 @@ describe( 'device type utilities', () => {
};

expect( getCanvasWidthByDeviceType( 'Tablet', viewportSettings ) ).toBe(
1024
1023
);
expect( getDeviceTypeByCanvasWidth( 640, viewportSettings ) ).toBe(
'Mobile'
Expand All @@ -76,7 +92,7 @@ describe( 'device type utilities', () => {
undefined
);
expect( getCanvasWidthByDeviceType( 'Tablet', viewportSettings ) ).toBe(
1024
1023
);
expect( getDeviceTypeByCanvasWidth( 800, viewportSettings ) ).toBe(
'Tablet'
Expand All @@ -93,7 +109,7 @@ describe( 'device type utilities', () => {
};

expect( getCanvasWidthByDeviceType( 'Mobile', viewportSettings ) ).toBe(
1024
1023
);
expect( getCanvasWidthByDeviceType( 'Tablet', viewportSettings ) ).toBe(
undefined
Expand Down
Loading