@altinn/altinn-components 0.4.1 → 0.5.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.
Files changed (178) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/biome.jsonc +1 -1
  3. package/lib/components/Attachment/AttachmentLink.stories.ts +1 -1
  4. package/lib/components/Attachment/AttachmentList.stories.ts +1 -1
  5. package/lib/components/Button/Button.tsx +13 -10
  6. package/lib/components/Button/ButtonBase.tsx +1 -1
  7. package/lib/components/Button/ButtonIcon.tsx +16 -0
  8. package/lib/components/Button/ButtonLabel.tsx +16 -0
  9. package/lib/components/Button/Buttons.stories.tsx +64 -0
  10. package/lib/components/Button/ComboButton.tsx +9 -7
  11. package/lib/components/Button/IconButton.stories.tsx +47 -0
  12. package/lib/components/Button/IconButton.tsx +15 -5
  13. package/lib/components/Button/button.module.css +5 -46
  14. package/lib/components/Button/buttonBase.module.css +55 -23
  15. package/lib/components/Button/buttonIcon.module.css +17 -0
  16. package/lib/components/Button/buttonLabel.module.css +17 -0
  17. package/lib/components/Button/comboButton.module.css +15 -65
  18. package/lib/components/Button/iconButton.module.css +21 -4
  19. package/lib/components/Button/index.ts +2 -0
  20. package/lib/components/ContextMenu/ContextMenu.stories.ts +49 -0
  21. package/lib/components/ContextMenu/ContextMenu.tsx +12 -20
  22. package/lib/components/ContextMenu/ContextMenuBase.tsx +33 -0
  23. package/lib/components/Dialog/Dialog.tsx +2 -0
  24. package/lib/components/Dialog/DialogGroup.tsx +24 -0
  25. package/lib/components/Dialog/DialogList.stories.ts +14 -10
  26. package/lib/components/Dialog/DialogList.tsx +26 -11
  27. package/lib/components/Dialog/DialogListItem.tsx +12 -2
  28. package/lib/components/Dialog/DialogListItemBase.tsx +4 -2
  29. package/lib/components/Dialog/DialogNav.stories.ts +5 -5
  30. package/lib/components/Dialog/DialogNav.tsx +2 -6
  31. package/lib/components/Dialog/DialogSelect.tsx +1 -1
  32. package/lib/components/Dialog/dialogGroup.module.css +35 -0
  33. package/lib/components/Dropdown/Backdrop.tsx +4 -3
  34. package/lib/components/Dropdown/DrawerBase.tsx +5 -2
  35. package/lib/components/Dropdown/DrawerBody.tsx +12 -0
  36. package/lib/components/Dropdown/DrawerButton.tsx +17 -0
  37. package/lib/components/Dropdown/DrawerFooter.tsx +12 -0
  38. package/lib/components/Dropdown/DrawerHeader.tsx +19 -0
  39. package/lib/components/Dropdown/DrawerOrDropdown.tsx +29 -0
  40. package/lib/components/Dropdown/DropdownBase.tsx +5 -2
  41. package/lib/components/Dropdown/backdrop.module.css +3 -0
  42. package/lib/components/Dropdown/drawerBase.module.css +9 -0
  43. package/lib/components/Dropdown/drawerBody.module.css +5 -0
  44. package/lib/components/Dropdown/drawerButton.module.css +6 -0
  45. package/lib/components/Dropdown/drawerFooter.module.css +13 -0
  46. package/lib/components/Dropdown/drawerHeader.module.css +17 -0
  47. package/lib/components/Dropdown/drawerOrDropdown.module.css +19 -0
  48. package/lib/components/Dropdown/dropdownBase.module.css +15 -3
  49. package/lib/components/Dropdown/index.ts +7 -1
  50. package/lib/components/Footer/footerMenu.module.css +5 -0
  51. package/lib/components/GlobalMenu/GlobalMenu.stories.tsx +9 -9
  52. package/lib/components/GlobalMenu/GlobalMenu.tsx +5 -5
  53. package/lib/components/Header/{Header.stories.ts → Header.stories.tsx} +79 -20
  54. package/lib/components/Header/Header.tsx +24 -37
  55. package/lib/components/Header/HeaderBase.tsx +7 -3
  56. package/lib/components/Header/header.module.css +10 -42
  57. package/lib/components/Header/headerBase.module.css +43 -0
  58. package/lib/components/Header/headerButton.module.css +1 -0
  59. package/lib/components/Layout/Layout.stories.tsx +77 -38
  60. package/lib/components/Layout/Layout.tsx +5 -3
  61. package/lib/components/Layout/LayoutBase.tsx +3 -2
  62. package/lib/components/Layout/layoutBase.module.css +11 -0
  63. package/lib/components/Layout/layoutBody.module.css +1 -0
  64. package/lib/components/LayoutAction/ActionHeader.tsx +1 -1
  65. package/lib/components/LayoutAction/ActionMenu.tsx +2 -4
  66. package/lib/components/LayoutAction/actionMenu.module.css +3 -0
  67. package/lib/components/List/List.stories.tsx +43 -0
  68. package/lib/components/List/List.tsx +6 -6
  69. package/lib/components/List/ListBase.tsx +6 -6
  70. package/lib/components/List/ListItem.tsx +4 -1
  71. package/lib/components/List/ListItemBase.tsx +20 -4
  72. package/lib/components/List/listBase.module.css +3 -3
  73. package/lib/components/List/listItemBase.module.css +4 -0
  74. package/lib/components/Menu/Menu.stories.ts +46 -46
  75. package/lib/components/Menu/Menu.tsx +3 -102
  76. package/lib/components/Menu/MenuBase.tsx +47 -3
  77. package/lib/components/Menu/MenuItem.tsx +6 -4
  78. package/lib/components/Menu/MenuItemBase.tsx +7 -0
  79. package/lib/components/Menu/MenuItems.stories.ts +438 -0
  80. package/lib/components/Menu/MenuItems.tsx +96 -0
  81. package/lib/components/Menu/MenuOption.tsx +4 -1
  82. package/lib/components/Menu/{MenuGroup.tsx → __MenuGroup.tsx} +1 -1
  83. package/lib/components/Menu/index.ts +1 -1
  84. package/lib/components/Menu/menu.module.css +2 -3
  85. package/lib/components/Menu/menuBase.module.css +25 -0
  86. package/lib/components/Menu/menuItemBase.module.css +5 -4
  87. package/lib/components/Meta/MetaItemBase.tsx +1 -1
  88. package/lib/components/Meta/MetaItemLabel.tsx +1 -1
  89. package/lib/components/Meta/MetaItemMedia.tsx +1 -1
  90. package/lib/components/Page/PageBase.tsx +14 -0
  91. package/lib/components/Page/PageHeader.tsx +21 -0
  92. package/lib/components/Page/PageHeaderMedia.tsx +25 -0
  93. package/lib/components/Page/SectionBase.tsx +52 -0
  94. package/lib/components/Page/SectionFooter.tsx +15 -0
  95. package/lib/components/Page/SectionHeader.tsx +16 -0
  96. package/lib/components/Page/index.ts +5 -0
  97. package/lib/components/Page/pageHeader.module.css +5 -0
  98. package/lib/components/Page/sectionBase.module.css +82 -0
  99. package/lib/components/Page/sectionFooter.module.css +8 -0
  100. package/lib/components/Page/sectionHeader.module.css +9 -0
  101. package/lib/components/RootProvider/RootProvider.tsx +43 -7
  102. package/lib/components/Searchbar/Autocomplete.stories.tsx +77 -0
  103. package/lib/components/Searchbar/Autocomplete.tsx +44 -0
  104. package/lib/components/Searchbar/AutocompleteBase.tsx +16 -0
  105. package/lib/components/Searchbar/AutocompleteGroup.tsx +17 -0
  106. package/lib/components/Searchbar/AutocompleteItem.tsx +23 -0
  107. package/lib/components/Searchbar/SearchField.tsx +78 -0
  108. package/lib/components/Searchbar/Searchbar.stories.tsx +151 -0
  109. package/lib/components/Searchbar/Searchbar.tsx +18 -0
  110. package/lib/components/Searchbar/SearchbarBase.tsx +23 -0
  111. package/lib/components/Searchbar/autocompleteBase.module.css +17 -0
  112. package/lib/components/Searchbar/autocompleteGroup.module.css +3 -0
  113. package/lib/components/Searchbar/autocompleteItem.module.css +19 -0
  114. package/lib/components/Searchbar/index.ts +1 -0
  115. package/lib/components/Searchbar/searchField.module.css +54 -0
  116. package/lib/components/Searchbar/searchbarBase.module.css +20 -0
  117. package/lib/components/Toolbar/Toolbar.stories.tsx +10 -10
  118. package/lib/components/Toolbar/Toolbar.tsx +28 -10
  119. package/lib/components/Toolbar/ToolbarAdd.tsx +6 -5
  120. package/lib/components/Toolbar/ToolbarBase.tsx +5 -20
  121. package/lib/components/Toolbar/ToolbarButton.tsx +1 -1
  122. package/lib/components/Toolbar/ToolbarFilter.tsx +11 -6
  123. package/lib/components/Toolbar/ToolbarMenu.tsx +8 -7
  124. package/lib/components/Toolbar/ToolbarOptions.stories.ts +5 -5
  125. package/lib/components/Toolbar/ToolbarOptions.tsx +34 -21
  126. package/lib/components/Toolbar/toolbarAdd.module.css +7 -0
  127. package/lib/components/Toolbar/toolbarBase.module.css +19 -0
  128. package/lib/components/Toolbar/toolbarButton.module.css +1 -1
  129. package/lib/components/Toolbar/toolbarFilter.module.css +25 -0
  130. package/lib/components/Toolbar/toolbarMenu.module.css +7 -0
  131. package/lib/components/Typography/Heading.tsx +23 -0
  132. package/lib/components/Typography/Typography.tsx +8 -5
  133. package/lib/components/Typography/heading.module.css +21 -0
  134. package/lib/components/Typography/index.ts +1 -0
  135. package/lib/components/Typography/typography.module.css +8 -0
  136. package/lib/components/index.ts +2 -0
  137. package/lib/hooks/index.ts +3 -0
  138. package/lib/{components/Menu → hooks}/useEscapeKey.ts +2 -2
  139. package/lib/hooks/useMenu.tsx +80 -0
  140. package/lib/index.ts +1 -0
  141. package/lib/stories/Color/MenuItem.stories.tsx +43 -0
  142. package/lib/stories/Color/Swatches.stories.tsx +19 -0
  143. package/lib/stories/Color/Swatches.tsx +42 -0
  144. package/lib/stories/Color/colors.json +62 -0
  145. package/lib/stories/Color/swatches.module.css +14 -0
  146. package/lib/stories/Inbox/BookmarksPage.tsx +52 -0
  147. package/lib/stories/Inbox/DialogPage.tsx +15 -0
  148. package/lib/stories/Inbox/Inbox.stories.tsx +55 -0
  149. package/lib/stories/Inbox/Inbox.tsx +12 -0
  150. package/lib/stories/Inbox/InboxLayout.tsx +50 -0
  151. package/lib/stories/Inbox/InboxPage.tsx +50 -0
  152. package/lib/stories/Inbox/InboxProvider.tsx +136 -0
  153. package/lib/stories/Inbox/InboxSection.tsx +39 -0
  154. package/lib/stories/Inbox/InboxToolbar.tsx +94 -0
  155. package/lib/stories/Inbox/ProfilePage.tsx +35 -0
  156. package/lib/stories/Inbox/SettingsPage.tsx +19 -0
  157. package/lib/stories/Inbox/accounts/accounts.ts +24 -0
  158. package/lib/stories/Inbox/accounts/index.ts +1 -0
  159. package/lib/stories/Inbox/actionMenu.ts +24 -0
  160. package/lib/stories/Inbox/dialogs/brreg-completed.json +35 -0
  161. package/lib/stories/Inbox/dialogs/brreg-draft.json +45 -0
  162. package/lib/stories/Inbox/dialogs/index.ts +10 -0
  163. package/lib/stories/Inbox/dialogs/skatt-2023.json +33 -0
  164. package/lib/stories/Inbox/groupBy.ts +19 -0
  165. package/lib/stories/Inbox/inboxSection.module.css +19 -0
  166. package/lib/stories/Inbox/index.ts +15 -0
  167. package/lib/stories/Inbox/layout/footer.ts +27 -0
  168. package/lib/stories/Inbox/layout/header.ts +11 -0
  169. package/lib/stories/Inbox/layout/index.ts +3 -0
  170. package/lib/stories/Inbox/layout/menu.ts +64 -0
  171. package/package.json +1 -1
  172. package/tsconfig.json +7 -2
  173. package/lib/components/Header/HeaderSearch.stories.ts +0 -20
  174. package/lib/components/Header/HeaderSearch.tsx +0 -44
  175. package/lib/components/Header/headerSearch.module.css +0 -30
  176. package/lib/components/Toolbar/toolbar.module.css +0 -43
  177. /package/lib/components/ContextMenu/{contextMenu.module.css → contextMenuBase.module.css} +0 -0
  178. /package/lib/{components/Menu → hooks}/useClickOutside.ts +0 -0
