-
Notifications
You must be signed in to change notification settings - Fork 395
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
Add canonical link tags #6466
Add canonical link tags #6466
Conversation
Codecov Report
@@ Coverage Diff @@
## master mozilla/addons-frontend#6466 +/- ##
==========================================
+ Coverage 97.82% 97.82% +<.01%
==========================================
Files 239 239
Lines 6475 6488 +13
Branches 1238 1239 +1
==========================================
+ Hits 6334 6347 +13
Misses 126 126
Partials 15 15
Continue to review full report at Codecov.
|
f44f0d5
to
fcae912
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@willdurand, I have some minor comments/questions noted but this looks really good to me 👍
r+wc
config/lib/shared.js
Outdated
@@ -6,4 +6,8 @@ export const apiDevHost = 'https://addons-dev.allizom.org'; | |||
export const apiProdHost = 'https://addons.mozilla.org'; | |||
export const apiStageHost = 'https://addons.allizom.org'; | |||
|
|||
export const baseUrlDev = 'https://addons-dev.allizom.org'; | |||
export const baseUrlProd = 'https://addons.mozilla.org'; | |||
export const baseUrlStage = 'https://addons.allizom.org'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since these are the same as above, could we use those?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you mean: baseUrlDev = apiDevHost
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeh that's what I was thinking (since it's the same)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have some questions and change requests. I'm not sure that it's a good idea to execute the URL calculation in every mapStateToProps
so I offered an alternative. Let me know what you think.
src/amo/pages/LanguageTools/index.js
Outdated
@@ -67,14 +69,21 @@ export const LanguageToolList = ({ languageTools }: LanguageToolListProps) => { | |||
}; | |||
|
|||
type Props = {| | |||
languageTools: Array<LanguageToolType>, | |||
// eslint-disable-next-line react/no-unused-prop-types |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need this for pretty much any file that uses Flow so I'd just put it at the top.
tests/unit/amo/test_utils.js
Outdated
const lang = 'fr'; | ||
|
||
const _config = getFakeConfig({ baseURL }); | ||
const { state } = dispatchClientMetadata({ clientApp, lang }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than rely on the implicit pathname
set by dispatchClientMetadata
, this should set an explicit pathname
since that's integral to the test.
src/amo/pages/LandingPage/index.js
Outdated
const { landing, viewContext } = state; | ||
|
||
return { | ||
addonTypeOfResults: landing.addonType, | ||
currentURL: getCurrentURL({ state, _config: ownProps._config }), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than execute this in every mapStateToProps
(which would happen a lot), how about just passing through the state we need? That would cause the function to only be executed when the state value changes and is more in line with how we typically do things in mapStateToProps
.
Example:
diff --git a/src/amo/pages/LandingPage/index.js b/src/amo/pages/LandingPage/index.js
index 67a639446..cceeb9a94 100644
--- a/src/amo/pages/LandingPage/index.js
+++ b/src/amo/pages/LandingPage/index.js
@@ -12,7 +12,7 @@ import { setViewContext } from 'amo/actions/viewContext';
import LandingAddonsCard from 'amo/components/LandingAddonsCard';
import NotFound from 'amo/components/ErrorPage/NotFound';
import Categories from 'amo/components/Categories';
-import { getCurrentURL } from 'amo/utils';
+import { getCanonicalURL } from 'amo/utils';
import {
ADDON_TYPE_EXTENSION,
ADDON_TYPE_THEME,
@@ -212,11 +212,12 @@ export class LandingPageBase extends React.Component {
render() {
const {
- currentURL,
+ _config,
errorHandler,
featuredAddons,
highlyRatedAddons,
loading,
+ locationPathname,
trendingAddons,
i18n,
} = this.props;
@@ -250,7 +251,10 @@ export class LandingPageBase extends React.Component {
>
<Helmet>
<title>{headingText[addonType]}</title>
- <link rel="canonical" href={currentURL} />
+ <link
+ rel="canonical"
+ href={getCanonicalURL({ pathname: locationPathname, _config })}
+ />
</Helmet>
{errorHandler.renderErrorIfPresent()}
@@ -319,15 +323,15 @@ export class LandingPageBase extends React.Component {
}
export function mapStateToProps(state, ownProps) {
- const { landing, viewContext } = state;
+ const { landing, router, viewContext } = state;
return {
addonTypeOfResults: landing.addonType,
- currentURL: getCurrentURL({ state, _config: ownProps._config }),
context: viewContext.context,
featuredAddons: landing.featured.results,
highlyRatedAddons: landing.highlyRated.results,
loading: landing.loading,
+ locationPathname: router.location.pathname,
trendingAddons: landing.trending.results,
resultsLoaded: landing.resultsLoaded && landing.category === null,
};
diff --git a/src/amo/utils.js b/src/amo/utils.js
index f1f96a1b3..82f0db32e 100644
--- a/src/amo/utils.js
+++ b/src/amo/utils.js
@@ -9,6 +9,7 @@ import NotFound from 'amo/components/ErrorPage/NotFound';
import ServerError from 'amo/components/ErrorPage/ServerError';
import { makeQueryString } from 'core/api';
import type { AppState } from 'amo/store';
+import type { ReactRouterLocationType } from 'core/types/router';
export function getErrorComponent(status: number | null) {
switch (status) {
@@ -56,6 +57,16 @@ export const makeQueryStringWithUTM = ({
});
};
+export const getCanonicalURL = ({
+ _config = config,
+ pathname,
+}: {|
+ _config?: typeof config,
+ pathname: string,
+|}): string => {
+ return `${_config.get('baseURL')}${pathname}`;
+};
+
export const getCurrentURL = ({
_config = config,
state,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than execute this in every
mapStateToProps
(which would happen a lot), how about just passing through the state we need? That would cause the function to only be executed when the state value changes and is more in line with how we typically do things inmapStateToProps
.
I think that does make a lot of sense. I knew something was wrong with this patch but could not see what.. Thanks for your input!
cd0f774
to
6118e2c
Compare
updated and rebased |
3d76ebe
to
36f13e0
Compare
re-rebased |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
r+wc
I am uneasy about copying/pasting onLocatonChange
but at least your approach is simple 🤷♀️ I dunno, I say ship it but maybe we can revisit it later.
src/amo/store.js
Outdated
import type { CreateStoreParams, CreateReducerType } from 'core/types/store'; | ||
|
||
export type AppState = {| | ||
type AppStateWithoutRouter = {| |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we call this AMOAppState
or InternalAppState
? The distinction seems to be that these are all our explicitly mapped reducers whereas AppState
also includes reducers added by third party libraries. The name isn't a big deal, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, I go for InternalAppState
in case there are other reducers we have to move out of this set.
src/core/types/router.js
Outdated
hash: string, // e.g. #some-anchor | ||
key: string, | ||
pathname: string, // e.g. /en-US/firefox/addon/tab-mix-plus/reviews/ | ||
search: string, // e.g. ?q=search-string | ||
state?: Object, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe just add a comment here that sometimes location
contains state
but sometimes it doesn't (in my debugging, I don't see it). This comment would help dissuade someone from relying on this property. I realize that state?
was always defined here (before your patch) but I only just noticed it 😬
Fixes mozilla/addons#12123
This PR adds canonical link tags to the following pages:
pathname
) from the redux state. It ensures that our canonical URLs are in sync with the router config, but that's why test cases don't have the "production" values. It also avoids issues with trailing slashes (because we can trust the middleware and we don't have to think about them).