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

feat: add aspect ratio support by calculating client-side #201

Merged
merged 15 commits into from
Oct 11, 2018

Conversation

frederickfogerty
Copy link
Contributor

@frederickfogerty frederickfogerty commented Oct 10, 2018

Description

This PR allows the developer to specify a desired aspect ratio (AR) when creating an image.

This PR is based off the awesome work from @maxkolyanov!

This fixes #161

API

The developer specifies the AR along with normal imgix params, such as fit.

e.g.

<Imgix
  src={src}
  sizes="(max-width: 30em) 100vw, (max-width: 50em) 50vw"
  imgixParams={{ ar: "2:1" }}
/>

The AR has to be specified in the form number:number. When an invalid AR is detected, no height values will be generated, and a warning is thrown.

TODO

  • [N/A] Audit tests
  • Round h up to comply with API spec
  • Add Max as contributor

New Feature

  • If this is a big feature with breaking changes, consider [opening an issue][issues] to discuss first. This is completely up to you, but please keep in mind that your PR might not be accepted.
  • Run unit tests to ensure all existing tests are still passing
  • Add new passing unit tests to cover the code introduced by your PR
  • Update the readme
  • Add some steps so we can test your cool new feature!
  • The PR title is in the conventional commit format: e.g. feat(<area>): added new way to do this cool thing #issue-number
  • Add your info to the contributors array in package.json!

Steps to Test

Review changes to unit tests.

minortext and others added 6 commits September 11, 2018 18:20
* master: (29 commits)
  chore(deps): update dependency puppeteer to v1.9.0 (#200)
  chore(deps): update dependency sinon to v6.3.5 (#199)
  chore(deps): update dependency jest-extended to v0.11.0 (#198)
  chore(deps): update dependency sinon to v6 (#197)
  chore(deps): update dependency karma to v3 (#196)
  chore(deps): update dependency babel-eslint to v10 (#193)
  chore(deps): update jest monorepo to v23.6.0 (#191)
  chore(deps): update dependency webpack-cli to v3.1.2 (#190)
  chore(deps): update dependency webpack to v4.20.2 (#189)
  chore(deps): update dependency standard-version to v4.4.0 (#188)
  chore(deps): update dependency puppeteer to v1.8.0 (#187)
  chore(deps): update dependency jest-extended to v0.10.0 (#178)
  chore(deps): update dependency webpack-dev-server to v3.1.9 (#186)
  chore(deps): update dependency karma-webpack to v3.0.5 (#184)
  chore(deps): update dependency cross-env to v5.2.0 (#175)
  chore(deps): update dependency prettier to v1.14.3 (#182)
  chore(renovate): update automerge config (#185)
  chore(renovate): enable auto-merging (#183)
  chore(deps): update dependency babel-preset-env to v1.7.0 (#174)
  chore(travis): only run travis branch builds on master (#179)
  ...
@frederickfogerty frederickfogerty mentioned this pull request Oct 10, 2018
9 tasks
Copy link

@ghost ghost left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some questions & comments 🙂


const [width, height] = aspectRatio.split(":");

return width / height;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't want to be nitpicky, but this feels a bit obscure. 💭
Is there a reason for not using something like parseFloat explicitly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you saying this should be return parseFloat(width) / parseFloat(height)? I'm not quite understanding this comment

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think he's saying that relying on the implicit type conversion here feels a bit cloudy, and I agree. I'd rather see the parseFloat() calls in there, as you've stated.

Copy link

@ghost ghost Oct 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is exactly what I trying to say!
Sorry, ironically I phrased it weirdly as well.

return false;
}
const isValidFormat = str => !/^\d(\.\d+)?:\d(\.\d+)?$/.test(str);
if (isValidFormat(aspectRatio)) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does isValidFormat meant to be renamed to isInvalidFormat?
That ! in front of regex is a bit hard to notice, it looks like it is a part of the regex.

Alternatively, I'm in favor of moving ! to condition, something like:
if (!isValidFormat(aspectRatio)) { ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch 🙌

* Parse an aspect ratio in the format w:h to a decimal. If false is returned, the aspect ratio is in the wrong format.
*/
function parseAspectRatio(aspectRatio) {
if (typeof aspectRatio == null) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is potentially redundant.
It would be sufficient to make sure that aspectRatio is a string.
So check below would be sufficient on its own.

...srcOptions,
width: targetWidth
});
};
const aspectRatioDecimal = aspectRatio && parseAspectRatio(aspectRatio);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following on comments on type checks in parseAspectRatio;
It seems to be sufficient to just pass aspectRatio into parseAspectRatio and throw warning if false was returned.

});
};
const aspectRatioDecimal = aspectRatio && parseAspectRatio(aspectRatio);
if (aspectRatio != null && aspectRatioDecimal === false) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above.
If all type checks just happen inside parseAspectRatio, aspectRatio != null can be removed.

Copy link
Contributor Author

@frederickfogerty frederickfogerty Oct 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason for having aspectRatio != null here is so that a warning is only thrown when the user has tried to pass something. e.g. it wouldn't throw a warning when no prop is passed, but it would when the user passes "". Do you think it's still valid? @maxkolyanov

Copy link

@ghost ghost Oct 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call! Totally makes sense to check if the prop is there, before showing a warning.
One question I guess is wouldn't it be equal to undefined then instead of null, if the prop is absent/not passed?

Copy link
Contributor Author

@frederickfogerty frederickfogerty Oct 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely! This is the reason for using != rather than !==, as this covers both undefined and null.

i.e.

undefined != null // false -- this is what we want
undefined !== null // true -- this would break the conditional check

Does this make sense?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally! Great work on PR, LGTM 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @maxkolyanov! 🙌 thanks again for your work for the base for this PR.

@@ -38,6 +38,7 @@ const COMMON_PROP_TYPES = {

const SHARED_IMGIX_AND_SOURCE_PROP_TYPES = {
...COMMON_PROP_TYPES,
aspectRatio: PropTypes.number,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like aspectRatio is a string

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was fixed in a commit I didn't push 😬

if (typeof aspectRatio !== "string") {
return false;
}
const isValidFormat = str => /^\d(\.\d+)?:\d(\.\d+)?$/.test(str);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't allow inputs with more than one digit left of the decimal point, right? Should be /^\d+(\.\d+)?:\d+(\.\d+)?$/, I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we also update the unit tests to make sure this is covered?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, all done!


const [width, height] = aspectRatio.split(":");

return width / height;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think he's saying that relying on the implicit type conversion here feels a bit cloudy, and I agree. I'd rather see the parseFloat() calls in there, as you've stated.

aspectRatioDecimal &&
aspectRatioDecimal > 0
) {
urlParams.height = Math.round(targetWidth / aspectRatioDecimal);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After discussing this with some other engineers internally, this should be Math.ceil() to match the behavior in our backend when rounding dimensions.

@frederickfogerty
Copy link
Contributor Author

@maxkolyanov @jayeb I think I've covered everything that was brought up. Can I please get another quick review?

@frederickfogerty frederickfogerty merged commit 7ce0411 into master Oct 11, 2018
@frederickfogerty frederickfogerty deleted the fred/aspect-ratio branch October 11, 2018 22:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Ability to set aspectRatio
3 participants