@@ -1,7 +1,4 @@
1
1
  .button {
2
- border: 1px solid;
3
- width: 2.75rem;
4
- height: 2.75rem;
5
2
  display: flex;
6
3
  align-items: center;
7
4
  justify-content: center;
@@ -10,5 +7,25 @@
10
7
 
11
8
  .icon {
12
9
  font-size: 1.5rem;
13
- margin: 0.625rem;
10
+ }
11
+
12
+ /* sm 36 */
13
+
14
+ .button[data-size="sm"] {
15
+ width: 2.25rem;
16
+ height: 2.25rem;
17
+ }
18
+
19
+ /* md 44 */
20
+
21
+ .button[data-size="md"] {
22
+ width: 2.75rem;
23
+ height: 2.75rem;
24
+ }
25
+
26
+ /* lg 56 */
27
+
28
+ .button[data-size="lg"] {
29
+ width: 3.5rem;
30
+ height: 3.5rem;
14
31
  }
@@ -1,4 +1,6 @@
1
1
  export * from './ButtonBase';
2
+ export * from './ButtonLabel';
3
+ export * from './ButtonIcon';
2
4
  export * from './Button';
3
5
  export * from './ComboButton';
4
6
  export * from './IconButton';
@@ -0,0 +1,49 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { ContextMenu } from './ContextMenu';
3
+
4
+ const meta = {
5
+ title: 'ContextMenu/ContextMenu',
6
+ component: ContextMenu,
7
+ tags: ['autodocs'],
8
+ parameters: {},
9
+ args: {
10
+ placement: 'left',
11
+ items: [
12
+ {
13
+ id: '1',
14
+ groupId: '1',
15
+ icon: 'arrow-redo',
16
+ label: 'Del og gi tilgang',
17
+ },
18
+ {
19
+ id: '2',
20
+ groupId: '1',
21
+ icon: 'eye-closed',
22
+ label: 'Marker som ny',
23
+ },
24
+ {
25
+ id: '3',
26
+ groupId: '2',
27
+ icon: 'archive',
28
+ label: 'Flytt til arkiv',
29
+ },
30
+ {
31
+ id: '4',
32
+ groupId: '2',
33
+ icon: 'trash',
34
+ label: 'Flytt til papirkurv',
35
+ },
36
+ {
37
+ id: '5',
38
+ groupId: '3',
39
+ icon: 'clock-dashed',
40
+ label: 'Aktivitetslogg',
41
+ },
42
+ ],
43
+ },
44
+ } satisfies Meta<typeof ContextMenu>;
45
+
46
+ export default meta;
47
+ type Story = StoryObj<typeof meta>;
48
+
49
+ export const Default: Story = {};
@@ -1,28 +1,20 @@
1
- import type { MouseEventHandler } from 'react';
2
- import { ButtonBase } from '../Button';
3
- import { Icon } from '../Icon';
4
- import { Menu, type MenuGroups, type MenuItemProps } from '../Menu';
5
- import styles from './contextMenu.module.css';
1
+ import type { DropdownPlacement, MenuItemProps } from '../';
2
+ import { type MenuItemGroups, MenuItems } from '../';
3
+ import { useRootContext } from '../RootProvider';
4
+ import { ContextMenuBase } from './ContextMenuBase';
6
5
 
7
6
  export interface ContextMenuProps {
8
- onToggle?: MouseEventHandler;
9
- label: string;
10
- value: string | number;
7
+ id: string;
11
8
  items: MenuItemProps[];
12
- groups?: MenuGroups;
13
- expanded?: boolean;
14
- className?: string;
9
+ placement?: DropdownPlacement;
10
+ groups?: MenuItemGroups;
15
11
  }
16
12
 
17
- export const ContextMenu = ({ expanded = true, onToggle, groups = {}, items }: ContextMenuProps) => {
13
+ export const ContextMenu = ({ id = 'context-menu', placement = 'right', groups = {}, items }: ContextMenuProps) => {
14
+ const { currentId, toggleId } = useRootContext();
18
15
  return (
19
- <div className={styles.toggle}>
20
- <ButtonBase className={styles.button} as="button" color="secondary" onClick={onToggle}>
21
- <Icon className={styles.icon} name="menu-elipsis-horizontal" />
22
- </ButtonBase>
23
- <div className={styles.dropdown} aria-expanded={expanded}>
24
- <Menu theme="global" defaultItemColor="subtle" groups={groups} items={items} />
25
- </div>
26
- </div>
16
+ <ContextMenuBase placement={placement} expanded={currentId === id} onToggle={() => toggleId(id)}>
17
+ <MenuItems groups={groups} items={items} />
18
+ </ContextMenuBase>
27
19
  );
28
20
  };
@@ -0,0 +1,33 @@
1
+ import type { ReactNode } from 'react';
2
+ import { DropdownBase, IconButton } from '../';
3
+ import type { DropdownPlacement } from '../';
4
+ import styles from './contextMenuBase.module.css';
5
+
6
+ export interface ContextMenuBaseProps {
7
+ placement: DropdownPlacement;
8
+ expanded: boolean;
9
+ onToggle?: () => void;
10
+ children?: ReactNode;
11
+ }
12
+
13
+ export const ContextMenuBase = ({
14
+ placement = 'right',
15
+ expanded = false,
16
+ onToggle,
17
+ children,
18
+ }: ContextMenuBaseProps) => {
19
+ return (
20
+ <div className={styles.toggle}>
21
+ <IconButton
22
+ className={styles.button}
23
+ icon="menu-elipsis-horizontal"
24
+ variant="text"
25
+ color="secondary"
26
+ onClick={onToggle}
27
+ />
28
+ <DropdownBase className={styles.dropdown} placement={placement} expanded={expanded}>
29
+ {children}
30
+ </DropdownBase>
31
+ </div>
32
+ );
33
+ };
@@ -19,6 +19,8 @@ import { type DialogBackButtonProps, DialogNav } from './DialogNav';
19
19
  import type { DialogStatusProps } from './DialogStatus';
20
20
 
21
21
  export interface DialogProps {
22
+ /** Dialog id */
23
+ id: string;
22
24
  /** Title */
23
25
  title: string;
24
26
  /** Back button */
@@ -0,0 +1,24 @@
1
+ import type { ReactNode } from 'react';
2
+ import { Heading, ListBase, SectionBase, SectionHeader } from '../';
3
+ import { Button } from '../Button';
4
+
5
+ export interface DialogGroupProps {
6
+ title?: string;
7
+ children?: ReactNode;
8
+ }
9
+
10
+ export const DialogGroup = ({ title, children }: DialogGroupProps) => {
11
+ return (
12
+ <SectionBase spacing="md">
13
+ {title && (
14
+ <SectionHeader padding margin>
15
+ <Heading>{title}</Heading>
16
+ <Button variant="text" icon="checkmark" size="sm" reverse>
17
+ Velg alle
18
+ </Button>
19
+ </SectionHeader>
20
+ )}
21
+ <ListBase spacing="md">{children}</ListBase>
22
+ </SectionBase>
23
+ );
24
+ };
@@ -1,6 +1,4 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react';
2
- import { fn } from '@storybook/test';
3
-
4
2
  import { DialogList } from './DialogList';
5
3
 
6
4
  const meta = {
@@ -16,26 +14,31 @@ const meta = {
16
14
  title: 'Støtte til utbygging av solceller',
17
15
  summary: 'Din støtte er innvilget',
18
16
  status: { value: 'draft' },
17
+ groupId: '2024-01',
19
18
  },
20
19
  {
21
20
  title: 'Støtte til utbygging av solceller',
22
21
  summary: 'Din støtte er innvilget',
23
22
  status: { value: 'sent' },
23
+ groupId: '2024-02',
24
24
  },
25
25
  {
26
26
  title: 'Støtte til utbygging av solceller',
27
27
  summary: 'Din støtte er innvilget',
28
28
  status: { value: 'requires-attention' },
29
+ groupId: '2024-01',
29
30
  },
30
31
  {
31
32
  title: 'Støtte til utbygging av solceller',
32
33
  summary: 'Din støtte er innvilget',
33
34
  status: { value: 'in-progress' },
35
+ groupId: '2024-02',
34
36
  },
35
37
  {
36
38
  title: 'Støtte til utbygging av solceller',
37
39
  summary: 'Din støtte er innvilget.',
38
40
  status: { value: 'completed' },
41
+ groupId: '2024-01',
39
42
  },
40
43
  ],
41
44
  },
@@ -48,14 +51,15 @@ export const Default: Story = {
48
51
  args: {},
49
52
  };
50
53
 
51
- export const Company: Story = {
54
+ export const Grouped: Story = {
52
55
  args: {
53
- theme: 'company',
54
- },
55
- };
56
-
57
- export const Person: Story = {
58
- args: {
59
- theme: 'person',
56
+ groups: {
57
+ '2024-01': {
58
+ title: 'Januar 2024',
59
+ },
60
+ '2024-02': {
61
+ title: 'Februar 2024',
62
+ },
63
+ },
60
64
  },
61
65
  };
@@ -1,20 +1,35 @@
1
- import type { LayoutTheme } from '../Layout';
2
- import { ListBase } from '../List';
1
+ import { SectionBase } from '../';
2
+ import { useMenu } from '../../hooks';
3
+ import { DialogGroup, type DialogGroupProps } from './DialogGroup';
3
4
  import { DialogListItem, type DialogListItemProps } from './DialogListItem';
4
- import type { DialogListItemSize } from './DialogListItemBase';
5
5
 
6
6
  export interface DialogListProps {
7
- size?: DialogListItemSize;
8
- theme?: LayoutTheme;
9
- items?: DialogListItemProps[];
7
+ items: DialogListItemProps[];
8
+ groups?: Record<string, DialogGroupProps>;
10
9
  }
11
10
 
12
- export const DialogList = ({ theme, size = 'md', items }: DialogListProps) => {
11
+ export const DialogList = ({ items, groups = {} }: DialogListProps) => {
12
+ const { menu } = useMenu<DialogListItemProps, DialogGroupProps>({
13
+ items,
14
+ groups,
15
+ groupByKey: 'groupId',
16
+ keyboardEvents: false,
17
+ });
18
+
13
19
  return (
14
- <ListBase theme={theme} size={size}>
15
- {items?.map((item, index) => {
16
- return <DialogListItem {...item} size={size} key={'item' + index} />;
20
+ <SectionBase spacing="none" margin="md">
21
+ {menu?.map((group, groupIndex) => {
22
+ const groupProps = group.props || {};
23
+
24
+ return (
25
+ <DialogGroup {...groupProps} key={groupIndex}>
26
+ {group?.items.map((item, index) => {
27
+ const itemProps = item.props || {};
28
+ return <DialogListItem {...itemProps} key={index} />;
29
+ })}
30
+ </DialogGroup>
31
+ );
17
32
  })}
18
- </ListBase>
33
+ </SectionBase>
19
34
  );
20
35
  };
@@ -55,6 +55,8 @@ export type DialogListItemProps = {
55
55
  attachmentsCount?: number;
56
56
  /** OnClick handler */
57
57
  onClick?: () => void;
58
+ /** Group id */
59
+ groupId?: string;
58
60
  };
59
61
 
60
62
  /**
@@ -68,6 +70,7 @@ export const DialogListItem = ({
68
70
  size = 'lg',
69
71
  variant = 'neutral',
70
72
  href,
73
+ onClick,
71
74
  select,
72
75
  selected,
73
76
  status,
@@ -84,10 +87,17 @@ export const DialogListItem = ({
84
87
  attachmentsCount,
85
88
  title,
86
89
  summary,
87
- ...rest
88
90
  }: DialogListItemProps) => {
89
91
  return (
90
- <DialogListItemBase as={as} size={size} href={href} select={select} selected={selected} variant={variant} {...rest}>
92
+ <DialogListItemBase
93
+ as={as}
94
+ size={size}
95
+ href={href}
96
+ select={select}
97
+ selected={selected}
98
+ variant={variant}
99
+ onClick={onClick}
100
+ >
91
101
  <DialogBorder className={styles.border} size={size} seen={seen}>
92
102
  <header data-size={size} className={styles.header}>
93
103
  <DialogTitle size={size} seen={seen} variant={variant}>
@@ -20,6 +20,8 @@ export type DialogListItemBaseProps = {
20
20
  children?: ReactNode;
21
21
  /** Variant */
22
22
  variant?: DialogListItemVariant;
23
+ /** OnClick handler */
24
+ onClick?: () => void;
23
25
  };
24
26
 
25
27
  /**
@@ -35,13 +37,13 @@ export const DialogListItemBase = ({
35
37
  select,
36
38
  selected,
37
39
  children,
38
- ...rest
40
+ onClick,
39
41
  }: DialogListItemBaseProps) => {
40
42
  const Component = as || 'button';
41
43
 
42
44
  return (
43
45
  <article className={styles.item} data-size={size} aria-selected={selected}>
44
- <Component className={styles.link} data-size={size} href={href} {...rest}>
46
+ <Component className={styles.link} data-size={size} href={href} onClick={onClick} tabIndex={0}>
45
47
  {children}
46
48
  </Component>
47
49
  {select && <DialogSelect className={styles.select} {...select} />}
@@ -54,31 +54,31 @@ export const ContextMenu: Story = {
54
54
  items: [
55
55
  {
56
56
  id: '1',
57
- group: '1',
57
+ groupId: '1',
58
58
  icon: 'arrow-redo',
59
59
  label: 'Del og gi tilgang',
60
60
  },
61
61
  {
62
62
  id: '2',
63
- group: '1',
63
+ groupId: '1',
64
64
  icon: 'eye-closed',
65
65
  label: 'Marker som ny',
66
66
  },
67
67
  {
68
68
  id: '3',
69
- group: '2',
69
+ groupId: '2',
70
70
  icon: 'archive',
71
71
  label: 'Flytt til arkiv',
72
72
  },
73
73
  {
74
74
  id: '4',
75
- group: '2',
75
+ groupId: '2',
76
76
  icon: 'trash',
77
77
  label: 'Flytt til papirkurv',
78
78
  },
79
79
  {
80
80
  id: '5',
81
- group: '3',
81
+ groupId: '3',
82
82
  icon: 'clock-dashed',
83
83
  label: 'Aktivitetslogg',
84
84
  },
@@ -1,8 +1,7 @@
1
1
  'use client';
2
- import { useState } from 'react';
3
2
  import type { ElementType } from 'react';
4
3
  import { Button } from '../Button';
5
- import { ContextMenu, type ContextMenuProps } from '../ContextMenu/ContextMenu.tsx';
4
+ import { ContextMenu, type ContextMenuProps } from '../ContextMenu';
6
5
  import { MetaTimestamp } from '../Meta';
7
6
  import { DialogStatus, type DialogStatusProps } from './DialogStatus';
8
7
  import { DialogTouchedBy, type DialogTouchedByActor } from './DialogTouchedBy';
@@ -37,9 +36,6 @@ export const DialogNav = ({
37
36
  touchedBy,
38
37
  menu,
39
38
  }: DialogNavProps) => {
40
- const [expandedItem, setexpandedItem] = useState<boolean>(false);
41
- const onToggle = () => setexpandedItem((expanded) => !expanded);
42
-
43
39
  return (
44
40
  <nav className={styles.nav}>
45
41
  <Button {...backButton} variant="text" color="secondary" icon="arrow-left" reverse>
@@ -53,7 +49,7 @@ export const DialogNav = ({
53
49
  )}
54
50
  {status && <DialogStatus {...status} />}
55
51
  {touchedBy && <DialogTouchedBy touchedBy={touchedBy} />}
56
- {menu && <ContextMenu {...menu} expanded={expandedItem} onToggle={onToggle} />}
52
+ {menu && <ContextMenu {...menu} />}
57
53
  </div>
58
54
  </nav>
59
55
  );
@@ -16,7 +16,7 @@ export type DialogSelectProps = {
16
16
  export const DialogSelect = ({ checked = false, onChange, className }: DialogSelectProps) => {
17
17
  return (
18
18
  <label className={cx(styles.label, className)}>
19
- <input type="checkbox" checked={checked} onChange={onChange} className={styles.input} />
19
+ <input type="checkbox" checked={checked} onChange={onChange} className={styles.input} tabIndex={-1} />
20
20
  <CheckboxIcon hover={true} checked={checked} className={styles.icon} />
21
21
  </label>
22
22
  );
@@ -0,0 +1,35 @@
1
+ .section {
2
+ display: flex;
3
+ flex-direction: column;
4
+ row-gap: 0.5rem;
5
+ margin: 0.5rem 0;
6
+ }
7
+
8
+ .header {
9
+ display: flex;
10
+ justify-content: space-between;
11
+ margin: 0.5rem 0;
12
+ }
13
+
14
+ .title {
15
+ font-size: 1.25rem;
16
+ font-weight: 500;
17
+ line-height: 1.5rem;
18
+ margin: 0.375rem 0;
19
+ }
20
+
21
+ .title {
22
+ padding: 0 1rem;
23
+ }
24
+
25
+ @media (min-width: 1024px) {
26
+ .title {
27
+ padding: 0;
28
+ }
29
+ }
30
+
31
+ .list {
32
+ display: flex;
33
+ flex-direction: column;
34
+ row-gap: 0.5rem;
35
+ }
@@ -1,11 +1,12 @@
1
1
  import cx from 'classnames';
2
+ import type { MouseEventHandler } from 'react';
2
3
  import styles from './backdrop.module.css';
3
4
 
4
5
  export interface BackdropProps {
5
- expanded?: boolean;
6
6
  className?: string;
7
+ onClick?: MouseEventHandler;
7
8
  }
8
9
 
9
- export const Backdrop = ({ expanded = false, className }: BackdropProps) => {
10
- return <div className={cx(styles.backdrop, className)} aria-expanded={expanded} />;
10
+ export const Backdrop = ({ className, onClick }: BackdropProps) => {
11
+ return <div className={cx(styles.backdrop, className)} onMouseDown={onClick} />;
11
12
  };
@@ -2,15 +2,18 @@ import cx from 'classnames';
2
2
  import type { ReactNode } from 'react';
3
3
  import styles from './drawerBase.module.css';
4
4
 
5
+ export type DrawerPlacement = 'inline' | 'bottom';
6
+
5
7
  export interface DrawerBaseProps {
8
+ placement?: DrawerPlacement;
6
9
  expanded?: boolean;
7
10
  className?: string;
8
11
  children?: ReactNode;
9
12
  }
10
13
 
11
- export const DrawerBase = ({ expanded = false, className, children }: DrawerBaseProps) => {
14
+ export const DrawerBase = ({ placement = 'inline', expanded = false, className, children }: DrawerBaseProps) => {
12
15
  return (
13
- <div className={cx(styles.drawer, className)} aria-expanded={expanded}>
16
+ <div className={cx(styles.drawer, className)} data-placement={placement} aria-expanded={expanded}>
14
17
  {children}
15
18
  </div>
16
19
  );
@@ -0,0 +1,12 @@
1
+ import cx from 'classnames';
2
+ import type { ReactNode } from 'react';
3
+ import styles from './drawerBody.module.css';
4
+
5
+ export interface DrawerBodyProps {
6
+ className?: string;
7
+ children?: ReactNode;
8
+ }
9
+
10
+ export const DrawerBody = ({ className, children }: DrawerBodyProps) => {
11
+ return <div className={cx(styles.body, className)}>{children}</div>;
12
+ };
@@ -0,0 +1,17 @@
1
+ import type { MouseEventHandler, ReactNode } from 'react';
2
+ import { ButtonBase, ButtonLabel } from '../';
3
+ import styles from './drawerButton.module.css';
4
+
5
+ export interface DrawerButtonProps {
6
+ label?: string;
7
+ children?: ReactNode;
8
+ onClick?: MouseEventHandler;
9
+ }
10
+
11
+ export const DrawerButton = ({ label, children, onClick }: DrawerButtonProps) => {
12
+ return (
13
+ <ButtonBase variant="solid" size="md" className={styles.button} onClick={onClick}>
14
+ <ButtonLabel size="md">{children || label}</ButtonLabel>
15
+ </ButtonBase>
16
+ );
17
+ };
@@ -0,0 +1,12 @@
1
+ import cx from 'classnames';
2
+ import type { ReactNode } from 'react';
3
+ import styles from './drawerFooter.module.css';
4
+
5
+ export interface DrawerFooterProps {
6
+ className?: string;
7
+ children?: ReactNode;
8
+ }
9
+
10
+ export const DrawerFooter = ({ className, children }: DrawerFooterProps) => {
11
+ return <footer className={cx(styles.footer, className)}>{children}</footer>;
12
+ };
@@ -0,0 +1,19 @@
1
+ import cx from 'classnames';
2
+ import type { MouseEventHandler } from 'react';
3
+ import { IconButton } from '../Button';
4
+ import styles from './drawerHeader.module.css';
5
+
6
+ export interface DrawerHeaderProps {
7
+ className?: string;
8
+ title?: string;
9
+ onClose?: MouseEventHandler;
10
+ }
11
+
12
+ export const DrawerHeader = ({ className, title, onClose }: DrawerHeaderProps) => {
13
+ return (
14
+ <header className={cx(styles.header, className)}>
15
+ <h2 className={styles.title}>{title}</h2>
16
+ <IconButton size="sm" icon="x-mark" variant="outline" onClick={onClose} className={styles.close} />
17
+ </header>
18
+ );
19
+ };
@@ -0,0 +1,29 @@
1
+ import type { MouseEventHandler, ReactNode } from 'react';
2
+ import { DrawerBase, DrawerBody, DrawerButton, DrawerFooter, DrawerHeader, DropdownBase } from '../';
3
+ import type { DrawerButtonProps } from '../';
4
+ import { Backdrop } from './Backdrop';
5
+ import styles from './drawerOrDropdown.module.css';
6
+
7
+ export interface DrawerOrDropdownProps {
8
+ title: string;
9
+ children: ReactNode;
10
+ expanded?: boolean;
11
+ onClose?: MouseEventHandler;
12
+ button?: DrawerButtonProps;
13
+ }
14
+
15
+ export const DrawerOrDropdown = ({ expanded = false, title, onClose, button, children }: DrawerOrDropdownProps) => {
16
+ return (
17
+ <>
18
+ {expanded && <Backdrop onClick={onClose} />}
19
+ <DropdownBase className={styles.dropdown} expanded={expanded}>
20
+ {children}
21
+ </DropdownBase>
22
+ <DrawerBase className={styles.drawer} placement="bottom" expanded={expanded}>
23
+ <DrawerHeader title={title} onClose={onClose} />
24
+ <DrawerBody>{children}</DrawerBody>
25
+ <DrawerFooter>{button && <DrawerButton onClick={button?.onClick}>{button?.label}</DrawerButton>}</DrawerFooter>
26
+ </DrawerBase>
27
+ </>
28
+ );
29
+ };
@@ -2,15 +2,18 @@ import cx from 'classnames';
2
2
  import type { ReactNode } from 'react';
3
3
  import styles from './dropdownBase.module.css';
4
4
 
5
+ export type DropdownPlacement = 'left' | 'right';
6
+
5
7
  export interface DropdownBaseProps {
8
+ placement?: DropdownPlacement;
6
9
  expanded?: boolean;
7
10
  className?: string;
8
11
  children?: ReactNode;
9
12
  }
10
13
 
11
- export const DropdownBase = ({ expanded = false, className, children }: DropdownBaseProps) => {
14
+ export const DropdownBase = ({ placement = 'left', expanded = false, className, children }: DropdownBaseProps) => {
12
15
  return (
13
- <div className={cx(styles.dropdown, className)} aria-expanded={expanded}>
16
+ <div className={cx(styles.dropdown, className)} data-placement={placement} aria-expanded={expanded}>
14
17
  {children}
15
18
  </div>
16
19
  );
@@ -1,8 +1,11 @@
1
1
  .backdrop {
2
+ z-index: 1;
2
3
  background-color: rgba(17, 29, 70, 0.25);
3
4
  position: fixed;
4
5
  top: 0;
5
6
  right: 0;
6
7
  bottom: 0;
7
8
  left: 0;
9
+ width: 100%;
10
+ height: 100%;
8
11
  }