Skip to content

Commit

Permalink
Merge pull request #1686 from guardian/db/fix-image-ratio-to-allow-sq…
Browse files Browse the repository at this point in the history
…uare

Square cropped grid image and thumbnail change for chef card
  • Loading branch information
Divs-B authored Oct 14, 2024
2 parents cb2609a + 60a9d7d commit 27709ff
Show file tree
Hide file tree
Showing 9 changed files with 123 additions and 27 deletions.
1 change: 1 addition & 0 deletions fronts-client/src/components/card/chef/ChefCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export const ChefCard = ({
<ImageAndGraphWrapper size="small">
<ThumbnailSmall
url={chef?.chefImageOverride?.src ?? chef?.bylineLargeImageUrl}
showSquareThumbnail={true}
/>
</ImageAndGraphWrapper>
<HoverActionsAreaOverlay data-testid="hover-overlay">
Expand Down
4 changes: 2 additions & 2 deletions fronts-client/src/components/form/ChefMetaForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { Chef } from 'types/Chef';
import { useSelector } from 'react-redux';
import InputTextArea from 'components/inputs/InputTextArea';
import InputImage from 'components/inputs/InputImage';
import { defaultCardTrailImageCriteria } from 'constants/image';
import { squareImageCriteria } from 'constants/image';
import { ImageOptionsInputGroup } from './ImageOptionsInputGroup';
import Row from 'components/Row';
import { ImageRowContainer } from './ImageRowContainer';
Expand Down Expand Up @@ -118,7 +118,7 @@ const Form = ({
<Field
name="chefImageOverride"
component={InputImage}
criteria={defaultCardTrailImageCriteria}
criteria={squareImageCriteria}
/>
</ImageCol>
</Row>
Expand Down
22 changes: 18 additions & 4 deletions fronts-client/src/components/image/ImageInputImageContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,32 @@ const normalLandscape54Style = `
maxWidth: 180px;
`;

const squareStyle = `
width: ${NORMAL_PORTRAIT_WIDTH}px;
height: ${Math.floor(NORMAL_PORTRAIT_WIDTH + TEXTINPUT_HEIGHT)}px;
`;

const getVariableImageContainerStyle = ({
portrait = false,
small = false,
shouldShowLandscape54: shouldShowLandscape54 = false,
showSquare = false,
}: {
small?: boolean;
portrait?: boolean;
shouldShowLandscape54?: boolean;
showSquare?: boolean;
}) => {
if (portrait) return small ? smallPortaitStyle : normalPortraitStyle;
else if (shouldShowLandscape54)
return small ? smallLandscape54Style : normalLandscape54Style;
else return small ? smallLandscapeStyle : normalLandscapeStyle;
switch (true) {
case showSquare:
return squareStyle;
case portrait:
return small ? smallPortaitStyle : normalPortraitStyle;
case shouldShowLandscape54:
return small ? smallLandscape54Style : normalLandscape54Style;
default:
return small ? smallLandscapeStyle : normalLandscapeStyle;
}
};

// assuming any portrait image (ie height>width)
Expand All @@ -68,6 +81,7 @@ export const ImageInputImageContainer = styled.div<{
small?: boolean;
portrait?: boolean;
shouldShowLandscape54?: boolean;
showSquare?: boolean;
}>`
display: flex;
flex-direction: column;
Expand Down
9 changes: 9 additions & 0 deletions fronts-client/src/components/image/Thumbnail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const ThumbnailSmall = styled(ThumbnailBase)<{
imageHide?: boolean;
isPortrait?: boolean;
showLandscape54?: boolean;
showSquareThumbnail?: boolean;
}>`
position: relative;
width: ${theme.thumbnailImage.width};
Expand Down Expand Up @@ -46,6 +47,14 @@ const ThumbnailSmall = styled(ThumbnailBase)<{
top: 18px;
left: 9px;
}`};
${({ showSquareThumbnail }) =>
showSquareThumbnail &&
`width: ${theme.thumbnailImageSquare.width};
min-width: ${theme.thumbnailImageSquare.width};
height: ${theme.thumbnailImageSquare.height}
aspect-ratio: 1/1;
`};
`;

const ThumbnailCutout = styled.img<{
Expand Down
47 changes: 28 additions & 19 deletions fronts-client/src/components/inputs/InputImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
DRAG_DATA_GRID_IMAGE_URL,
landscape5To4CardImageCriteria,
portraitCardImageCriteria,
squareImageCriteria,
} from 'constants/image';
import ImageDragIntentIndicator from 'components/image/ImageDragIntentIndicator';
import { ImageInputImageContainer as ImageContainer } from 'components/image/ImageInputImageContainer';
Expand Down Expand Up @@ -284,7 +285,7 @@ class InputImage extends React.Component<ComponentProps, ComponentState> {
const {
small = false,
input,
gridUrl:gridBaseUrl,
gridUrl: gridBaseUrl,
useDefault,
defaultImageUrl,
message = 'Replace image',
Expand All @@ -305,21 +306,24 @@ class InputImage extends React.Component<ComponentProps, ComponentState> {
);
}


const hasImage = !useDefault && !!input.value && !!input.value.thumb;
const imageUrl =
!useDefault && input.value && input.value.thumb
? input.value.thumb
: defaultImageUrl;

// e.g. https://media.guim.co.uk/db6bf997dee6d43f8dca1ab9cd2c7402725434b6/0_214_3960_2376/500.jpg
const maybeDefaultImagePathParts = defaultImageUrl && new URL(defaultImageUrl).pathname.split("/");
const maybeDefaultImageId = maybeDefaultImagePathParts?.[1] // pathname starts with / so index 0 is empty string
const maybeDefaultCropId = maybeDefaultImagePathParts?.[2]
const gridUrl = this.state.isRecropping && maybeDefaultImageId && maybeDefaultCropId
? `${gridBaseUrl}/images/${maybeDefaultImageId}/crop?seedCropId=${maybeDefaultCropId}&`
: `${gridBaseUrl}?`;
const gridModalUrl = `${gridUrl}${new URLSearchParams(this.criteriaToGridQueryParams()).toString()}`
const maybeDefaultImagePathParts =
defaultImageUrl && new URL(defaultImageUrl).pathname.split('/');
const maybeDefaultImageId = maybeDefaultImagePathParts?.[1]; // pathname starts with / so index 0 is empty string
const maybeDefaultCropId = maybeDefaultImagePathParts?.[2];
const gridUrl =
this.state.isRecropping && maybeDefaultImageId && maybeDefaultCropId
? `${gridBaseUrl}/images/${maybeDefaultImageId}/crop?seedCropId=${maybeDefaultCropId}&`
: `${gridBaseUrl}?`;
const gridModalUrl = `${gridUrl}${new URLSearchParams(
this.criteriaToGridQueryParams()
).toString()}`;

const portraitImage = !!(
!useDefault &&
Expand All @@ -329,6 +333,10 @@ class InputImage extends React.Component<ComponentProps, ComponentState> {
const shouldShowLandscape54 =
criteria != null &&
this.compareAspectRatio(landscape5To4CardImageCriteria, criteria);
const showSquare =
criteria != null &&
this.compareAspectRatio(squareImageCriteria, criteria);

return (
<InputImageContainer
small={small}
Expand All @@ -353,6 +361,7 @@ class InputImage extends React.Component<ComponentProps, ComponentState> {
small={small}
portrait={portraitImage}
shouldShowLandscape54={shouldShowLandscape54}
showSquare={showSquare}
>
<ImageComponent
style={{
Expand Down Expand Up @@ -589,32 +598,32 @@ class InputImage extends React.Component<ComponentProps, ComponentState> {
private criteriaToGridQueryParams = (): Record<string, string> => {
const { criteria, editMode } = this.props;

if(editMode === "editions"){
if (editMode === 'editions') {
return {};
}

if (!criteria) {
return {
cropType: "portrait,landscape"
cropType: 'portrait,landscape',
};
}

// assumes the only criteria that will be passed as props the defined
// constants for portrait(4:5), landscape (5:3) and landscape (5:4)
if (this.compareAspectRatio(portraitCardImageCriteria, criteria)) {
return {
cropType: "portrait"
cropType: 'portrait',
};
}
else if (this.compareAspectRatio(landscape5To4CardImageCriteria, criteria)) {
} else if (
this.compareAspectRatio(landscape5To4CardImageCriteria, criteria)
) {
return {
cropType: "Landscape",
customRatio: "Landscape,5,4"
cropType: 'Landscape',
customRatio: 'Landscape,5,4',
};
}
else {
} else {
return {
cropType: "landscape"
cropType: 'landscape',
};
}
};
Expand Down
8 changes: 7 additions & 1 deletion fronts-client/src/constants/image.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pageConfig from 'util/extractConfigFromPage';
import {
FLEXIBLE_GENERAL_NAME,
FLEXIBLE_SPECIAL_NAME
FLEXIBLE_SPECIAL_NAME,
} from './flexibleContainers';

export const SUPPORT_PORTRAIT_CROPS =
Expand All @@ -15,6 +15,12 @@ export const landScapeCardImageCriteria = {
heightAspectRatio: 3,
};

export const squareImageCriteria = {
minWidth: 400,
widthAspectRatio: 1,
heightAspectRatio: 1,
};

export const portraitCardImageCriteria = {
minWidth: 400,
widthAspectRatio: 4,
Expand Down
6 changes: 6 additions & 0 deletions fronts-client/src/constants/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,11 @@ const thumbnailImage = {
height: '50px',
};

const thumbnailImageSquare = {
width: '50px',
height: '50px',
};

export const theme = {
base,
front,
Expand All @@ -167,6 +172,7 @@ export const theme = {
card,
collection,
thumbnailImage,
thumbnailImageSquare,
};

export type Theme = typeof theme;
Expand Down
53 changes: 52 additions & 1 deletion fronts-client/src/util/__tests__/validateImageSrc.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
import ImageMock from 'util/ImageMock';
import grid from 'util/grid';
import { landScapeCardImageCriteria } from 'constants/image';

(global as any).Image = ImageMock;

jest.mock('constants/url', () => ({
Expand Down Expand Up @@ -130,6 +129,58 @@ describe('Validate images', () => {
);
});

it('fails if the aspect ratio not square(example chef thumbnails)', (done) => {
const criteria = {
widthAspectRatio: 1,
heightAspectRatio: 1,
};

grid.gridInstance.getImage = () =>
Promise.resolve({
data: {
exports: [
{
id: 'image_crop',
assets: [
{ dimensions: { width: 1400, height: 1000 } },
{
secureUrl: getPath('notsquare.png'),
dimensions: { width: 140, height: 100 },
},
],
},
],
},
});
ImageMock.defaultWidth = 140;
ImageMock.defaultHeight = 100;

validateImageSrc(getPath('notsquare.png'), 'front', criteria).then(
(err) => done.fail(err.toString()),
(err) => {
expect(err.message).toMatch(/aspect ratio/i);
done();
}
);
});

it('matches with square requirement(example chef thumbnails)', (done) => {
const squareImageCriteria = {
widthAspectRatio: 1,
heightAspectRatio: 1,
};

validateImageSrc(
getPath('square.png'),
'front',
squareImageCriteria
).then((image) => {
expect((image as ValidationResponse).width).toBe(100);
expect((image as ValidationResponse).height).toBe(100);
done();
});
});

it('works with no criteria', (done) => {
validateImageSrc(getPath('square.png'), 'front').then(
(image) => {
Expand Down
Binary file added public/test/fixtures/notsquare.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 27709ff

Please sign in to comment.