Skip to content

Commit

Permalink
update heading rendering and take the stories back plus add docs
Browse files Browse the repository at this point in the history
  • Loading branch information
broccolinisoup committed Nov 10, 2023
1 parent b929c00 commit 11daf88
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 148 deletions.
31 changes: 30 additions & 1 deletion src/ActionList/ActionList.docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,35 @@
}
]
},
{
"name": "ActionList.GroupHeading",
"props": [
{
"name": "children",
"type": "React.ReactNode",
"defaultValue": "",
"required": true,
"description": "Use to give a heading to the groups"
},
{
"name": "variant",
"type": "'filled' | 'subtle'",
"defaultValue": "'subtle'",
"description": "`filled` style has a background color and top and bottom borders. Subtle style has no background or borders."
},
{
"name": "as",
"type": "h1 | h2 | h3 | h4 | h5 | h6",
"defaultValue": "h3",
"required": false,
"description": "The level of the heading and it is only required (enforce by runtime warning) for lists. (i.e. not required for ActionMenu or listbox roles)"
},
{
"name": "sx",
"type": "SystemStyleObject"
}
]
},
{
"name": "ActionList.Group",
"props": [
Expand Down Expand Up @@ -230,4 +259,4 @@
]
}
]
}
}
140 changes: 0 additions & 140 deletions src/ActionList/ActionList.features.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,146 +96,6 @@ export const WithVisualListHeading = () => (
</ActionList>
)

export const ListWithNewGroupHeadingAPI = () => (
<ActionList>
<ActionList.Heading as="h2">List Heading</ActionList.Heading>
<ActionList.Group data-test-id="ActionList.Group">
<ActionList.GroupHeading as="h3">Group 1 Heading</ActionList.GroupHeading>
<ActionList.Item onClick={() => {}}>
<ActionList.LeadingVisual>
<FileDirectoryIcon />
</ActionList.LeadingVisual>
app/assets/modules
</ActionList.Item>
<ActionList.Item onClick={() => {}}>
<ActionList.LeadingVisual>
<FileDirectoryIcon />
</ActionList.LeadingVisual>
src/react/components
</ActionList.Item>
<ActionList.Item onClick={() => {}}>
<ActionList.LeadingVisual>
<FileDirectoryIcon />
</ActionList.LeadingVisual>
memex/shared-ui/components
</ActionList.Item>
<ActionList.Item onClick={() => {}}>
<ActionList.LeadingVisual>
<FileDirectoryIcon />
</ActionList.LeadingVisual>
views/assets/modules
</ActionList.Item>
</ActionList.Group>

<ActionList.Group>
<ActionList.GroupHeading as="h3">Group 2 Heading</ActionList.GroupHeading>
<ActionList.Item onClick={() => {}}>
<ActionList.LeadingVisual>
<PlusCircleIcon />
</ActionList.LeadingVisual>
Owner
</ActionList.Item>
<ActionList.Item onClick={() => {}}>
<ActionList.LeadingVisual>
<PlusCircleIcon />
</ActionList.LeadingVisual>
Symbol
</ActionList.Item>
<ActionList.Item onClick={() => {}}>
<ActionList.LeadingVisual>
<PlusCircleIcon />
</ActionList.LeadingVisual>
Exclude archived
</ActionList.Item>
</ActionList.Group>
</ActionList>
)

export const MenuWithNewGroupHeadingAPI = () => {
const [assignees, setAssignees] = React.useState(users.slice(0, 1))

const toggleAssignee = (assignee: (typeof users)[number]) => {
const assigneeIndex = assignees.findIndex(a => a.login === assignee.login)

if (assigneeIndex === -1) setAssignees([...assignees, assignee])
else setAssignees(assignees.filter((_, index) => index !== assigneeIndex))
}

return (
<ActionList selectionVariant="multiple" role="menu" showDividers aria-label="Reviewers">
<ActionList.Group>
<ActionList.GroupHeading>Everyone</ActionList.GroupHeading>
{users.slice(2).map(user => (
<ActionList.Item
role="menuitemcheckbox"
key={user.login}
selected={Boolean(assignees.find(assignee => assignee.login === user.login))}
aria-checked={Boolean(assignees.find(assignee => assignee.login === user.login))}
onSelect={() => toggleAssignee(user)}
>
<ActionList.LeadingVisual>
<Avatar src={`https://github.com/${user.login}.png`} />
</ActionList.LeadingVisual>
{user.login}
<ActionList.Description>{user.name}</ActionList.Description>
</ActionList.Item>
))}
</ActionList.Group>
</ActionList>
)
}

