diff --git a/.changeset/lemon-berries-run.md b/.changeset/lemon-berries-run.md new file mode 100644 index 00000000000..1e7bfe65ad4 --- /dev/null +++ b/.changeset/lemon-berries-run.md @@ -0,0 +1,7 @@ +--- +"@primer/react": patch +--- + +`ActionList` and `NavList` are now SSR-compatible. + +Warning: In this new implementation, `ActionList.LeadingVisual`, `ActionList.TrailingVisual,` and `ActionList.Description` must be direct children of `ActionList`. The same applies to `NavList`. diff --git a/src/ActionList/Description.tsx b/src/ActionList/Description.tsx index 63b89d4356b..4027ec22f58 100644 --- a/src/ActionList/Description.tsx +++ b/src/ActionList/Description.tsx @@ -1,8 +1,8 @@ import React from 'react' import Box from '../Box' -import {SxProp, merge} from '../sx' import Truncate from '../Truncate' -import {Slot, ItemContext} from './shared' +import {SxProp, merge} from '../sx' +import {ItemContext} from './shared' export type ActionListDescriptionProps = { /** @@ -28,29 +28,25 @@ export const Description: React.FC - {({blockDescriptionId, inlineDescriptionId, disabled}: ItemContext) => - variant === 'block' ? ( - - {props.children} - - ) : ( - - {props.children} - - ) - } - + const {blockDescriptionId, inlineDescriptionId, disabled} = React.useContext(ItemContext) + + return variant === 'block' ? ( + + {props.children} + + ) : ( + + {props.children} + ) } diff --git a/src/ActionList/Item.tsx b/src/ActionList/Item.tsx index ca479ff450f..d594cc66f66 100644 --- a/src/ActionList/Item.tsx +++ b/src/ActionList/Item.tsx @@ -1,16 +1,19 @@ -import {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic' import React from 'react' import styled from 'styled-components' import Box, {BoxProps} from '../Box' +import {useId} from '../hooks/useId' +import {useSlots} from '../hooks/useSlots' import sx, {BetterSystemStyleObject, merge, SxProp} from '../sx' import {useTheme} from '../ThemeProvider' -import {useId} from '../hooks/useId' +import {defaultSxProp} from '../utils/defaultSxProp' +import {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic' import {ActionListContainerContext} from './ActionListContainerContext' +import {Description} from './Description' import {ActionListGroupProps, GroupContext} from './Group' import {ActionListProps, ListContext} from './List' import {Selection} from './Selection' -import {ActionListItemProps, Slots, TEXT_ROW_HEIGHT, getVariantStyles} from './shared' -import {defaultSxProp} from '../utils/defaultSxProp' +import {ActionListItemProps, getVariantStyles, ItemContext, TEXT_ROW_HEIGHT} from './shared' +import {LeadingVisual, TrailingVisual} from './Visuals' const LiBox = styled.li(sx) @@ -30,6 +33,11 @@ export const Item = React.forwardRef( }, forwardedRef, ): JSX.Element => { + const [slots, childrenWithoutSlots] = useSlots(props.children, { + leadingVisual: LeadingVisual, + trailingVisual: TrailingVisual, + description: Description, + }) const {variant: listVariant, showDividers, selectionVariant: listSelectionVariant} = React.useContext(ListContext) const {selectionVariant: groupSelectionVariant} = React.useContext(GroupContext) const {container, afterSelect, selectionAttribute} = React.useContext(ActionListContainerContext) @@ -169,66 +177,57 @@ export const Item = React.forwardRef( const ItemWrapper = _PrivateItemWrapper || React.Fragment + const menuItemProps = { + onClick: clickHandler, + onKeyPress: keyPressHandler, + 'aria-disabled': disabled ? true : undefined, + tabIndex: disabled ? undefined : 0, + 'aria-labelledby': `${labelId} ${ + slots.description && slots.description.props.variant !== 'block' ? inlineDescriptionId : '' + }`, + 'aria-describedby': slots.description?.props.variant === 'block' ? blockDescriptionId : undefined, + ...(selectionAttribute && {[selectionAttribute]: selected}), + role: role || itemRole, + } + + const containerProps = _PrivateItemWrapper ? {role: role || itemRole ? 'none' : undefined} : menuItemProps + + const wrapperProps = _PrivateItemWrapper ? menuItemProps : {} + return ( - - {slots => { - const menuItemProps = { - onClick: clickHandler, - onKeyPress: keyPressHandler, - 'aria-disabled': disabled ? true : undefined, - tabIndex: disabled ? undefined : 0, - 'aria-labelledby': `${labelId} ${slots.InlineDescription ? inlineDescriptionId : ''}`, - 'aria-describedby': slots.BlockDescription ? blockDescriptionId : undefined, - ...(selectionAttribute && {[selectionAttribute]: selected}), - role: role || itemRole, - } - const containerProps = _PrivateItemWrapper - ? { - role: role || itemRole ? 'none' : undefined, - } - : menuItemProps - const wrapperProps = _PrivateItemWrapper ? menuItemProps : {} - - return ( - (styles, sxProp)} - {...containerProps} - {...props} + + (styles, sxProp)} {...containerProps} {...props}> + + + {slots.leadingVisual} + - - - {slots.LeadingVisual} - + - - - - {props.children} - - {slots.InlineDescription} - - {slots.TrailingVisual} - - {slots.BlockDescription} - - - - ) - }} - + + {childrenWithoutSlots} + + {slots.description?.props.variant !== 'block' ? slots.description : null} + + {slots.trailingVisual} + + {slots.description?.props.variant === 'block' ? slots.description : null} + + + + ) }, ) as PolymorphicForwardRefComponent<'li', ActionListItemProps> diff --git a/src/ActionList/Visuals.tsx b/src/ActionList/Visuals.tsx index 5ca38b66943..4a290b20f3e 100644 --- a/src/ActionList/Visuals.tsx +++ b/src/ActionList/Visuals.tsx @@ -1,8 +1,8 @@ import React from 'react' import Box from '../Box' -import {SxProp, merge} from '../sx' import {get} from '../constants' -import {getVariantStyles, Slot, ItemContext, TEXT_ROW_HEIGHT} from './shared' +import {SxProp, merge} from '../sx' +import {ItemContext, TEXT_ROW_HEIGHT, getVariantStyles} from './shared' type VisualProps = SxProp & React.HTMLAttributes @@ -30,48 +30,42 @@ export const LeadingVisualContainer: React.FC> = ({sx = {}, ...props}) => { + const {variant, disabled} = React.useContext(ItemContext) return ( - - {({variant, disabled}: ItemContext) => ( - - {props.children} - + + {...props} + > + {props.children} + ) } export type ActionListTrailingVisualProps = VisualProps export const TrailingVisual: React.FC> = ({sx = {}, ...props}) => { + const {variant, disabled} = React.useContext(ItemContext) return ( - - {({variant, disabled}: ItemContext) => ( - - {props.children} - + + {...props} + > + {props.children} + ) } diff --git a/src/ActionList/shared.ts b/src/ActionList/shared.ts index 6e281c46b9e..10b79112915 100644 --- a/src/ActionList/shared.ts +++ b/src/ActionList/shared.ts @@ -1,6 +1,5 @@ import React from 'react' import {SxProp} from '../sx' -import createSlots from '../utils/create-slots' import {AriaRole} from '../utils/types' export type ActionListItemProps = { @@ -56,10 +55,12 @@ type MenuItemProps = { } export type ItemContext = Pick & { - inlineDescriptionId: string - blockDescriptionId: string + inlineDescriptionId?: string + blockDescriptionId?: string } +export const ItemContext = React.createContext({}) + export const getVariantStyles = ( variant: ActionListItemProps['variant'], disabled: ActionListItemProps['disabled'], @@ -90,6 +91,4 @@ export const getVariantStyles = ( } } -export const {Slots, Slot} = createSlots(['LeadingVisual', 'InlineDescription', 'BlockDescription', 'TrailingVisual']) - export const TEXT_ROW_HEIGHT = '20px' // custom value off the scale diff --git a/src/NavList/__snapshots__/NavList.test.tsx.snap b/src/NavList/__snapshots__/NavList.test.tsx.snap index 20a4fc9ba53..d4a6444a291 100644 --- a/src/NavList/__snapshots__/NavList.test.tsx.snap +++ b/src/NavList/__snapshots__/NavList.test.tsx.snap @@ -1278,12 +1278,6 @@ exports[`NavList.Item with NavList.SubNav has active styles if SubNav contains t list-style: none; } -.c9 { - padding: 0; - margin: 0; - display: none; -} - .c5 { display: -webkit-box; display: -webkit-flex; @@ -1295,6 +1289,12 @@ exports[`NavList.Item with NavList.SubNav has active styles if SubNav contains t flex-grow: 1; } +.c9 { + padding: 0; + margin: 0; + display: none; +} + .c7 { height: 20px; -webkit-flex-shrink: 0;