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
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,17 @@ import { MAX_ZOOM } from '../../image-editor/core/constants';
import { getMinZoom } from '../../image-editor/core/containment';
import type { AspectRatioPreset } from '../../image-editor/core/constants';

const ZOOM_PERCENTAGE_SCALE = 100;
const MAX_ZOOM_PERCENTAGE = MAX_ZOOM * ZOOM_PERCENTAGE_SCALE;

function getZoomPercentageForDisplay( zoom: number ): number {
return Math.round( zoom * ZOOM_PERCENTAGE_SCALE );
}

function getMinZoomPercentageForDisplay( zoom: number ): number {
return Math.ceil( zoom * ZOOM_PERCENTAGE_SCALE );
}

export interface MediaEditorCropPanelProps {
/**
* Selected aspect-ratio preset value as a string (so it round-trips
Expand Down Expand Up @@ -63,6 +74,8 @@ export default function MediaEditorCropPanel( {
const { state, setZoom } = useMediaEditor();
const zoomGestureHandlers = useCropGestureHandlers();
const minZoom = getMinZoom( state );
const zoomPercentage = getZoomPercentageForDisplay( state.zoom );
const minZoomPercentage = getMinZoomPercentageForDisplay( minZoom );

return (
// Tag the whole panel as a crop-control region so the modal's
Expand Down Expand Up @@ -94,22 +107,29 @@ export default function MediaEditorCropPanel( {
<div role="presentation" { ...zoomGestureHandlers }>
<RangeControl
__next40pxDefaultSize
label={ __( 'Zoom' ) }
min={ minZoom }
max={ MAX_ZOOM }
step={ 0.1 }
value={ state.zoom }
label={ __( 'Zoom (%)' ) }
min={ minZoomPercentage }
max={ MAX_ZOOM_PERCENTAGE }
step={ 1 }
shiftStep={ 10 }
Comment thread
ramonjd marked this conversation as resolved.
Resolved
value={ zoomPercentage }
onChange={ ( value ) => {
onPlacementControlInteraction?.();
setZoom( typeof value === 'number' ? value : minZoom );
setZoom(
typeof value === 'number'
? value / ZOOM_PERCENTAGE_SCALE
: minZoom
);
} }
renderTooltipContent={ ( value ) => {
const zoom =
typeof value === 'number' ? value : minZoom;
const percentage =
typeof value === 'number'
? value
: minZoomPercentage;
return sprintf(
/* translators: %d: zoom level as a percentage. */
__( '%d%%' ),
Math.round( zoom * 100 )
Math.round( percentage )
);
} }
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ import { fireEvent, render, screen } from '@testing-library/react';
*/
import MediaEditorCropPanel from '..';
import type { MediaEditorCropPanelProps } from '..';
import { MediaEditorStateProvider } from '../../../state';
import { MediaEditorStateProvider, useMediaEditor } from '../../../state';
import type { CropperState } from '../../../image-editor';

function setupCropPanel(
overrides: Partial< MediaEditorCropPanelProps > = {}
overrides: Partial< MediaEditorCropPanelProps > = {},
initialCropperState?: Partial< CropperState >
) {
const props: MediaEditorCropPanelProps = {
aspectRatioValue: '1',
Expand All @@ -27,18 +29,19 @@ function setupCropPanel(
};

render(
<MediaEditorStateProvider>
<MediaEditorStateProvider initialCropperState={ initialCropperState }>
<MediaEditorCropPanel { ...props } />
<CurrentZoomValue />
</MediaEditorStateProvider>
);

return props;
}

function expectElementBefore( first: HTMLElement, second: HTMLElement ) {
expect( first.compareDocumentPosition( second ) ).toBe(
Node.DOCUMENT_POSITION_FOLLOWING
);
function CurrentZoomValue() {
const { state } = useMediaEditor();

return <output data-testid="current-zoom">{ state.zoom }</output>;
}

describe( 'MediaEditorCropPanel', () => {
Expand All @@ -47,10 +50,14 @@ describe( 'MediaEditorCropPanel', () => {

const aspectRatio = screen.getByLabelText( 'Aspect ratio' );
const resizeCropArea = screen.getByLabelText( 'Show resize handles' );
const zoom = screen.getByRole( 'slider', { name: 'Zoom' } );
const zoom = screen.getByRole( 'slider', { name: 'Zoom (%)' } );

expectElementBefore( aspectRatio, resizeCropArea );
expectElementBefore( resizeCropArea, zoom );
expect( aspectRatio.compareDocumentPosition( resizeCropArea ) ).toBe(
Node.DOCUMENT_POSITION_FOLLOWING
);
expect( resizeCropArea.compareDocumentPosition( zoom ) ).toBe(
Node.DOCUMENT_POSITION_FOLLOWING
);
} );

it( 'passes selected aspect ratio changes to the caller', () => {
Expand Down Expand Up @@ -80,4 +87,35 @@ describe( 'MediaEditorCropPanel', () => {

expect( controls.onFreeformChange ).toHaveBeenCalledWith( false );
} );

it( 'displays zoom as a percentage without changing cropper state', () => {
setupCropPanel( {}, { zoom: 3.749999999999999 } );

const zoomInput = screen.getByRole( 'spinbutton', {
name: 'Zoom (%)',
} );

expect( zoomInput ).toHaveValue( 375 );
expect( screen.getByTestId( 'current-zoom' ) ).toHaveTextContent(
'3.749999999999999'
);
} );

it( 'converts percentage input back to the cropper zoom multiplier', () => {
const controls = setupCropPanel( {
onPlacementControlInteraction: jest.fn(),
} );

fireEvent.change(
screen.getByRole( 'spinbutton', { name: 'Zoom (%)' } ),
{
target: { value: '250' },
}
);

expect( screen.getByTestId( 'current-zoom' ) ).toHaveTextContent(
'2.5'
);
expect( controls.onPlacementControlInteraction ).toHaveBeenCalled();
} );
} );
Loading