export const ListboxWithNewGroupHeadingAPI = () => {
const [assignees, setAssignees] = React.useState(users.slice(0, 1))

const toggleAssignee = (assignee: (typeof users)[number]) => {
const assigneeIndex = assignees.findIndex(a => a.login === assignee.login)

if (assigneeIndex === -1) setAssignees([...assignees, assignee])
else setAssignees(assignees.filter((_, index) => index !== assigneeIndex))
}

return (
<ActionList selectionVariant="multiple" role="listbox" showDividers aria-label="Reviewers">
<ActionList.Group>
<ActionList.GroupHeading>Everyone</ActionList.GroupHeading>
{users.slice(2).map(user => (
<ActionList.Item
role="option"
key={user.login}
selected={Boolean(assignees.find(assignee => assignee.login === user.login))}
aria-checked={Boolean(assignees.find(assignee => assignee.login === user.login))}
onSelect={() => toggleAssignee(user)}
>
<ActionList.LeadingVisual>
<Avatar src={`https://github.com/${user.login}.png`} />
</ActionList.LeadingVisual>
{user.login}
<ActionList.Description>{user.name}</ActionList.Description>
</ActionList.Item>
))}
</ActionList.Group>
<ActionList.Group>
<ActionList.GroupHeading>Review Requested</ActionList.GroupHeading>
{users.slice(2).map(user => (
<ActionList.Item
role="option"
key={user.login}
selected={Boolean(assignees.find(assignee => assignee.login === user.login))}
aria-checked={Boolean(assignees.find(assignee => assignee.login === user.login))}
onSelect={() => toggleAssignee(user)}
>
<ActionList.LeadingVisual>
<Avatar src={`https://github.com/${user.login}.png`} />
</ActionList.LeadingVisual>
{user.login}
<ActionList.Description>{user.name}</ActionList.Description>
</ActionList.Item>
))}
</ActionList.Group>
</ActionList>
)
}
export const WithCustomHeading = () => (
<>
<Heading as="h1" id="list-heading" sx={{fontSize: 3, marginX: 3}}>
Expand Down
16 changes: 9 additions & 7 deletions src/ActionList/Group.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,10 @@ export const Group: React.FC<React.PropsWithChildren<ActionListGroupProps>> = ({
{...props}
>
<GroupContext.Provider value={{selectionVariant, groupHeadingId}}>
{slots.groupHeading || title ? (
<GroupHeading title={title} variant={variant} auxiliaryText={auxiliaryText} as={slots.groupHeading?.props.as}>
{slots.groupHeading ? slots.groupHeading.props.children : null}
</GroupHeading>
{title && !slots.groupHeading ? (
<GroupHeading title={title} variant={variant} auxiliaryText={auxiliaryText} />
) : null}
{!title && slots.groupHeading ? React.cloneElement(slots.groupHeading) : null}
<Box
as="ul"
sx={{paddingInlineStart: 0}}
Expand All @@ -113,9 +112,12 @@ export type GroupHeadingProps = Pick<ActionListGroupProps, 'variant' | 'title' |
}

/**
* Displays the name and description of a `Group`.
* Heading of a `Group`.
*
* For visual presentation only.
* As default, the role of ActionList is "list" and therefore group heading is rendered as a proper heading tag.
* If the role is "listbox" or "menu" (ActionMenu), the group heading is rendered as a div with presentation role and it is
* hidden from the accessibility tree due to the limitation of listbox children. https://w3c.github.io/aria/#listbox
* groups under menu or listbox are labelled by `aria-label`
*/
export const GroupHeading: React.FC<React.PropsWithChildren<GroupHeadingProps>> = ({
as,
Expand All @@ -130,7 +132,7 @@ export const GroupHeading: React.FC<React.PropsWithChildren<GroupHeadingProps>>
const {groupHeadingId} = React.useContext(GroupContext)
// for list role, the headings are proper heading tags, for menu and listbox, they are just representational and divs
warning(
listRole === undefined && children !== null && as === undefined,
listRole === undefined && children !== undefined && as === undefined,
`You are setting a heading for a list, that requires a heading level. Please use 'as' prop to set a proper heading level.`,
)

Expand Down

0 comments on commit 11daf88

Please sign in to comment.