Skip to content

Refactor SiteContentForm and use not-allowed cursor for read-only string fields - #2776

Merged
pt2302 merged 6 commits into
masterfrom
pt/refactor_gdrive_url
Nov 6, 2025
Merged

pt2302 merged 6 commits into
masterfrom
pt/refactor_gdrive_url

Conversation

@pt2302

@pt2302 pt2302 commented Nov 3, 2025 •

Copy link
Copy Markdown
Contributor

What are the relevant tickets?

Closes https://github.com/mitodl/hq/issues/9141.

Description (What does it do?)

This PR refactors SiteContentForm to make the special-case handling for certain fields clearer. It also modifies all read-only string fields to use the not-allowed cursor.

How can this be tested?

Ensure that no functionality is affected; the only visible change is that any read-only fields (such as the Google Drive URL field) will now use a not allowed cursor, similar to the File field.

@pt2302
pt2302 requested a review from Copilot November 3, 2025 18:37
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @pt2302, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the user interface and internal structure of the SiteContentForm component. It standardizes the display of the Google Drive URL field to match other resource fields, providing a more consistent user experience, and significantly improves the maintainability of the form's rendering logic through refactoring.

Highlights

  • Refactored SiteContentForm: The SiteContentForm component has been significantly refactored to improve readability and maintainability by centralizing field rendering logic.
  • Google Drive URL as Label: The 'Google Drive URL' field in the 'Edit Resource' modal now uses a Label widget, aligning its display behavior with the 'File' field and showing a 'not allowed' cursor.
  • Improved Field Rendering Logic: New helper functions (hideForNewVideoResource, showAsLabel, labelValue) and a renderField function were introduced to clarify and consolidate conditional rendering logic for various form fields.
  • Constant Reordering: Constants in gdrive_sync/constants.py were reordered alphabetically for better organization and readability.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR refactors the field rendering logic in SiteContentForm.tsx to improve code maintainability and alphabetically reorganizes constants in gdrive_sync/constants.py.

  • Extracted inline field rendering logic into separate helper functions (hideForNewVideoResource, showAsLabel, labelValue, and renderField)
  • Replaced a large inline .map() callback with a cleaner .map(renderField) call
  • Alphabetically sorted constant definitions in the Python constants file

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
static/js/components/forms/SiteContentForm.tsx Refactored field rendering logic by extracting it into reusable helper functions to improve code readability and maintainability
gdrive_sync/constants.py Reorganized constants in alphabetical order for better code organization

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

const labelValue = (field: ConfigField) =>
field.name === "gdrive_url"
? ((values[field.name] as string) ?? "")
: filenameFromPath(((values[field.name] as string) ?? "") as string)

Copilot AI Nov 3, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The double type assertion as string is redundant. The outer type assertion is unnecessary since the result of ?? \"\" is already a string.

Suggested change
: filenameFromPath(((values[field.name] as string) ?? "") as string)
: filenameFromPath((values[field.name] as string) ?? "")

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in a893d5e.

Comment thread static/js/components/forms/SiteContentForm.tsx Outdated

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request successfully refactors the SiteContentForm to improve readability by extracting field rendering logic into a dedicated renderField function and several helpers. This is a great improvement for maintainability. The change to use a Label widget for the Google Drive URL is also implemented. I've added a couple of suggestions to further improve code clarity and ensure consistent DOM structure across form fields.

Comment on lines +159 to +162
const labelValue = (field: ConfigField) =>
field.name === "gdrive_url"
? ((values[field.name] as string) ?? "")
: filenameFromPath(((values[field.name] as string) ?? "") as string)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This function can be simplified for better readability. The expression for the filenameFromPath case has a redundant as string cast, and the parentheses can be reduced in both cases.

Suggested change
const labelValue = (field: ConfigField) =>
field.name === "gdrive_url"
? ((values[field.name] as string) ?? "")
: filenameFromPath(((values[field.name] as string) ?? "") as string)
const labelValue = (field: ConfigField) =>
field.name === "gdrive_url"
? (values[field.name] as string) ?? ""
: filenameFromPath((values[field.name] as string) ?? "")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in a893d5e.

