Skip to content
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

Feature Request: Level 4 headings render h4 > a with fragment (and id) #4503

Closed
3 of 8 tasks
MaoShizhong opened this issue Apr 18, 2024 · 9 comments · Fixed by #4507
Closed
3 of 8 tasks

Feature Request: Level 4 headings render h4 > a with fragment (and id) #4503

MaoShizhong opened this issue Apr 18, 2024 · 9 comments · Fixed by #4507
Assignees

Comments

@MaoShizhong
Copy link
Contributor

MaoShizhong commented Apr 18, 2024

Checks

  • I have thoroughly read and understand The Odin Project Contributing Guide
  • The title of this issue follows the Feature Request: brief description of feature request format, e.g. Feature Request: Add a dark mode to the website
  • Would you like to work on this issue?

Description of the Feature Request

Currently, Kramdown converts level 3 0-indent headings to h3 > a with a parameterized fragment href, and provides its parent section with the parameterized id for scrolling.
Level 4 headings do not generate their own link and fragment, meaning several lessons have resorted to doing things like adding

<span id="subsection-title"><span>

below the level 4 heading for things like knowledge checks.

An issue to consider is that simply amending lib/kramdown/convert/odin_html.rb to

HEADER_LEVELS_TO_CONVERT = [3, 4].freeze

# ... inside #convert_header
if HEADER_LEVELS_TO_CONVERT.include?(element.options[:level])
  # ...

  format_as_block_html("h#{element.options[:level]}", element.attr, body, indent)

is insufficient, as there isn't a good place to put the matching id (since the h3's generated id gets placed on the parent section). Note that the tailwind config and lesson_content.css would need some minor alterations to include h4 in the relevant rules.

A possible non-intrusive workaround

  1. Do the above and move the generated id to the h(3,4) > a:
    def convert_header(element, indent)
      if HEADER_LEVELS_TO_CONVERT.include?(element.options[:level])
        section_anchor = generate_id(element.options[:raw_text]).parameterize
        body = "<a#{html_attributes({ href: "##{section_anchor}", id: section_anchor, class: 'anchor-link' })}>#{inner(element, indent)}</a>"
    
        format_as_block_html("h#{element.options[:level]}", element.attr, body, indent)
      else
        super
      end
    end
  2. Replace section id with data-title:
    def content
      "<section data-title='#{title.parameterize}' markdown='1'>#{formatted_content}</section>"
    end
    Which produces the following DOM structure
    image
  3. Amend lesson_toc_controller.js to account for change of section attribute:
    // LessonTocController.prototype.connect
    this.lessonContentTarget.querySelectorAll('section[data-title]')
    
    // callback in LessonTocController.prototype.activeSection
    const { title } = entry.target.dataset;
    const tocItem = this.tocTarget.querySelector(`li a[href="#${title}"]`).parentElement;

If ids are removed from sections or changed to data-title, I'm not 100% sure if there are any further impacts on any other part of the app (e.g. document_sections.rb:32 passing in title: 'content' for unsectionable_content). A cursory and inexperienced look seems like it wouldn't, but more experience likely would know.

Acceptance criteria

  • Have Kramdown:Convert:OdinHtml#convert_header act upon both heading levels 3 and 4
  • Include generated id in the h(3|4) > a
  • Replace parent section id with data-*. Amend class and .new calls as necessary.
  • Amend lesson_toc_controller.js to account for sections having data-* instead of id.
  • Adjust tailwind.config.js and lesson-content.css to account for both h3 and h4 instead of just h3

Additional Comments

An issue I can imagine might crop up is if any lessons have bare spans or bare anchors (some have <a id="fragment"></a> or <a name="fragment"></a> instead of spans) that already have the id that would be generated by a level 4 heading.
querySelector will still select the first id match which likely won't be an issue, but it would still be worth then grepping the curriculum for bare anchors/spans with ids/names and see if most/all can be removed if this feature is implemented.

@MaoShizhong MaoShizhong added the Status: Needs Review This issue/PR needs an initial or additional review label Apr 18, 2024
@MaoShizhong
Copy link
Contributor Author

MaoShizhong commented Apr 18, 2024

EDIT: Resolved by further amending lesson-content.css

  /* old */
  .lesson-content section[id] {
    scroll-margin-top: 40px;
  }

  /* new */
  .lesson-content a[id] {
    scroll-margin-top: 40px;
  }

Just came to mind that putting the id in the anchor instead of the parent section will cause scrolling to scroll to the very top of the heading, without any top margin (since the h3 will have a top margin between it and the parent section) and that seems to mess with the intersection observer for the side list, where being on a section has 2 sections highlighted.

Still familiarising myself with how the JS controllers actually work to see if there's a simple solution there.

@KevinMulhern
Copy link
Member

KevinMulhern commented Apr 19, 2024

