uikit-react-public 0.43.4 → 0.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,6 +3,7 @@ import { Meta, Title, Subtitle, Canvas, Controls } from "@storybook/addon-docs/b
3
3
 
4
4
  export const usage = {
5
5
  default: `<Header title='App Name' />`,
6
+ noBorder: `<Header title='App Name' showBorder={false} />`,
6
7
  homeLink: `<Header title='App Name' homeLinkHref='/' homeLinkAriaLabel='Go to home' />`,
7
8
  menu: `<Header title='App Name' homeLinkHref='/'>
8
9
  <Header.Menu>
@@ -26,6 +27,12 @@ to add a menu button on the right.
26
27
 
27
28
  <Canvas source={{ code: usage.default }} />
28
29
 
30
+ ## Border
31
+
32
+ Set `showBorder={false}` when another component, such as `NavigationBar`, sits directly below the header and should provide the dividing border.
33
+
34
+ <Canvas of={HeaderStories.WithoutBorder} source={{ code: usage.noBorder }} />
35
+
29
36
  ## Home link
30
37
 
31
38
  Add `homeLinkHref` (and optionally `homeLinkAriaLabel`) to make the logo/title act as a home link.
@@ -49,4 +56,4 @@ Use `fixed` to pin the header to the top of the viewport.
49
56
  Full props specification for `<Header>` is below.
50
57
  <Canvas source={{ code: usage.default }} />
51
58
  You can use the controls below to manipulate the `<Header>` component above.
52
- <Controls />
59
+ <Controls />
@@ -13,6 +13,7 @@ const meta = {
13
13
  },
14
14
  argTypes: {
15
15
  title: { control: 'text' },
16
+ showBorder: { control: 'boolean' },
16
17
  fixed: { control: 'boolean' },
17
18
  homeLinkHref: { control: 'text' },
18
19
  homeLinkAriaLabel: { control: 'text' },
@@ -77,6 +78,18 @@ export const WithHomeLink: Story = {
77
78
  },
78
79
  };
79
80
 
81
+ export const WithoutBorder: Story = {
82
+ name: 'Without border',
83
+ render: (args) => (
84
+ <div style={{ overflow: 'hidden' }}>
85
+ <Header {...args} />
86
+ </div>
87
+ ),
88
+ args: {
89
+ showBorder: false,
90
+ },
91
+ };
92
+
80
93
  export const WithMenu: Story = {
81
94
  render: (args) => (
82
95
  <div style={storyWrapperStyle}>
@@ -13,6 +13,7 @@ export const DEFAULT_Z_INDEX = 3;
13
13
 
14
14
  export interface HeaderProps extends HTMLAttributes<HTMLElement> {
15
15
  title?: string;
16
+ showBorder?: boolean;
16
17
  /** @deprecated Only used by the default-theme legacy Header. */
17
18
  fixed?: boolean;
18
19
  titleAs?: React.ElementType<React.HTMLAttributes<HTMLElement>>;
@@ -164,6 +165,7 @@ const LegacyHeader = ({
164
165
 
165
166
  const Header = ({
166
167
  title,
168
+ showBorder = true,
167
169
  titleAs = 'div',
168
170
  titleClassName,
169
171
  titleProps,
@@ -206,6 +208,7 @@ const Header = ({
206
208
  return (
207
209
  <HeaderNew
208
210
  title={title ?? ''}
211
+ showBorder={showBorder}
209
212
  titleAs={titleAs}
210
213
  titleClassName={titleClassName}
211
214
  titleProps={titleProps}
@@ -89,4 +89,26 @@ describe('Header', () => {
89
89
  expect(link).toBeInTheDocument();
90
90
  expect(link.getAttribute('href')).toBe('/');
91
91
  });
92
+
93
+ test('passes showBorder to the new header implementation', () => {
94
+ render(
95
+ <ThemeContextProvider theme='light'>
96
+ <Header
97
+ title='LIDS'
98
+ showBorder={false}
99
+ />
100
+ </ThemeContextProvider>
101
+ );
102
+
103
+ const header = screen.getByTestId('ucl-uikit-header');
104
+ const generatedClass = [...header.classList].find((className) =>
105
+ className.startsWith('css-')
106
+ );
107
+
108
+ expect(generatedClass).toBeDefined();
109
+ expect(header).not.toHaveAttribute('showBorder');
110
+ expect(document.head.textContent).toContain(
111
+ `.${generatedClass}{height:78px;padding:0 48px 0 64px;}`
112
+ );
113
+ });
92
114
  });
@@ -12,6 +12,7 @@ export const NAME = 'ucl-uikit-header';
12
12
 
13
13
  export interface HeaderProps extends HTMLAttributes<HTMLElement> {
14
14
  title: string;
15
+ showBorder?: boolean;
15
16
  titleAs?: React.ElementType<React.HTMLAttributes<HTMLElement>>;
16
17
  titleClassName?: string;
17
18
  titleProps?: Record<string, unknown>;
@@ -22,6 +23,7 @@ export interface HeaderProps extends HTMLAttributes<HTMLElement> {
22
23
 
23
24
  const Header = ({
24
25
  title,
26
+ showBorder = true,
25
27
  titleAs = 'div',
26
28
  titleClassName,
27
29
  titleProps,
@@ -51,7 +53,11 @@ const Header = ({
51
53
  @media (min-width: ${theme.breakpoints.desktop}px) {
52
54
  height: ${HEADER_TABLET_HEIGHT_PX}px;
53
55
  padding: 0 ${theme.padding.p48} 0 ${theme.padding.p64};
54
- border-bottom: 1px solid ${theme.colour.border.subtle};
56
+ ${
57
+ showBorder
58
+ ? `border-bottom: 1px solid ${theme.colour.border.subtle};`
59
+ : ''
60
+ }
55
61
  }
56
62
  `;
57
63
 
@@ -40,6 +40,47 @@ describe('Header', () => {
40
40
  expect(link.getAttribute('href')).toBe('/');
41
41
  });
42
42
 
43
+ test('shows the bottom border by default and can hide it', () => {
44
+ const { rerender } = render(
45
+ <ThemeContextProvider>
46
+ <Header title='LIDS' />
47
+ </ThemeContextProvider>
48
+ );
49
+
50
+ const header = screen.getByTestId('ucl-uikit-header');
51
+ const defaultClass = [...header.classList].find((className) =>
52
+ className.startsWith('css-')
53
+ );
54
+
55
+ expect(defaultClass).toBeDefined();
56
+ expect(document.head.textContent).toContain(
57
+ `.${defaultClass}{position:relative`
58
+ );
59
+ expect(document.head.textContent).toContain(
60
+ `.${defaultClass}{height:78px;padding:0 48px 0 64px;border-bottom:1px solid`
61
+ );
62
+
63
+ rerender(
64
+ <ThemeContextProvider>
65
+ <Header
66
+ title='LIDS'
67
+ showBorder={false}
68
+ />
69
+ </ThemeContextProvider>
70
+ );
71
+
72
+ const borderlessHeader = screen.getByTestId('ucl-uikit-header');
73
+ const borderlessClass = [...borderlessHeader.classList].find((className) =>
74
+ className.startsWith('css-')
75
+ );
76
+
77
+ expect(borderlessClass).toBeDefined();
78
+ expect(borderlessHeader).not.toHaveAttribute('showBorder');
79
+ expect(document.head.textContent).toContain(
80
+ `.${borderlessClass}{height:78px;padding:0 48px 0 64px;}`
81
+ );
82
+ });
83
+
43
84
  test('spaces subsequent header items on smaller screens and resets spacing on desktop', () => {
44
85
  render(
45
86
  <ThemeContextProvider>
@@ -0,0 +1,99 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { css } from '@emotion/css';
3
+ import NavigationBar from './NavigationBar';
4
+ import Icon from '../Icon';
5
+
6
+ const meta = {
7
+ title: 'Components/NavigationBar',
8
+ component: NavigationBar,
9
+ parameters: {
10
+ layout: 'fullscreen',
11
+ },
12
+ tags: ['autodocs'],
13
+ } satisfies Meta<typeof NavigationBar>;
14
+
15
+ export default meta;
16
+ type Story = StoryObj<typeof meta>;
17
+
18
+ const pageStyle = css`
19
+ min-height: 160px;
20
+ `;
21
+
22
+ export const Default: Story = {
23
+ render: () => (
24
+ <div className={pageStyle}>
25
+ <NavigationBar>
26
+ <NavigationBar.Item href='/courses'>Courses</NavigationBar.Item>
27
+ <NavigationBar.Item
28
+ href='/research'
29
+ active
30
+ >
31
+ Research
32
+ </NavigationBar.Item>
33
+ <NavigationBar.Item
34
+ href='/open-days'
35
+ icon={<Icon.ChevronDown />}
36
+ >
37
+ Open days
38
+ </NavigationBar.Item>
39
+ <NavigationBar.Item href='/events'>Events</NavigationBar.Item>
40
+ <NavigationBar.Item
41
+ href='/news'
42
+ disabled
43
+ >
44
+ News
45
+ </NavigationBar.Item>
46
+ </NavigationBar>
47
+ </div>
48
+ ),
49
+ };
50
+
51
+ export const Wrapped: Story = {
52
+ render: () => (
53
+ <div
54
+ className={css`
55
+ width: 720px;
56
+ `}
57
+ >
58
+ <NavigationBar>
59
+ <NavigationBar.Item
60
+ href='/courses'
61
+ active
62
+ >
63
+ Courses
64
+ </NavigationBar.Item>
65
+ <NavigationBar.Item href='/research'>Research</NavigationBar.Item>
66
+ <NavigationBar.Item href='/open-days'>Open days</NavigationBar.Item>
67
+ <NavigationBar.Item href='/events'>Events</NavigationBar.Item>
68
+ <NavigationBar.Item href='/news'>News</NavigationBar.Item>
69
+ <NavigationBar.Item href='/blogs'>IOE blogs</NavigationBar.Item>
70
+ <NavigationBar.Item href='/work-with-us'>
71
+ Work with us
72
+ </NavigationBar.Item>
73
+ <NavigationBar.Item href='/departments-and-centres'>
74
+ Departments and Centres
75
+ </NavigationBar.Item>
76
+ <NavigationBar.Item href='/about'>About IOE</NavigationBar.Item>
77
+ </NavigationBar>
78
+ </div>
79
+ ),
80
+ };
81
+
82
+ export const WithRouterLinks: Story = {
83
+ render: () => (
84
+ <NavigationBar>
85
+ <NavigationBar.Item
86
+ active
87
+ asChild
88
+ >
89
+ <a href='/overview'>Overview</a>
90
+ </NavigationBar.Item>
91
+ <NavigationBar.Item asChild>
92
+ <a href='/modules'>Modules</a>
93
+ </NavigationBar.Item>
94
+ <NavigationBar.Item asChild>
95
+ <a href='/people'>People</a>
96
+ </NavigationBar.Item>
97
+ </NavigationBar>
98
+ ),
99
+ };
@@ -0,0 +1,76 @@
1
+ import {
2
+ Children,
3
+ HTMLAttributes,
4
+ NamedExoticComponent,
5
+ isValidElement,
6
+ memo,
7
+ } from 'react';
8
+ import { css, cx } from '@emotion/css';
9
+ import { useTheme } from '../../theme';
10
+ import NavigationBarItem, { NavigationBarItemProps } from './NavigationBarItem';
11
+
12
+ export const NAME = 'ucl-uikit-navigation-bar';
13
+
14
+ export interface NavigationBarProps extends HTMLAttributes<HTMLElement> {
15
+ testId?: string;
16
+ }
17
+
18
+ const NavigationBarBase = ({
19
+ testId = NAME,
20
+ className,
21
+ children,
22
+ 'aria-label': ariaLabel = 'Primary navigation',
23
+ ...props
24
+ }: NavigationBarProps) => {
25
+ const [theme] = useTheme();
26
+ const items = Children.toArray(children).filter(isValidElement);
27
+
28
+ const baseStyle = css`
29
+ box-sizing: border-box;
30
+ display: flex;
31
+ width: 100%;
32
+ padding: 0 ${theme.padding.p16};
33
+ align-items: flex-end;
34
+ background-color: ${theme.colour.surface.primary};
35
+ border-bottom: ${theme.border.b1} solid ${theme.colour.border.subtle};
36
+
37
+ @media (min-width: ${theme.breakpoints.tablet}px) {
38
+ padding: 0 ${theme.padding.p48};
39
+ }
40
+ `;
41
+
42
+ const listStyle = css`
43
+ display: flex;
44
+ flex-wrap: wrap;
45
+ align-items: center;
46
+ gap: 2px 0;
47
+ margin: 0;
48
+ padding: 0;
49
+ list-style: none;
50
+ `;
51
+
52
+ const style = cx(NAME, baseStyle, className);
53
+
54
+ return (
55
+ <nav
56
+ className={style}
57
+ data-testid={testId}
58
+ aria-label={ariaLabel}
59
+ {...props}
60
+ >
61
+ <ul className={listStyle}>{items}</ul>
62
+ </nav>
63
+ );
64
+ };
65
+
66
+ export interface NavigationBarComponent extends NamedExoticComponent<NavigationBarProps> {
67
+ Item: typeof NavigationBarItem;
68
+ }
69
+
70
+ const NavigationBar = Object.assign(memo(NavigationBarBase), {
71
+ Item: NavigationBarItem,
72
+ }) as NavigationBarComponent;
73
+
74
+ export type { NavigationBarItemProps };
75
+
76
+ export default NavigationBar;
@@ -0,0 +1,236 @@
1
+ import {
2
+ Children,
3
+ ComponentPropsWithoutRef,
4
+ ElementType,
5
+ ReactElement,
6
+ ReactNode,
7
+ cloneElement,
8
+ forwardRef,
9
+ isValidElement,
10
+ memo,
11
+ } from 'react';
12
+ import { css, cx } from '@emotion/css';
13
+ import { useTheme } from '../../theme';
14
+ import Text from '../Text';
15
+ import BaseLink, { BaseLinkProps } from '../Link/BaseLink';
16
+ import type { IconProps } from '../Icon';
17
+
18
+ export const NAME = 'ucl-uikit-navigation-bar__item';
19
+
20
+ type NavigationBarItemOwnProps = {
21
+ active?: boolean;
22
+ disabled?: boolean;
23
+ icon?: ReactElement<IconProps>;
24
+ iconPosition?: 'left' | 'right';
25
+ testId?: string;
26
+ className?: string;
27
+ children?: ReactNode;
28
+ };
29
+
30
+ export type NavigationBarItemProps<C extends ElementType = 'a'> =
31
+ NavigationBarItemOwnProps &
32
+ BaseLinkProps<C> &
33
+ Omit<ComponentPropsWithoutRef<C>, keyof NavigationBarItemOwnProps>;
34
+
35
+ type NavigationBarItemComponent = <C extends ElementType = 'a'>(
36
+ props: NavigationBarItemProps<C> & {
37
+ ref?: React.ComponentPropsWithRef<C>['ref'];
38
+ }
39
+ ) => React.JSX.Element;
40
+
41
+ const NavigationBarItem = forwardRef(function NavigationBarItemInner(
42
+ {
43
+ active = false,
44
+ disabled = false,
45
+ icon,
46
+ iconPosition = 'right',
47
+ testId = NAME,
48
+ className,
49
+ children,
50
+ asChild = false,
51
+ ...props
52
+ }: NavigationBarItemProps<ElementType>,
53
+ ref: React.Ref<Element>
54
+ ) {
55
+ const [theme] = useTheme();
56
+
57
+ const listItemStyle = css`
58
+ display: inline-flex;
59
+ `;
60
+
61
+ const baseStyle = css`
62
+ box-sizing: border-box;
63
+ display: inline-flex;
64
+ min-height: 40px;
65
+ padding: ${theme.padding.p8} ${theme.padding.p16} 0;
66
+ align-items: stretch;
67
+ justify-content: center;
68
+ color: ${theme.colour.link.primary};
69
+ background-color: transparent;
70
+ text-decoration: none;
71
+ cursor: pointer;
72
+ outline: none;
73
+
74
+ &:visited {
75
+ color: ${theme.colour.link.primary};
76
+ }
77
+
78
+ &:hover {
79
+ color: ${theme.colour.link.primary};
80
+ background-color: ${theme.colour.fill.brandSubtleHover};
81
+ text-decoration: none;
82
+ }
83
+
84
+ &:focus-visible {
85
+ box-shadow: ${theme.boxShadow.focus};
86
+ z-index: 1;
87
+ }
88
+ `;
89
+
90
+ const activeStyle = css`
91
+ color: ${theme.colour.link.primary};
92
+
93
+ &:visited,
94
+ &:hover {
95
+ color: ${theme.colour.link.primary};
96
+ }
97
+ `;
98
+
99
+ const disabledStyle = css`
100
+ color: ${theme.colour.link.disabled};
101
+ background-color: transparent;
102
+ cursor: not-allowed;
103
+
104
+ &:visited,
105
+ &:hover {
106
+ color: ${theme.colour.link.disabled};
107
+ background-color: transparent;
108
+ }
109
+ `;
110
+
111
+ const contentStyle = css`
112
+ display: inline-flex;
113
+ flex-direction: column;
114
+ align-items: stretch;
115
+ justify-content: flex-start;
116
+ gap: 2px;
117
+ `;
118
+
119
+ const labelRowStyle = css`
120
+ display: inline-flex;
121
+ min-height: 24px;
122
+ align-items: center;
123
+ justify-content: center;
124
+ gap: ${theme.padding.p4};
125
+ color: inherit;
126
+ white-space: nowrap;
127
+ `;
128
+
129
+ const textStackStyle = css`
130
+ display: inline-grid;
131
+ align-items: center;
132
+ justify-items: start;
133
+ color: inherit;
134
+ `;
135
+
136
+ const textStyle = css`
137
+ grid-area: 1 / 1;
138
+ color: inherit;
139
+ white-space: nowrap;
140
+ `;
141
+
142
+ const hiddenTextStyle = css`
143
+ height: 0;
144
+ overflow: hidden;
145
+ visibility: hidden;
146
+ pointer-events: none;
147
+ `;
148
+
149
+ const indicatorStyle = css`
150
+ display: block;
151
+ height: 6px;
152
+ background-color: ${active ? theme.colour.fill.brand : 'transparent'};
153
+ `;
154
+
155
+ const iconStyle = css`
156
+ display: block;
157
+ flex-shrink: 0;
158
+ `;
159
+
160
+ const style = cx(
161
+ NAME,
162
+ baseStyle,
163
+ active && activeStyle,
164
+ disabled && disabledStyle,
165
+ className
166
+ );
167
+
168
+ const renderContent = (content: ReactNode) => (
169
+ <span className={contentStyle}>
170
+ <span className={labelRowStyle}>
171
+ {icon &&
172
+ iconPosition === 'left' &&
173
+ cloneElement(icon, {
174
+ size: icon.props.size ?? 20,
175
+ className: cx(iconStyle, icon.props.className),
176
+ 'aria-hidden': icon.props['aria-hidden'] ?? true,
177
+ })}
178
+ <span className={textStackStyle}>
179
+ <Text
180
+ level={active ? 'md-semibold' : 'md'}
181
+ className={textStyle}
182
+ >
183
+ {content}
184
+ </Text>
185
+ <Text
186
+ aria-hidden
187
+ level='md-semibold'
188
+ className={cx(textStyle, hiddenTextStyle)}
189
+ >
190
+ {content}
191
+ </Text>
192
+ </span>
193
+ {icon &&
194
+ iconPosition === 'right' &&
195
+ cloneElement(icon, {
196
+ size: icon.props.size ?? 20,
197
+ className: cx(iconStyle, icon.props.className),
198
+ 'aria-hidden': icon.props['aria-hidden'] ?? true,
199
+ })}
200
+ </span>
201
+ <span
202
+ className={indicatorStyle}
203
+ aria-hidden='true'
204
+ />
205
+ </span>
206
+ );
207
+
208
+ const content = asChild
209
+ ? (() => {
210
+ const child = Children.only(children);
211
+
212
+ return isValidElement<{ children?: ReactNode }>(child)
213
+ ? cloneElement(child, {}, renderContent(child.props.children))
214
+ : child;
215
+ })()
216
+ : renderContent(children);
217
+
218
+ return (
219
+ <li className={listItemStyle}>
220
+ <BaseLink
221
+ ref={ref}
222
+ className={style}
223
+ testId={testId}
224
+ disabled={disabled}
225
+ asChild={asChild}
226
+ aria-current={active ? 'page' : undefined}
227
+ data-state={active ? 'active' : 'inactive'}
228
+ {...props}
229
+ >
230
+ {content}
231
+ </BaseLink>
232
+ </li>
233
+ );
234
+ }) as unknown as NavigationBarItemComponent;
235
+
236
+ export default memo(NavigationBarItem) as NavigationBarItemComponent;
@@ -0,0 +1,90 @@
1
+ import { describe, expect, test } from 'vitest';
2
+ import { render, screen } from '@testing-library/react';
3
+ import NavigationBar from '../NavigationBar';
4
+ import Icon from '../../Icon';
5
+ import { ThemeContextProvider } from '../../../theme/useTheme';
6
+
7
+ const wrap = (ui: React.ReactElement) =>
8
+ render(<ThemeContextProvider>{ui}</ThemeContextProvider>);
9
+
10
+ describe('NavigationBar', () => {
11
+ test('renders a navigation landmark with link items', () => {
12
+ wrap(
13
+ <NavigationBar aria-label='Section navigation'>
14
+ <NavigationBar.Item href='/courses'>Courses</NavigationBar.Item>
15
+ <NavigationBar.Item href='/research'>Research</NavigationBar.Item>
16
+ </NavigationBar>
17
+ );
18
+
19
+ expect(
20
+ screen.getByRole('navigation', { name: 'Section navigation' })
21
+ ).toBeInTheDocument();
22
+ expect(screen.getByRole('link', { name: 'Courses' })).toHaveAttribute(
23
+ 'href',
24
+ '/courses'
25
+ );
26
+ });
27
+
28
+ test('marks the active item and reserves bold label width', () => {
29
+ wrap(
30
+ <NavigationBar>
31
+ <NavigationBar.Item
32
+ href='/courses'
33
+ active
34
+ >
35
+ Courses
36
+ </NavigationBar.Item>
37
+ </NavigationBar>
38
+ );
39
+
40
+ const item = screen.getByRole('link', { name: 'Courses' });
41
+ const generatedClass = [...item.classList].find((className) =>
42
+ className.startsWith('css-')
43
+ );
44
+
45
+ expect(item).toHaveAttribute('aria-current', 'page');
46
+ expect(item).toHaveAttribute('data-state', 'active');
47
+ expect(screen.getAllByText('Courses')).toHaveLength(2);
48
+ expect(generatedClass).toBeDefined();
49
+ expect(document.head.textContent).toContain('visibility:hidden');
50
+ expect(document.head.textContent).toContain('white-space:nowrap');
51
+ });
52
+
53
+ test('supports disabled items', () => {
54
+ wrap(
55
+ <NavigationBar>
56
+ <NavigationBar.Item
57
+ href='/news'
58
+ disabled
59
+ >
60
+ News
61
+ </NavigationBar.Item>
62
+ </NavigationBar>
63
+ );
64
+
65
+ const item = screen.getAllByText('News')[0].closest('a');
66
+
67
+ expect(item).toBeInTheDocument();
68
+ expect(item).toHaveAttribute('aria-disabled', 'true');
69
+ expect(item).toHaveAttribute('tabindex', '-1');
70
+ expect(item).not.toHaveAttribute('href');
71
+ });
72
+
73
+ test('supports icons and asChild links', () => {
74
+ wrap(
75
+ <NavigationBar>
76
+ <NavigationBar.Item
77
+ icon={<Icon.ChevronDown />}
78
+ asChild
79
+ >
80
+ <a href='/open-days'>Open days</a>
81
+ </NavigationBar.Item>
82
+ </NavigationBar>
83
+ );
84
+
85
+ const item = screen.getByRole('link', { name: 'Open days' });
86
+
87
+ expect(item).toHaveAttribute('href', '/open-days');
88
+ expect(item.querySelector('svg')).toBeInTheDocument();
89
+ });
90
+ });
@@ -0,0 +1,5 @@
1
+ export { default } from './NavigationBar';
2
+ export type {
3
+ NavigationBarItemProps,
4
+ NavigationBarProps,
5
+ } from './NavigationBar';
@@ -148,6 +148,12 @@ export type { HeaderProps } from './Header';
148
148
  export { default as HeaderNew } from './HeaderNew';
149
149
  export type { HeaderProps as HeaderNewProps } from './HeaderNew';
150
150
 
151
+ export { default as NavigationBar } from './NavigationBar';
152
+ export type {
153
+ NavigationBarItemProps,
154
+ NavigationBarProps,
155
+ } from './NavigationBar';
156
+
151
157
  export { default as HeaderDraft } from './HeaderDraft';
152
158
  export type { HeaderDraftProps } from './HeaderDraft';
153
159