@altinn/altinn-components 0.4.1 → 0.5.1

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.
Files changed (194) hide show
  1. package/.storybook/StoryDecorator.tsx +1 -1
  2. package/.storybook/main.ts +3 -4
  3. package/.storybook/preview.tsx +28 -0
  4. package/CHANGELOG.md +14 -0
  5. package/biome.jsonc +1 -1
  6. package/lib/components/Attachment/AttachmentLink.stories.ts +1 -1
  7. package/lib/components/Attachment/AttachmentList.stories.ts +1 -1
  8. package/lib/components/Button/Button.tsx +13 -10
  9. package/lib/components/Button/ButtonBase.tsx +1 -1
  10. package/lib/components/Button/ButtonIcon.tsx +16 -0
  11. package/lib/components/Button/ButtonLabel.tsx +18 -0
  12. package/lib/components/Button/Buttons.stories.tsx +64 -0
  13. package/lib/components/Button/ComboButton.tsx +9 -7
  14. package/lib/components/Button/IconButton.stories.tsx +47 -0
  15. package/lib/components/Button/IconButton.tsx +15 -5
  16. package/lib/components/Button/button.module.css +5 -46
  17. package/lib/components/Button/buttonBase.module.css +55 -23
  18. package/lib/components/Button/buttonIcon.module.css +17 -0
  19. package/lib/components/Button/buttonLabel.module.css +17 -0
  20. package/lib/components/Button/comboButton.module.css +15 -65
  21. package/lib/components/Button/iconButton.module.css +21 -4
  22. package/lib/components/Button/index.ts +2 -0
  23. package/lib/components/ContextMenu/ContextMenu.stories.ts +49 -0
  24. package/lib/components/ContextMenu/ContextMenu.tsx +12 -20
  25. package/lib/components/ContextMenu/ContextMenuBase.tsx +33 -0
  26. package/lib/components/Dialog/Dialog.stories.ts +12 -5
  27. package/lib/components/Dialog/Dialog.tsx +2 -0
  28. package/lib/components/Dialog/DialogGroup.tsx +24 -0
  29. package/lib/components/Dialog/DialogList.stories.ts +14 -10
  30. package/lib/components/Dialog/DialogList.tsx +26 -11
  31. package/lib/components/Dialog/DialogListItem.tsx +12 -2
  32. package/lib/components/Dialog/DialogListItemBase.tsx +4 -2
  33. package/lib/components/Dialog/DialogNav.stories.ts +5 -5
  34. package/lib/components/Dialog/DialogNav.tsx +2 -6
  35. package/lib/components/Dialog/DialogSelect.tsx +1 -1
  36. package/lib/components/Dialog/dialogGroup.module.css +35 -0
  37. package/lib/components/Dropdown/Backdrop.tsx +4 -3
  38. package/lib/components/Dropdown/DrawerBase.tsx +5 -2
  39. package/lib/components/Dropdown/DrawerBody.tsx +12 -0
  40. package/lib/components/Dropdown/DrawerButton.tsx +17 -0
  41. package/lib/components/Dropdown/DrawerFooter.tsx +12 -0
  42. package/lib/components/Dropdown/DrawerHeader.tsx +19 -0
  43. package/lib/components/Dropdown/DrawerOrDropdown.tsx +29 -0
  44. package/lib/components/Dropdown/DropdownBase.tsx +17 -2
  45. package/lib/components/Dropdown/backdrop.module.css +3 -0
  46. package/lib/components/Dropdown/drawerBase.module.css +9 -0
  47. package/lib/components/Dropdown/drawerBody.module.css +5 -0
  48. package/lib/components/Dropdown/drawerButton.module.css +6 -0
  49. package/lib/components/Dropdown/drawerFooter.module.css +13 -0
  50. package/lib/components/Dropdown/drawerHeader.module.css +17 -0
  51. package/lib/components/Dropdown/drawerOrDropdown.module.css +19 -0
  52. package/lib/components/Dropdown/dropdownBase.module.css +20 -4
  53. package/lib/components/Dropdown/index.ts +7 -1
  54. package/lib/components/Footer/footerMenu.module.css +5 -0
  55. package/lib/components/GlobalMenu/AccountButton.tsx +29 -0
  56. package/lib/components/GlobalMenu/AccountMenu.stories.tsx +65 -0
  57. package/lib/components/GlobalMenu/AccountMenu.tsx +73 -0
  58. package/lib/components/GlobalMenu/BackButton.tsx +10 -0
  59. package/lib/components/GlobalMenu/GlobalMenu.stories.tsx +112 -121
  60. package/lib/components/GlobalMenu/GlobalMenu.tsx +41 -89
  61. package/lib/components/GlobalMenu/GlobalMenuBase.tsx +22 -0
  62. package/lib/components/GlobalMenu/LogoutButton.tsx +19 -0
  63. package/lib/components/GlobalMenu/globalMenuBase.module.css +39 -0
  64. package/lib/components/GlobalMenu/index.tsx +1 -1
  65. package/lib/components/GlobalMenu/logoutButton.module.css +9 -0
  66. package/lib/components/Header/{Header.stories.ts → Header.stories.tsx} +79 -20
  67. package/lib/components/Header/Header.tsx +25 -38
  68. package/lib/components/Header/HeaderBase.tsx +7 -3
  69. package/lib/components/Header/header.module.css +10 -42
  70. package/lib/components/Header/headerBase.module.css +43 -0
  71. package/lib/components/Header/headerButton.module.css +1 -0
  72. package/lib/components/Layout/Layout.stories.tsx +77 -38
  73. package/lib/components/Layout/Layout.tsx +5 -3
  74. package/lib/components/Layout/LayoutBase.tsx +3 -2
  75. package/lib/components/Layout/layoutBase.module.css +11 -0
  76. package/lib/components/Layout/layoutBody.module.css +1 -0
  77. package/lib/components/LayoutAction/ActionHeader.tsx +1 -1
  78. package/lib/components/LayoutAction/ActionMenu.tsx +2 -4
  79. package/lib/components/LayoutAction/actionMenu.module.css +3 -0
  80. package/lib/components/List/List.stories.tsx +43 -0
  81. package/lib/components/List/List.tsx +6 -6
  82. package/lib/components/List/ListBase.tsx +6 -6
  83. package/lib/components/List/ListItem.tsx +4 -1
  84. package/lib/components/List/ListItemBase.tsx +20 -4
  85. package/lib/components/List/listBase.module.css +3 -3
  86. package/lib/components/List/listItemBase.module.css +4 -0
  87. package/lib/components/Menu/Menu.stories.ts +46 -46
  88. package/lib/components/Menu/Menu.tsx +3 -102
  89. package/lib/components/Menu/MenuBase.tsx +47 -3
  90. package/lib/components/Menu/MenuItem.tsx +8 -4
  91. package/lib/components/Menu/MenuItemBase.tsx +15 -2
  92. package/lib/components/Menu/MenuItems.stories.ts +438 -0
  93. package/lib/components/Menu/MenuItems.tsx +96 -0
  94. package/lib/components/Menu/MenuOption.tsx +4 -1
  95. package/lib/components/Menu/index.ts +1 -1
  96. package/lib/components/Menu/menu.module.css +2 -3
  97. package/lib/components/Menu/menuBase.module.css +25 -0
  98. package/lib/components/Menu/menuItemBase.module.css +11 -5
  99. package/lib/components/Menu/menuItemLabel.module.css +11 -1
  100. package/lib/components/Menu/menuSearch.module.css +1 -0
  101. package/lib/components/Meta/MetaItemBase.tsx +1 -1
  102. package/lib/components/Meta/MetaItemLabel.tsx +1 -1
  103. package/lib/components/Meta/MetaItemMedia.tsx +1 -1
  104. package/lib/components/Page/PageBase.tsx +14 -0
  105. package/lib/components/Page/PageHeader.tsx +21 -0
  106. package/lib/components/Page/PageHeaderMedia.tsx +25 -0
  107. package/lib/components/Page/SectionBase.tsx +52 -0
  108. package/lib/components/Page/SectionFooter.tsx +15 -0
  109. package/lib/components/Page/SectionHeader.tsx +16 -0
  110. package/lib/components/Page/index.ts +5 -0
  111. package/lib/components/Page/pageHeader.module.css +5 -0
  112. package/lib/components/Page/sectionBase.module.css +82 -0
  113. package/lib/components/Page/sectionFooter.module.css +8 -0
  114. package/lib/components/Page/sectionHeader.module.css +9 -0
  115. package/lib/components/RootProvider/RootProvider.tsx +43 -7
  116. package/lib/components/Searchbar/Autocomplete.stories.tsx +77 -0
  117. package/lib/components/Searchbar/Autocomplete.tsx +44 -0
  118. package/lib/components/Searchbar/AutocompleteBase.tsx +16 -0
  119. package/lib/components/Searchbar/AutocompleteGroup.tsx +17 -0
  120. package/lib/components/Searchbar/AutocompleteItem.tsx +23 -0
  121. package/lib/components/Searchbar/SearchField.tsx +78 -0
  122. package/lib/components/Searchbar/Searchbar.stories.tsx +151 -0
  123. package/lib/components/Searchbar/Searchbar.tsx +18 -0
  124. package/lib/components/Searchbar/SearchbarBase.tsx +23 -0
  125. package/lib/components/Searchbar/autocompleteBase.module.css +17 -0
  126. package/lib/components/Searchbar/autocompleteGroup.module.css +3 -0
  127. package/lib/components/Searchbar/autocompleteItem.module.css +19 -0
  128. package/lib/components/Searchbar/index.ts +1 -0
  129. package/lib/components/Searchbar/searchField.module.css +54 -0
  130. package/lib/components/Searchbar/searchbarBase.module.css +20 -0
  131. package/lib/components/Toolbar/Toolbar.stories.tsx +10 -10
  132. package/lib/components/Toolbar/Toolbar.tsx +28 -10
  133. package/lib/components/Toolbar/ToolbarAdd.tsx +6 -5
  134. package/lib/components/Toolbar/ToolbarBase.tsx +5 -20
  135. package/lib/components/Toolbar/ToolbarButton.tsx +1 -1
  136. package/lib/components/Toolbar/ToolbarFilter.tsx +11 -6
  137. package/lib/components/Toolbar/ToolbarMenu.tsx +8 -7
  138. package/lib/components/Toolbar/ToolbarOptions.stories.ts +5 -5
  139. package/lib/components/Toolbar/ToolbarOptions.tsx +34 -21
  140. package/lib/components/Toolbar/toolbarAdd.module.css +7 -0
  141. package/lib/components/Toolbar/toolbarBase.module.css +19 -0
  142. package/lib/components/Toolbar/toolbarButton.module.css +1 -1
  143. package/lib/components/Toolbar/toolbarFilter.module.css +25 -0
  144. package/lib/components/Toolbar/toolbarMenu.module.css +7 -0
  145. package/lib/components/Typography/Heading.tsx +23 -0
  146. package/lib/components/Typography/Typography.tsx +8 -5
  147. package/lib/components/Typography/heading.module.css +21 -0
  148. package/lib/components/Typography/index.ts +1 -0
  149. package/lib/components/Typography/typography.module.css +8 -0
  150. package/lib/components/index.ts +2 -0
  151. package/lib/hooks/index.ts +3 -0
  152. package/lib/{components/Menu → hooks}/useEscapeKey.ts +2 -2
  153. package/lib/hooks/useMenu.tsx +80 -0
  154. package/lib/index.ts +1 -0
  155. package/lib/stories/Color/MenuItem.stories.tsx +43 -0
  156. package/lib/stories/Color/Swatches.stories.tsx +19 -0
  157. package/lib/stories/Color/Swatches.tsx +42 -0
  158. package/lib/stories/Color/colors.json +62 -0
  159. package/lib/stories/Color/swatches.module.css +14 -0
  160. package/lib/stories/Inbox/BookmarksPage.tsx +52 -0
  161. package/lib/stories/Inbox/DialogPage.tsx +15 -0
  162. package/lib/stories/Inbox/Inbox.stories.tsx +55 -0
  163. package/lib/stories/Inbox/Inbox.tsx +12 -0
  164. package/lib/stories/Inbox/InboxLayout.tsx +50 -0
  165. package/lib/stories/Inbox/InboxPage.tsx +50 -0
  166. package/lib/stories/Inbox/InboxProvider.tsx +136 -0
  167. package/lib/stories/Inbox/InboxSection.tsx +39 -0
  168. package/lib/stories/Inbox/InboxToolbar.tsx +94 -0
  169. package/lib/stories/Inbox/ProfilePage.tsx +35 -0
  170. package/lib/stories/Inbox/SettingsPage.tsx +19 -0
  171. package/lib/stories/Inbox/accounts/accounts.ts +24 -0
  172. package/lib/stories/Inbox/accounts/index.ts +1 -0
  173. package/lib/stories/Inbox/actionMenu.ts +24 -0
  174. package/lib/stories/Inbox/dialogs/brreg-completed.json +35 -0
  175. package/lib/stories/Inbox/dialogs/brreg-draft.json +45 -0
  176. package/lib/stories/Inbox/dialogs/index.ts +10 -0
  177. package/lib/stories/Inbox/dialogs/skatt-2023.json +33 -0
  178. package/lib/stories/Inbox/groupBy.ts +19 -0
  179. package/lib/stories/Inbox/inboxSection.module.css +19 -0
  180. package/lib/stories/Inbox/index.ts +15 -0
  181. package/lib/stories/Inbox/layout/footer.ts +27 -0
  182. package/lib/stories/Inbox/layout/header.ts +11 -0
  183. package/lib/stories/Inbox/layout/index.ts +3 -0
  184. package/lib/stories/Inbox/layout/menu.ts +64 -0
  185. package/package.json +15 -13
  186. package/tsconfig.json +7 -2
  187. package/lib/components/Header/HeaderSearch.stories.ts +0 -20
  188. package/lib/components/Header/HeaderSearch.tsx +0 -44
  189. package/lib/components/Header/headerSearch.module.css +0 -30
  190. package/lib/components/Menu/MenuGroup.tsx +0 -18
  191. package/lib/components/Menu/__menuItem.module.css +0 -130
  192. package/lib/components/Toolbar/toolbar.module.css +0 -43
  193. /package/lib/components/ContextMenu/{contextMenu.module.css → contextMenuBase.module.css} +0 -0
  194. /package/lib/{components/Menu → hooks}/useClickOutside.ts +0 -0
