Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(gatsby-plugin-sharp): presentation sizes #23905

Merged
merged 12 commits into from
May 19, 2020
Original file line number Diff line number Diff line change
Expand Up @@ -352,86 +352,21 @@ exports[`gatsby-plugin-sharp fluid adds pathPrefix if defined 1`] = `
}
`;

exports[`gatsby-plugin-sharp fluid calculate height based on width when maxWidth & maxHeight are present 1`] = `
[MockFunction] {
"calls": Array [
Array [
Object {
"args": Object {
"operations": Array [
Object {
"args": Object {
"height": 5,
"toFormat": "png",
"width": 5,
},
"outputPath": "9363c/test.png",
},
Object {
"args": Object {
"height": 10,
"toFormat": "png",
"width": 10,
},
"outputPath": "480f5/test.png",
},
Object {
"args": Object {
"height": 20,
"toFormat": "png",
"width": 20,
},
"outputPath": "cad77/test.png",
},
Object {
"args": Object {
"height": 30,
"toFormat": "png",
"width": 30,
},
"outputPath": "ef107/test.png",
},
Object {
"args": Object {
"height": 40,
"toFormat": "png",
"width": 40,
},
"outputPath": "4fa69/test.png",
},
Object {
"args": Object {
"height": 281,
"toFormat": "png",
"width": 281,
},
"outputPath": "1a70e/test.png",
},
],
"pluginOptions": Object {
"defaultQuality": 50,
"forceBase64Format": false,
"lazyImageGeneration": true,
"stripMetadata": true,
"useMozJpeg": false,
},
},
"inputPaths": Array [
"<PROJECT_ROOT>/packages/gatsby-plugin-sharp/src/__tests__/images/144-density.png",
],
"name": "IMAGE_PROCESSING",
"outputDir": "<PROJECT_ROOT>/public/static/1234",
},
],
],
"results": Array [
Object {
"type": "return",
"value": Promise {},
},
],
}
`;
exports[`gatsby-plugin-sharp fluid calculate height based on width when maxWidth & maxHeight are present 1`] = `undefined`;

exports[`gatsby-plugin-sharp fluid calculate height based on width when maxWidth & maxHeight are present 2`] = `undefined`;

exports[`gatsby-plugin-sharp fluid calculate height based on width when maxWidth & maxHeight are present 3`] = `undefined`;

exports[`gatsby-plugin-sharp fluid calculate height based on width when maxWidth & maxHeight are present 4`] = `undefined`;

exports[`gatsby-plugin-sharp fluid calculate height based on width when maxWidth & maxHeight are present 5`] = `undefined`;

exports[`gatsby-plugin-sharp fluid calculate height based on width when maxWidth & maxHeight are present 6`] = `undefined`;

exports[`gatsby-plugin-sharp fluid calculate height based on width when maxWidth & maxHeight are present 7`] = `undefined`;

exports[`gatsby-plugin-sharp fluid calculate height based on width when maxWidth & maxHeight are present 8`] = `undefined`;

exports[`gatsby-plugin-sharp fluid does not change the arguments object it is given 1`] = `
[MockFunction] {
Expand Down
52 changes: 44 additions & 8 deletions packages/gatsby-plugin-sharp/src/__tests__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -247,15 +247,51 @@ describe(`gatsby-plugin-sharp`, () => {
})

it(`calculate height based on width when maxWidth & maxHeight are present`, async () => {
const args = { maxWidth: 20, maxHeight: 20 }
const result = await fluid({
file: getFileObject(path.join(__dirname, `images/144-density.png`)),
args,
})
const testsCases = [
{ args: { maxWidth: 20, maxHeight: 20 }, result: [20, 20] },
{
args: { maxWidth: 20, maxHeight: 20, fit: sharp.fit.fill },
result: [20, 20],
},
{
args: { maxWidth: 20, maxHeight: 20, fit: sharp.fit.inside },
result: [20, 10],
},
{
args: { maxWidth: 20, maxHeight: 20, fit: sharp.fit.outside },
result: [41, 20],
},
{ args: { maxWidth: 200, maxHeight: 200 }, result: [200, 200] },
{
args: { maxWidth: 200, maxHeight: 200, fit: sharp.fit.fill },
result: [200, 200],
},
{
args: { maxWidth: 200, maxHeight: 200, fit: sharp.fit.inside },
result: [200, 97],
},
{
args: { maxWidth: 200, maxHeight: 200, fit: sharp.fit.outside },
result: [413, 200],
pieh marked this conversation as resolved.
Show resolved Hide resolved
},
]
const fileObject = getFileObject(
path.join(__dirname, `images/144-density.png`)
)

expect(result.presentationWidth).toEqual(20)
expect(result.presentationHeight).toEqual(10)
expect(boundActionCreators.createJobV2).toMatchSnapshot()
await Promise.all(
testsCases.map(async function (testCase) {
boundActionCreators.createJobV2.mockClear()
const result = await fluid({
file: fileObject,
args: testCase.args,
})

expect(boundActionCreators.createJobV2.calls).toMatchSnapshot()
pieh marked this conversation as resolved.
Show resolved Hide resolved
expect(result.presentationWidth).toEqual(testCase.result[0])
expect(result.presentationHeight).toEqual(testCase.result[1])
})
)
})

it(`should throw if maxWidth is less than 1`, async () => {
Expand Down
42 changes: 25 additions & 17 deletions packages/gatsby-plugin-sharp/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -467,27 +467,19 @@ async function fluid({ file, args = {}, reporter, cache }) {
// if no maxWidth is passed, we need to resize the image based on the passed maxHeight
const fixedDimension =
options.maxWidth === undefined ? `maxHeight` : `maxWidth`
const maxWidth = options.maxWidth
? Math.min(options.maxWidth, width)
: undefined
const maxHeight = options.maxHeight
? Math.min(options.maxHeight, height)
: undefined

if (options[fixedDimension] < 1) {
throw new Error(
`${fixedDimension} has to be a positive int larger than zero (> 0), now it's ${options[fixedDimension]}`
)
}