Thanks for the detailed issue @MaoShizhong. I might be misunderstanding the problem (very likely lol), But we should be generating ids for h4's, I think Kramdown does it by default and we only override it for h3's - This h4 can be used as an anchor link and linked to for example: https://www.theodinproject.com/lessons/node-path-intermediate-html-and-css-css-units#viewport-units.

Is the problem you'd like to solve with this making h4's clickable and behave like h3's in that way?

@MaoShizhong
Copy link
Contributor Author

@KevinMulhern Yes, the primary goal is to make h4s clickable just like h3s, and it felt a little odd just having the h3s link to an id on the parent section, with h4s having their id on themselves. As opposed to both h3s and h4s' having the id on themselves, and using a data-* for the parent section.

I actually didn't clock that the h4s generate their own ids because when I was looking at them, I was looking via the markdown preview tool, in which the current CSS Units lesson markdown produces this:

image

But looking at it now, it also doesn't generate sections like with the real page. I think I actually missed the lack of sections in the markdown preview tool because I then started looking at how the CSS and Kramdown changes proposed above affected the real page, and so didn't look at the markdown preview tool anymore 😅

@MaoShizhong
Copy link
Contributor Author

If it won't have any impact elsewhere, then perhaps to make h4s clickable like h3s, it might make sense then to not override Kramdown's h3 id generation?

Then we could have both h3s and h4s render a child anchor, have the h3s and h4s have their ids generated by default, then just change the parent sections' ids to data-title or equivalent. Then the relevant CSS can be amended to account for these.

The end result should hopefully then be no change to site behaviour (no change to the "current section" list), but then both h3s and h4s are clickable.

@KevinMulhern
Copy link
Member

Makes a lot of sense to me @MaoShizhong. Changing the sections to use a data attribute would be a nice refactor and I agree it would make it clearer. The toc is the main thing that will be impacted, but it sounds like you have that well covered. Lets do it!

@KevinMulhern KevinMulhern removed the Status: Needs Review This issue/PR needs an initial or additional review label Apr 19, 2024
@MaoShizhong
Copy link
Contributor Author

@KevinMulhern any ideas about the same lesson file producing different HTML.via the markdown preview tool vs when done via the curriculum:update_content process?

@KevinMulhern
Copy link
Member

Its got something to do with the html santisation we added to the preview tool recently I bet. I'll have a look into it 👀

@MaoShizhong
Copy link
Contributor Author

Want me to open an issue for that for record-keeping?

@KevinMulhern
Copy link
Member

Thanks @MaoShizhong, I've got a fix here. It wasn't as bad as I was expecting lol

MaoShizhong added a commit that referenced this issue Apr 20, 2024
<!-- Thank you for taking the time to contribute to The Odin Project. In
order to get this pull request (PR) merged in a reasonable amount of
time, you must complete this entire template. -->

## Because
<!-- Summarize the purpose or reasons for this PR, e.g. what problem it
solves or what benefit it provides. -->
`h4` headings were generating IDs but were not clickable like with `h3`
headings.


## This PR
<!-- A bullet point list of one or more items describing the specific
changes. -->
- Makes level 4 headings render `h4 > a` like with level 3 headings
- Moves the generated IDs to the heading elements themselves, instead of
the parent `section`
- Replaces `section` `id` attribute with `data-title`
- Amends CSS and Tailwind accordingly
- Amends toc controller accordingly
- Amends rspec tests to account for new HTML structure
- Allows `data-title` to render in preview component
- Reduces duplication in a method


## Issue
<!--
If this PR closes an open issue in this repo, replace the XXXXX below
with the issue number, e.g. Closes #2013.

If this PR closes an open issue in another TOP repo, replace the #XXXXX
with the URL of the issue, e.g. Closes
https://github.com/TheOdinProject/curriculum/issues/XXXXX

If this PR does not close, but is related to another issue or PR, you
can link it as above without the 'Closes' keyword, e.g. 'Related to
#2013'.
-->
Closes #4503

## Additional Information
<!-- Any other information about this PR, such as a link to a Discord
discussion. -->


## Pull Request Requirements
<!-- Replace the whitespace between the square brackets with an 'x',
e.g. [x]. After you create the PR, they will become checkboxes that you
can click on. -->
- [x] I have thoroughly read and understand [The Odin Project
Contributing
Guide](https://github.com/TheOdinProject/theodinproject/blob/main/CONTRIBUTING.md)
- [x] The title of this PR follows the `keyword: brief description of
change` format, using one of the following keywords:
    - `Feature` - adds new or amends existing user-facing behavior
- `Chore` - changes that have no user-facing value, refactors,
dependency bumps, etc
    - `Fix` - bug fixes
-   [x] The `Because` section summarizes the reason for this PR
- [x] The `This PR` section has a bullet point list describing the
changes in this PR
- [x] I have verified all tests and linters pass after making these
changes.
- [x] If this PR addresses an open issue, it is linked in the `Issue`
section
-   [x] If applicable, this PR includes new or updated automated tests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Done
Development

Successfully merging a pull request may close this issue.

2 participants