Skip to content

Context Slots are not Extensible #6992

Open
@ArrayKnight

Description

@ArrayKnight

Provide a general summary of the issue here

When it comes to expanding on the the slots that an RAC provides, it's impossible to maintain the parent context and implement new slots without running into errors.

I'm providing a concrete example based on a single RAC, but this pattern applies to all RAC. The overall concept of named slots is currently very hardcoded and inflexible

🤔 Expected Behavior?

It should be possible to add named slots to any context without errors being thrown within RAC. Conflicts between DS and RAC slot names should only result in warnings and should merge on all applicable props as intended

😯 Current Behavior

Given the scenario:
DSButton consumes a new DSButtonContext and is built on top of the RAC Button
DSGridListItem implements a DSButtonContext with named slots that don't match the RAC GridListItem ButtonContext named slots

An error is thrown or functionality is lost

For context, RAC GridListItem provides DEFAULT_SLOT and "drag" as a named slot

Error Thrown:

If the DSGridListItem implements a named slot of "foobar", and the DSButton attempts to utilize that slot, RAC will throw an error that only "drag" is a valid named slot and that "foobar" is an invalid named slot

It's also not possible to override the slot prop from the DSButtonContext, because local props always take precedence. For example, if the "foobar" slot were to try and pass slot: DEAFULT_SLOT,, the error is still thrown because the component has the higher precedence prop slot="foobar" already on it

Functionality Lost:

If the DSGridListItem were to implement the RAC ButtonContext with the named slot "foobar", then all props for the slots provided by RAC are lost

💁 Possible Solution

Make the RAC slot validation (

throw new Error(`Invalid slot "${slot}". Valid slot names are ${availableSlots}.`);
) dev environment only warnings instead of errors

🔦 Context

While we have had a workaround solution (#6610), my team has been discussing how to align more closely with the guidance provided by RAC folks. The main guidance we've been provided is to utilize the new context for every DS component approach.

This is why we created a wrapper for all of the simple, low level components that are not receiving direct DS attention:

function wrap<P extends object, E extends HTMLElement>(
  Component: (props: P & RefAttributes<E>) => ReactElement | null
) {
  const Context = createContext<ContextValue<P, E>>(null);

  return {
    Component: forwardRef(function WrappedComponent(
      props: P,
      ref: ForwardedRef<E>
    ) {
      [props, ref] = useContextProps(props, ref, Context);

      return <Component {...props} ref={ref} />;
    }),
    Context,
  };
}

export const { Component: AriaText, Context: AriaTextContext } = wrap(Text);

🖥️ Steps to Reproduce

https://codesandbox.io/p/sandbox/delicate-worker-9dmh72

Version

"@react-aria/utils": "3.25.1", "@react-stately/utils": "3.10.1", "react-aria": "3.33.1", "react-aria-components": "1.2.1", "react-stately": "3.31.1",

What browsers are you seeing the problem on?

Chrome

If other, please specify.

No response

What operating system are you using?

Mac & Windows

🧢 Your Company/Team

No response

🕷 Tracking Issue

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions