-
Notifications
You must be signed in to change notification settings - Fork 81
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
[BD-03] [BB-2542] ]Add discussions configuration UI wireframe in MFE #2
[BD-03] [BB-2542] ]Add discussions configuration UI wireframe in MFE #2
Conversation
Thanks for the pull request, @rusrushal13! I've created BLENDED-371 to keep track of it in Jira. When this pull request is ready, tag your edX technical lead. |
75dbee4
to
0b69e69
Compare
Looking great! I will flag this to the UX team who can provide final visual details on this next. Thanks! |
<main> | ||
<div className="container-fluid"> | ||
<div className="container-fluid-container d-flex justify-content-between align-items-center border-bottom"> | ||
<h1 className="container-fluid-heading mt-3">Pages & Resources</h1> |
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.
All text like this should be translated not directly entered here.
The process for that is roughly as follows:
- Create a messages.js file in the same directory. You can see a random example I picked here: https://github.com/edx/frontend-app-learning/blob/master/src/courseware/course/celebration/messages.js
-- You can follow the same exact format, it is an object of unique keys mapped to different messages.
-- Each message has anid
that is globally unique, and at the very least, adefaultMesage
, this is the text that would actually be placed here.
-- It can also contain a description, this can be helpful to translators to get some context about where the word is used so they can pick the most appropriate translation. - Next import this file.
- The
intl
object will already be injected into the component since the component is wrapped withinjectIntl
. So you can useintl.formatMessage(message.key)
wherever you'd want to use some text. Here is an example: https://github.com/edx/frontend-app-learning/blob/a6edc9132f648890eb5693e644998174894fbe41/src/courseware/course/celebration/CelebrationModal.jsx#L34
In this case here is how I would do it:
messages.js
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
heading: {
id: 'course-authoring.pages-resources.heading`,
defaultMessage: 'Pages & Resourses'
}
...
}
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.
The "View Live" button, the "Course Pages" subheading etc all should be in the messages file.
<h1 className="container-fluid-heading mt-3">Pages & Resources</h1> | ||
<Button className="container-fluid-button font-weight-bold rounded my-3"> | ||
View Live | ||
</Button> |
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 link masquerading as a button, so you can just use a link button styling in bootstrap.
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.
Using the following, I get a rounded button instead of the rectangular button in the wireframe. What should I do?
<a class="btn btn-primary" href="#" role="button">
{intl.formatMessage(messages['course-authoring.pages-resources.viewLive.button'])}
</a>
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.
@taniwha Feel free to just leave it as is for right now.
We'll have the UI folks run over the details later to confirm expectations.
enabledButton | ||
coursePageStatus={false} | ||
description="Encourage Participation and engagement in your course with discussion forums" | ||
/> |
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 should not be hard-coded, it will be constructed based on data from the API.
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 assume you mean cousePageStatus
.
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: As I mentioned above, these tiles should not be hard-coded, they will be returned by an API. So you did not need to move all this to the translation file. i.e. Remove all <CoursePageConfigTile
objects from here and have this list be provided by the parent element for CoursePageResources
@@ -1,4 +1,4 @@ | |||
describe('example', () => { | |||
describe('coursepageresources', () => { | |||
it('will pass because it is an example', () => { | |||
|
|||
}); |
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.
Add some snapshot tests.
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 - we'd like to avoid overuse of snapshot tests, as while they're easy to write, they're brittle and difficult to maintain. Favoring specific assertions about the DOM is preferable, as it's much less likely to break when trivial changes are made to the markup/CSS classnames.
Snapshots are good for small components, but not great for larger components. i.e., the cost of ensuring that the snapshot is always 100% correct is so high for large components that it's very easy for the snapshot to become invalid, in which case the test starts asserting that things are still wrong, but no one realizes because it passes. :)
A large, invalid snapshot is very difficult to go back and prove wrong because it requires a developer to go understand every little thing that's in the snapshot, and to go against what the machine is telling them - that the test passed. Visual inconsistencies are better caught with our eyes when viewing the UI, rather than trying to parse a not-very-human-readable HTML document.
Some good rationale in here: https://engineering.ezcater.com/the-case-against-react-snapshot-testing
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.
Thanks for this explanation! Definitely useful to keep in mind for future PRs as well.
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 are snapshot tests? Or maybe better, can you point me to some examples? Will I find some in frontend-app-learning?
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.
Just read David Joy's comment (my page was out of date) and I'm inclined to agree that meaning is more important than appearance (to paraphrase).
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.
Snapshot tests record a copy of the DOM generated from react when they're first run, and then on subsequent runs compare the results to that original snapshot. It's like asserting that the entire DOM is what you expect it to be, down to every single tag and attribute. As mentioned above, this means that any tiny change no matter how trivial invalidates a snapshot test and can make noisy diffs - often this can result in masking real issues if the test is "retrained" under faulty assumptions about what's correct.
You can see some in this commit in frontend-app-learning: openedx/frontend-app-learning@ada0750
Note that I've asked that those be modified (in the same spirit as above), so that code may not make it into master. :)
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 read the rational and couldn't agree more. I had done similar by hand years ago doing compiler testing and hated it, thus avoided doing automated tests until I figured out how to do meaningful tests. More work to write the tests, but they are very much more reliable.
src/CoursePageResources/index.scss
Outdated
@@ -0,0 +1,48 @@ | |||
.container-fluid { | |||
background-color: #EAF3FB; | |||
color: #2E8ED3; |
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.
Colours shouldn't be hard-coded. Get them from the theme, or at least set them globally in a theme file.
src/CoursePageResources/index.scss
Outdated
.border-enabled { | ||
border-color: #4293C4 !important; | ||
border-width: 2px; | ||
border-style: solid; | ||
border-radius: 5px; | ||
} | ||
|
||
.border-disabled { | ||
border-color: #ffffff !important; | ||
border-width: 2px; | ||
border-style: solid; | ||
border-radius: 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.
Both of these share most of their code. It would be better to have it as:
.course-page-tile {
border: 2px solid $white;
border-radius: 5px; //Maybe this can be standardised?
.disabled {
border-coloor: #4293C4;
}
}
}
src/CoursePageResources/index.scss
Outdated
.container-fluid-container { | ||
border-color: #B2D5EE; | ||
} | ||
|
||
.container-fluid-enabled-button { | ||
border-color: #0075B4; | ||
color: #2E8ED3; | ||
} | ||
|
||
.container-fluid-enabled-button:hover { | ||
color: inherit; | ||
} | ||
|
||
.sub-container-text { | ||
color: #39AAE3; | ||
} | ||
|
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.
These names are very generic and tied to bootstraps naming scheme instead of being specific to this app.
src/CoursePageResources/index.scss
Outdated
.container-fluid-button { | ||
background-color: #0075B4; | ||
color: #ffffff; | ||
font-size: 12px; | ||
&:hover { | ||
color: #ffffff; | ||
} | ||
} |
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 very generic name for this button, and again uses the bootstrap names instead of something like course-pages-button
or something.
Other points:
- Colours should be in the theme, not here.
- Font size should be in rem not pixels.
src/course-page/CoursePage.jsx
Outdated
import { faCog } from '@fortawesome/free-solid-svg-icons'; | ||
import { Button } from '@edx/paragon'; | ||
|
||
function CoursePage({ |
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 this component name is a bit misleading. A better name I feel would be: CoursePageConfigTile
.
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 believe these are generally called "Cards" by both Bootstrap and the edX UX team: https://getbootstrap.com/docs/4.0/components/card/
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.
Should I rename the file as well? What about the directory? The former seems very reasonable, but I am unsure about the latter.
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 it's a virtue to keep naming consistent... so if we think these are 'cards' instead of 'pages', then I'd consistently change the term, yeah. Assuming that the course-page module is all about code related to these cards.
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 seems to be about the cards, and I'll go with that naming convention. Thank you.
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.
@taniwha As mentioned above in the comment, the convention here is to call these UI elements Cards, not Tiles. So could you rename this accordingly?
src/course-page/CoursePage.jsx
Outdated
let classes = 'bg-white shadow p-3 m-3'; | ||
classes += coursePageStatus ? ' border-enabled' : ' border-disabled'; | ||
return classes; | ||
}; |
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.
Instead of this, use the classNames
utility. You can read more about that here, but to give a brief intro with that you can replace the above with:
classNames('course-page-tile bg-white shadow p-3 m-d', {'disabled': !coursePageStatus});
src/CoursePageResources/index.scss
Outdated
@@ -0,0 +1,48 @@ | |||
.container-fluid { |
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.
To agree with @xitij2000 's points below, we try to minimize semantic class names in our code to the bare minimum. Instead, we can use Bootstrap's utility classes to accomplish most of these things.
Paragon is built on top of Bootstrap, and provides a wealth of utility class names: https://edx.github.io/paragon/foundations/css-utilities
Also, as noted below, we definitely don't want to attach additional styling to existing Bootstrap class names; this will create very difficult to debug situations where unrelated code receive styling they're not expecting.
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.
Thank you. I will look into that.
src/CoursePageResources/messages.js
Outdated
import { defineMessages } from '@edx/frontend-platform/i18n'; | ||
|
||
const messages = defineMessages({ | ||
'course-authoring.pages-resources.heading': { |
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: I think the idea is to give is a more friendly name here.
I think the 'id' needs to be globally unique, but this key in the dictionary does not need to be.
So I would recommend keeping them simple, so, 'heading' instead of 'course-authoring.pages-resources.heading'.
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.
OK, I'll remove "course-authoring.pages-resources." from the dictionary keys but leave the id fields alone.
src/CoursePageResources/index.scss
Outdated
.disabled { | ||
border-color: theme-color("gray", 100); | ||
} | ||
} |
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 am wondering if this can all be standardised, and then this could just include the standard styling if it needed to be customised.
src/course-page/CoursePage.jsx
Outdated
return classNames('bg-white shadow p-3 m-3', { | ||
'border-disabled': !coursePageStatus, | ||
'border-enabled': coursePageStatus, | ||
'course-page-title.disabled': !coursePageStatus, | ||
'course-page-title': coursePageStatus, | ||
}); |
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 can be simplified to:
return classNames('bg-white shadow p-3 m-3 course-page-title', {disabled: !coursePageStatus});
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 didn't work as intended because course-page-title.disabled is required, so I had to use a variation.
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.
@taniwha that might be because you are defining the SCSS styling incorrectly.
Note:
.class1 {
color: red;
.class2 {
color: green;
}
}
In the above the inner class2 will not match a div with both class1 and class2, It will match a child element of class2 inside an element with class1.
If you want to match an element that has both class1 and class2 (in this case both course-page-title
and disabled
), the correct scss code is:
.class1 {
color: red;
&.class2 {
color: green;
}
}
src/course-page/CoursePage.jsx
Outdated
@@ -35,7 +35,7 @@ function CoursePageConfigTile({ | |||
<p>{coursePageStatus ? 'Enabled' : 'Disabled'}</p> | |||
<div>{description}</div> | |||
<div className={getEnabledButtonClasses()}> |
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 see why we need to call an intermediary function here. Just add the entire classNames
function call here.
If that makes it harder to understand, you can just put it in a variable. So something like:
const containerClasses = classNames('course-page-tile bg-white shadow p-3 m-d', {'disabled': !coursePageStatus});
... // Later
<div className={containerClasses} ...
enabledButton | ||
coursePageStatus={false} | ||
description="Encourage Participation and engagement in your course with discussion forums" | ||
/> |
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: As I mentioned above, these tiles should not be hard-coded, they will be returned by an API. So you did not need to move all this to the translation file. i.e. Remove all <CoursePageConfigTile
objects from here and have this list be provided by the parent element for CoursePageResources
src/course-page/CoursePage.jsx
Outdated
import { faCog } from '@fortawesome/free-solid-svg-icons'; | ||
import { Button } from '@edx/paragon'; | ||
|
||
function CoursePage({ |
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.
@taniwha As mentioned above in the comment, the convention here is to call these UI elements Cards, not Tiles. So could you rename this accordingly?
@xitij2000 I had passed on the renaming for the moment because the user-view shows pages and wanted to query that, but I suppose it doesn't really matter if it's "page" externally and card internally (especially since it's a repleacable string). |
@xitij2000 I believe I have addressed all of your points other than the testing (not sure what to do there at this stage) and the API issue (thus some instances of page remain: they will be deleted when the API is sorted out). For that... |
I think you misunderstood the intent behind the renaming. Just to clarify, we are building the UI for the "Course Pages" part of the app. So, it makes sense to call the internal component there: The original name for the each individual component inside that Page (i.e. Discussion, Teams, Progress etc) was:
Sure, I can reach out about this on the internal Mattermost. |
I had indeed misunderstood. Thank you for the clarification. I will sort that out ASAP. |
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.
<div className="card-footer"> | ||
<div className={buttonClasses}> | ||
<Button className="course-page-button bg-white border font-weight-bold rounded"> | ||
Enable |
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.
Move to messages file.
</div> | ||
</div> | ||
<div className="card-body"> | ||
<p>{isEnabled ? 'Enabled' : 'Disabled'}</p> |
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.
Move to messages file for translation.
}].isRequired, | ||
}; | ||
|
||
export default injectIntl(CoursePageResources); |
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.
Rename this directory to course-page-resources to stick with the approach everywhere else.
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.
Also add an index.js file that re exports this component. i.e an index.js file that has:
export {default as CoursePageResources} from './CoursePageResources';
This inport this in the main index.jsx file as:
import {CoursePageResources} from './course-page-resources'
isEnabled: null, | ||
}; | ||
|
||
export default injectIntl(CoursePageConfigCard); |
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 component is only going to be used by CoursePageResources, so move it to a subdirectory.
@xitij2000 I've addressed your points and got the page looking much more like the wireframe. However, I noticed one issue that is probably a mistake on my part as I failed to notice it before I made my simplifications, but the wireframe has "Disabled" on some cards that do not have an "Enable" button: this currently does not make sense to me. Is this a mistake in the wireframe, or do I need to make the logic for the two items separate again? [edit] question sorted in mattermost. |
const messages = defineMessages({ | ||
heading: { | ||
id: 'course-authoring.pages-resources.heading', | ||
defaultMessage: 'Pages & Resourses', |
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: fix spelling
}, | ||
'resources.custom.description': { | ||
id: 'course-authoring.pages-resources.resources.custom.description', | ||
defaultMessage: 'Create and edit custom pages to provide students with additional course content and resources. Pages are publicaly visible. If users know the URL of a page, they can view the page even if they are not registered for or logged in to your course.', |
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: spelling fixes
src/course-page-resources/index.scss
Outdated
.course-page-title { | ||
border: 2px solid $white; | ||
border-color: theme-color("info", 300); | ||
border-radius: 5px; //Maybe this can be standardised? | ||
&.disabled { | ||
border-color: theme-color("gray", 100); | ||
} | ||
} | ||
|
||
.course-page-button { | ||
outline: 2px solid theme-color("info", 500); | ||
outline-radius: 5px; //Maybe this can be standardised? | ||
background-color: theme-color("gray", 100); | ||
color: theme-color("info", 500); | ||
font-size: 0.8; | ||
&:hover { | ||
color: inherit; | ||
} | ||
} |
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 these are hurting more than helping, just remove them. Let's get the basic structure in.
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 now, I have left them in as they are what provide the specified shading.
@@ -1,6 +1,6 @@ | |||
@import '~@edx/paragon/scss/edx/theme.scss'; | |||
|
|||
@import './example/index.scss'; | |||
@import './course-page-resources/index.scss'; |
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.
Remove this as well.
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 above.
const cardSettings = 'bg-white shadow p-3 m-0 my-3 course-page-title'; | ||
const cardClasses = classNames('card', cardSettings, { disabled: !pageStatus }); | ||
const cardSubSettings = 'bg-white px-3 py-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.
Why are these called settings? I think extracting them only makes sense if the styles are locked, i.e. if you change one you'll change all. That isn't the case here.
Please don't use the 'card' classes. While they might match the bootstrap concept we have a pretty different structure and styling requiremtn so we shouldn't force them in here.
i.e. remove the card style here, the card-header, card-footer, and card-body styles as well.
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.
They probably should be styles rather than settings. I was stuck for naming at the time.
Also, I am no longer using the card class, but still referring to the objects as cards. However, I am still in the process of cleaning up that area of the code.
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.
Actually, this has been cleaned up entirely.
const buttonSettings = 'd-flex mt-0 justify-content-center align-items-center'; | ||
const buttonClasses = classNames(buttonSettings, { visible: !pageStatus, invisible: pageStatus }); |
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.
const buttonSettings = 'd-flex mt-0 justify-content-center align-items-center'; | |
const buttonClasses = classNames(buttonSettings, { visible: !pageStatus, invisible: pageStatus }); | |
const buttonClasses = classNames('d-flex mt-0 justify-content-center align-items-center', { visible: !pageStatus, invisible: pageStatus }); |
Don't make it set visibility, just don't add it to the dom if it shouldn't show up unless there is a good reason to have it invisible (for screenreaders for instance).
Also buttonClasses is a bit odd since they don't apply to a button.
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 has been cleaned up entirely.
<div className={cardClasses} key={id}> | ||
<div className={classNames('card-header', cardSubSettings)}> | ||
<div className="row"> |
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.
Too many divs for such a simple layout. Condense this.
</div> | ||
</div> | ||
<div className={classNames('card-body', cardSubSettings)}> | ||
{showSettings && <p>{intl.formatMessage(messages[pageStatusMsgId])}</p>} |
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.
{showSettings && <p>{intl.formatMessage(messages[pageStatusMsgId])}</p>} | |
<span>{intl.formatMessage(messages[pageStatusMsgId])}</span> |
These are not related, and this is not a para of text.
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.
They seem to be related: the page enabled/disabled status is shown only when the settings gear is shown. However, span seems to work nicely after some tweaking of the surroundings.
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.
@taniwha The mockup doesn't describe the functionality, it's just a mockup with a few sample cases.
</div> | ||
<div className={classNames('card-footer', cardSubSettings)}> | ||
<div className={buttonClasses}> | ||
{!showSettings && ( |
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.
Showing the enabled button has nothing to do with settings? Why is it linked here?
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 is not "enabled" (status) but "enable" (action), and it is shown only when the settings icon is not shown.
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.
As I mentioned in the previous comment, the mockup doesn't dictate this functonality.
id: PropTypes.string, | ||
title: PropTypes.string, | ||
description: PropTypes.string, | ||
pageStatus: PropTypes.bool, |
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 very ambiguous. What status is this? It seems to signify that the page/app is enabled.
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.
From examining the various combinations shown in the wire-frame, that is my understanding of what is going on: the page can be added (possibly in a disabled state) and then later enabled and disabled at will. This is why I have lumped the behavior of several seemingly unrelated items into "showSettings".
I thought I had asked for specs on actual behavior (not just appearance), but I can't find the request. Knowing how things are meant to behave would help immensely by doing away with a lot of the guesswork.
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.
@taniwha I think did mention that you should make it as flexible as possible i.e. support all combinations.
@taniwha Please rebase this as well. |
@taniwha To save some rounds I've applied some of my suggestions and made further changes to reduce usage of custom styling in favour of bootstrap classes. |
@xitij2000 Your changes have resulted in a rather broken layout. The merging has broken the display entirely. Also, why did you undo the passing of the coursePage object through to CoursePageConfigCard? It kept CoursePageResources simple because CoursePageResources then has no need to know anything about what is in a cousePage object other than the id (needed for the key). It makes no architectural sense for the outer container (CoursePageResources) to care what is in the inner container: that is the job of CoursePageConfigCard. I was even trying to sort out how to keep the course page shape out of CoursePageResources.jsx (since it duplicates the shape in CoursePageConfigCard.jsx) However, the merging breaking the display is probably something I would have to take care of no matter what as it would have happened eventually. The layout I'll probably be able to fix. |
@taniwha For the second one, that's because the previous PR that merged added routing, so you need to have a path at which the component is mounted. You should be able to see this at: http://localhost:2001/course-pages/demo now.
I think having them as explicit properties is cleaner.
Currently, we don't even know if it will have an id.
I think you're trying to over architect things here. This is not a generic container like a list or queue or something. It is a UI element that's designed to do one thing. It will not be reused, and making it generic is of no value, so it is better instead to have everything be explicit and clear. Even if you want to go down the route of making things generic, designing an item based on the container it will be in also seems wrong doesn't it? Why should the CoursePageConfigCard get a single parameter because that makes it easier to pass in rather than what makes sense there? Note there is another way out here. Instead of the way it's coded right now, you can do: <CoursePageConfigCard key={coursePage.id} {...coursePage} /> This will work instead of sending each value explicitly like I have. If we really wanted to make things generic, then you would need to do the following:
Unfortunately, this is of little value here because you can get a similar layout using just a few css classes. |
Thank you for the that url. As for the layout, it is due to the use of m-3 on the course-page-config-card div. The margin attribute effectively grows the div (by adding space around the outside of the div's box), so columns with a width of 1/3 of the available space cannot fit. This is why I had an outer div with p-3: same overall effect, but 3 columns fit side-by-side even when their width is 1/3 of the available space. Explicit properties to CoursePageConfigCard do not make it clearer that CoursePageConfigCard is for displaying a course page's configuration. Instead, the properties make it seem like CoursePageConfigCard is for displaying a title, description, and some buttons and a status. Such "clarity" is neither necessary nor desirable. Also, having the explicit parameters means one more place that needs to be edited when the contents of a course page changes, meaning more testing and the opening for more errors. I added the id field only to take care of the warning about list items needing a key (and not simply use the map index). I am not particularly attached to it. I have fixed the layout to work for any horizontal resolution (tested from as narrow as firefox will go to as wide as 1920), and I have gone back to passing the object, this time with the shape defined reuseably. |
I understand your point, however the layout you are using will squish the tiles to accommodate the desired number in each row as the UI is resized, whereas the one I used would simply flow them over to the next line. Neither of these approaches are in the mockup so fair enough.
You are right, explicit properties do not make it clear that it is for display a course page's configuration. What makes that clear is the name of the component
I think we need to think in broader terms here. Right now this is just about getting the mockup, so it is at its simplest state, there will be a data layer for this app eventually, and the UI logic will only get more complicated from here. The data that the At that point either The middle-ground here is that Currently, the settings icon, and enable button are decorative. Eventually, they will perform actions, these actions will be performed by functions that will be passed to the This task is a first step in starting to build up other parts of the UI, and a lot of it will be updated, and changed anyway, so I don't think it makes sense to spend more time on this. |
I very much agreed with your push. It's just that at the time, I got a little confused about the intended name (and the classes to use in the implementation). Really, I don't think I can thank you enough. I have learned a lot from this. |
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 I disagree with the approach of passing all the arguments as an object, I don't want to block this work on that point. It's good to go from my side after addressing the point in my comments.
- I tested this: Tested on master devstack locally via
npm start
- I read through the code
showEnable: PropTypes.bool.isRequired, | ||
}); | ||
|
||
export default CoursePageShape; |
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 does not need to be a .jsx file. In fact, it does not need to be a separate file, you can keep this with the same component and just export it from there.
@stvstnfrd This PR is ready. |
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 looking good! Thanks for all of the iteration!
And thanks for bearing with me; we just finally launched the new Courseware/learning MFE, so BD-03 has much more of my attention now :)
I've got a few questions, pretty minor, and some of which are general styling questions (on which I probably tagged @davidjoy lol) that aren't necessarily an issue now, but just wanted to flag in case they fall under any potential "best practices" that I just wanted to get out of the way now, before later iteration.
Regarding next steps:
Is the screenshot(s) up-to-date?
I know things changed a lot, so just want to make sure we can run the most recent by @marcotuts and UX. Also, if there are any unresolved questions, we can run it by them too (I saw there was discussion about whether or not to collapse columns as screen width decreases).
And then regarding merging, would merging this in its current state (or thereabouts) cause any issues? I know it wouldn't be functional (actions not hooked up, API not wired up, etc.), but if it will "sit there quietly" until we start linking to it, we can merge this sooner. Just to mention, in case you haven't seen, other edx developers have begun contributing to other parts of this repo (and I believe deploying it live, CC: @MichaelRoytman ), so it's no longer just us "messing around in an empty repo" 😁 and we'll need to take appropriate caution.
@@ -44,6 +44,7 @@ | |||
"@fortawesome/free-solid-svg-icons": "5.11.2", | |||
"@fortawesome/react-fontawesome": "0.1.9", | |||
"babel-polyfill": "6.26.0", | |||
"classnames": "^2.2.6", |
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.
edit: I see @xitij2000 originally suggested this approach, though the relevant lines were much shorter then, but happy to discuss.
Tagging @davidjoy since you're often a firewall against new dependencies (which is a good thing!).
Do we use this library in other frontend-apps?
It's not a big library, but it still feels like a heavy integration for what is effectively "append one class to the list based on a boolean".
From below:
return (
<div className={classNames('course-page-config-card d-flex flex-column align-content-stretch bg-white p-3 border shadow', { 'border-info-300': coursePage.isEnabled, 'border-gray-100': !coursePage.isEnabled })}>
// ...
could be simplified (without dependency) as:
let classNames = 'course-page-config-card d-flex flex-column align-content-stretch bg-white p-3 border shadow';
if (coursePage.isEnabled) {
classNames += ' border-info-300';
} else {
classNames += ' border-gray-100;
}
return (
<div className={classNames}>
// ...
or maybe even:
let extraClassNames;
if (coursePage.isEnabled) {
extraClassNames = 'border-info-300';
} else {
extraClassNames = 'border-gray-100;
}
return (
<div className={`${extraClassNames} course-page-config-card d-flex flex-column align-content-stretch bg-white p-3 border shadow`}>
// ...
Comparing the example and counter-examples, beyond the dependency issue itself, my other concern is with just how long the line is, and in particular, that the important parts (the conditionals) are tucked in at the end.
Important code that happens off-screen tends to be more likely to introduce issues/regressions, as it is easy for both author/reviewer/debugger to overlook mistakes; it's literally harder to see.
I don't mean to push for 80 character widths or anything (I mean, I do want to, but I wont; and I think 120 is fairly standard in the edx ecosystem), but this clocks in at 210 (!?) characters, with the critical bits happening off-screen.
Python's ternary operator should also be avoided, at least in verbose situations, for the same reasoning:
my_variable = the_result_of_some_computed_value('with', 'arguments' and 'stuff').trim().lower()) if CONDITION_THAT_RUNS_SO_FAR_OFF_THE_SCREEN_AND_CANT_BE_EASILY_SEEN else IT_IS_MORE_LIKELY_TO_CAUSE_BUGS
Yes, that code is contrived, but yes, this issue does bite us/me in real life, including the first time I read this PR and missed that line 😉
So unless @davidjoy is strongly in favor of this library (and we can still refactor to shrink lines), I'd prefer to remove this.
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.
and if we were to keep this, is there a package-lock.json
that should be committed too?
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.
Do we use this library in other frontend-apps?
Yes, I think it is used in almost all other frontend app, and in edx-platform itself. While I had prior experience with it, I did look into it before suggesting it here.
Even if we'd like to avoid another dependency (npm dependencies can quickly get unwieldy), I think a helper for this is a pretty good thing to include in the frontend-platform
.
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.
Unless @davidjoy protests, I'm fine with this library then, thanks for checking.
If we could though, still refactor its usage to break up the long line(s) so the conditional logic isn't hidden off-screen?
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, we use classnames in many apps and have found it really useful. 👍 It shorthands a lot of complicated logic very nicely.
|
||
import messages from './messages'; | ||
|
||
function CoursePageResources({ intl, coursePages }) { |
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.
@davidjoy Is there any pattern to where/how we unpack the props (in the function signature versus in the function body)? I know I've seen it (eg: frontend-app-learning
) done both ways, but just curious if there are (stylistic) benefits either way or if we're looking to consolidate around either approach.
@@ -12,6 +12,9 @@ temp/babel-plugin-react-intl | |||
### pyenv ### | |||
.python-version | |||
|
|||
### vim ### |
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.
Respect :)
00f23b3
to
7a9169c
Compare
@stvstnfrd Note this is ready for review as well I had applied the suggested fixes. |
.editorconfig
Outdated
@@ -0,0 +1,83 @@ | |||
# *************************** |
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.
FWIW, we haven't put any .editorconfig
files in our frontend repos... I'm not sure it's necessary. I'm fairly sure our eslint config accounts for all this.
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.
Think this is fine to merge in - not sure I think we need the .editorconfig
.
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.
@rusrushal13 @xitij2000 Maybe pull out the .editorconfig, but otherwise this looks good.
Let me know if it needs rebased/squashed or anything, otherwise I'll merge it.
Thanks!
CC: @MichaelRoytman That we'd like to merge this, though it won't be used anywhere, nor does it do anything dynamic yet, but we want to start iterating on the UX. Please let me know if you have any concerns. |
Adds views (with test data) for the course apps configuration pages.
7a9169c
to
09a21c9
Compare
@stvstnfrd I've removed the .editorconfig file and squashed the commits. |
# This is the 1st commit message: WIP # The commit message #2 will be skipped: # WIP
feat: setup repo as NPM package with edX standards.
Wireframe for the discussion configurations, currently work in progress
JIRA tickets:
Discussions:
Dependencies:
Mockups: https://edx.invisionapp.com/share/XDXMC3XU9MR#/screens/420742420
Screenshots:
Sandbox URL: TBD - sandbox is being provisioned.
Merge deadline:
Testing instructions:
Author notes and concerns:
Reviewers