-
Notifications
You must be signed in to change notification settings - Fork 2
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
Field edit page with preliminary appearance tab #3661
Conversation
c1cde9c
to
6dcf480
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.
I was just skimming this, two small comments.
38a4947
to
f92724f
Compare
Codecov Report
@@ Coverage Diff @@
## master #3661 +/- ##
==========================================
+ Coverage 87.59% 87.62% +0.03%
==========================================
Files 286 291 +5
Lines 12837 12938 +101
Branches 2115 2128 +13
==========================================
+ Hits 11244 11337 +93
- Misses 1369 1371 +2
- Partials 224 230 +6
Help us with your feedback. Take ten seconds to tell us how you rate us. |
6034e74
to
a2cbf8a
Compare
frontends/infinite-corridor/src/components/forms/EditChannelAppearanceForm.tsx
Outdated
Show resolved
Hide resolved
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 mentioned: I have to run but this looks good! I left some comments/suggestions.
return editFieldChannel(field?.name, data) | ||
}) | ||
|
||
const appearanceSchema = Yup.object().shape({ |
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.
Requested Change: Move this outside the component function definition.
In general, when an object doesn't depend on the component parameters, it's probably better to define it outside of the component. Otherwise it's a new object on each render, and can cause react to over-eagerly re-render stuff.
frontends/infinite-corridor/src/components/forms/EditChannelAppearanceForm.tsx
Outdated
Show resolved
Hide resolved
{field.data ? ( | ||
<> | ||
<FieldAvatar field={field.data} imageSize="medium" /> | ||
<h2>{field.data.title}</h2> | ||
</> | ||
) : 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.
Minor Suggestion: Use condition && <SomeComponent />
when conditionally rendering a component instead of the ternary.
expect( | ||
await screen.findByText("You do not have permission to access this page.") | ||
).toBeInTheDocument() |
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 slightly conflicted about this suggestion, because I like how explicit expect
is. But...
Suggestion: Just do await screen.findByText(...)
without the expect
.
because findBy...
and getBy...
both query the document body (so the result is guaranteed to be in the document, not some crazy detached DOM node) and they both throw if no results are returned. (Well, getBy
throws, and findBy
returns a rejected promise).
Reminder:
getBy
= synchronous + throws if no result ...getAllBy
also throws if 0 elements foundfindBy
= asynchronous + rejects if no result ...findAllBy
also rejects if 0 elements foundqueryBy
= synchronous + can return null ...queryAllBy
can return an empty list
import * as factory from "../../api/fields/factories" | ||
import { urls } from "../../api/fields" | ||
|
||
describe("EditChannelAppearanceForm", () => { |
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.
Question: I know fields are channels, so maybe the name is intentional, but I just wanted to double check.
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.
Changed to EditFieldAppearanceForm
frontends/infinite-corridor/src/components/forms/EditChannelAppearanceForm.test.tsx
Outdated
Show resolved
Hide resolved
titleError = await screen.findByText("Title is required") | ||
expect(titleError).toBeInTheDocument() |
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 mentioned above, find
will reject, so this can just be
titleError = await screen.findByText("Title is required") | |
expect(titleError).toBeInTheDocument() | |
await screen.findByText("Title is required") |
frontends/infinite-corridor/src/components/forms/EditChannelAppearanceForm.test.tsx
Outdated
Show resolved
Hide resolved
@@ -0,0 +1,131 @@ | |||
import React from "react" |
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.
Rushed comment since I have to run:
Would it make sense to put this component in pages/admin
? I do not have a strong feeling.
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.
forms/admin
?
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.
But I guess all the forms for infinite will be admin forms
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.
My thought was along the lines of co-locating the admin-editing code: pages/admin
directory would contain the significant components that it uses, would import some stuff from ol-util or ol-forms. To make pages/admin
more self-contained.
I.e., along the lines of this article Ferdi linked a while ago: https://alexmngn.medium.com/how-to-better-organize-your-react-applications-2fd3ea1920f1 Note: If you do read that, its examples are a bit outdated. Like, people don't really have a containers/
folder any more for redux-store connected components.
What do you think? We can also keep it where it is for now reconsider in future prs.
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.
Moving to /pages/admin
it("Displays a small avatar image for the field", async () => { | ||
const field = makeField() | ||
render(<FieldAvatar field={field} imageSize="small" />) | ||
const img = screen.getByAltText(`Channel avatar for ${field.title}`) |
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.
Suggestion: I think we should get rid of the alt text, and replace this with getByRole("img")
.
I am by no means an accessibility expert, but my understanding is that alt text is better left blank if it does not add meaningful context. Like on the field page, a screenreader would currently read, I believe
"Channel avatar for physics. Physics".
Better (I think?) just to say "Physics".
Related: https://silktide.com/blog/is-alt-text-making-your-site-less-accessible/
Actually: Probably best to make this component accept alt
as a prop. Then if we ever use the avatar as an unlabeled link, we can use pass an alt tag for it. But... we don't need that right now.
fireEvent.change(titleInput, { | ||
target: { value: "" } | ||
}) | ||
fireEvent.blur(titleInput) |
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.
@mbertrand One more "by the way" comment: I usually trigger events via user
:
import user from "@testing-library/userEvent" // test-utils also exports this as `{ user }` for convenience.
user.click(htmlElement)
user.type("blah blah blah{Enter}")
The user
interface is a higher-level API that more closely resembles real user interaction. (@testing-library
's general philosophy is "write tests that resemble user interaction".)
For example, user.type
types the text (character by character, I think... which is still plenty fast). And user.click
fires most (all?) the events that would be fired by a click: pointerdown, pointermove, pointerup, click.
Anyway, that's why I generally use those methods though I suspect it doesn't usually matter.
BTW: user.type
can be slightly annoying because it's like typing...you have to clear the text first:
input.setSelectionRange(0, input.value.length) // select all the text
user.type(input, 'whatever new thing you want') // typing clears the selected text
testing-library/user-event#232
cc @abeglova
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! I remember why user.click
is nice for clicking... it does not trigger events if the element is disabled, whereas fireEvent just triggers the event no matter what.
Related...
- dispatching click event executes click handler for disabled elements jsdom/jsdom#2665 _Closed because apparently this follows some spec, and browsers do not follow the spec. 🤷 _
- fireEvent.click() performs click on disabled input field testing-library/dom-testing-library#92
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, I noticed that user.type(input, text)
throws an error if text
is blank ("") - Expected key descriptor but found "" in ""
- so I left that test as is but changed the others.
87fc407
to
fcb6bb2
Compare
alt={`Channel avatar for ${field.title}`} | ||
className={`avatar-image`} |
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 changes look good. I saw a change to the test to not use alt text...did you want to change the alt text to ""
now?
Note: alt=""
is different from no alt. My understanding is that if there is no alt, screen readers tend ot read the src attribute. Which seems like a strange default to me.
related: https://mitodl.slack.com/archives/G1VJY4CCV/p1658871529668839
fcb6bb2
to
7feba81
Compare
Pre-Flight checklist
What are the relevant tickets?
Partially addresses #3632
What's this PR do?
Adds a form for editing field title and description, but not banner and avatar. These will be added in a separate PR.
Adds a tabbed edit page with 3 tabs (basic, appearance, moderators). All but appearance tab are just placeholders right now.
Creates a separate component for field avatar
Adds
is_moderator
to the API response to determine if the current user can access the field edit page/forms.How should this be manually tested?
/infinite/fields/<your_field_name>/manage/
and verify that you see 3 tabs.Screenshots (if appropriate)