Skip to content

Conversation

@jamos-tay
Copy link
Contributor

What is the purpose of this pull request? (put "X" next to an item, remove the rest)

• [X] New feature

Fixes #435

What is the rationale for this request?

The user may want to be able to show/hide certain elements from a page depending on the deploy configuration.

Cited use case:

When generating multiple sites with only slightly differing content, it might be easier to store all the information in one page, and allow the author to show/hide them. For example, if I have a Java tutorial page and a C# tutorial page, the text content can be exactly the same, only the code will differ:

# Print a line

<!-- Example syntax -->
<div tag="lang--java">
    System.out.println("Hello world");
</div>
<div tag="lang--Csharp">
    Console.WriteLine("Hello world");
</div>

The user can then deploy a Java or a C# version of the site without having two projects.

What changes did you make? (Give an overview)

Functionality implemented based on what was discussed here: #435 (comment)

Tags ({tagType}--{tagName}) are added to any HTML element.
User can specify which tags to include in site.json or frontmatter. Frontmatter takes precedence.
Only elements with tags in site.json are displayed, rest are hidden.

Simplified from include: { type: 'lang', include: ['java'] } to just include: { 'lang': ['java'] } though.

Is there anything you'd like reviewers to focus on?

Behavior works according to spec, but I'm wondering if it's a little unintuitive - do we want to mandate categorizing tags into different types (lang etc.) rather than just having general use tags, without the {type}--? I.e. include : ['tag1', 'tag2' ... ] I don't think we lose any functionality by having the latter.

@damithc
Copy link
Contributor

damithc commented Dec 12, 2018

Behavior works according to spec, but I'm wondering if it's a little unintuitive - do we want to mandate categorizing tags into different types (lang etc.) rather than just having general use tags, without the {type}--? I.e. include : ['tag1', 'tag2' ... ] I don't think we lose any functionality by having the latter.

Yes, that's fine too.

Shouldn't we allow multiple tags i.e., <div tags="java, basic, optional">

Tagged elements are shown by default or hidden by default?

@jamos-tay
Copy link
Contributor Author

jamos-tay commented Dec 12, 2018

They're always hidden by default unless a matching tag is specified.

I'm not sure about multiple tags... Does that mean if a single tag matches, we display the element?

@acjh
Copy link
Contributor

acjh commented Dec 12, 2018

  1. General-use tags

    without the {type}--? I.e. include : ['tag1', 'tag2' ... ]

    Yes, that's fine too.

    Agreed. tag1 should be able to contain dashes though.

  2. Multiple tags

    Shouldn't we allow multiple tags i.e., <div tags="java, basic, optional">

    Yes. Shall we follow HTML class attribute syntax? i.e. <div tag="java basic optional">

    Does that mean if a single tag matches, we display the element?

    Yes, I think so.

  3. Tagged elements are shown by default or hidden by default.

    They're always hidden by default unless a matching tag is specified.

    Agreed, with an important exception: if the config is undefined or null, then show all.

  4. include: and "includedTags": keywords

    "renderTags": is more appropriate as it's in MarkBind's post-rendering phase.

  5. Specifying in front matter

    Is there a compelling reason to specify in front matter? It seems sufficient to be in site.json.

  6. Tagging functionality as a plugin

    The clean implementation makes me wonder if we can consider a plugin concept, e.g. in Eslint.
    For a start, we can have a postRender entry point. This means something like this in site.json:

    {
      "plugins": [
        "renderTags"
      ],
      "context": { // Can consider a different name
        "renderTags": ["shown", "shown-2"]
      }
    }

    And in src/plugins/render-tags.js:

    module.exports = {
      postRender: function (content, siteContext, pageConfig) {
        const renderTags = siteContext.renderTags;
        // ...
        return $.html();
      },
    };

    And in Page.js:

    - .then(result => this.filterTags(this.frontMatter.includedTags, result))
    + .then(result => this.postRender(result, siteContext, pageConfig))
    Page.prototype.postRender = function (content, siteContext, pageConfig) {
      let postRenderedContent = content;
      plugins.forEach((plugin) => { // Might be able to do `reduce` instead of `forEach`
        if (plugin.postRender) {
          postRenderedContent = plugin.postRender(postRenderedContent, siteContext, pageConfig);
        }
      });
      return postRenderedContent;
    };
  7. src/ directory (can be discussed in a separate issue)

    As suggested with src/plugins/render-tags.js, let's start using src/ instead of lib/.

    • src/ is pretty common.
    • libs/ implies libraries' code, rather than this library's code. It's more nested but clearer.

    Current directory structure:

    lib/
      asset/
      markbind/
        lib/
          markdown-it/
          parser.js
          utils.js
        package-lock.json
        package.json
      template/
      util/
      Page.js
      Site.js
    package-lock.json
    package.json
    

    Proposed directory structure:

    src/
      asset/
      lib/
        markbind/
          src/
            lib/
              markdown-it/
            parser.js
            utils.js
          package-lock.json
          package.json
      template/
      util/
      Page.js
      Site.js
    package-lock.json
    package.json
    