let presentationWidth, presentationHeight
if (fixedDimension === `maxWidth`) {
presentationWidth = Math.min(options.maxWidth, width)
presentationHeight = Math.round(presentationWidth * (height / width))
} else {
presentationHeight = Math.min(options.maxHeight, height)
presentationWidth = Math.round(presentationHeight * (width / height))
}

// If the users didn't set default sizes, we'll make one.
if (!options.sizes) {
options.sizes = `(max-width: ${presentationWidth}px) 100vw, ${presentationWidth}px`
}

// Create sizes (in width) for the image if no custom breakpoints are
// provided. If the max width of the container for the rendered markdown file
// is 800px, the sizes would then be: 200, 400, 800, 1200, 1600.
Expand Down Expand Up @@ -519,21 +511,26 @@ async function fluid({ file, args = {}, reporter, cache }) {
fluidSizes.push(breakpoint)
})
}
const filteredSizes = fluidSizes.filter(
let filteredSizes = fluidSizes.filter(
size => size < (fixedDimension === `maxWidth` ? width : height)
)

// Add the original image to ensure the largest image possible
// is available for small images. Also so we can link to
// the original image.
filteredSizes.push(fixedDimension === `maxWidth` ? width : height)
filteredSizes = _.sortBy(filteredSizes)

// Queue sizes for processing.
const dimensionAttr = fixedDimension === `maxWidth` ? `width` : `height`
const otherDimensionAttr = fixedDimension === `maxWidth` ? `height` : `width`

const imageWithDensityOneIndex = filteredSizes.findIndex(
size => size === (fixedDimension === `maxWidth` ? maxWidth : maxHeight)
)

// Sort sizes for prettiness.
const transforms = _.sortBy(filteredSizes).map(size => {
const transforms = filteredSizes.map(size => {
const arrrgs = createTransformObject(options)
if (arrrgs[otherDimensionAttr]) {
arrrgs[otherDimensionAttr] = undefined
Expand Down Expand Up @@ -588,6 +585,7 @@ async function fluid({ file, args = {}, reporter, cache }) {
const fallbackSrc = _.minBy(images, image =>
Math.abs(options[fixedDimension] - image[dimensionAttr])
).src

const srcSet = images
.map(image => `${image.src} ${Math.round(image.width)}w`)
.join(`,\n`)
Expand All @@ -614,13 +612,23 @@ async function fluid({ file, args = {}, reporter, cache }) {
}
}

// calculate presentationSizes
const imageWithDensityOne = images[imageWithDensityOneIndex]
const presentationWidth = imageWithDensityOne.width
const presentationHeight = imageWithDensityOne.height

// If the users didn't set default sizes, we'll make one.
const sizes =
options.sizes ||
`(max-width: ${presentationWidth}px) 100vw, ${presentationWidth}px`

return {
base64: base64Image && base64Image.src,
aspectRatio: images[0].aspectRatio,
src: fallbackSrc,
srcSet,
srcSetType,
sizes: options.sizes,
sizes,
originalImg,
originalName,
density,
Expand Down