Skip to content

Commit

Permalink
Feature/ Navigation List (#740)
Browse files Browse the repository at this point in the history
  • Loading branch information
AdhamAH authored Oct 14, 2024
1 parent a951bea commit 549ad64
Show file tree
Hide file tree
Showing 16 changed files with 581 additions and 1 deletion.
1 change: 1 addition & 0 deletions packages/components-css/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
@import "link/index";
@import "logo/index";
@import "navbar/index";
@import "navigation-list/index";
@import "ordered-list/index";
@import "radio/index";
@import "radio-group/index";
Expand Down
88 changes: 88 additions & 0 deletions packages/components-css/navigation-list/_mixin.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* @license EUPL-1.2
* Copyright (c) 2021 Community for NL Design System
*/
@mixin rhc-navigation-list {
display: flex;
flex-direction: column;
list-style: none;
margin-block: 0;
padding-inline: 0;
&:last-child {
border-block-start-color: var(--rhc-navigation-list-item-border-color, var(--rhc-color-grijs-300));
border-block-start-style: solid;
border-block-start-width: var(--rhc-navigation-list-item-border-width, var(--rhc-border-width-default));
}
}
@mixin rhc-navigation-list__item {
align-items: center;
background-color: var(--rhc-navigation-list-item-background-color, var(--rhc-color-canvas));
border-block-end-color: var(--rhc-navigation-list-item-border-color, var(--rhc-color-grijs-300));
border-block-end-style: solid;
border-block-end-width: var(--rhc-navigation-list-item-border-width, var(--rhc-border-width-default));
color: var(--rhc-navigation-list-item-color, var(--rhc-color-foreground-subdued));
column-gap: var(--rhc-navigation-list-item-column-gap, var(--rhc-space-100));
display: flex;
min-block-size: var(--rhc-navigation-list-item-min-height, var(--rhc-size-target));
padding-block: var(--rhc-navigation-list-item-padding-block, var(--rhc-space-100));
padding-inline: var(--rhc-navigation-list-item-padding-inline, var(--rhc-space-200));
position: relative;
text-decoration: none;
&:hover {
background-color: var(--rhc-navigation-list-item-hover-background-color, var(--rhc-color-grijs-50));
}

&:focus {
background-color: var(--rhc-navigation-list-item-focus-background-color, var(--rhc-color-lintblauw-50));
z-index: 999;
}

&:active {
background-color: var(--rhc-navigation-list-item-active-background-color, var(--rhc-color-grijs-100));
}
}

@mixin rhc-navigation-list__item__start-icon {
background-color: var(--rhc-navigation-list-icon-background-color, var(--rhc-color-wit));
block-size: var(--rhc-navigation-list-item-icon-size);
border-radius: var(--rhc-navigation-list-icon-border-radius, var(--rhc-border-radius-circle));
color: var(--rhc-navigation-list-icon-color, var(--rhc-color-foreground-subdued));
grid-area: start-icon;
min-inline-size: var(--rhc-navigation-list-item-icon-size);
padding-block: var(--rhc-navigation-list-item-icon-padding-block);
padding-inline: var(--rhc-navigation-list-item-icon-padding-inline);
}

@mixin rhc-navigation-list__item-content {
align-items: center;
display: grid;
flex: 1;
grid-column-gap: var(--rhc-navigation-list-item-content-column-gap, var(--rhc-space-100));
grid-row-gap: var(--rhc-navigation-list-item-content-row-gap, var(--rhc-space-100));
grid-template-areas:
"label content content end-icon"
"label content content end-icon";
grid-template-columns: 1fr 1fr 1fr auto;
}

@mixin rhc-navigation-list__item__label {
color: var(--rhc-navigation-list-item-heading-color, var(--rhc-color-foreground-lint));
font-family: var(--rhc-navigation-list-item-label-font-family, var(--rhc-font-family-primary)), sans-serif;
font-size: var(--rhc-navigation-list-item-label-font-size, var(--rhc-font-size-md-desktop));
font-weight: var(--rhc-navigation-list-item-label-font-weight, var(--rhc-font-weight-bold));
grid-area: label;
line-height: var(--rhc-navigation-list-item-label-line-height, var(--rhc-line-height-md));
}
@mixin rhc-navigation-list__item__description {
color: inherit;
grid-area: content;
@media (width<= 768px) {
text-align: start;
}
}

@mixin rhc-navigation-list__item__end-icon {
align-self: center;
grid-area: end-icon;
justify-self: end;
}
43 changes: 43 additions & 0 deletions packages/components-css/navigation-list/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* @license EUPL-1.2
* Copyright (c) 2021 Community for NL Design System
*/
@import "./mixin";

.rhc-navigation-list {
@include rhc-navigation-list;
&__item {
@include rhc-navigation-list__item;
&-content {
@include rhc-navigation-list__item-content;
}
&__label {
@include rhc-navigation-list__item__label;
}
&__description {
@include rhc-navigation-list__item__description;
}
&__start-icon {
@include rhc-navigation-list__item__start-icon;
}
&__end-icon {
@include rhc-navigation-list__item__end-icon;
}
}
}
.rhc-navigation-list--container-small {
container-type: inline-size;
}
@container (width <= 768px) {
.rhc-navigation-list__item-content {
grid-template-areas:
"label label label end-icon"
"content content content end-icon";
}
.rhc-navigation-list__item__start-icon {
align-self: start;
}
.rhc-navigation-list__item__description {
text-align: start;
}
}
29 changes: 29 additions & 0 deletions packages/components-react/src/NavigationList.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import { NavigationList } from './NavigationList';

describe('NavigationList', () => {
it('renders children correctly', () => {
const testContent = 'Test NavigationList Content';
render(
<NavigationList>
<div>{testContent}</div>
</NavigationList>,
);

expect(screen.getByText(testContent)).toBeInTheDocument();
});

it('forwards ref correctly', () => {
render(<NavigationList />);

expect(screen.getByRole('list')).toBeInstanceOf(HTMLUListElement);
});

it('applies custom class name', () => {
const testClassName = 'test-class';
render(<NavigationList className={testClassName} />);

expect(screen.getByRole('list')).toHaveClass(testClassName);
});
});
22 changes: 22 additions & 0 deletions packages/components-react/src/NavigationList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import clsx from 'clsx';
import { forwardRef, HTMLAttributes } from 'react';

export type NavigationListProps = HTMLAttributes<HTMLUListElement>;

export const NavigationList = forwardRef<HTMLUListElement, NavigationListProps>(
({ children, className, ...restProps }, ref) => {
return (
<ul
aria-label="Navigation List"
className={clsx('rhc-navigation-list rhc-navigation-list--container-small', className)}
ref={ref}
role={'list'}
{...restProps}
>
{children}
</ul>
);
},
);

NavigationList.displayName = 'NavigationList';
36 changes: 36 additions & 0 deletions packages/components-react/src/NavigationListItem.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import { NavigationListItem } from './NavigationListItem';

const label = 'label';
const description = 'description';

describe('NavigationListItem', () => {
it('renders a NavigationListItem', () => {
render(<NavigationListItem description={description} href={'#'} icon={'chevron-right'} label={label} />);
expect(screen.getByText(description)).toBeInTheDocument();
expect(screen.getByText(label)).toBeInTheDocument();
expect(screen.getByRole('listitem')).toBeInTheDocument();
});

it('forwards ref correctly', () => {
render(<NavigationListItem description={description} href={'#'} icon={'chevron-right'} label={label} />);

expect(screen.getByRole('listitem')).toBeInstanceOf(HTMLLIElement);
});

it('applies custom class name', () => {
const testClassName = 'test-class';
render(
<NavigationListItem
className={testClassName}
description={description}
href={'#'}
icon={'chevron-right'}
label={label}
/>,
);

expect(screen.getByRole('link')).toHaveClass(testClassName);
});
});
39 changes: 39 additions & 0 deletions packages/components-react/src/NavigationListItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import clsx from 'clsx';
import { forwardRef, HTMLAttributes, ReactNode } from 'react';
import { Heading } from './Heading';
import { Paragraph } from './Paragraph';
import { Icon } from './icon/Icon';

export interface NavigationListItemProps extends HTMLAttributes<HTMLLIElement> {
label: ReactNode;
description: ReactNode;
icon: ReactNode;
href: string;
}

export const NavigationListItem = forwardRef<HTMLLIElement, NavigationListItemProps>(
({ label, href, description, icon, className, ...restProps }, ref) => {
return (
<li ref={ref} role="listitem" {...restProps}>
<a className={clsx('rhc-navigation-list__item', className)} href={href} role={'link'}>
{typeof icon === 'string' ? (
<Icon className={clsx('rhc-navigation-list__item__start-icon')} icon={icon} />
) : (
<Icon className={clsx('rhc-navigation-list__item__start-icon')}>{icon}</Icon>
)}
<span className={clsx('rhc-navigation-list__item-content')}>
<Heading className={clsx('rhc-navigation-list__item__label')} level={3}>
{label}
</Heading>
<Paragraph small className={'rhc-navigation-list__item__description'}>
{description}
</Paragraph>
<Icon className={clsx('rhc-navigation-list__item__end-icon')} icon={'chevron-right'} />
</span>
</a>
</li>
);
},
);

NavigationListItem.displayName = 'NavigationListItem';
2 changes: 1 addition & 1 deletion packages/components-react/src/SideNavItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export interface SideNavItemProps extends HTMLAttributes<HTMLLIElement> {}

export const SideNavItem = forwardRef<HTMLLIElement, SideNavItemProps>((props, ref) => {
return (
<li {...props} className={clsx('rhc-side-nav__item', props.className)} ref={ref}>
<li role="listitem" {...props} className={clsx('rhc-side-nav__item', props.className)} ref={ref}>
{props.children}
</li>
);
Expand Down
4 changes: 4 additions & 0 deletions packages/components-react/src/icon/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
IconArrowUpRight,
IconBackhoe,
IconBell,
IconBriefcase,
IconBubble,
IconCalendarCheck,
IconCaretDown,
Expand All @@ -24,6 +25,7 @@ import {
IconClock,
IconCloudLock,
IconCornerLeftUp,
IconCurrencyEuro,
IconDeviceFloppy,
IconDeviceMobile,
IconDotsVertical,
Expand Down Expand Up @@ -87,6 +89,7 @@ export const IconenSet: Partial<Record<RijkshuisstijlIconID, ReactNode>> = {
bel: <IconBell />,
bewerken: <IconEdit />,
blog: <IconFilePencil />,
briefcase: <IconBriefcase />,
'circle-check': <IconCircleCheck />,
'chevron-right': <IconChevronRight />,
comment: <IconMessageDots />,
Expand All @@ -98,6 +101,7 @@ export const IconenSet: Partial<Record<RijkshuisstijlIconID, ReactNode>> = {
'delta-omlaag': <IconCaretDown />,
'diagonale-pijl': <IconArrowUpRight />,
downloaden: <IconDownload />,
'currency-euro': <IconCurrencyEuro />,
'externe-link': <IconExternalLink />,
favoriet: <IconHeart />,
'foto-vergroten': <IconArrowsDiagonal />,
Expand Down
2 changes: 2 additions & 0 deletions packages/components-react/src/icon/IconTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ export type RijkshuisstijlIconID =
| 'alert-circle'
| 'alert-triangle'
| 'arrows-sort'
| 'briefcase'
| 'crisisoverleg'
| 'circle-check'
| 'chevron-right'
| 'dansen'
| 'docent-voor-klas'
| 'elleboognies'
| 'currency-euro'
| 'fysiotherapeut'
| 'gevangene'
| 'hulpverleners'
Expand Down
2 changes: 2 additions & 0 deletions packages/components-react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ export { LinkList, LinkListLink, type LinkListLinkProps, type LinkListProps } fr
export { LinkListCard } from './LinkListCard';
export { Logo, type LogoProps } from './Logo';
export { NavBar, type NavBarItemProps, type NavBarProps } from './NavBar';
export { NavigationList, type NavigationListProps } from './NavigationList';
export { NavigationListItem, type NavigationListItemProps } from './NavigationListItem';
export { OrderedList, OrderedListItem, type OrderedListItemProps, type OrderedListProps } from './OrderedList';
export { PageContent, type PageContentProps } from './PageContent';
export { PageHeader, type PageHeaderProps } from './PageHeader';
Expand Down
13 changes: 13 additions & 0 deletions packages/storybook/src/community/navigation-list-item.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- @license CC0-1.0 -->

# Rijkshuisstijl Community Navigation List Item component

NL design system - geen | [Figma](https://www.figma.com/design/Nv5EsCW9ioWBUSi9m9JqOa/Local---Rijkshuisstijl---Bibliotheek?node-id=4074-1580&node-type=canvas&t=HuDzyBW9wHdB2QVh-0) | [GitHub](https://github.com/nl-design-system/rijkshuisstijl-community/issues/557)

## Usage

```tsx
import { NavigationListItem } from '@rijkshuisstijl-community/components-react';

<NavigationListItem description="Uw gegevens, familie en identiteitsbewijs" href="#" icon="user" label="Identiteit" />;
```
Loading

0 comments on commit 549ad64

Please sign in to comment.