From 4ed8606a7c69dd2e5b56d1ba1fe4230186898e27 Mon Sep 17 00:00:00 2001 From: Bernardo Sunderhus Date: Fri, 11 Aug 2023 11:19:11 +0000 Subject: [PATCH] chore(public-docsite-v9): updates slot documentation --- .../src/Concepts/Slots/Slots.stories.mdx | 80 +++++++++++++++---- 1 file changed, 66 insertions(+), 14 deletions(-) diff --git a/apps/public-docsite-v9/src/Concepts/Slots/Slots.stories.mdx b/apps/public-docsite-v9/src/Concepts/Slots/Slots.stories.mdx index 1459fa8c1ee818..d762e9f9d609b4 100644 --- a/apps/public-docsite-v9/src/Concepts/Slots/Slots.stories.mdx +++ b/apps/public-docsite-v9/src/Concepts/Slots/Slots.stories.mdx @@ -89,7 +89,7 @@ For example, `Accordion` is a hierarchy of `AccordionItem` elements. ## Usage examples -### Passing content +### Passing a shorthand value You can pass a primitive value to slots. The `Input` component's `contentBefore` and `contentAfter` slots can be passed strings. @@ -98,7 +98,8 @@ The `Input` component's `contentBefore` and `contentAfter` slots can be passed s ``` -You can pass JSX elements to slots. For example, the `Button` component can be passed an `img` or an `CalendarRegular24` icon. +You can pass JSX elements to slots. +For example, the `Button` component can be passed an `img` or an `CalendarRegular24` icon. ```tsx <> @@ -107,7 +108,20 @@ You can pass JSX elements to slots. For example, the `Button` component can be p ``` -### Passing props +Any shorthand value provided to a slot is converted to that slot's children content. In the example above, when the `icon` slot is passed an `img` JSX element, the `img` element is rendered as the `icon` slot's children: + +```html + + +``` + +### Passing slot properties object A slot can take in the props of the type it is rendering. When a slot renders an element, you can pass any native element props for that element type. @@ -138,7 +152,18 @@ You can pass the `as` prop to change the element type of a slot. Note that you must choose from one of the available element types the slot supports. ```tsx -Accordion Header as h1 +// here we render the AccordionHeader as h1, by default it is a div +// and internally AccordionHeader has a button slot that is a button by default, +// but in this case we are changing it to an anchor + + Accordion Header as h1 + +``` + +```html +

+ Accordion Header as h1 +

``` ### Replacing the entire slot @@ -297,7 +322,7 @@ export type InputSlots = { }; ``` -### Styling and rendering components with slots +### Styling components with slots You can think of each slot as a container for props and style that will be rendered as an element. @@ -333,27 +358,54 @@ const useButtonStyles_unstable = (state: ButtonState) Hooks like `useButtonStyles` create classes and conditionally apply them based on the input state. Classes are typically created and applied with `makeStyles` and `mergeClasses`. For instance, if a component is disabled then disabled styles are added. +### Declaring components slots in the state + +The `Button` component defines its slots `root` and `icon` in the state. + +```ts +const useButton_unstable = (props: ButtonProps, ref: React.Ref): ButtonState => { + return { + root: slot.always({ ...props, ref }, { elementType: 'button' }), + icon: slot.optional(props.icon, { elementType: 'span' }), + }; +}; +``` + +- `slot.always` creates a slot that will always render, and as such the user may not provide `null` to opt-out of this slot (NonNullable slot). +- `slot.optional` creates a slot that can be opted out of and is not rendered by default, it only renders if `props.icon` is different from `undefined` (Optional slot). + +Both `slot.always` and `slot.optional` methods will create a slot definition that can be consumed by the render method to properly render a slot, these methods ensure the local logic provided by the state hook will remain on the slots internals. + +### Rendering components with slots + - `renderButton` takes in `ButtonState` and renders content. ```tsx +/** @jsxRuntime classic */ +/** @jsx createElement */ + +// createElement custom JSX pragma is required to support slot creation +import { createElement } from '@fluentui/react-jsx-runtime'; +import { assertSlots } from '@fluentui/react-utilities'; + const renderButton_unstable = (state: ButtonState) => { - const { slots, slotProps } = getSlots(state); const { iconOnly, iconPosition } = state; + assertSlots(state); + return ( - - {iconPosition !== 'after' && slots.icon && } + + {iconPosition !== 'after' && state.icon && } {!iconOnly && state.root.children} - {iconPosition === 'after' && slots.icon && } - + {iconPosition === 'after' && state.icon && } + ); }; ``` -The `getSlots` method splits up the state to return the slot elements to render and the props to apply to each slot. -This makes writing render methods straightforward and mostly boilerplate as most of the work was done in the hooks. -This also help separate behavior, element structure, and style concerns. -The component's render method can conditionally render slots. +The `assertSlots` method ensures the state has the expected slots. It also provides strong typings to ensure slots are renderable, meaning you can simply use `` and all properties provided to a `slot` creation will be already baked into it. + +The `createElement` method is a custom JSX pragma ([What is JSX pragma?](https://www.gatsbyjs.com/blog/2019-08-02-what-is-jsx-pragma/)) that allows us to create slots in a JSX environment. It ensures that all race conditions between logic provided in the state hook and the render method are properly handled, and is required for `slot.always`, `slot.optional` and `assertSlots` to work properly. ## Wrap up