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 Bug 1603610 - Enable users to leave comments on translations #1524
Conversation
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.
Awesome work, @abowler2!
I think you'll need a Comments
component in the Translation
component. It should render two other components:
Comment
for the display of the commentAddComment
for adding the comment
I believe you'll indeed need to prefetch comments in get_translation_history()
before you serialize them. I can help with that if you run into issues.
Current progress for review. There is obviously still a lot to be done but wanted to check in to make sure all is on track (Note: I used some temporary filler values for titles and inline styling in the components that is not intended to stay 😄 ) In addition to the obvious need for styling, I still have to add the Prefetch in Please let me know what changes are needed at this point. |
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.
Great progress!
We should move all three Comment*
components from core/translation
to the newly created core/comments
. Looking at the spec, we should benefit from reusing these components for team comments.
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.
This is a great start April! I've made a bunch of comments about good practice, conventions and stuff, but overall the code looks good.
In terms of workflow, maybe it won't be needed in this case (this PR is still of manageable size) but I encourage you to split your work into the smallest possible chunks. For example, you could have one PR for the back-end / models features, using unit tests to make sure it works as expected, and then have a follow-up PR for the front-end work.
Also, please do not forget unit tests! They are your greatest allies to make sure your code works. And even though I'm not good at it, I assure you that test-driven development is great. It forces you to think about what exactly you want to build before coding it, and that's both a great exercise and just good development practice.
Made the requested changes. Please let me know if any further changes are needed. RE: the failing |
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.
Great follow-up! I've made a few more comments, all little things around conventions or accessibility.
Please do add unit tests for all your code though! 😇
RE: the failing Translation test ~ I refactored the user's avatar to use the new UserImage component which causes the test to fail based on only one a tag being present now. Not sure if I should adjust the test to account for the change or if there is something I missed when using the new component.
You do need to adjust the test. Since you've added a layer of indirection, you do not want to test the content of the component anymore (instead you want to do that in that component's unit tests) but just verify that the component is created. Eventually that it is passed the right props, if that makes sense.
pontoon/base/models.py
Outdated
'translation': self.translation_id | ||
"author": self.author.name_or_email, | ||
"username": self.author.username, | ||
"userGravatarUrlSmall": self.author.gravatar_url(88), |
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.
Ah, here's the trick: Python variables should use underscore_case
. :) This is confusing, sorry. We effectively have two different languages with different naming conventions. We want to strictly enforce those naming conventions. The only time when they conflict is through the API. And that's where we need some specific code to manually convert those names.
So, here you should revert this change. But keep your type definition in JS. Then on the API function, in front-end, where you get the data, you should have some logic to transform the data, mapping one convention (underscore_case from Python) to the other (camelCase for JS).
Here's where the data gets fetched:
pontoon/frontend/src/core/api/entity.js
Line 115 in 6cc116b
async getHistory( |
Side note: if this data was exposed with GraphQL, we wouldn't have that problem, as it handles that conversion automatically for us. Sadly, this is not the case yet, but doing this conversion now means it will be easier later on to switch to fetching that data through GraphQL.
Sending up the updates from the last review. I'm planning to work on the The one thing I did have issues with was the |
.vscode/settings.json
Outdated
@@ -0,0 +1,2 @@ | |||
{ | |||
} |
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.
Please don't send this file to the repo. :) (You can add it in the .gitignore
file if you want, or in your .git/info/exclude
file so that it's only for your local git folder.)
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.
This one is still around. :)
frontend/src/core/api/entity.js
Outdated
renamedResultsKeys.comments = renamedCommentsKeys; | ||
} | ||
|
||
return [renamedResultsKeys]; |
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 find this code too complex, and too specific. Once again, apologies for opening a can of worms here. Let's still get to the end of it, but by using a more generic solution that we can then apply to other places around the API.
I found this article that shows a nice solution to transform code from snake_case to camelCase: https://matthiashager.com/converting-snake-case-to-camel-case-object-keys-with-javascript
I'd take that code and put in the api/base.js
file, add a method there to transform objects, and then use that method here to convert the results.
Updated the camelCase conversion as per @adngdb's note. However, I'm not sure if I 'typed' the argument that is passed in properly. Technically it is being passed an |
const { comment } = props; | ||
|
||
if (!comment) { | ||
return null; |
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.
Nit: too much indent.
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.
Nit: Still too much indent.
I believe this is one of those rare cases where using |
Early styling of Comment element for review. One of the issues that came up with the change in structure is that the bottom border is now above the comments since they have been separated into individual |
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.
.vscode/settings.json
Outdated
@@ -0,0 +1,2 @@ | |||
{ | |||
} |
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.
This one is still around. :)
const { comment } = props; | ||
|
||
if (!comment) { | ||
return null; |
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.
Nit: Still too much indent.
The Black formatting commit has been merged to master, causing conflict on this branch. Please rebase and fix the conflicts as soon as you can, to avoid potentially more conflicts later on! And my apologies for disturbing your workflow, but I strongly believe now was the best time to do this. :-) |
|
||
import type { TranslationComment } from 'core/api'; | ||
import type { HistoryTranslation } from 'modules/history' | ||
import type { UserState } from 'core/user'; |
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.
Nit: we sort imports by module, i.e. the value following from
.
let canReject = (canReview || (ownTranslation && !translation.approved)) && !isReadOnlyEditor; | ||
let canDelete = (isTranslator || ownTranslation) && !isReadOnlyEditor; | ||
let canReject = (isTranslator || (ownTranslation && !translation.approved)) && !isReadOnlyEditor; | ||
let canComment = (isTranslator || ownTranslation); |
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.
According to the "Permissions" section of the spec, any logged in contributor canComment
, so the value should be user.isAuthenticated
. And then you don't need the !!
part of the !!canComment
below.
I think it's still valuable to have the canComment
variable defined along with other CanSomething
permissions.
Sorry, pushed and then realized the translation test was broken. Working on it now |
@@ -286,6 +302,7 @@ describe('<TranslationBase>', () => { | |||
translation={ translation } | |||
entity={ DEFAULT_ENTITY } | |||
locale={ DEFAULT_LOCALE } | |||
user={ 'Andy_Dwyer' } |
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.
While this probably makes the test pass, this is better: user={ { username: 'Andy_Dwyer' } }
.
These are the CSS fixes I promised, which:
Please apply the patch. |
@abowler2 One more patch please - it makes the input fields styled consistently. diff --git a/frontend/src/core/comments/components/AddComment.css b/frontend/src/core/comments/components/AddComment.css
index a3fd9f5d1..0f0915bfd 100644
--- a/frontend/src/core/comments/components/AddComment.css
+++ b/frontend/src/core/comments/components/AddComment.css
@@ -1,10 +1,10 @@
.comments-list .add-comment textarea {
- background-color: transparent;
- border: solid 1px #5E6475;
+ background: #333941;
+ border: none;
border-radius: 4px;
color: #FFFFFF;
font-size: 11px;
- height: 22px;
- line-height: 22px;
+ height: 24px;
+ line-height: 24px;
padding: 6px;
}
diff --git a/frontend/src/modules/machinery/components/Machinery.css b/frontend/src/modules/machinery/components/Machinery.css
index b988b51a0..438089d2e 100644
--- a/frontend/src/modules/machinery/components/Machinery.css
+++ b/frontend/src/modules/machinery/components/Machinery.css
@@ -23,7 +23,7 @@
}
.machinery > .search-wrapper input[type=search] {
- background: #4D5967;
+ background: #333941;
border: none;
border-radius: 20px;
box-sizing: border-box;
diff --git a/frontend/src/modules/search/components/SearchBox.css b/frontend/src/modules/search/components/SearchBox.css
index 0817c68cb..fa4e2b90f 100644
--- a/frontend/src/modules/search/components/SearchBox.css
+++ b/frontend/src/modules/search/components/SearchBox.css
@@ -4,7 +4,7 @@
}
.search-box input[type="search"] {
- background: #4D5967;
+ background: #333941;
border: none;
border-radius: 20px;
box-sizing: border-box; |
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.
Nearly there! Excellent work @abowler2!
## Allows user to leave comments on translations | ||
|
||
comments-AddComment--input = | ||
.placeholder = Write a comment... |
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.
Nit: please use the actual character here: …
.
[one] { $commentCount } Comment | ||
*[other] { $commentCount } Comments | ||
} | ||
.title = Toggle translation comment |
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'd do "comment" -> "comments".
id='comment-input' | ||
name='comment' | ||
dir='auto' | ||
placeholder={ `Write a comment ${'\u2026'}` } |
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.
No space before the ellipsis, and I believe you can simply use the actual character, no need to use the Unicode representation: …
(you can just copy and paste it ;) ).
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.
Note that, whenever you change localized content, what actually matters is what is in the en-US
FTL file, not what is in the code. That's there only to help developers understand what the content is without having to go look into the .ftl
files, but that content will never be shown to users. We should aim to have content in code always in sync with content in the en-US
reference FTL files, even if that's a bit painful to do.
|
||
const DEFAULT_USER = { | ||
user: 'RSwanson', | ||
username: 'Ron_Swanson', |
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.
😍
/> | ||
</Localized> | ||
</div>; | ||
|
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.
Nit: please remove this empty line.
/> | ||
className={ commentCount === 0 ? 'toggle-comments' : | ||
'toggle-comments' | ||
} |
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 the className is now the same regardless of commentCount
this should just set the className to toggle-comments
without the ternary, correct?
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 went ahead and made this change. Just let me know if I'm missing something and the logic needs to be reapplied.
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.
That ternary was weird anyways. :) "If true X else X". :)
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.
Originally there were two different classes based on the condition, which made more sense 😄
id={ commentCount === 0 ? | ||
'history-Translation--button-comment' : | ||
'history-Translation--button-comments' | ||
} |
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.
It would be more readable if each <button>
would be wrapped in their own <Localized>
element.
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 tried that first but couldn't get it working. I'll have another go to see if I can work it out as I agree it would be much easier to understand what is happening there.
/> | ||
className={ commentCount === 0 ? 'toggle-comments' : | ||
'toggle-comments' | ||
} |
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.
That ternary was weird anyways. :) "If true X else X". :)
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.
Great work, only found two nits!
{ commentCount === 1 ? | ||
`${commentCount} Comment` : | ||
`${commentCount} Comments` | ||
} |
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 should simplify this to a simple string, e.g.:
{ `${commentCount} Comments` }
See:
#1524 (comment)
|
||
if (!translation.uid) { | ||
return <span>{ translation.user }</span>; | ||
renderCommentButton(commentCount: number) { |
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.
renderCommentToggle
would be consistent with the naming used by the Diff toggle.
if (!translation.uid) { | ||
return <span>{ translation.user }</span>; | ||
renderCommentButton(commentCount: number) { | ||
if(commentCount === 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.
Nit: Missing space.
return <Localized | ||
id='history-Translation--button-comment' | ||
attrs={{ title: true }} | ||
$commentCount={ commentCount } |
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 don't use this variable.
> | ||
{ 'Comment' } | ||
</button> | ||
</Localized> | ||
} | ||
|
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.
Nit: remove blank line.
{ commentCount === 1 ? | ||
`1 Comment` : | ||
`${commentCount} Comments` | ||
} |
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.
See #1524 (comment).
Alright, we're done here! Great work, April! Before we merge, please disable the feature until we land Team Comment by:
|
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.
Excellent job, @abowler2! \o/
Initial work to add the ability to leave comments on translations.
At the moment I'm just printing the comments into the history panel. Moving forward from here I believe I will need to add a Comment component in order to render and style all the information properly. If that is indeed the case should I add that component to the History module or the Translation module?
Also, I'm not sure if a Prefetch is needed for retrieving the comments in
map_entities
withinmodels.py
, but I was unsuccessful in my attempts at that. Would appreciate your thoughts and insights on this.TODO:
./manage.py makemigrations
says we need to make some migrations.UserAvatar
component wherever possible.