-
Notifications
You must be signed in to change notification settings - Fork 400
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 Language Tools and Dictionaries page #3289
Conversation
0305862
to
db6b062
Compare
package.json
Outdated
@@ -193,7 +193,8 @@ | |||
"react-redux": "5.0.6", | |||
"react-router": "2.8.1", | |||
"react-router-scroll": "0.4.3", | |||
"react-textarea-autosize": "^5.1.0", | |||
"react-super-responsive-table": "0.3.0", | |||
"react-textarea-autosize": "5.1.0", |
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 should probably have a test for this similar to our devDependencies test that ensures we use the caret.
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.
Oh wow, yeah. I filed https://github.com/mozilla/addons-frontend/issues/3391
Sorry I think this isn't quite ready for review, have to fix a bunch of flow stuff. |
Codecov Report
@@ Coverage Diff @@
## master mozilla/addons-frontend#3289 +/- ##
==========================================
+ Coverage 95.73% 95.76% +0.02%
==========================================
Files 175 176 +1
Lines 3376 3398 +22
Branches 675 680 +5
==========================================
+ Hits 3232 3254 +22
Misses 124 124
Partials 20 20
Continue to review full report at Codecov.
|
Okay I have no idea what's happening with flow and I can't understand this output. Is it broken/buggy or just like this?
Could you have a look at these flow errors @kumar303? I am absolutely stumped and everything works fine otherwise. |
There is one confusing error in here which is saying the type of each item in your Flow is telling you that the type definition causing the mismatch originates from here:
If you open up You can work around it like this which isn't too bad (this makes the errors go away for me): diff --git a/src/amo/components/LanguageTools/index.js b/src/amo/components/LanguageTools/index.js
index 4fc59c12..7d19737a 100644
--- a/src/amo/components/LanguageTools/index.js
+++ b/src/amo/components/LanguageTools/index.js
@@ -64,7 +64,8 @@ export class LanguageToolsBase extends React.Component {
}
languageTools() {
- return Object.values(this.props.addons).filter((addon) => {
+ const { addons } = this.props;
+ return Object.keys(addons).map((key) => addons[key]).filter((addon) => {
return addon.type === ADDON_TYPE_DICT || addon.type === ADDON_TYPE_LANG;
});
}
Hopefully that helps explain Flow's error output but let me know if anything else is unclear. There are other errors but they are straight forward. Flow wants you to protect against nulls a little more directly. I documented some debugging techniques here which is what I use when I think Flow is not inferring the correct types: https://github.com/mozilla/addons-frontend#flow Specifically: "If you're stumped on why some Flow annotations aren't working, try using the |
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 like how it looks but I had a few change requests.
package.json
Outdated
@@ -193,7 +193,8 @@ | |||
"react-redux": "5.0.6", | |||
"react-router": "2.8.1", | |||
"react-router-scroll": "0.4.3", | |||
"react-textarea-autosize": "^5.1.0", | |||
"react-super-responsive-table": "0.3.0", | |||
"react-textarea-autosize": "5.1.0", |
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.
Oh wow, yeah. I filed https://github.com/mozilla/addons-frontend/issues/3391
header={i18n.gettext('Dictionaries and Language Packs')} | ||
> | ||
<p> | ||
{i18n.gettext(`Installing a dictionary add-on will add a new languag |
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.
typo: language
</p> | ||
<p> | ||
{i18n.gettext(`Language pack add-ons change the language of your | ||
Firefox browser includes menu options and settings.`)} |
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.
Can you re-word this? I'm not sure what it's saying
componentWillMount() { | ||
const { dispatch, errorHandler } = this.props; | ||
|
||
dispatch(fetchLanguageTools({ errorHandlerId: errorHandler.id })); |
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.
For new page loads this will make two API requests: once on the server and once on the client. A simple way to fix it would be:
export class LanguageToolsBase extends React.Component {
componentWillMount() {
const { addons, dispatch, errorHandler } = this.props;
if (addons === null) {
dispatch(fetchLanguageTools({ errorHandlerId: errorHandler.id }));
}
}
}
export const mapStateToProps = (state: Object) => {
const languageToolAddons = Object.values(state.addons).filter((addon) => {
return addon.type === ADDON_TYPE_DICT || addon.type === ADDON_TYPE_LANG;
});
return {
addons: languageToolAddons.length ? languageToolAddons : null,
lang: state.api.lang,
};
};
However, this state mapper may run us into performance issues eventually. A better way to do it would be to create a new addons reducer state property just for language tool add-ons. The saga would have to use a new action for loading the API response and you'd have to update the existing add-ons reducer a bit. The new property wouldn't have to be an array of add-ons, it could be an array of slugs. You would then use the existing add-ons map to fetch the objects.
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.
Arg, good point, I totally overlooked that.
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.
Doing this requires keying add-ons in the addons reducer state by slug, as we do elsewhere. It'll touch a lot of code I think... I'm happy to do it but I think it's a separate issue.
I'm gonna put stuff in the mapStateToProps
for now, but agree this is the way to go in the future.
|
||
// This means there are no language tools available in this | ||
// known locale. | ||
if (languageTools && !toolsInLocale.length) { |
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.
Flow wants you to protect against nulls more directly:
if (toolsInLocale && !toolsInLocale.length) {
return null;
}
const root = renderShallow({ store }); | ||
|
||
const dictionary = root.find('.LanguageTools-in-your-locale-list-item') | ||
.at(0); |
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 don't think it's a good idea to rely on 0 and 1 because the order is not predictable (since it relies on Object.values()
). Is there another way to separate dictionaries from lang packs?
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.
Good catch; I added a type classname to the elements to select them that way.
.find(LanguageToolList); | ||
expect(dictionaryList).toHaveLength(1); | ||
expect(languagePackList).toHaveLength(1); | ||
expect(dictionaryList.shallow().find('li')).toHaveLength(1); |
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 think we should avoid this sort of thing -- the test is reaching into a separate component. You should be able to it in two passes:
- Make sure
LanguageTools
is passing the right props toLanguageToolList
- Assert that
LanguageToolList
is rendering the right thing.
b70963e
to
a3ef849
Compare
5b0973d
to
96f9a8e
Compare
Alright, this is all set for another review. |
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 added some straight forward change requests but let me know if you'd like another review.
} | ||
} | ||
|
||
export const mapStateToProps = (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.
state
can be typed which will help Flow provide better safety. You can write the code less defensively too.
import type { AddonState } from 'core/reducers/addons';
export const mapStateToProps = (state: {| addons: AddonState |}) => { ...
export const mapStateToProps = (state: Object) => { | ||
const { addons } = state; | ||
const languageToolAddons = addons && Object.values(addons).length ? | ||
Object.values(addons).filter((addon) => { |
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.
Flow complains about checking if addon.type
is defined because you did not declare the type of state.addons
. However, after fixing that you'll probably have to work around the Object.values()
bug with:
Object.keys(addons).map((key) => addons[key]).filter(...)
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.
Yes, this Object.values()
bug is very annoying.
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 could declare our own definition for Object.values()
in https://github.com/mozilla/addons-frontend/tree/master/flow/libs
}) : null; | ||
|
||
return { | ||
addons: languageToolAddons && languageToolAddons.length ? |
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.
If the API responds with a zero length array then this will return null
and create an infinite dispatch loop in componentWillMount()
. It is unlikely that this will happen -- probably only with a new Docker instance of the API. The way to fix it is to make a new reducer block that responds to fetching language tools and then update the saga, etc but I know that would take a bit more time. No worries either way.
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.
Yeah, I wanted to store languageToolsRequested
as a boolean in the addons reducer state but am waiting on #3421. I've filed #3437.
@import "~ui/css/vars"; | ||
|
||
.LanguageTools { | ||
margin: 10px; |
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.
what about 12px to align with the specs?
|
||
th, | ||
td { | ||
padding: 5px; |
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.
what about 6px to align with the specs?
96f9a8e
to
98ff590
Compare
Fix mozilla/addons#10584.
Fix mozilla/addons#10884.
Adds a responsive table similar to the old site with language tools and dictionaries. Tried a few different UIs but this one with a responsive table actually worked pretty well so I went with it.
Screenshots
Loading Animation