Comment on lines +201 to +212
return (
<div key={field.name}>
<label htmlFor={field.name}>{field.label}</label>
<Field
as={Label}
name={field.name}
value={labelValue(field)}
className="form-control"
onChange={handleChange}
/>
</div>
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current implementation for fields shown as labels results in an inconsistent DOM structure compared to other fields. Specifically, the <label> element ends up outside the form-group div, which is rendered by the Label component. This can lead to styling inconsistencies.

To ensure a consistent structure where the form-group div wraps both the label and the input, I suggest rendering the read-only input directly using Formik's Field component instead of using the Label component. This also makes the implementation self-contained and less dependent on the Label component's internal structure.

      return (
        <div key={field.name} className="form-group">
          <label htmlFor={field.name}>{field.label}</label>
          <Field
            as="input"
            type="text"
            id={field.name}
            name={field.name}
            value={labelValue(field)}
            className="form-control"
            readOnly
            style={{ cursor: "not-allowed" }}
          />
        </div>
      )

@ChristopherChudzicki ChristopherChudzicki left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall I think moving things into renderField makes the component easier to understand 👍 I left a question/suggestion for possible future simplification around Page url field.

</div>
<Label value={`/pages/${content.filename}`} name="page-url" />
</div>
) : null}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't realize we showed this. It makes sense.

Thoughts:

  • the content url isn't really specific to just pages. It could be shown for other things.
  • It seems very central to WebsiteContent objects. We could make it mostly not special if we (A) add it as a (method) field on WebsiteContentDetailSerializer, (B) add it to the hugo config as a readOnly string field.
    • This makes it a lot less special and lets us control the help text via hugo config, and easily add different help text / title for pages, resources, etc.
    • it is still slightly special in that it would be a top-level WebsiteContent property, not nested under metadata, similar to "title" and "markdown". See contentInitialValues

If we did ☝️ , wouldn't we be able to remove all special handling (in this file) related to title? There's still some via contentInitialValues, since it's a top-level property.

@ChristopherChudzicki ChristopherChudzicki left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking Question: Mentioned this in slack, but in https://github.com/mitodl/hq/issues/9141 you wrote:

The Google Drive URL field introduced in #2742 does not use the Label widget, resulting in a different appearance from other read-only fields such as File.

I'm curious about the part

... resulting in a different appearance from other read-only fields such as File.

It seems to me that:

  • "file" widget is the weird exception, all** other readOnly fields use widget: string and readOnly: true in their configs.
  • The styling doesn't seem inconsistent to me? At least if I remove the helptext from the hugo-project config, the grdive_url and File styles seem identical

Maybe: gdrive_url and wayback_url are the only two right now? But I think content_url suggested in #2776 (comment) could be another

The refactoring in this PR generally looks good, but I'm not actually sure we should use Label for gdrive_url?

@pt2302 pt2302 changed the title Use Label widget for Google Drive URL and refactor SiteContentForm Refactor SiteContentForm and use not-allowed cursor for read-only string fields Nov 5, 2025

@ChristopherChudzicki ChristopherChudzicki left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Only request is to move the readonly styling to forms.scss with the rest of the form styling.

Comment on lines +44 to +47
style={{
...(extraProps?.style || {}),
...(isReadOnly ? { cursor: "not-allowed" } : {}),
}}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be done in forms.scss. (Also... currently the extraProps.style inclusion here isn't doing anything. If extraProps.style does exist, the readonly styling will be overwritten on the next {...extraProps} spread. But the best thing to do is to do the readonly styling in forms.scss anyway.)

@pt2302
pt2302 merged commit 554891e into master Nov 6, 2025
8 checks passed
@pt2302
pt2302 deleted the pt/refactor_gdrive_url branch November 6, 2025 17:36
@odlbot odlbot mentioned this pull request Nov 10, 2025
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants