@altinn/altinn-components 0.5.1 → 0.6.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 (78) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/lib/components/Bookmarks/BookmarksList.tsx +17 -0
  3. package/lib/components/Bookmarks/BookmarksListItem.stories.tsx +79 -0
  4. package/lib/components/Bookmarks/BookmarksListItem.tsx +32 -0
  5. package/lib/components/Bookmarks/QueryLabel.tsx +33 -0
  6. package/lib/components/Bookmarks/index.ts +3 -0
  7. package/lib/components/Bookmarks/queryLabel.module.css +45 -0
  8. package/lib/components/Button/ButtonBase.tsx +2 -1
  9. package/lib/components/Button/IconButton.tsx +3 -1
  10. package/lib/components/ContextMenu/ContextMenuBase.tsx +3 -1
  11. package/lib/components/Datepicker/Datepicker.tsx +49 -0
  12. package/lib/components/Datepicker/DatepickerBase.tsx +12 -0
  13. package/lib/components/Datepicker/DatepickerHeader.tsx +20 -0
  14. package/lib/components/Datepicker/DatepickerTable.tsx +50 -0
  15. package/lib/components/Datepicker/datepickerBase.module.css +5 -0
  16. package/lib/components/Datepicker/datepickerHeader.module.css +17 -0
  17. package/lib/components/Datepicker/datepickerTable.module.css +37 -0
  18. package/lib/components/Datepicker/index.ts +1 -0
  19. package/lib/components/Datepicker/useDatepicker.tsx +146 -0
  20. package/lib/components/Dropdown/DrawerOrDropdown.tsx +1 -1
  21. package/lib/components/Dropdown/dropdownBase.module.css +2 -1
  22. package/lib/components/Footer/FooterMenu.tsx +1 -1
  23. package/lib/components/GlobalMenu/AccountButton.tsx +20 -3
  24. package/lib/components/GlobalMenu/AccountMenu.tsx +13 -4
  25. package/lib/components/GlobalMenu/GlobalMenu.stories.tsx +6 -6
  26. package/lib/components/GlobalMenu/GlobalMenu.tsx +36 -24
  27. package/lib/components/Header/Header.stories.tsx +24 -8
  28. package/lib/components/Header/Header.tsx +15 -16
  29. package/lib/components/Header/HeaderButton.tsx +1 -0
  30. package/lib/components/Header/HeaderSearch.tsx +17 -0
  31. package/lib/components/Header/header.module.css +0 -21
  32. package/lib/components/Header/headerSearch.module.css +21 -0
  33. package/lib/components/History/HistoryList.stories.ts +3 -1
  34. package/lib/components/Layout/Layout.stories.tsx +7 -2
  35. package/lib/components/List/ListItemBase.tsx +4 -0
  36. package/lib/components/List/listBase.module.css +14 -0
  37. package/lib/components/List/listItemBase.module.css +9 -2
  38. package/lib/components/Menu/MenuInputField.tsx +38 -0
  39. package/lib/components/Menu/MenuItemBase.tsx +1 -1
  40. package/lib/components/Menu/MenuOption.tsx +1 -1
  41. package/lib/components/Menu/index.ts +1 -0
  42. package/lib/components/Menu/menuInputField.module.css +54 -0
  43. package/lib/components/Menu/menuItemBase.module.css +7 -0
  44. package/lib/components/Page/PageHeader.tsx +10 -6
  45. package/lib/components/Page/PageNav.tsx +34 -0
  46. package/lib/components/Page/pageHeader.module.css +13 -0
  47. package/lib/components/Page/pageNav.module.css +12 -0
  48. package/lib/components/Page/sectionBase.module.css +11 -1
  49. package/lib/components/RootProvider/RootProvider.tsx +1 -0
  50. package/lib/components/Searchbar/SearchField.tsx +4 -1
  51. package/lib/components/Searchbar/Searchbar.tsx +10 -2
  52. package/lib/components/Toolbar/Toolbar.tsx +4 -1
  53. package/lib/components/Toolbar/ToolbarAdd.tsx +3 -3
  54. package/lib/components/Toolbar/ToolbarDate.stories.ts +62 -0
  55. package/lib/components/Toolbar/ToolbarDaterange.stories.ts +24 -0
  56. package/lib/components/Toolbar/ToolbarDaterange.tsx +73 -0
  57. package/lib/components/Toolbar/ToolbarFilter.tsx +12 -5
  58. package/lib/components/Toolbar/ToolbarFilterBase.tsx +15 -0
  59. package/lib/components/Toolbar/ToolbarMenu.tsx +3 -3
  60. package/lib/components/Toolbar/ToolbarOptions.tsx +2 -2
  61. package/lib/components/Toolbar/toolbarDaterange.module.css +12 -0
  62. package/lib/components/Toolbar/{toolbarAdd.module.css → toolbarFilterBase.module.css} +2 -2
  63. package/lib/components/Typography/heading.module.css +1 -1
  64. package/lib/components/index.ts +3 -1
  65. package/lib/css/global.css +1 -0
  66. package/lib/hooks/useMenu.tsx +1 -0
  67. package/lib/stories/Inbox/BookmarksPage.tsx +38 -12
  68. package/lib/stories/Inbox/InboxLayout.tsx +1 -0
  69. package/lib/stories/Inbox/InboxPage.tsx +1 -0
  70. package/lib/stories/Inbox/InboxProvider.tsx +1 -1
  71. package/lib/stories/Inbox/InboxToolbar.tsx +3 -3
  72. package/lib/stories/Inbox/ProfilePage.tsx +1 -0
  73. package/lib/stories/Inbox/dialogs/brreg-draft.json +5 -9
  74. package/lib/stories/Inbox/dialogs/enova-in-progress.json +35 -0
  75. package/lib/stories/Inbox/dialogs/index.ts +44 -1
  76. package/lib/stories/Inbox/dialogs/sykmelding-referat.json +41 -0
  77. package/package.json +2 -2
  78. package/lib/components/Toolbar/toolbarFilter.module.css +0 -25
