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

Passing the as prop to a multiple style layered component only applies the first styles #3115

Open
1 of 3 tasks
rapgodnpm opened this issue Jan 28, 2025 · 0 comments
Open
1 of 3 tasks

Comments

@rapgodnpm
Copy link

Description

Hello,

Thank you for your contribution to this wonderful library. You can see the bug in the reproduction link. Bellow is an explanation with the problem and a fix.

The issue is in the src/styled-system/jsx/factory.js file with this line:

let { as: Element = __base__, ...elementProps } = props

If as is a string, for example, h1 it will call:

return createElement(Element, {
        ref,
        ...elementProps,
        className: classes(),
      })

And will create an h1 with the classes and styles from the component. The problem is that __base__ is a react component. In our case comp B which renders comp A. They will be ignored and the as value will be used.

To fix it, I've changed the code to be the following in factory.js:

import { createElement, forwardRef } from 'react'
import { getDisplayName } from './factory-helper.js'
import { css, cx } from '../css/index.js'

function createStyledFn(Dynamic) {
  const __base__ = Dynamic.__base__ || Dynamic
  return function styledFn(template) {
    const styles = css.raw(template)

    const StyledComponent = /* @__PURE__ */ forwardRef(function StyledComponent(
      props,
      ref,
    ) {
      let { as: Element = __base__, ...elementProps } = props

      if (typeof __base__ !== 'string') {
        Element = __base__
      }

      function classes() {
        return cx(css(__base__.__styles__, styles), elementProps.className)
      }

      return createElement(Element, {
        ref,
        ...elementProps,
        as: props.as,
        className: classes(),
      })
    })

    const name = getDisplayName(__base__)

    StyledComponent.displayName = `styled.${name}`
    StyledComponent.__styles__ = styles
    StyledComponent.__base__ = __base__

    return StyledComponent
  }
}

function createJsxFactory() {
  const cache = new Map()

  return new Proxy(createStyledFn, {
    apply(_, __, args) {
      return createStyledFn(...args)
    },
    get(_, el) {
      if (!cache.has(el)) {
        cache.set(el, createStyledFn(el))
      }
      return cache.get(el)
    },
  })
}

export const styled = /* @__PURE__ */ createJsxFactory()

This way the styles are kept from the other components and the as thingy also works.

Link to Reproduction

https://stackblitz.com/edit/vitejs-vite-enjwupkj?file=src%2FApp.tsx

Steps to reproduce

  1. Use template literal syntax
  2. Create at least 2 components like this: A = styled.spansome styles and B = styled(A)other styles
  3. Use the last styled component with the as prop: fdfd
  4. The styles from comp A are not applied

JS Framework

No response

Panda CSS Version

0.48.0

Browser

No response

Operating System

  • macOS
  • Windows
  • Linux

Additional Information

No response

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

1 participant