carbon-design-system / carbon-for-ibm-dotcom

Carbon for IBM.com is based on the Carbon Design System for IBM
https://www.ibm.com/standards/carbon/
Apache License 2.0
264 stars 157 forks source link

Masthead: Better support server-side rendering of the contents #3397

Closed joshua-andrassy closed 3 years ago

joshua-andrassy commented 4 years ago

The problem

Non-javascript backend's often rely primarily on templated html outside context of javascript to render the frontend and component APIs that can be invoked simply through markup/DOM structure can provide the cleanest/easiest integration path.

In experimenting with the web-components library in an effort to migrate a POC from v18 to the ibm-dotcom carbon library, my integration of dds-masthead-container looked a lot like this...

<!-- page-base.template -->
...
<body>
    <dds-masthead-container>
    </dds-masthead-container>
    {% include "script.template" %}
    <script src="/static/js/app.js"></script>
</body>
...
<!-- script.template -->
<script>
    const pageState = {
        subSites: [
            {% for site in sub_sites %}
            { title: "{{ site.name }}", url: "{{ site.url }}" }
            {% endfor %}
        ]
    }
</script>
// app.js
document.querySelector('dds-masthead-container').navLinks = [
{
    title: "Sites",
    url: "#",
    menuSections: [
        { menuItems: pageState.sites }
    ]
}]

The solution

Leverage slots to enable customization through page structure alone.

With slots, my above integration could potentially simply be:

<!-- page-base.template -->
...
<body>
    <dds-masthead-container>
        <dds-top-nav slot="top-nav">
                   <dds-top-nav-menu menu-label="Sites" trigger-content="Sites">
                     {% for site in sub_sites %}
                       <dds-top-nav-menu-item href="{{ site.url }}" title="{{ site.name }}"></dds-top-nav-menu-item>
                     {% endfor %}
                   </dds-top-nav-menu>
               </dds-top-nav>
    </dds-masthead-container>
</body>
...

Potential Considerations: I do not believe slots can be "typed" per se and it would enable users of the library to misuse slots or use them incorrectly (ex: the "top-nav" slot is expected to always be a dds-top-nav)

Additional information

Have no additional information or statistics.

asudoh commented 4 years ago

Hi @joshua-andrassy thank you for trying out our Web Components codebase and reporting this! Just to double-check; You are trying to render nav items server-side; Is it correct?

joshua-andrassy commented 4 years ago

Correct - I can definitely render the nav items client side, just currently not happy with how that looks. But it works for sure.

asudoh commented 4 years ago

I see thank you for clarifying. If we put the search box aside not using <dds-masthead-container> will be the way to go (<dds-masthead-container> is merely for client-side rendering nav, etc. from links data). Otherwise, will mull over about search-only renderer.

joshua-andrassy commented 4 years ago

In my use case, I do need all of the capabilities that <dds-masthead-container> provides including the ability to render the <dds-top-nav>.

I did think about creating my own variation of <dds-masthead-container> - but that didn't necessarily feel right either (as it would literally be a copy/paste of the existing component with a change to allow the top nav to be rendered via slot).

I also like the idea of my navigation elements represented in the actual structure of the page sans javascript.

asudoh commented 4 years ago

In my use case, I do need all of the capabilities that <dds-masthead-container> provides including the ability to render the <dds-top-nav>.

@joshua-andrassy Seems that your "rendering" word refers more to getting the working/styled UI, rather than generating HTML tags from data (which is what <dds-masthead-container> does). Is it correct? For getting the working/styled UI, <dds-top-nav>, <dds-top-nav-menu>, etc. tags do everything you need. In other word, below plain HTML (that does not have <dds-masthead-container>) generates working/styled top-nav UI:

<!DOCTYPE html>
<html>
  <head>
    <script type="module">
      import 'https://jspm.dev/@carbon/ibmdotcom-web-components@canary/es/components/masthead/masthead.js';
      import 'https://jspm.dev/@carbon/ibmdotcom-web-components@canary/es/components/masthead/top-nav.js';
      import 'https://jspm.dev/@carbon/ibmdotcom-web-components@canary/es/components/masthead/top-nav-menu.js';
      import 'https://jspm.dev/@carbon/ibmdotcom-web-components@canary/es/components/masthead/top-nav-menu-item.js';
      import 'https://jspm.dev/@carbon/ibmdotcom-web-components@canary/es/components/masthead/top-nav-item.js';
    </script>
    <style type="text/css">
      body {
        font-family: 'IBM Plex Sans', 'Helvetica Neue', Arial, sans-serif;
      }
    </style>
  </head>
  <body>
    <dds-masthead>
      <dds-top-nav>
        <dds-top-nav-menu menu-label="Products &amp; Solutions" trigger-content="Products &amp; Solutions">
          <dds-top-nav-menu-item href="https://www.ibm.com/products" title="Top products &amp; platforms" ></dds-top-nav-menu-item>
          <dds-top-nav-menu-item href="https://www.ibm.com/industries" title="Industries"></dds-top-nav-menu-item>
          <dds-top-nav-menu-item href="https://www.ibm.com/artificial-intelligence" title="Artificial intelligence"></dds-top-nav-menu-item>
        </dds-top-nav-menu>
        <dds-top-nav-menu menu-label="Services &amp; Consulting" trigger-content="Services &amp; Consulting">
          <dds-top-nav-menu-item href="https://www.ibm.com/services/process" title="Business process services"></dds-top-nav-menu-item>
          <dds-top-nav-menu-item href="https://www.ibm.com/services/ibmix/" title="Design &amp; business strategy"></dds-top-nav-menu-item>
          <dds-top-nav-menu-item href="https://www.ibm.com/talent" title="Talent &amp; transformation"></dds-top-nav-menu-item>
        </dds-top-nav-menu>
      </dds-top-nav>
    </dds-masthead>
  </body>
</html>

image

joshua-andrassy commented 4 years ago

In my particular use case, I would need all what <dds-masthead-container> provides out of the box.

Specifically, the POC is being built with Elixir and Phoenix - and rendering is primarily done through Elixir's templating engine.

Setting navLinks is not an issue per se, it just isn't necessarily pretty. It means I am writing JavaScript in Eex in order to store some state info client side and using that state to set navLinks on page load. The other option is to make a variant of <dds-masthead-container> and modify it slightly to better suit the stack I'm working with (I really-really-really hate one-off solutions, especially when they do not need to exist). For reference, a similar app was built using Fomantic and the only JavaScript needed was a few lines to activate its various components.

Definitely ways to achieve what I need with the library as it currently works. Having the option to leverage light dom where appropriate would certainly be appreciated as the library grows and provide flexibility in terms of library usage.

A Red Hat developer posted this interesting blog about considerations they made in their own library (granted, their concerns were primarily SEO)

asudoh commented 4 years ago

Thank you for clarifying @joshua-andrassy. To clarify one thing from my side, <dds-mathead-container> renders stuffs in light DOM given <dds-top-nav-menu> etc. are not something to be hide as implementation details.

To go through your list, IBM logo, profile and top nav have their corresponding Web Components that works just by putting their tags. Search is the missing piece, given one of its core functionality (API call for search query) is tight-coupled to <dds-masthead-container>. So what we need to do is de-coupling it.

joshua-andrassy commented 4 years ago

@asudoh Gotcha - if you need someone to do that de-coupling, would be more than happy to take a stab at it.