@@ -1,6 +1,7 @@
1
1
  import { MenuItemBase, MenuItemLabel, MenuItemMedia } from '../Menu';
2
2
 
3
- type Account = {
3
+ export type Account = {
4
+ id: string;
4
5
  type: 'person' | 'company';
5
6
  name: string;
6
7
  description?: string;
@@ -11,11 +12,27 @@ export type AccountButtonProps = {
11
12
  description?: string;
12
13
  linkText?: string;
13
14
  onClick?: () => void;
15
+ multipleAccounts?: boolean;
14
16
  };
15
17
 
16
- export const AccountButton = ({ account, linkText, onClick }: AccountButtonProps) => {
18
+ export const AccountButton = ({ account, linkText, onClick, multipleAccounts }: AccountButtonProps) => {
19
+ if (multipleAccounts) {
20
+ return (
21
+ <MenuItemBase size="lg" onClick={onClick} linkText={linkText} linkIcon="arrow-right" as="button">
22
+ <MenuItemMedia
23
+ size="lg"
24
+ avatar={{
25
+ name: account.name,
26
+ type: account.type,
27
+ }}
28
+ />
29
+ <MenuItemLabel size="lg" title={account?.name} description={account?.description} />
30
+ </MenuItemBase>
31
+ );
32
+ }
33
+
17
34
  return (
18
- <MenuItemBase size="lg" onClick={onClick} linkText={linkText} linkIcon="arrow-right">
35
+ <MenuItemBase size="lg" as="div">
19
36
  <MenuItemMedia
20
37
  size="lg"
21
38
  avatar={{
@@ -10,7 +10,7 @@ export interface AccountSearch extends MenuSearchProps {
10
10
  export interface AccountMenuItem {
11
11
  type: 'person' | 'company';
12
12
  name: string;
13
- id?: string;
13
+ id: string;
14
14
  groupId?: string;
15
15
  selected?: boolean;
16
16
  }
@@ -19,20 +19,29 @@ export interface AccountMenuProps {
19
19
  accounts?: AccountMenuItem[];
20
20
  accountGroups?: MenuItemGroups;
21
21
  accountSearch?: AccountSearch;
22
+ currentAccount?: AccountMenuItem;
23
+ onSelectAccount?: (id: string) => void;
22
24
  }
23
25
 
24
26
  const defaultResultLabel = (hits: number) => `${hits} hits`;
25
27
 
26
- export const AccountMenu = ({ accounts = [], accountGroups = {}, accountSearch }: AccountMenuProps) => {
28
+ export const AccountMenu = ({
29
+ accounts = [],
30
+ accountGroups = {},
31
+ accountSearch,
32
+ onSelectAccount,
33
+ currentAccount,
34
+ }: AccountMenuProps) => {
27
35
  const accountMenu: MenuItemProps[] = accounts.map((account) => ({
28
- id: account.name,
36
+ id: account.id || account.name,
29
37
  groupId: account.groupId || 'search',
30
- selected: account.selected,
38
+ selected: account.selected ?? currentAccount?.id === account.id,
31
39
  title: account.name,
32
40
  avatar: {
33
41
  type: account.type,
34
42
  name: account.name,
35
43
  },
44
+ onClick: () => onSelectAccount?.(account.id || account.name),
36
45
  }));
37
46
 
38
47
  const [filterString, setFilterString] = useState<string>('');
@@ -23,40 +23,40 @@ const meta = {
23
23
  },
24
24
  accounts: [
25
25
  {
26
+ id: 'party:mathias',
26
27
  groupId: 'primary',
27
28
  type: 'person',
28
29
  name: 'Mathias Dyngeland',
29
- selected: true,
30
30
  },
31
31
  {
32
+ id: 'party:bergerbar',
32
33
  groupId: 'favourites',
33
34
  type: 'company',
34
35
  name: 'Bergen bar',
35
- selected: false,
36
36
  },
37
37
  {
38
+ id: 'party:keeperhansker',
38
39
  groupId: 'secondary',
39
40
  type: 'company',
40
41
  name: 'Keeperhansker AS',
41
- selected: false,
42
42
  },
43
43
  {
44
+ id: 'party:stadiondrift',
44
45
  groupId: 'secondary',
45
46
  type: 'company',
46
47
  name: 'Stadion drift AS',
47
- selected: false,
48
48
  },
49
49
  {
50
+ id: 'party:brann',
50
51
  groupId: 'favourites',
51
52
  type: 'company',
52
53
  name: 'Sportsklubben Brann',
53
- selected: false,
54
54
  },
55
55
  {
56
+ id: 'party:landslaget',
56
57
  groupId: 'secondary',
57
58
  type: 'company',
58
59
  name: 'Landslaget',
59
- selected: false,
60
60
  },
61
61
  ],
62
62
  groups: {
@@ -1,67 +1,79 @@
1
1
  'use client';
2
- import { type MouseEventHandler, useState } from 'react';
2
+ import { useState } from 'react';
3
3
  import { Menu, type MenuItemGroups, type MenuItemProps } from '../Menu';
4
- import { AccountButton } from './AccountButton';
4
+ import { type Account, AccountButton } from './AccountButton';
5
5
  import { AccountMenu, type AccountMenuProps } from './AccountMenu';
6
6
  import { BackButton } from './BackButton';
7
7
  import { GlobalMenuBase, GlobalMenuFooter, GlobalMenuHeader } from './GlobalMenuBase';
8
- import { LogoutButton } from './LogoutButton';
9
-
10
- export interface CurrentAccount {
11
- type: 'person' | 'company';
12
- name: string;
13
- description?: string;
14
- }
8
+ import { LogoutButton, type LogoutButtonProps } from './LogoutButton';
15
9
 
16
10
  export interface GlobalMenuProps extends AccountMenuProps {
17
- currentEndUser?: CurrentAccount;
18
- expanded: boolean;
19
- onToggle: MouseEventHandler;
20
11
  items: MenuItemProps[];
21
12
  groups?: MenuItemGroups;
22
13
  menuLabel?: string;
23
14
  backLabel?: string;
15
+ logoutButton?: LogoutButtonProps;
24
16
  changeLabel?: string;
25
- logoutLabel?: string;
26
17
  className?: string;
18
+ currentAccount?: Account;
19
+ changeCurrentAccount?: (id: string) => void;
27
20
  }
28
21
 
29
22
  export const GlobalMenu = ({
30
- currentEndUser,
31
23
  accounts = [],
32
24
  accountGroups = {},
33
25
  accountSearch,
34
26
  items = [],
35
27
  groups,
36
28
  changeLabel = 'Change',
37
- logoutLabel = 'Logout',
38
29
  backLabel = 'Back',
30
+ currentAccount,
31
+ changeCurrentAccount,
32
+ logoutButton,
39
33
  }: GlobalMenuProps) => {
40
- const [selectAccount, setSelectAccount] = useState<boolean>(false);
34
+ const [selectingAccount, setSelectingAccount] = useState<boolean>(false);
41
35
 
42
36
  const onToggleAccounts = () => {
43
- setSelectAccount((prevState) => !prevState);
37
+ setSelectingAccount((prevState) => !prevState);
38
+ };
39
+
40
+ const onSelectAccount = (id: string) => {
41
+ onToggleAccounts();
42
+ changeCurrentAccount?.(id);
44
43
  };
45
44
 
46
- if (selectAccount) {
45
+ if (selectingAccount) {
47
46
  return (
48
47
  <GlobalMenuBase>
49
48
  <BackButton onClick={onToggleAccounts} label={backLabel} />
50
- <AccountMenu accounts={accounts} accountGroups={accountGroups} accountSearch={accountSearch} />
49
+ <AccountMenu
50
+ currentAccount={currentAccount}
51
+ accounts={accounts}
52
+ accountGroups={accountGroups}
53
+ accountSearch={accountSearch}
54
+ onSelectAccount={onSelectAccount}
55
+ />
51
56
  </GlobalMenuBase>
52
57
  );
53
58
  }
54
59
 
55
- if (currentEndUser) {
60
+ if (currentAccount) {
56
61
  return (
57
62
  <GlobalMenuBase>
58
63
  <GlobalMenuHeader>
59
- <AccountButton account={currentEndUser} linkText={changeLabel} onClick={onToggleAccounts} />
64
+ <AccountButton
65
+ account={currentAccount}
66
+ linkText={changeLabel}
67
+ multipleAccounts={accounts.length > 1}
68
+ onClick={onToggleAccounts}
69
+ />
60
70
  </GlobalMenuHeader>
61
71
  <Menu groups={groups} items={items} />
62
- <GlobalMenuFooter>
63
- <LogoutButton label={logoutLabel} />
64
- </GlobalMenuFooter>
72
+ {logoutButton && (
73
+ <GlobalMenuFooter>
74
+ <LogoutButton {...logoutButton} />
75
+ </GlobalMenuFooter>
76
+ )}
65
77
  </GlobalMenuBase>
66
78
  );
67
79
  }
@@ -25,46 +25,46 @@ const meta = {
25
25
  },
26
26
  accounts: [
27
27
  {
28
+ id: 'party:aurora',
28
29
  groupId: 'primary',
29
30
  type: 'person',
30
31
  name: 'Aurora Mikalsen',
31
- selected: true,
32
32
  },
33
33
  {
34
+ id: 'party:rakel',
34
35
  groupId: 'favourites',
35
36
  type: 'person',
36
37
  name: 'Rakel Engelsvik',
37
- selected: false,
38
38
  },
39
39
  {
40
+ id: 'party:auroraskeeperskole',
40
41
  groupId: 'favourites',
41
42
  type: 'company',
42
43
  name: 'Auroras keeperskole',
43
- selected: false,
44
44
  },
45
45
  {
46
+ id: 'party:aurorashandsker',
46
47
  groupId: 'secondary',
47
48
  type: 'company',
48
49
  name: 'Keeperhansker AS',
49
- selected: false,
50
50
  },
51
51
  {
52
+ id: 'party:aurorasfotballskole',
52
53
  groupId: 'secondary',
53
54
  type: 'company',
54
55
  name: 'Stadion drift AS',
55
- selected: false,
56
56
  },
57
57
  {
58
+ id: 'party:aurorasfotballskole',
58
59
  groupId: 'secondary',
59
60
  type: 'company',
60
61
  name: 'Sportsklubben Brann',
61
- selected: false,
62
62
  },
63
63
  {
64
+ id: 'party:aurorasfotballskole',
64
65
  groupId: 'secondary',
65
66
  type: 'company',
66
67
  name: 'Landslaget',
67
- selected: false,
68
68
  },
69
69
  ],
70
70
  items: [
@@ -94,10 +94,20 @@ const meta = {
94
94
  export default meta;
95
95
  type Story = StoryObj<typeof meta>;
96
96
 
97
- export const Default: Story = {};
97
+ export const Default: Story = {
98
+ args: {
99
+ currentAccount: {
100
+ id: 'party:aurora',
101
+ type: 'person',
102
+ name: 'Aurora Mikalsen',
103
+ },
104
+ },
105
+ };
98
106
 
99
107
  export const ControlledState = (args) => {
100
108
  const [q, setQ] = useState<string>('');
109
+ const currentEndUserId = 'party:aurora';
110
+ const [selectedAccountId, setSelectedAccountId] = useState<string>(currentEndUserId);
101
111
  const onChange = (event) => {
102
112
  setQ(event.target.value);
103
113
  };
@@ -160,6 +170,12 @@ export const ControlledState = (args) => {
160
170
  return (
161
171
  <Header
162
172
  {...args}
173
+ currentAccount={args.menu.accounts.find((account) => account.id === selectedAccountId)}
174
+ menu={{
175
+ ...args.menu,
176
+ ...args.menu.accounts,
177
+ changeCurrentAccount: setSelectedAccountId,
178
+ }}
163
179
  search={{
164
180
  ...args.search,
165
181
  value: q,
@@ -2,26 +2,24 @@
2
2
  import { useEscapeKey } from '../../hooks';
3
3
  import { DrawerBase, DropdownBase } from '../Dropdown';
4
4
  import { GlobalMenu, type GlobalMenuProps } from '../GlobalMenu';
5
+ import type { Account } from '../GlobalMenu/AccountButton.tsx';
5
6
  import { useRootContext } from '../RootProvider';
6
7
  import { Searchbar, type SearchbarProps } from '../Searchbar';
7
8
  import { HeaderBase } from './HeaderBase';
8
9
  import { HeaderButton } from './HeaderButton';
9
10
  import { HeaderLogo } from './HeaderLogo';
10
11
  import { HeaderMenu } from './HeaderMenu';
12
+ import { HeaderSearch } from './HeaderSearch';
11
13
  import styles from './header.module.css';
12
14
 
13
15
  export interface HeaderProps {
14
16
  menu: GlobalMenuProps;
15
17
  search?: SearchbarProps;
18
+ currentAccount?: Account;
16
19
  }
17
20
 
18
- export const Header = ({ search, menu }: HeaderProps) => {
21
+ export const Header = ({ search, menu, currentAccount }: HeaderProps) => {
19
22
  const { currentId, toggleId, openId, closeAll } = useRootContext();
20
- const selectedAccount = menu.accounts?.find((account) => account.selected);
21
- const selectedAvatar = selectedAccount && {
22
- type: selectedAccount.type,
23
- name: selectedAccount.name,
24
- };
25
23
 
26
24
  useEscapeKey(closeAll);
27
25
 
@@ -42,29 +40,30 @@ export const Header = ({ search, menu }: HeaderProps) => {
42
40
  <HeaderLogo className={styles.logo} />
43
41
  <HeaderMenu className={styles.menu}>
44
42
  <HeaderButton
45
- avatar={selectedAvatar}
43
+ avatar={
44
+ currentAccount && {
45
+ type: currentAccount.type,
46
+ name: currentAccount.name,
47
+ }
48
+ }
46
49
  onClick={onToggleMenu}
47
50
  expanded={currentId === 'menu'}
48
51
  label={menu?.menuLabel}
49
52
  />
50
53
  {menu && (
51
54
  <DropdownBase padding={false} placement="right" expanded={currentId === 'menu'} className={styles.dropdown}>
52
- <GlobalMenu {...menu} currentEndUser={selectedAccount} />
55
+ <GlobalMenu {...menu} currentAccount={currentAccount} />
53
56
  </DropdownBase>
54
57
  )}
55
58
  </HeaderMenu>
56
59
  {search && (
57
- <Searchbar
58
- {...search}
59
- className={styles.search}
60
- expanded={currentId === 'search'}
61
- onClose={onSearchClose}
62
- onFocus={onSearchFocus}
63
- />
60
+ <HeaderSearch expanded={currentId === 'search'}>
61
+ <Searchbar {...search} expanded={currentId === 'search'} onClose={onSearchClose} onFocus={onSearchFocus} />
62
+ </HeaderSearch>
64
63
  )}
65
64
  {menu && (
66
65
  <DrawerBase expanded={currentId === 'menu'} className={styles.drawer}>
67
- <GlobalMenu {...menu} currentEndUser={selectedAccount} expanded={currentId === 'menu'} />
66
+ <GlobalMenu {...menu} currentAccount={currentAccount} />
68
67
  </DrawerBase>
69
68
  )}
70
69
  </HeaderBase>
@@ -15,6 +15,7 @@ export interface HeaderButtonProps extends ButtonProps {
15
15
  className?: string;
16
16
  expanded?: boolean;
17
17
  icon?: IconName;
18
+ tabIndex?: number;
18
19
  }
19
20
 
20
21
  export const HeaderButton = ({
@@ -0,0 +1,17 @@
1
+ import cx from 'classnames';
2
+ import type { ReactNode } from 'react';
3
+ import styles from './headerSearch.module.css';
4
+
5
+ export interface HeaderSearchProps {
6
+ className?: string;
7
+ expanded?: boolean;
8
+ children?: ReactNode;
9
+ }
10
+
11
+ export const HeaderSearch = ({ expanded = false, className, children }: HeaderSearchProps) => {
12
+ return (
13
+ <div className={cx(styles.search, className)} aria-expanded={expanded}>
14
+ {children}
15
+ </div>
16
+ );
17
+ };
@@ -22,24 +22,3 @@
22
22
  display: block;
23
23
  }
24
24
  }
25
-
26
- /* search */
27
-
28
- .search {
29
- width: 100%;
30
- }
31
-
32
- @media (min-width: 1024px) {
33
- .search {
34
- position: absolute;
35
- left: 0;
36
- right: 0;
37
- margin: 0 auto;
38
- max-width: 320px;
39
- }
40
-
41
- .search[aria-expanded="true"] {
42
- z-index: 2;
43
- max-width: 640px;
44
- }
45
- }
@@ -0,0 +1,21 @@
1
+ .search {
2
+ width: 100%;
3
+ }
4
+
5
+ .search[aria-expanded="true"] {
6
+ z-index: 2;
7
+ }
8
+
9
+ @media (min-width: 1024px) {
10
+ .search {
11
+ position: absolute;
12
+ left: 0;
13
+ right: 0;
14
+ margin: 0 auto;
15
+ max-width: 320px;
16
+ }
17
+
18
+ .search[aria-expanded="true"] {
19
+ max-width: 640px;
20
+ }
21
+ }
@@ -11,7 +11,8 @@ const meta = {
11
11
  args: {
12
12
  items: [
13
13
  {
14
- createdAt: '2004-09-22 13:34',
14
+ createdAt: '2024-09-22 13:34',
15
+ createdAtLabel: '22. september 2024 kl 13.34',
15
16
  createdBy: {
16
17
  name: 'Eirik Horneland',
17
18
  },
@@ -44,6 +45,7 @@ const meta = {
44
45
  },
45
46
  {
46
47
  createdAt: '2004-09-09 13:34',
48
+ createdAtLabel: '22. september 2024 kl 13.34',
47
49
  createdBy: {
48
50
  name: 'Eirik Horneland',
49
51
  },
@@ -9,12 +9,17 @@ const header: HeaderProps = {
9
9
  name: 'search',
10
10
  placeholder: 'Søk i Altinn',
11
11
  },
12
+ currentAccount: {
13
+ id: 'party:aurora',
14
+ type: 'person',
15
+ name: 'Aurora Mikalsen',
16
+ },
12
17
  menu: {
13
18
  accounts: [
14
19
  {
20
+ id: 'party:aurora',
15
21
  type: 'person',
16
22
  name: 'Aurora Mikalsen',
17
- selected: true,
18
23
  },
19
24
  ],
20
25
  },
@@ -197,7 +202,7 @@ export const ControlledStateSearch = (args) => {
197
202
  };
198
203
 
199
204
  export const InboxBulkMode = (args) => {
200
- const [snackbars, setSnacbars] = useState([]);
205
+ const [snackbars, setSnackbars] = useState([]);
201
206
 
202
207
  const [itemsById, setItemsById] = useState({
203
208
  1: {
@@ -1,6 +1,7 @@
1
1
  import cx from 'classnames';
2
2
  import type { ElementType, KeyboardEvent, KeyboardEventHandler, ReactNode } from 'react';
3
3
  import { Badge, type BadgeProps } from '../Badge';
4
+ import { ContextMenu, type ContextMenuProps } from '../ContextMenu';
4
5
  import { Icon, type IconName } from '../Icon';
5
6
  import styles from './listItemBase.module.css';
6
7
 
@@ -10,6 +11,7 @@ export type ListItemSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
10
11
  interface ListItemBaseProps {
11
12
  as?: ElementType;
12
13
  size?: ListItemSize;
14
+ menu?: ContextMenuProps;
13
15
  linkIcon?: IconName;
14
16
  color?: ListItemColor;
15
17
  badge?: BadgeProps;
@@ -39,6 +41,7 @@ export const ListItemBase = ({
39
41
  selected,
40
42
  expanded,
41
43
  linkIcon,
44
+ menu,
42
45
  badge,
43
46
  onClick,
44
47
  onKeyPress,
@@ -71,6 +74,7 @@ export const ListItemBase = ({
71
74
  </div>
72
75
  <div className={styles.action}>
73
76
  {badge && <Badge {...badge} />}
77
+ {menu && <ContextMenu {...menu} />}
74
78
  {applicableIcon && <Icon name={applicableIcon} className={styles.linkIcon} />}
75
79
  </div>
76
80
  </Component>
@@ -14,3 +14,17 @@
14
14
  .list[data-spacing="lg"] {
15
15
  row-gap: 16px;
16
16
  }
17
+
18
+ /*
19
+
20
+ .list[data-spacing="none"] > :first-child {
21
+ border-top-left-radius: 0.25rem;
22
+ border-top-right-radius: 0.25rem;
23
+ }
24
+
25
+ .list[data-spacing="none"] > :last-child {
26
+ border-bottom-left-radius: 0.25rem;
27
+ border-bottom-right-radius: 0.25rem;
28
+ }
29
+
30
+ */
@@ -1,8 +1,6 @@
1
1
  /* reset */
2
2
 
3
3
  .item {
4
- border: none;
5
-
6
4
  color: inherit;
7
5
  font: inherit;
8
6
  text-align: inherit;
@@ -77,6 +75,15 @@
77
75
  box-shadow: var(--ds-shadow-xs);
78
76
  }
79
77
 
78
+ .item:hover {
79
+ outline: 2px solid var(--theme-border-strong);
80
+ z-index: 2;
81
+ }
82
+
83
+ .item:hover strong {
84
+ text-decoration: underline;
85
+ }
86
+
80
87
  .item[data-active="true"] {
81
88
  background-color: var(--theme-surface-default);
82
89
  }
@@ -0,0 +1,38 @@
1
+ import type { ChangeEventHandler } from 'react';
2
+ import styles from './menuInputField.module.css';
3
+
4
+ type InputType = 'text' | 'date' | 'search';
5
+
6
+ export interface MenuInputFieldProps {
7
+ placeholder?: string;
8
+ type?: InputType;
9
+ label?: string;
10
+ name: string;
11
+ value: string;
12
+ onChange: ChangeEventHandler;
13
+ }
14
+
15
+ export const MenuInputField = ({
16
+ label,
17
+ type = 'text',
18
+ value,
19
+ name,
20
+ placeholder = 'Søk',
21
+ onChange,
22
+ }: MenuInputFieldProps) => {
23
+ return (
24
+ <label className={styles.field}>
25
+ {label && <span className={styles.label}>{label}</span>}
26
+ <input
27
+ data-value={value && true}
28
+ type={type}
29
+ value={value}
30
+ name={name}
31
+ placeholder={placeholder}
32
+ className={styles.input}
33
+ onChange={onChange}
34
+ autoComplete="off"
35
+ />
36
+ </label>
37
+ );
38
+ };
@@ -51,7 +51,7 @@ export const MenuItemBase = ({
51
51
  return (
52
52
  <Component
53
53
  role="menuitem"
54
- tabIndex={!disabled && tabIndex}
54
+ tabIndex={disabled ? '-1' : (tabIndex ?? 0)}
55
55
  data-size={size}
56
56
  data-color={color}
57
57
  data-active={active}
@@ -10,7 +10,7 @@ export type MenuOptionType = 'checkbox' | 'radio';
10
10
  export interface MenuOptionProps {
11
11
  value: string | number;
12
12
  label: string;
13
- group?: string;
13
+ groupId?: string;
14
14
  size?: MenuItemSize;
15
15
  name?: string;
16
16
  title?: string;
@@ -2,6 +2,7 @@ export * from './MenuItemBase';
2
2
  export * from './MenuItemLabel';
3
3
  export * from './MenuItemMedia';
4
4
  export * from './MenuItem';
5
+ export * from './MenuInputField';
5
6
  export * from './MenuOption';
6
7
  export * from './MenuSearch';
7
8
  export * from './MenuHeader';