@@ -1,5 +1,5 @@
1
1
  import cx from 'classnames';
2
- import type { ElementType, ReactNode } from 'react';
2
+ import type { ElementType, MouseEventHandler, ReactNode } from 'react';
3
3
  import { Badge, type BadgeProps } from '../Badge';
4
4
  import { Icon, type IconName } from '../Icon';
5
5
  import styles from './menuItemBase.module.css';
@@ -9,12 +9,16 @@ export type MenuItemSize = 'sm' | 'md' | 'lg';
9
9
 
10
10
  export interface MenuItemBaseProps {
11
11
  as?: ElementType;
12
+ onClick?: MouseEventHandler;
12
13
  color?: MenuItemColor;
13
14
  children?: ReactNode;
15
+ tabIndex?: number;
14
16
  size?: MenuItemSize;
15
17
  linkIcon?: IconName;
18
+ linkText?: string;
16
19
  badge?: BadgeProps;
17
20
  collapsible?: boolean;
21
+ active?: boolean;
18
22
  expanded?: boolean;
19
23
  selected?: boolean;
20
24
  disabled?: boolean;
@@ -23,10 +27,14 @@ export interface MenuItemBaseProps {
23
27
 
24
28
  export const MenuItemBase = ({
25
29
  as,
30
+ onClick,
26
31
  size,
27
32
  color,
28
33
  linkIcon,
34
+ linkText,
29
35
  badge,
36
+ tabIndex = 0,
37
+ active = false,
30
38
  collapsible = false,
31
39
  expanded = false,
32
40
  selected = false,
@@ -42,11 +50,15 @@ export const MenuItemBase = ({
42
50
 
43
51
  return (
44
52
  <Component
53
+ role="menuitem"
54
+ tabIndex={!disabled && tabIndex}
45
55
  data-size={size}
46
56
  data-color={color}
57
+ data-active={active}
47
58
  aria-expanded={expanded}
48
59
  aria-disabled={disabled}
49
60
  aria-selected={selected}
61
+ onClick={onClick}
50
62
  className={cx(styles.item, className)}
51
63
  {...rest}
52
64
  >
@@ -56,7 +68,8 @@ export const MenuItemBase = ({
56
68
  </div>
57
69
  {applicableIcon && (
58
70
  <div className={styles.action}>
59
- {applicableIcon && <Icon name={applicableIcon} className={styles.actionIcon} />}
71
+ <span className={styles.linkText}>{linkText}</span>
72
+ {applicableIcon && <Icon name={applicableIcon} className={styles.linkIcon} />}
60
73
  </div>
61
74
  )}
62
75
  </Component>
@@ -0,0 +1,438 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { MenuItems } from './MenuItems';
3
+
4
+ const meta = {
5
+ title: 'Menu/MenuItems',
6
+ component: MenuItems,
7
+ tags: ['autodocs'],
8
+ parameters: {},
9
+ args: {},
10
+ } satisfies Meta<typeof MenuItems>;
11
+
12
+ export default meta;
13
+ type Story = StoryObj<typeof meta>;
14
+
15
+ export const InboxMenu: Story = {
16
+ args: {
17
+ groups: {
18
+ shortcuts: {
19
+ title: 'Snarveier',
20
+ defaultItemColor: 'default',
21
+ },
22
+ },
23
+ defaultItemColor: 'subtle',
24
+ items: [
25
+ {
26
+ id: 'innboks',
27
+ groupId: '1',
28
+ size: 'lg',
29
+ icon: 'inbox',
30
+ title: 'Innboks',
31
+ color: 'strong',
32
+ badge: { color: 'alert', label: '4' },
33
+ },
34
+ {
35
+ id: 'utkast',
36
+ groupId: '2',
37
+ icon: 'doc-pencil',
38
+ title: 'Utkast',
39
+ badge: {
40
+ label: '3',
41
+ },
42
+ },
43
+ {
44
+ id: 'sendt',
45
+ groupId: '2',
46
+ icon: 'file-checkmark',
47
+ selected: true,
48
+ title: 'Sendt',
49
+ badge: {
50
+ label: '2',
51
+ },
52
+ },
53
+ {
54
+ id: 'lagret',
55
+ groupId: '3',
56
+ icon: 'bookmark',
57
+ title: 'Lagrede søk',
58
+ badge: {
59
+ label: '5',
60
+ },
61
+ },
62
+ {
63
+ id: 'arkivert',
64
+ groupId: '4',
65
+ icon: 'archive',
66
+ title: 'Arkivert',
67
+ badge: {
68
+ label: '100+',
69
+ },
70
+ },
71
+ {
72
+ id: 'papirkurv',
73
+ groupId: '4',
74
+ disabled: true,
75
+ icon: 'trash',
76
+ title: 'Papirkurv',
77
+ badge: {
78
+ label: '45',
79
+ },
80
+ },
81
+ {
82
+ id: 'users',
83
+ groupId: 'shortcuts',
84
+ icon: 'person-group',
85
+ title: 'Brukere',
86
+ },
87
+ {
88
+ id: 'settings',
89
+ groupId: 'shortcuts',
90
+ icon: 'cog',
91
+ title: 'Innstillinger',
92
+ },
93
+ ],
94
+ },
95
+ };
96
+
97
+ export const PersonMenu: Story = {
98
+ args: {
99
+ groups: {},
100
+ defaultItemColor: 'subtle',
101
+ items: [
102
+ {
103
+ id: 'person',
104
+ groupId: '1',
105
+ size: 'lg',
106
+ avatar: {
107
+ type: 'person',
108
+ name: 'Erik Huseklepp',
109
+ },
110
+ title: 'Erik Huseklepp',
111
+ },
112
+ {
113
+ id: 'profil',
114
+ groupId: '2',
115
+ icon: 'person-circle',
116
+ title: 'Kontaktinformasjon',
117
+ },
118
+ {
119
+ id: 'varslinger',
120
+ groupId: '2',
121
+ icon: 'bell',
122
+ title: 'Varslingsinnstillinger',
123
+ },
124
+ {
125
+ id: 'bookmarks',
126
+ groupId: '3',
127
+ icon: 'bookmark',
128
+ title: 'Favoritter',
129
+ },
130
+ {
131
+ id: 'grupper',
132
+ groupId: '3',
133
+ icon: 'hexagon-grid',
134
+ title: 'Grupper',
135
+ },
136
+ {
137
+ id: 'logg',
138
+ groupId: '4',
139
+ icon: 'clock-dashed',
140
+ title: 'Aktivitetslogg',
141
+ },
142
+ ],
143
+ },
144
+ };
145
+
146
+ export const CompanyMenu: Story = {
147
+ args: {
148
+ groups: {},
149
+ defaultItemColor: 'subtle',
150
+ items: [
151
+ {
152
+ id: 'company',
153
+ groupId: '1',
154
+ size: 'lg',
155
+ avatar: {
156
+ type: 'company',
157
+ name: 'Bergen Bar',
158
+ },
159
+ title: 'Bergen Bar',
160
+ },
161
+ {
162
+ id: 'profil',
163
+ groupId: '2',
164
+ icon: 'buildings2',
165
+ title: 'Firmaprofil',
166
+ },
167
+ {
168
+ id: 'brukere',
169
+ groupId: '3',
170
+ icon: 'person-group',
171
+ title: 'Brukere',
172
+ },
173
+ {
174
+ id: 'grupper',
175
+ groupId: '3',
176
+ icon: 'hexagon-grid',
177
+ title: 'Grupper',
178
+ },
179
+ {
180
+ id: 'logg',
181
+ groupId: '4',
182
+ icon: 'clock-dashed',
183
+ title: 'Aktivitetslogg',
184
+ },
185
+ ],
186
+ },
187
+ };
188
+
189
+ export const AccountMenu: Story = {
190
+ args: {
191
+ groups: {
192
+ a1: {
193
+ title: 'Deg selv, favoritter og grupper',
194
+ },
195
+ b1: {
196
+ id: 'companies',
197
+ title: 'Andre kontoer',
198
+ },
199
+ },
200
+ items: [
201
+ {
202
+ id: '1',
203
+ groupId: 'a1',
204
+ avatar: {
205
+ type: 'person',
206
+ name: 'Dolly Duck',
207
+ },
208
+ title: 'Dolly Duck',
209
+ badge: {
210
+ label: '15',
211
+ },
212
+ },
213
+ {
214
+ id: '2',
215
+ groupId: 'a2',
216
+ avatar: {
217
+ type: 'company',
218
+ name: 'Bergen Bar',
219
+ },
220
+ title: 'Bergen Bar',
221
+ badge: {
222
+ label: '21',
223
+ },
224
+ },
225
+ {
226
+ id: '3',
227
+ groupId: 'a2',
228
+ avatar: {
229
+ type: 'company',
230
+ name: 'Sportsklubben Brann',
231
+ },
232
+ title: 'Sportsklubben Brann',
233
+ badge: {
234
+ label: '4',
235
+ },
236
+ },
237
+ {
238
+ id: '4',
239
+ groupId: 'a3',
240
+ avatarGroup: {
241
+ type: 'company',
242
+ items: [
243
+ {
244
+ name: 'Sportsklubben Brann',
245
+ },
246
+ {
247
+ name: 'Bergen Bar',
248
+ },
249
+ ],
250
+ },
251
+ title: 'Alle virksomheter',
252
+ badge: {
253
+ label: '45',
254
+ },
255
+ },
256
+ {
257
+ id: '5',
258
+ groupId: 'b1',
259
+ avatar: {
260
+ type: 'company',
261
+ name: 'Jensens Laks',
262
+ },
263
+ title: 'Jensens laks',
264
+ },
265
+ {
266
+ id: '6',
267
+ groupId: 'b1',
268
+ avatar: {
269
+ type: 'company',
270
+ name: 'Haralds gym',
271
+ },
272
+ title: 'Haralds gym',
273
+ badge: {
274
+ label: '2',
275
+ },
276
+ },
277
+ {
278
+ id: '7',
279
+ groupId: 'b1',
280
+ avatar: {
281
+ type: 'company',
282
+ name: 'Trim og tran',
283
+ },
284
+ title: 'Trim og tran',
285
+ },
286
+ ],
287
+ },
288
+ };
289
+
290
+ export const CollapsibleGlobalMenu: Story = {
291
+ args: {
292
+ defaultItemColor: 'subtle',
293
+ groups: {
294
+ settings: {
295
+ defaultItemColor: 'neutral',
296
+ },
297
+ },
298
+ items: [
299
+ {
300
+ id: 'account',
301
+ groupId: 'account',
302
+ size: 'lg',
303
+ avatar: {
304
+ type: 'person',
305
+ name: 'Herman Friele',
306
+ },
307
+ title: 'Herman Friele',
308
+ description: 'Fødselsnr: XX.XX.XXXX',
309
+ },
310
+ {
311
+ id: 'innboks',
312
+ groupId: 'apps',
313
+ size: 'lg',
314
+ icon: 'inbox',
315
+ title: 'Innboks',
316
+ collapsible: true,
317
+ items: [
318
+ {
319
+ id: 'utkast',
320
+ groupId: '1',
321
+ icon: 'doc-pencil',
322
+ title: 'Utkast',
323
+ },
324
+ {
325
+ id: 'sent',
326
+ groupId: '1',
327
+ icon: 'file-checkmark',
328
+ selected: true,
329
+ title: 'Sendt',
330
+ },
331
+ {
332
+ id: 'bookmarks',
333
+ groupId: '3',
334
+ icon: 'bookmark',
335
+ title: 'Lagrede søk',
336
+ },
337
+ {
338
+ id: 'arkiv',
339
+ groupId: '4',
340
+ icon: 'archive',
341
+ title: 'Arkivert',
342
+ },
343
+ {
344
+ id: 'trash',
345
+ groupId: '4',
346
+ icon: 'trash',
347
+ title: 'Papirkurv',
348
+ },
349
+ ],
350
+ },
351
+ {
352
+ id: 'tilganger',
353
+ groupId: 'apps',
354
+ size: 'lg',
355
+ icon: 'bookmark',
356
+ title: 'Tilganger',
357
+ },
358
+ {
359
+ id: 'skjema',
360
+ groupId: 'apps',
361
+ size: 'lg',
362
+ icon: 'menu-grid',
363
+ title: 'Alle skjema',
364
+ },
365
+ {
366
+ id: 'settings',
367
+ groupId: 'settings',
368
+ icon: 'cog',
369
+ title: 'Innstillinger',
370
+ },
371
+ ],
372
+ },
373
+ };
374
+
375
+ export const ExpandedGlobalMenu: Story = {
376
+ args: {
377
+ ...CollapsibleGlobalMenu.args,
378
+ items: [...CollapsibleGlobalMenu.args.items].map((item) => {
379
+ if (item.collapsible) {
380
+ return {
381
+ ...item,
382
+ expanded: true,
383
+ };
384
+ }
385
+ return item;
386
+ }),
387
+ },
388
+ };
389
+
390
+ export const DrilldownMenu: Story = {
391
+ args: {
392
+ defaultItemColor: 'subtle',
393
+ groups: {
394
+ 'level-1': {
395
+ divider: true,
396
+ },
397
+ 'level-2': {
398
+ divider: true,
399
+ },
400
+ },
401
+ items: [
402
+ {
403
+ id: 'people',
404
+ groupId: 'level-1',
405
+ size: 'lg',
406
+ icon: 'menu-grid',
407
+ title: 'Alle skjema',
408
+ expanded: true,
409
+ items: [
410
+ {
411
+ groupId: 'level-2',
412
+ name: 'tema',
413
+ icon: 'teddy-bear',
414
+ title: 'Tema',
415
+ expanded: true,
416
+ items: [
417
+ {
418
+ id: 'c1',
419
+ groupId: 'level-3',
420
+ title: 'Kategori 1',
421
+ },
422
+ {
423
+ groupId: 'level-3',
424
+ id: 'c2',
425
+ title: 'Kategori 2',
426
+ },
427
+ {
428
+ groupId: 'level-3',
429
+ id: 'c3',
430
+ title: 'Kategori 3',
431
+ },
432
+ ],
433
+ },
434
+ ],
435
+ },
436
+ ],
437
+ },
438
+ };
@@ -0,0 +1,96 @@
1
+ 'use client';
2
+ import { Fragment } from 'react';
3
+ import { MenuHeader, MenuItem, MenuList, MenuListItem } from '../';
4
+ import type { MenuItemColor, MenuItemProps, MenuItemSize } from '../';
5
+ import { useMenu } from '../../hooks';
6
+
7
+ export interface MenuGroupProps {
8
+ title?: string;
9
+ divider?: boolean;
10
+ defaultItemColor?: MenuItemColor;
11
+ defaultItemSize?: MenuItemSize;
12
+ }
13
+
14
+ export type MenuItemGroups = Record<string, MenuGroupProps>;
15
+
16
+ export interface MenuItemsProps {
17
+ level?: number;
18
+ expanded?: boolean;
19
+ items: MenuItemProps[];
20
+ groups?: MenuItemGroups;
21
+ defaultItemColor?: MenuItemColor;
22
+ defaultItemSize?: MenuItemSize;
23
+ }
24
+
25
+ export const MenuItems = ({
26
+ level = 0,
27
+ expanded,
28
+ items,
29
+ groups = {},
30
+ defaultItemColor,
31
+ defaultItemSize,
32
+ }: MenuItemsProps) => {
33
+ const { menu } = useMenu<MenuItemProps, MenuGroupProps>({
34
+ items,
35
+ groups,
36
+ groupByKey: 'groupId',
37
+ keyboardEvents: false,
38
+ });
39
+
40
+ return (
41
+ <MenuList expanded={expanded}>
42
+ {menu.map((group, groupIndex) => {
43
+ const groupProps: MenuGroupProps = group?.props || {};
44
+ const { title, divider = true } = groupProps;
45
+ const nextGroup = menu[groupIndex + 1];
46
+
47
+ return (
48
+ <Fragment key={groupIndex}>
49
+ {/** Render a separator if this is a new group or a new level */}
50
+
51
+ {(level > 0 || groupIndex) && divider ? <MenuListItem role="separator" /> : ''}
52
+
53
+ {title && (
54
+ <MenuListItem>
55
+ <MenuHeader title={title} />
56
+ </MenuListItem>
57
+ )}
58
+
59
+ {group?.items.map((item, index) => {
60
+ const { active } = item;
61
+ const { groupId: _, ...itemProps } = item.props || {};
62
+ const { expanded } = itemProps;
63
+ const nextItem = group?.items[index + 1];
64
+
65
+ return (
66
+ <MenuListItem role="presentation" expanded={expanded} key={index}>
67
+ <MenuItem
68
+ {...itemProps}
69
+ color={itemProps?.color || groupProps?.defaultItemColor || defaultItemColor}
70
+ size={itemProps?.size || groupProps?.defaultItemSize || defaultItemSize}
71
+ active={active}
72
+ tabIndex={itemProps?.disabled ? -1 : 0}
73
+ />
74
+ {expanded && itemProps?.items && (
75
+ <>
76
+ <MenuItems
77
+ expanded={expanded}
78
+ level={level + 1}
79
+ items={itemProps?.items}
80
+ groups={groups}
81
+ defaultItemColor={defaultItemColor}
82
+ defaultItemSize={defaultItemSize}
83
+ />
84
+ {/** Render a separator if expanded and there are items underneath */}
85
+ {(nextGroup || nextItem) && <MenuListItem role="separator" />}
86
+ </>
87
+ )}
88
+ </MenuListItem>
89
+ );
90
+ })}
91
+ </Fragment>
92
+ );
93
+ })}
94
+ </MenuList>
95
+ );
96
+ };
@@ -1,4 +1,5 @@
1
1
  import type { ChangeEventHandler } from 'react';
2
+ import type { BadgeProps } from '../Badge';
2
3
  import { CheckboxIcon, RadioIcon } from '../Icon';
3
4
  import { MenuItemBase, type MenuItemSize } from './MenuItemBase';
4
5
  import { MenuItemLabel } from './MenuItemLabel';
@@ -14,6 +15,7 @@ export interface MenuOptionProps {
14
15
  name?: string;
15
16
  title?: string;
16
17
  description?: string;
18
+ badge?: BadgeProps;
17
19
  checked?: boolean;
18
20
  disabled?: boolean;
19
21
  onChange?: ChangeEventHandler;
@@ -28,12 +30,13 @@ export const MenuOption = ({
28
30
  label,
29
31
  title,
30
32
  description,
33
+ badge,
31
34
  checked = false,
32
35
  disabled,
33
36
  onChange,
34
37
  }: MenuOptionProps) => {
35
38
  return (
36
- <MenuItemBase className={styles.label} disabled={disabled} selected={checked} size={size} as="label">
39
+ <MenuItemBase className={styles.label} disabled={disabled} selected={checked} size={size} badge={badge} as="label">
37
40
  <input className={styles.input} name={name} value={value} type={type} checked={checked} onChange={onChange} />
38
41
  {type === 'checkbox' && <CheckboxIcon checked={checked} hover={true} className={styles.icon} />}
39
42
  {type === 'radio' && <RadioIcon checked={checked} hover={true} className={styles.icon} />}
@@ -4,7 +4,7 @@ export * from './MenuItemMedia';
4
4
  export * from './MenuItem';
5
5
  export * from './MenuOption';
6
6
  export * from './MenuSearch';
7
- export * from './MenuGroup';
8
7
  export * from './MenuHeader';
8
+ export * from './MenuItems';
9
9
  export * from './MenuBase';
10
10
  export * from './Menu';
@@ -3,16 +3,15 @@
3
3
  flex-direction: column;
4
4
  }
5
5
 
6
- .menu ul {
6
+ .group {
7
7
  list-style: none;
8
8
  padding: 0;
9
9
  margin: 0;
10
10
  text-indent: 0;
11
11
  }
12
12
 
13
- .menu li {
13
+ .menuItem {
14
14
  list-style: none;
15
- list-style-type: none;
16
15
  }
17
16
 
18
17
  .group[data-divider="true"] + .group {
@@ -0,0 +1,25 @@
1
+ .menu {
2
+ list-style: none;
3
+ padding: 0;
4
+ margin: 0;
5
+ text-indent: 0;
6
+
7
+ display: flex;
8
+ flex-direction: column;
9
+ }
10
+
11
+ .list {
12
+ list-style: none;
13
+ padding: 0;
14
+ margin: 0;
15
+ }
16
+
17
+ .item {
18
+ list-style: none;
19
+ padding: 0;
20
+ margin: 0;
21
+ }
22
+
23
+ .item[role="separator"] {
24
+ border-top: 1px solid var(--theme-border-subtle);
25
+ }
@@ -36,7 +36,12 @@
36
36
  padding: 10px;
37
37
  }
38
38
 
39
- .actionIcon {
39
+ .linkText {
40
+ font-size: 0.875rem;
41
+ white-space: nowrap;
42
+ }
43
+
44
+ .linkIcon {
40
45
  font-size: 1.5rem;
41
46
  }
42
47
 
@@ -47,14 +52,15 @@
47
52
  color: var(--theme-text-default);
48
53
  }
49
54
 
50
- .item:hover {
51
- background-color: var(--theme-surface-hover);
52
- }
53
-
54
55
  .item[aria-selected="true"] {
55
56
  background-color: var(--theme-background-default);
56
57
  }
57
58
 
59
+ .item:hover,
60
+ .item:active {
61
+ background-color: var(--theme-surface-hover);
62
+ }
63
+
58
64
  .media[data-color="subtle"] {
59
65
  background-color: var(--theme-background-default);
60
66
  color: var(--theme-text-default);