diff --git a/.changeset/real-numbers-bake.md b/.changeset/real-numbers-bake.md
new file mode 100644
index 00000000000..6a4db2e9279
--- /dev/null
+++ b/.changeset/real-numbers-bake.md
@@ -0,0 +1,8 @@
+---
+"@keystone-6/core": major
+"@keystone-6/website": patch
+---
+
+Make the navigation wrapping ul component user customisable
+
+The `NavigationContainer` component rendered it's children inside a `ul` meaning if you wanted to render anything other than an `li` you would have to do some gymnastics to make it work. This change makes it the users' responsibility to properly wrap list items in a `ul`.
\ No newline at end of file
diff --git a/docs/pages/docs/guides/custom-admin-ui-navigation.md b/docs/pages/docs/guides/custom-admin-ui-navigation.md
index 928a4024f18..e496ecf3aee 100644
--- a/docs/pages/docs/guides/custom-admin-ui-navigation.md
+++ b/docs/pages/docs/guides/custom-admin-ui-navigation.md
@@ -62,7 +62,7 @@ For more information on the props, please see the [Navigation Props](#navigation
Next we'll want to import some components that Keystone provides to help us build our custom Navigation.
```tsx
-import { NavigationContainer, NavItem, ListNavItems } from '@keystone-6/core/admin-ui/components';
+import { NavigationContainer, NavItem, NavItemGroup, ListNavItems } from '@keystone-6/core/admin-ui/components';
```
The `NavigationContainer` component provides a container around your navigation links, as well as the different states of the `authenticatedItem` prop. We'll need this to:
@@ -71,7 +71,7 @@ The `NavigationContainer` component provides a container around your navigation
- Render out the hamburger menu with additional options should a user session be in progress via the `authenticatedItem` prop.
```tsx
-import { NavigationContainer, NavItem, ListNavItems } from '@keystone-6/core/admin-ui/components';
+import { NavigationContainer, NavItem, NavItemGroup, ListNavItems } from '@keystone-6/core/admin-ui/components';
import type { NavigationProps } from '@keystone-6/core/admin-ui/components';
export function CustomNavigation({ authenticatedItem, lists }: NavigationProps) {
@@ -91,15 +91,22 @@ For more information on the `NavigationContainer` see the [NavigationContainer](
The `ListNavItems` component takes the provided Array of `lists` and renders a list of NavItems. We'll use this component to help us easily create NavItems from Keystone lists.
+{% hint kind="tip" %}
+It's important to wrap all links in a `NavItemGroup` component. This is the `ul` to the `li` produced by `NavItem`.
+{% /hint %}
+
+
```tsx
-import { NavigationContainer, NavItem, ListNavItems } from '@keystone-6/core/admin-ui/components';
+import { NavigationContainer, NavItem, NavItemGroup, ListNavItems } from '@keystone-6/core/admin-ui/components';
import type { NavigationProps } from '@keystone-6/core/admin-ui/components';
export function CustomNavigation({ authenticatedItem, lists }: NavigationProps) {
return (
-
- {/* ... */}
+
+
+ {/* ... */}
+
)
}
@@ -120,11 +127,13 @@ import type { NavigationProps } from '@keystone-6/core/admin-ui/components';
export function CustomNavigation({ authenticatedItem, lists }: NavigationProps) {
return (
- Dashboard
-
-
- Keystone Docs
-
+
+ Dashboard
+
+
+ Keystone Docs
+
+
)
}
@@ -144,17 +153,19 @@ With all that done, your Custom Navigation component should be good to go, and y
```tsx
// admin/components/CustomNavigation.tsx
-import { NavigationContainer, NavItem, ListNavItems } from '@keystone-6/core/admin-ui/components';
+import { NavigationContainer, NavItem, NavItemGroup, ListNavItems } from '@keystone-6/core/admin-ui/components';
import type { NavigationProps } from '@keystone-6/core/admin-ui/components';
export function CustomNavigation({ authenticatedItem, lists }: NavigationProps) {
return (
- Dashboard
-
-
- Keystone Docs
-
+
+ Dashboard
+
+
+ Keystone Docs
+
+
)
}
@@ -230,7 +241,9 @@ Keystone exposes a variety of helper components to make building out your custom
- [NavigationContainer](#navigation-container)
- [ListNavItems](#list-nav-items)
+- [ListNavItem](#list-nav-item)
- [NavItem](#nav-item)
+- [NavItemGroup](#nav-item-group)
### NavigationContainer
@@ -280,7 +293,9 @@ If an `include` prop is supplied, the component will only render out lists that
```tsx
const CustomNavigation = ({ lists }) => (
-
+
+
+
)
```
@@ -292,13 +307,50 @@ Otherwise, all lists will be added.
```tsx
const CustomNavigation = ({ lists }) => (
-
+
+
+
)
```
![example of navigation without include prop values](/assets/guides/custom-admin-ui-navigation/listNavItems-without-include.png)
+### ListNavItem
+
+This component will render a single `NavItem` for the given list.
+
+```tsx
+import { ListNavItem } from '@keystone-6/core/admin-ui/components'
+```
+
+In this example we create groups for our lists.
+
+```tsx
+const listGroups = [
+ [
+ { name: 'People', lists: ['User', 'Bio', 'Role']},
+ { name: 'Posts', lists: ['Post', 'Category', 'Tag']},
+ ]
+];
+
+const CustomNavigation = ({ lists }) => (
+
+ {listGroups.map(group => (
+
+
{group.name}
+
+ {group.lists.map((key) => {
+ const list = lists.find((l) => l.key === key);
+ return list ? : null;
+ })}
+
+
+ ))}
+
+)
+```
+
### NavItem
This component is a thin styling and accessibility wrapper around the `Link` component from Next.js
@@ -323,6 +375,29 @@ type NavItemProps = {
By default the `isSelected` value will be evaluated by the condition `router.pathname === href`.
Pass in `isSelected` if you have a custom condition or would like more granular control over the "selected" state of Navigation items.
+### NavItemGroup
+
+This component is a styled unordered list `
` which should be used to wrap `NavItem`, `ListNavItem` and `comp
+
+```tsx
+import { NavItemGroup } from '@keystone-6/core/admin-ui/components'
+```
+
+{% hint kind="warn" %}
+Versions of `@keystone-6/core` before `1.2.0` wrapped all children of `NavigationContainer` with the `NavItemGroup` component.
+{% /hint %}
+
+```tsx
+const CustomNavigation = ({ lists }) => (
+
+
+
+
+
+)
+```
+
+
## Related resources
{% related-content %}
diff --git a/packages/core/src/admin-ui/components/Navigation.tsx b/packages/core/src/admin-ui/components/Navigation.tsx
index 2baecafed1a..350a6cbfc9e 100644
--- a/packages/core/src/admin-ui/components/Navigation.tsx
+++ b/packages/core/src/admin-ui/components/Navigation.tsx
@@ -60,6 +60,20 @@ export const NavItem = ({ href, children, isSelected: _isSelected }: NavItemProp
)
}
+export const NavItemGroup = ({ children }: { children: ReactNode }) => (
+
+ {children}
+
+);
+
const AuthenticatedItemDialog = ({ item }: { item: AuthenticatedItem | undefined }) => {
const { apiPath } = useKeystone()
const { spacing, typography } = useTheme()
@@ -154,17 +168,7 @@ export const NavigationContainer = ({ authenticatedItem, children }: NavigationC
>
)
@@ -234,8 +238,10 @@ export const Navigation = () => {
return (
- Dashboard
-
+
+ Dashboard
+
+
)
}
diff --git a/packages/core/src/admin-ui/components/index.ts b/packages/core/src/admin-ui/components/index.ts
index 1e5d683bd73..89156cb5f7f 100644
--- a/packages/core/src/admin-ui/components/index.ts
+++ b/packages/core/src/admin-ui/components/index.ts
@@ -6,7 +6,7 @@ export { ErrorBoundary, ErrorContainer } from './Errors'
// ADMIN-UI CUSTOM COMPONENTS
export { Logo } from './Logo'
-export { Navigation, NavigationContainer, NavItem, ListNavItems, ListNavItem } from './Navigation'
+export { Navigation, NavigationContainer, NavItem, NavItemGroup, ListNavItems, ListNavItem } from './Navigation'
// co-locating the type with the admin-ui/component for a more a salient mental model.
// importing this type from @keystone-6/core/admin-ui/components is probably intuitive for a user