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

Add a set of breadcrumb DX components #309

Open
rtablada opened this issue May 9, 2024 · 6 comments
Open

Add a set of breadcrumb DX components #309

rtablada opened this issue May 9, 2024 · 6 comments

Comments

@rtablada
Copy link
Contributor

rtablada commented May 9, 2024

Breadcrumb management would be really useful to have in this library.

It's a fairly common pattern for child interfaces to want to "push" a breadcrumb into a parent state without having implementation of where the actual breadcrumb is rendered higher in the component tree

Thinking about a potential public API for this, could be.

import { BreadcrumbRenderer, Breadcrumb } from 'ember-primatives';

export const ParentComponentSomewhere = <template>
  <BreadcrumbRenderer class="flex items-center space-x-2 bg-gray-500 border rounded">
    <:item as |BreadcrumbItem|>
      <BreadcrumbItem class="inline-flex items-center hover:text-blue-200" />
    </:item>
    <:separator as |BreadcrumbSeparator|>
      <BreadcrumbSeparator class="mx-4"> 
        <ChevronIcon />
      </BreadcrumbSeparator>
    </:separator>
  <BreadcrumbRenderer>

  <Breadcrumb>
    <a href="/">
      <HomeIcon />
      Home
    </a>
  </Breadcrumb>
  <Breadcrumb>
    <a href="/forum">Forum</a>
  </Breadcrumb>

  ...
</template>

export const ChildComponentSomewhere = <template>
  <Breadcrumb>
    <a href="/forum/topics/{{@topic.id}}">{{@topic.name}}</a>
  </Breadcrumb>

  ...
</template>

This would result in something to the effect of the following HTML being rendered in the DOM where the BreadcrumbRenderer was invoked:

<div class="flex items-center space-x-2 bg-gray-500 border rounded"> <!--  Classes applied to the `BreadcrumbRenderer` -->
  <div class="inline-flex items-center hover:text-blue-200"> <!-- :item block is rendered for each <Breadcrumb> and contents yielded in `BreadcrumbItem` with applied classes -->
    <a href="/"><HomeIcon /> Home</a>
  </div>
  
  <div class="mx-4"><ChevronIcon /></div> <!-- :separator block rendered between <Breadcrumb> contents -->

  <div class="inline-flex items-center hover:text-blue-200"> <!-- :item block is rendered for each <Breadcrumb> and contents yielded in `BreadcrumbItem` with applied classes -->
    <a href="/forum">Forum</a>
  </div>
  
  <div class="mx-4"><ChevronIcon /></div> <!-- :separator block rendered between <Breadcrumb> contents -->

  <div class="inline-flex items-center hover:text-blue-200"> <!-- :item block is rendered for each <Breadcrumb> and contents yielded in `BreadcrumbItem` with applied classes -->
    <a href="/forum/topics/{{@topic.id}}">{{@topic.name}}</a>
  </div>
<div>

Considerations:

  1. Do we need a :separator block?
  • Css psuedo selectors can be used to add display only content between breadcrumbs, however this likely would be awkward and could limit teams on being able to render the content they want in a maintainable GTS format
  • I potentially wouldn't want to limit what teams are ALLOWED to
  • ember-primitives aims to be "bring your own CSS/HTML" and forcing teams into psuedo selectors will not be universally compatible
  • :separator could be optional and when the block doesn't exist then nothing would be rendered between items, this could allow teams to still use psuedo selectors if they chose
  1. Does :separator need to yield a component?
  • Separator could be a simple block yield
  • Yielding a BlockSeparator component does allow some flexibility
  1. Are there any default aria roles/attrs that should be enforced?
  2. Yields and blocks for Breadcrumb?
  • It could be useful for breadcrumbs to get yielded values: isFirst or isLast: boolean (in case teams want to add something like aria-current="page" to the last breadcrumb [though this could be out of sync if more child UIs are rendered without adding their own breadcrumb])
  • Should Breadcrumb have a way to modify its associated selector separator?
@rtablada rtablada changed the title Add a breadcrumb util Add a set of breadcrumb DX components May 9, 2024
@NullVoxPopuli
Copy link
Contributor

Thanks for the issue! I am hugely in favor of this!

Does :separator need to yield a component?

(need) only if there is an a11y benefit to doing so
it may also be useful to have a wrapper element around this block for managing spacing or alignment consistency (flex/gap/basis/etc)

Are there any default aria roles/attrs that should be enforced?

I am not sure, would require research.

Some prior art though (and we may want to borrow some API design from here as well -- the simple case for the Svelte one is very nice):

(in case teams want to add something like aria-current="page" to the last breadcrumb

this may be something we want to control, as one of the goals of ember-primitives is to encode many of the non-default a11y patterns

Should Breadcrumb have a way to modify its associated selector?

how do you mean?

@rtablada
Copy link
Contributor Author

rtablada commented May 9, 2024

Sorry meant to say

Should Breadcrumb have a way to modify its associated separator

Probably not necessary for first implementation/release, but I'm thinking ahead of if teams would want a way to modify the separator before/after a given breadcrumb. Giving a good DX and determining what "wins" could be real 🤮 though. And all that is for a "what if" (I don't personally have a use in our apps but this was more thinking for maximum customization)

@NullVoxPopuli
Copy link
Contributor

if teams would want a way to modify the separator before/after a given breadcrumb

I would be surprised by this, as I've never seen per-item separator customization

@rtablada
Copy link
Contributor Author

rtablada commented May 9, 2024

Thinking about it having a "the instantiation modifies separator, this could be accomplished by omitting the :separator block and then using isLast/isFirst in the breadcrumb child population:

export const Separator = <template>
  <div class="mx-4" ...attributes>
    <ChevronIcon />
  </div>
</template>

export const ParentComponentSomewhere = <template>
  <BreadcrumbRenderer class="flex items-center space-x-2 bg-gray-500 border rounded">
    <:item as |BreadcrumbItem|>
      <BreadcrumbItem class="inline-flex items-center hover:text-blue-200" />
    </:item>
  <BreadcrumbRenderer>

  <Breadcrumb as |bc|>
    <a href="/">
      <HomeIcon />
      Home
    </a>
    {{#unless bc.isLast}}<Separator />{{/unless}}
  </Breadcrumb>
  <Breadcrumb>
    <a href="/forum">Forum</a>
    {{#unless bc.isLast}}<Separator class="some-customization-to-separator" />{{/unless}}
  </Breadcrumb>
  ...
</template>

It's not perfectly equal to a per instance separator, since now the stuff you're using to separate each item is in the same element as the breadcrumb contents, but... I think it gives maximum flexibility without really weird edge cases

@NullVoxPopuli
Copy link
Contributor

that's true -- might be worth adding to docs as an example, I suppose if someone really wanted to! 🙃 🎉

@rtablada
Copy link
Contributor Author

rtablada commented May 14, 2024

There's still room to go and the module scope TEMP_BREADCRUMB_MAP likely will break in all but the simplest app:

main...rtablada:ember-primitives:rt/poc-breadcrumb

There's some WIP and proof of concept stuff in there.

I mainly wanted to feel the basic handling and see far in-element would stretch. I'm using some of the same patterns (and a lot of doc copy 🍝 ) from portals to try and target elements elsewhere in the DOM.

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

No branches or pull requests

2 participants