oacore / design

Design Elements is shared components library for CORE. Made with ❤️ and React
https://design.oacore.now.sh
4 stars 0 forks source link

Class names discovery from the context ✨ #322

Open viktor-yakubiv opened 4 years ago

viktor-yakubiv commented 4 years ago

I’ve just come to the idea that we can simplify our components API if we utilise context technique in the library on the top level. For the instance, we have:

<Card>
  <Card.Title>I'm a card!</Card.Title>
</Card>

that turns into

<div class="card">
  <h2 class="card-title">I'm a card!</h2>
</div>

The proposal

Potentially, we can have a bit easier API like

<Card>
  <Heading>I'm a card!</Heading>
</Card>

that produces the same result.

Simple implementation

const SectionContext = React.createContext({ level: 1 })
const ClassNamesContext = React.createContext({})

const Card = ({ children, className, tag: Tag = 'div', ...restProps }) => (
  <SectionContext.Provider value={{ level: 2 }}>
    <ClassNamesContext.Provider value={{ heading: 'card-title' }}>
      <Tag className={joinClasses('card', className)} {...restProps}>
        {children}
      </Tag>
    </ClassNamesContext.Provider>
  </SectionContext.Provider>
)

const Heading = ({ children, className, ...restProps }) => {
  const level = useContext(SectionContext)?.level ?? 1
  const contextClassName = useContext(ClassNamesContext)?.heading

  const Tag = `h${level}`

  return (
    <Tag className={joinClasses(contextClassName, className)} {...restProps}>
      {children}
    </Tag>
  )
}

Use cases

My use case above is pretty simple and seems to be not worth it. I not sure either in this.

Apart from Heading replacing the Card.Title along with Section.Title we may have in the future, it looks more convenient for implicit passing classes to:

I am pretty sure, there are other cases. However, since it's implicit, it's not obvious: very easy to use, very hard to debug.

@Joozty what do you think?

viktor-yakubiv commented 4 years ago

This can be also scaled for the headers accessibility. Check the example:

<Section id="my-section">
  <Heading>My section</Heading>
</Section>

should produce:


<section id="my-section" aria-labelledby="my-section-title">
  <h2 id="my-section-title">My section</h2>
</section>
viktor-yakubiv commented 4 years ago

However, we should investigate React Context API performance. If it's bad, we should drop the idea, unfortunately. Especially interesting it the performance of combining many contexts.

Joozty commented 4 years ago

The idea is nice. Maybe it would be worth if you created a proof of concept. The API would be simplified a lot I am just a little bit worried though it simplifies the final API it also would complicate the design development itself.