@damithc
Copy link
Contributor

damithc commented Dec 13, 2018

2. es. Shall we follow HTML class attribute syntax? i.e. <div tag="java basic optional">

Sounds good.

2. Does that mean if a single tag matches, we display the element?

For this round, yes, we can do that. But eventually we are are going to need a exclude : ['tag1', 'tag2' ... ] and some sort of precedence (e.g., left-to-right) so as to provide more control over what gets included/excluded

4. "renderTags": is more appropriate as it's in MarkBind's post-rendering phase.

I prefer to not use include too as it is already a keyword in MarkBind. But should we use keywords that are more intuititve to the author e.g., keep/omit? render sounds like a dev term.

site.json vs frontmatter: note that tag filtering need not be site-wide. Ideally, there should be a way to specify it for a group of files.

@yamgent
Copy link
Member

yamgent commented Dec 13, 2018

6. Tagging functionality as a plugin
The clean implementation makes me wonder if we can consider a plugin concept, e.g. in Eslint.
For a start, we can have a postRender entry point. This means something like this in site.json:
js {

I think that it is a good idea going in the direction of allowing MarkBind to be extensible in the future. Separating the core logic with the "additional" features would create a good foundation for such a plugin system.

@jamos-tay
Copy link
Contributor Author

Changes:

Simplified tags:
tags : ['tag-1', 'tag-2']

I think we can just use the tags keyword everywhere, might be more succinct.
Removed the whole -- system, the user now just specifies a list of tags to include.
Allow multiple tags, space delimited. Element displays if at least one tag matches
All elements will be displayed if tags are not specified, or empty

I left in the front matter functionality - do we want it in? Either way it's only a couple of lines of code changed so it's not a big thing

Is having both include and exclude necessary? I think it's sufficient to either include all and the user specifies exclude, or exclude all and the user specifies include. Is there a use case where we have to have both?

Regarding the plugin thing, I think that's a great idea. It should be in its own PR though, I'll create another issue. We can migrate other functionalities (like anchors) to plugins too

@jamos-tay jamos-tay changed the title [WIP?] Add element tagging functionality Add element tagging functionality Dec 13, 2018
@damithc
Copy link
Contributor

damithc commented Dec 13, 2018

Is having both include and exclude necessary? I think it's sufficient to either include all and the user specifies exclude, or exclude all and the user specifies include. Is there a use case where we have to have both?

While one can be used in place of the other, depending on the reuse context, one is usually more convenient than the other. Sometimes you want to say show everything except x (e.g., show everything except optional topics) in which case more tags can be added to the page and they will appear in pages without needing additional configurations in frontmatter/site.json. At other times you want to say show only x (e.g., show only java examples) in which case one can add more tags and they will not appear in the pages unless specifically requested.

@yamgent yamgent self-requested a review December 13, 2018 14:34
@jamos-tay
Copy link
Contributor Author

Updated

@jamos-tay
Copy link
Contributor Author

Updated

Copy link
Member

@yamgent yamgent left a comment

Choose a reason for hiding this comment

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

Will need you to resolve the conflicts as the lib -> src thing has been merged recently.

Some more nits in documentation.

@acjh acjh mentioned this pull request Dec 20, 2018
Copy link
Contributor

@acjh acjh left a comment

Choose a reason for hiding this comment

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

Suggested improvements to phrasing and style.

</frontmatter>
```

Tags in the `frontmatter` will take precedence over the ones in `site.json`.
Copy link
Contributor

@acjh acjh Dec 22, 2018

Choose a reason for hiding this comment

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

"front matter"

"` front matter `"

Copy link
Contributor Author

@jamos-tay jamos-tay Dec 22, 2018

Choose a reason for hiding this comment

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

Sorry what's this one again? It's rendering like this for me:
image
I assume it's just removing the backticks and adding a space

Copy link
Contributor

Choose a reason for hiding this comment

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

Correct, edited.

@damithc
Copy link
Contributor

damithc commented Dec 22, 2018

Tags in the frontmatter will take precedence over the ones in site.json.

Actually, this precedence order is problematic for reuse. To paraphrase the open-closed principle from OOP, we should be able to extend existing pages (for reuse) without modifying them. i.e., we should be able to reuse a page without modifying its content while overriding its behavior using some external means, such as the site.json. What do you guys think?

@acjh
Copy link
Contributor

acjh commented Dec 22, 2018

Agreed, site.json should take precedence. I guess this followed Layouts, which also needs this change.

@jamos-tay
Copy link
Contributor Author

jamos-tay commented Dec 22, 2018

Sure - can change the order. Will change the other one in a separate PR.

Updated

@jamos-tay
Copy link
Contributor Author

Updated

Copy link
Member

@yamgent yamgent left a comment

Choose a reason for hiding this comment

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

Comments about the documentation.

@jamos-tay
Copy link
Contributor Author

Updated

@yamgent
Copy link
Member

yamgent commented Jan 21, 2019

@jamos-tay can you resolve conflict for this PR? Thanks!

@jamos-tay jamos-tay force-pushed the add-element-tagging branch from 76f7d9c to ad5ec3f Compare January 21, 2019 16:18
@jamos-tay
Copy link
Contributor Author

Rebased on master!

@yamgent yamgent added this to the v1.17.0 milestone Jan 21, 2019
@yamgent yamgent merged commit 0183497 into MarkBind:master Jan 21, 2019
@damithc
Copy link
Contributor

damithc commented Jan 29, 2019

@jamos-tay is it possible to override frontmatter tags of a specific page/glob from site.json? Or site.json tags can only apply to the entire site?

@yamgent
Copy link
Member

yamgent commented Jan 30, 2019

@jamos-tay is it possible to override frontmatter tags of a specific page/glob from site.json? Or site.json tags can only apply to the entire site?

Currently it applies to the entire site only, no way to do it for a particular page.

The current overriding mechanism is also rather aggressive atm: I realize that it is not true that we don't lose any functionality for not categorizing tags into different groups. Consider this scenario:


helloWorld-fragment.mbdf:

<div tags="level--beginner">

This is how you do Hello World:

<code tags="lang-cs">Console.WriteLine("Hello World");</code>
<code tags="lang-java">System.out.println("Hello World");</code>

</div>

<div tags="level--advanced">

Let's do a refresher. Recall that to print anything, you would use <code tags="lang-cs">Console.WriteLine()</code><code tags="lang-cs">println()</code>.

<question>
How would you print the string "Hello World"?
  ...
</question>

</div>

index-cs.mbd:

<frontmatter>
    tags: lang-cs
</frontmatter>

<include src="helloWorld-fragment.mbdf" />

index-java.mbd:

<frontmatter>
    tags: lang-java
</frontmatter>

<include src="helloWorld-fragment.mbdf" />

I want two different websites: for beginner, and for advanced. Both of these websites will render index-cs.mbd and index-java.mbd. I have no way of specifying either level--beginner or level--advanced without accidentally overriding the lang--* tags.

@damithc
Copy link
Contributor

damithc commented Jan 30, 2019

@jamos-tay perhaps we can do another iteration for this feature to iron out these rough edges?

@jamos-tay
Copy link
Contributor Author

Hmm... I think what we can do is a tag merge, i.e. instead of overwriting the frontmatter tags completely we merge them instead. We probably should have done it this way earlier.

@damithc
Copy link
Contributor

damithc commented Feb 2, 2019

@jamos-tay is it possible to override frontmatter tags of a specific page/glob from site.json? Or site.json tags can only apply to the entire site?

@jamos-tay don't forget this one too.

In addition, should we also have a way to specify tags at the point of inclusion, the same way we allow overriding variables at the point of inclusion?

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.

Allow authors to show/hide certain elements on deploy

4 participants