cristianbote/goober

Components referencing themselves

fabiospampinato opened this issue · 6 comments

For some components of mine I need to have them reference themselves, with styled-components I could write something like this to do that:

const Component = styled.div`
	&${() => Component} {
		color: pink;
	}
`;

But with goober that patterns seems unsupported. Moreover I will most probably need to reference components from other components, so I'd expect something like ${Component} to give me the unique class associated with that component.

What's the equivalent way of doing that with goober?

Adding a .toString = () => 'random-class-here' to components made with goober seems to work pretty well actually 🤔 would that be the recommended way to do this? Any issues that I might encounter with that?

I think that might not work properly for components that extend other components 🤔

I think this might work:

// Adding a className explicitly, so that it gets added to the DOM
Component1.className = 'class1';
// Added a toString method, so that components can be referenced elsewhere
Component1.toString = () => 'class1';
// Then when a component extends another one a new class is simply appended
Component2.className = 'class1 class2';
Component2.toString = () => 'class1 class2';

Slight update, since it's problematic to return multiple class from toString and it's more convenient to return them with the dot for easier interpolation:

// Adding a className explicitly, so that it gets added to the DOM
Component1.className = 'class1';
// Added a toString method, so that components can be referenced elsewhere
Component1.toString = () => '.class1';
// Then when a component extends another one a new class is simply appended
Component2.className = 'class1 class2';
Component2.toString = () => '.class2';

Heya @fabiospampinato thanks for opening this issue.

Nifty little findings you got there! The .toString method seems interesting. Now goober does support self-referencing notation with the & sign. No need to do the inline function &{() => Component}. Take a look at this snippet:

const Foo = styled("div")`
  background: silver;
  padding: 1em;
  border-radius: 2px;

  // The `&` is a self-referencing symbol and will be replaced by this component unique className
  & {
    background: red;
  }
`;

const WrappedFoo = styled("div")`
  margin: 1em;
  padding: 1em;
  border: 1px solid tomato;

  ${Foo} {
    background: dodgerblue;
  }
`;

const App = () => {
  return (
    <>
      <Styles />
      <Foo>foo</Foo>
      <WrappedFoo>
        This is the wrapper
        <Foo>content</Foo>
      </WrappedFoo>
    </>
  );
};

You can see it live here as well https://codesandbox.io/s/sweet-sound-nvtn4w

Does this answer your question?

@cristianbote using & works, but it's a little constraining in some situations, for example if I have to and prefer to write selectors like this now I can't reference the parent component anymore:

&.something {
  & {
     /* no way to reference the parent, Foo, not Foo.something */
  }
}

Potentially that's a limitation that can be worked around by rewriting the code like this:

&.something & {
   /* works */
}

And that would be ok in simple snippets like that, but I think it would get really messy potentially with other code 🤔