@ndla/ui 26.0.0 → 27.0.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 (74) hide show
  1. package/es/Breadcrumb/Breadcrumb.js +3 -4
  2. package/es/MyNdla/Resource/Folder.js +32 -14
  3. package/es/Resource/BlockResource.js +43 -61
  4. package/es/Resource/ListResource.js +44 -23
  5. package/es/Resource/resourceComponents.js +64 -38
  6. package/es/TreeStructure/ComboboxButton.js +162 -0
  7. package/es/TreeStructure/FolderItem.js +98 -78
  8. package/es/TreeStructure/FolderItems.js +25 -14
  9. package/es/TreeStructure/FolderNameInput.js +40 -33
  10. package/es/TreeStructure/NavigationLink.js +18 -10
  11. package/es/TreeStructure/TreeStructure.js +92 -165
  12. package/es/TreeStructure/arrowNavigation.js +3 -3
  13. package/es/TreeStructure/helperFunctions.js +3 -0
  14. package/es/locale/messages-en.js +8 -2
  15. package/es/locale/messages-nb.js +8 -2
  16. package/es/locale/messages-nn.js +8 -2
  17. package/es/locale/messages-se.js +8 -2
  18. package/es/locale/messages-sma.js +8 -2
  19. package/lib/Breadcrumb/Breadcrumb.js +3 -5
  20. package/lib/MyNdla/Resource/Folder.d.ts +2 -1
  21. package/lib/MyNdla/Resource/Folder.js +37 -14
  22. package/lib/Resource/BlockResource.d.ts +2 -1
  23. package/lib/Resource/BlockResource.js +48 -61
  24. package/lib/Resource/ListResource.d.ts +2 -1
  25. package/lib/Resource/ListResource.js +49 -23
  26. package/lib/Resource/resourceComponents.d.ts +6 -1
  27. package/lib/Resource/resourceComponents.js +64 -37
  28. package/lib/TreeStructure/ComboboxButton.d.ts +28 -0
  29. package/lib/TreeStructure/ComboboxButton.js +176 -0
  30. package/lib/TreeStructure/FolderItem.d.ts +1 -1
  31. package/lib/TreeStructure/FolderItem.js +99 -77
  32. package/lib/TreeStructure/FolderItems.d.ts +4 -2
  33. package/lib/TreeStructure/FolderItems.js +26 -14
  34. package/lib/TreeStructure/FolderNameInput.d.ts +3 -1
  35. package/lib/TreeStructure/FolderNameInput.js +41 -32
  36. package/lib/TreeStructure/NavigationLink.d.ts +1 -1
  37. package/lib/TreeStructure/NavigationLink.js +18 -10
  38. package/lib/TreeStructure/TreeStructure.d.ts +2 -2
  39. package/lib/TreeStructure/TreeStructure.js +92 -165
  40. package/lib/TreeStructure/arrowNavigation.d.ts +2 -1
  41. package/lib/TreeStructure/arrowNavigation.js +3 -3
  42. package/lib/TreeStructure/helperFunctions.d.ts +2 -1
  43. package/lib/TreeStructure/helperFunctions.js +8 -2
  44. package/lib/TreeStructure/types.d.ts +6 -7
  45. package/lib/locale/messages-en.d.ts +6 -0
  46. package/lib/locale/messages-en.js +8 -2
  47. package/lib/locale/messages-nb.d.ts +6 -0
  48. package/lib/locale/messages-nb.js +8 -2
  49. package/lib/locale/messages-nn.d.ts +6 -0
  50. package/lib/locale/messages-nn.js +8 -2
  51. package/lib/locale/messages-se.d.ts +6 -0
  52. package/lib/locale/messages-se.js +8 -2
  53. package/lib/locale/messages-sma.d.ts +6 -0
  54. package/lib/locale/messages-sma.js +8 -2
  55. package/package.json +11 -11
  56. package/src/Breadcrumb/Breadcrumb.tsx +1 -2
  57. package/src/MyNdla/Resource/Folder.tsx +21 -5
  58. package/src/Resource/BlockResource.tsx +43 -33
  59. package/src/Resource/ListResource.tsx +37 -29
  60. package/src/Resource/resourceComponents.tsx +60 -26
  61. package/src/TreeStructure/ComboboxButton.tsx +189 -0
  62. package/src/TreeStructure/FolderItem.tsx +89 -70
  63. package/src/TreeStructure/FolderItems.tsx +36 -16
  64. package/src/TreeStructure/FolderNameInput.tsx +43 -18
  65. package/src/TreeStructure/NavigationLink.tsx +17 -10
  66. package/src/TreeStructure/TreeStructure.tsx +63 -139
  67. package/src/TreeStructure/arrowNavigation.ts +7 -6
  68. package/src/TreeStructure/helperFunctions.ts +5 -1
  69. package/src/TreeStructure/types.ts +6 -7
  70. package/src/locale/messages-en.ts +7 -0
  71. package/src/locale/messages-nb.ts +6 -0
  72. package/src/locale/messages-nn.ts +6 -0
  73. package/src/locale/messages-se.ts +7 -0
  74. package/src/locale/messages-sma.ts +7 -0
@@ -16,6 +16,7 @@ import { colors, spacing, animations, spacingUnit, misc, fonts } from '@ndla/cor
16
16
  import SafeLink from '@ndla/safelink';
17
17
  import { CommonFolderItemsProps, FolderType } from './types';
18
18
  import { arrowNavigation } from './arrowNavigation';
19
+ import { treestructureId } from './helperFunctions';
19
20
 
20
21
  const OpenButton = styled.span<{ isOpen: boolean }>`
21
22
  display: flex;
@@ -40,22 +41,13 @@ const StyledName = styled.span`
40
41
  text-align: left;
41
42
  `;
42
43
 
43
- const WrapperForFolderChild = styled.div`
44
- display: flex;
45
- flex-direction: row;
46
- align-items: center;
47
- gap: ${spacing.xsmall};
48
- margin-left: auto;
49
- `;
50
-
51
- const shouldForwardProp = (name: string) => !['selected', 'noArrow', 'fullWidth', 'level'].includes(name);
44
+ const shouldForwardProp = (name: string) => !['selected', 'level', 'focused', 'isCreatingFolder'].includes(name);
52
45
 
53
46
  interface FolderNameProps {
54
47
  selected?: boolean;
55
- noArrow?: boolean;
56
- fullWidth?: boolean;
57
48
  level: number;
58
49
  isCreatingFolder?: boolean;
50
+ focused?: boolean;
59
51
  }
60
52
 
61
53
  const FolderName = styled(Button, { shouldForwardProp })<FolderNameProps>`
@@ -66,14 +58,15 @@ const FolderName = styled(Button, { shouldForwardProp })<FolderNameProps>`
66
58
  gap: ${spacing.xxsmall};
67
59
  border: none;
68
60
  outline: none;
69
- background: ${({ selected, isCreatingFolder }) => selected && !isCreatingFolder && colors.brand.lighter};
70
- color: ${({ isCreatingFolder, selected }) =>
71
- isCreatingFolder && selected ? colors.brand.primary : colors.text.primary};
61
+ background: ${({ selected, isCreatingFolder, focused }) =>
62
+ isCreatingFolder ? 'none' : selected ? colors.brand.lighter : focused && colors.brand.lightest};
63
+ color: ${({ isCreatingFolder, focused }) =>
64
+ isCreatingFolder && focused ? colors.brand.primary : colors.text.primary};
72
65
  transition: ${animations.durations.superFast};
73
66
  line-height: 1;
74
67
  word-break: break-word;
75
- &:hover,
76
- &:focus {
68
+
69
+ &:hover {
77
70
  box-shadow: none;
78
71
  outline: none;
79
72
  background: ${({ selected }) => (selected ? colors.brand.light : colors.brand.lightest)};
@@ -115,7 +108,7 @@ interface Props extends CommonFolderItemsProps {
115
108
  }
116
109
 
117
110
  const FolderItem = ({
118
- focusedFolderId,
111
+ focusedFolder,
119
112
  folder,
120
113
  isOpen,
121
114
  level,
@@ -124,45 +117,48 @@ const FolderItem = ({
124
117
  onCloseFolder,
125
118
  onOpenFolder,
126
119
  onSelectFolder,
127
- openOnFolderClick,
128
- setFocusedId,
120
+ setFocusedFolder,
129
121
  setSelectedFolder,
130
122
  targetResource,
131
123
  visibleFolders,
132
- framed,
133
124
  maxLevel,
134
125
  isCreatingFolder,
126
+ type,
127
+ closeTree,
135
128
  }: Props) => {
136
129
  const { t } = useTranslation();
137
- const { id, name } = folder;
130
+ const { id, name, isNavigation } = folder;
138
131
  const ref = useRef<HTMLButtonElement & HTMLAnchorElement>(null);
139
- const selected = selectedFolder && selectedFolder.id === id;
140
- const focused = focusedFolderId === id;
132
+ const selected = selectedFolder ? selectedFolder.id === id : false;
133
+
134
+ const focused = focusedFolder?.id === id;
141
135
 
142
136
  const handleClickFolder = () => {
143
- if (openOnFolderClick) {
144
- if (selected) {
145
- if (isOpen) {
146
- onCloseFolder(id);
147
- } else {
148
- onOpenFolder(id);
149
- }
150
- }
151
- }
152
137
  if (!selected) {
153
138
  setSelectedFolder(folder);
154
- setFocusedId(id);
155
139
  }
156
- onSelectFolder?.(id);
140
+ setFocusedFolder(folder);
141
+ if (type === 'picker') {
142
+ if (selected) {
143
+ closeTree();
144
+ }
145
+ onSelectFolder?.(id);
146
+ }
157
147
  };
158
148
 
159
149
  useEffect(() => {
160
- if (focusedFolderId === id && !isCreatingFolder) {
161
- ref.current?.focus();
150
+ if (focusedFolder?.id === id && !isCreatingFolder) {
151
+ if (type === 'navigation') {
152
+ ref.current?.focus();
153
+ }
154
+ ref.current?.scrollIntoView({
155
+ behavior: 'smooth',
156
+ block: 'nearest',
157
+ });
162
158
  }
163
- }, [focusedFolderId, ref, id, isCreatingFolder]);
159
+ }, [focusedFolder, ref, id, isCreatingFolder, type]);
164
160
 
165
- const linkPath = `/minndla${level > 0 ? '/folders' : ''}/${id}`;
161
+ const linkPath = `/minndla${!isNavigation ? '/folders' : ''}/${id}`;
166
162
 
167
163
  const containsResource =
168
164
  targetResource && folder.resources.some((resource) => resource.resourceId === targetResource.resourceId);
@@ -173,28 +169,36 @@ const FolderItem = ({
173
169
 
174
170
  const hideArrow = isMaxDepth || emptyFolder;
175
171
 
176
- return onSelectFolder ? (
177
- <FolderName
178
- variant="ghost"
179
- shape="sharp"
180
- fontWeight="normal"
181
- colorTheme="light"
172
+ return type === 'navigation' ? (
173
+ <FolderNameLink
174
+ role="treeitem"
175
+ aria-owns={folder.subfolders.length ? treestructureId(type, `subfolders-${folder.id}`) : undefined}
176
+ aria-expanded={isMaxDepth || emptyFolder ? undefined : isOpen}
177
+ aria-current={selected ? 'page' : undefined}
178
+ aria-describedby={containsResource ? `alreadyAdded-${folder.id}` : undefined}
182
179
  ref={ref}
183
180
  level={level}
184
- fullWidth={framed}
185
- onKeyDown={(e) => arrowNavigation(e, id, visibleFolders, setFocusedId, onOpenFolder, onCloseFolder)}
186
- noArrow={hideArrow}
181
+ onKeyDown={(e: KeyboardEvent<HTMLElement>) => {
182
+ if (e.key === 'Enter') {
183
+ setSelectedFolder(folder);
184
+ setFocusedFolder(folder);
185
+ return;
186
+ }
187
+ arrowNavigation(e, id, visibleFolders, setFocusedFolder, onOpenFolder, onCloseFolder);
188
+ }}
189
+ to={loading ? '' : linkPath}
187
190
  tabIndex={selected || focused ? 0 : -1}
188
191
  selected={selected}
189
- disabled={loading}
190
- onFocus={() => setFocusedId(id)}
191
- onClick={handleClickFolder}
192
- isCreatingFolder={isCreatingFolder}>
193
- {!hideArrow && (
192
+ onFocus={() => setFocusedFolder(folder)}
193
+ onClick={handleClickFolder}>
194
+ {(!hideArrow || level === 0) && (
194
195
  <OpenButton
196
+ aria-hidden
195
197
  tabIndex={-1}
196
198
  isOpen={isOpen}
197
- onClick={() => {
199
+ onClick={(e) => {
200
+ e.stopPropagation();
201
+ e.preventDefault();
198
202
  ref.current?.focus();
199
203
  if (isOpen) {
200
204
  onCloseFolder(id);
@@ -206,29 +210,37 @@ const FolderItem = ({
206
210
  </OpenButton>
207
211
  )}
208
212
  <StyledName>{name}</StyledName>
209
- <WrapperForFolderChild>
210
- {containsResource && <StyledDone title={t('myNdla.alreadyInFolder')} />}
211
- </WrapperForFolderChild>
212
- </FolderName>
213
+ </FolderNameLink>
213
214
  ) : (
214
- <FolderNameLink
215
+ <FolderName
216
+ tabIndex={-1}
217
+ role="treeitem"
218
+ id={treestructureId(type, folder.id)}
219
+ aria-expanded={isMaxDepth || emptyFolder ? undefined : isOpen}
220
+ aria-selected={selected}
221
+ focused={focusedFolder?.id === folder.id}
222
+ aria-describedby={containsResource ? `alreadyAdded-${folder.id}` : undefined}
223
+ variant="ghost"
224
+ shape="sharp"
225
+ fontWeight="normal"
226
+ colorTheme="light"
215
227
  ref={ref}
216
228
  level={level}
217
- onKeyDown={(e: KeyboardEvent<HTMLElement>) =>
218
- arrowNavigation(e, id, visibleFolders, setFocusedId, onOpenFolder, onCloseFolder)
219
- }
220
- noArrow={!isMaxDepth}
221
- to={loading ? '' : linkPath}
222
- tabIndex={selected || focused ? 0 : -1}
223
229
  selected={selected}
224
- onFocus={() => setFocusedId(id)}
225
- onClick={handleClickFolder}>
226
- {(!hideArrow || level === 0) && (
230
+ disabled={loading}
231
+ onFocus={(e) => {
232
+ setFocusedFolder(focusedFolder || folder, true);
233
+ }}
234
+ onClick={handleClickFolder}
235
+ isCreatingFolder={isCreatingFolder}>
236
+ {!hideArrow && (
227
237
  <OpenButton
238
+ aria-hidden
228
239
  tabIndex={-1}
229
240
  isOpen={isOpen}
230
- onClick={() => {
231
- ref.current?.focus();
241
+ onClick={(e) => {
242
+ e.stopPropagation();
243
+ setFocusedFolder(folder, true);
232
244
  if (isOpen) {
233
245
  onCloseFolder(id);
234
246
  } else {
@@ -239,7 +251,14 @@ const FolderItem = ({
239
251
  </OpenButton>
240
252
  )}
241
253
  <StyledName>{name}</StyledName>
242
- </FolderNameLink>
254
+ {containsResource && (
255
+ <StyledDone
256
+ aria-label={t('myNdla.alreadyInFolder')}
257
+ id={`alreadyAdded-${folder.id}`}
258
+ title={t('myNdla.alreadyInFolder')}
259
+ />
260
+ )}
261
+ </FolderName>
243
262
  );
244
263
  };
245
264
 
@@ -6,13 +6,14 @@
6
6
  *
7
7
  */
8
8
 
9
- import React from 'react';
9
+ import React, { ReactNode } from 'react';
10
10
  import styled from '@emotion/styled';
11
11
  import { animations } from '@ndla/core';
12
12
  import FolderItem from './FolderItem';
13
13
  import FolderNameInput from './FolderNameInput';
14
14
  import { CommonFolderItemsProps, FolderType, TreeStructureType } from './types';
15
15
  import NavigationLink from './NavigationLink';
16
+ import { treestructureId } from './helperFunctions';
16
17
 
17
18
  const StyledUL = styled.ul`
18
19
  ${animations.fadeInLeft(animations.durations.fast)};
@@ -43,6 +44,8 @@ export interface FolderItemsProps extends CommonFolderItemsProps {
43
44
  onCancelNewFolder: () => void;
44
45
  onSaveNewFolder: (name: string, parentId: string) => void;
45
46
  openFolders: string[];
47
+ parentFolder?: FolderType;
48
+ children?: ReactNode;
46
49
  }
47
50
 
48
51
  const FolderItems = ({
@@ -54,15 +57,28 @@ const FolderItems = ({
54
57
  onSaveNewFolder,
55
58
  openFolders,
56
59
  type,
60
+ parentFolder,
61
+ children,
57
62
  ...rest
58
63
  }: FolderItemsProps) => (
59
- <StyledUL role={level === 0 ? 'tree' : 'group'}>
64
+ <StyledUL
65
+ id={
66
+ level === 0 && type === 'picker'
67
+ ? treestructureId(type, 'popup')
68
+ : parentFolder
69
+ ? treestructureId(type, `subfolders-${parentFolder.id}`)
70
+ : undefined
71
+ }
72
+ tabIndex={-1}
73
+ aria-labelledby={level === 0 && type === 'picker' ? treestructureId(type, 'label') : undefined}
74
+ role={level === 0 ? 'tree' : 'group'}>
75
+ {children}
60
76
  {folders.map((folder) => {
61
77
  const { subfolders, id } = folder;
62
78
  const isOpen = openFolders?.includes(id);
63
79
 
64
80
  return (
65
- <StyledLI key={id} role="treeitem" type={type}>
81
+ <StyledLI key={id} tabIndex={-1} role="none" type={type}>
66
82
  {folder.isNavigation ? (
67
83
  <NavigationLink folder={folder} isOpen={isOpen} level={level} type={type} loading={loading} {...rest} />
68
84
  ) : (
@@ -73,20 +89,12 @@ const FolderItems = ({
73
89
  level={level}
74
90
  loading={loading}
75
91
  type={type}
76
- isCreatingFolder={newFolderParentId === folder.id}
92
+ isCreatingFolder={!!newFolderParentId}
77
93
  {...rest}
78
94
  />
79
- {newFolderParentId === id && (
80
- <FolderNameInput
81
- loading={loading}
82
- level={level}
83
- onCancelNewFolder={onCancelNewFolder}
84
- onSaveNewFolder={onSaveNewFolder}
85
- parentId={newFolderParentId}
86
- />
87
- )}
88
- {subfolders && isOpen && (
95
+ {((subfolders && isOpen) || newFolderParentId === id) && (
89
96
  <FolderItems
97
+ parentFolder={folder}
90
98
  folders={subfolders}
91
99
  level={level + 1}
92
100
  loading={loading}
@@ -95,8 +103,20 @@ const FolderItems = ({
95
103
  onCancelNewFolder={onCancelNewFolder}
96
104
  onSaveNewFolder={onSaveNewFolder}
97
105
  openFolders={openFolders}
98
- {...rest}
99
- />
106
+ {...rest}>
107
+ {newFolderParentId === id && (
108
+ <li role="none">
109
+ <FolderNameInput
110
+ loading={loading}
111
+ level={level}
112
+ onCancelNewFolder={onCancelNewFolder}
113
+ onSaveNewFolder={onSaveNewFolder}
114
+ parentId={newFolderParentId}
115
+ type={type}
116
+ />
117
+ </li>
118
+ )}
119
+ </FolderItems>
100
120
  )}
101
121
  </>
102
122
  )}
@@ -15,6 +15,9 @@ import { Spinner } from '@ndla/icons';
15
15
  import { IconButton } from '@ndla/button';
16
16
  import { Cross } from '@ndla/icons/action';
17
17
  import { Done } from '@ndla/icons/editor';
18
+ import { InputV2 as Input } from '@ndla/forms';
19
+ import { TreeStructureType } from './types';
20
+ import { treestructureId } from './helperFunctions';
18
21
 
19
22
  // Source: https://kovart.github.io/dashed-border-generator/
20
23
  const borderStyle = `url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' stroke='${encodeURIComponent(
@@ -57,7 +60,7 @@ const InputWrapper = styled.div<{ level: number }>`
57
60
  }
58
61
  `;
59
62
 
60
- const StyledInput = styled.input`
63
+ const StyledInput = styled(Input)`
61
64
  padding: ${spacing.small};
62
65
  flex-grow: 1;
63
66
  border: 0;
@@ -74,54 +77,65 @@ interface FolderNameInputProps {
74
77
  onCancelNewFolder: () => void;
75
78
  loading?: boolean;
76
79
  level: number;
80
+ type: TreeStructureType;
77
81
  }
78
82
 
79
- const FolderNameInput = ({ onSaveNewFolder, parentId, onCancelNewFolder, loading, level }: FolderNameInputProps) => {
83
+ const FolderNameInput = ({
84
+ onSaveNewFolder,
85
+ parentId,
86
+ onCancelNewFolder,
87
+ loading,
88
+ level,
89
+ type,
90
+ }: FolderNameInputProps) => {
80
91
  const { t } = useTranslation();
81
92
  const [name, setName] = useState<string>('');
82
93
  const inputRef = useRef<HTMLInputElement>(null);
83
94
 
84
95
  useEffect(() => {
85
- inputRef.current?.select();
86
96
  if (isMobile) {
87
97
  inputRef.current?.scrollIntoView({ behavior: 'smooth' });
88
98
  }
89
- return () => {
90
- onCancelNewFolder();
91
- };
92
- }, [onCancelNewFolder]);
99
+ // eslint-disable-next-line react-hooks/exhaustive-deps
100
+ }, []);
93
101
 
94
102
  return (
95
103
  <NewFolderWrapper>
96
104
  <InputWrapper level={level}>
97
105
  <StyledInput
106
+ name="name"
107
+ labelHidden
108
+ label={t('treeStructure.newFolder.folderName')}
109
+ aria-invalid={name.length === 0}
110
+ aria-disabled={loading ? true : undefined}
111
+ aria-describedby={loading ? treestructureId(type, 'spinner') : undefined}
98
112
  ref={inputRef}
99
113
  autoFocus
100
114
  placeholder={t('treeStructure.newFolder.placeholder')}
101
- disabled={loading}
102
115
  value={name}
103
116
  onKeyDown={(e: KeyboardEvent<HTMLInputElement>) => {
104
117
  if (e.key === 'Escape') {
105
118
  e.preventDefault();
106
119
  onCancelNewFolder();
107
- } else if (e.key === 'Enter' || e.key === 'Tab') {
120
+ } else if (e.key === 'Enter') {
108
121
  e.preventDefault();
109
- if (name === '') {
110
- onCancelNewFolder();
122
+ if (name === '' || loading) {
111
123
  return;
112
124
  }
113
125
  onSaveNewFolder(name, parentId);
114
126
  }
115
127
  }}
116
- onChange={(e: ChangeEvent<HTMLInputElement>) => setName(e.target.value)}
128
+ onChange={(e: ChangeEvent<HTMLInputElement>) => {
129
+ if (!loading) {
130
+ setName(e.target.value);
131
+ }
132
+ }}
117
133
  />
118
134
  <Row>
119
- {!loading ? (
135
+ {!loading && (
120
136
  <>
121
- <IconButton aria-label={t('close')} title={t('close')} size="small" ghostPill onClick={onCancelNewFolder}>
122
- <Cross />
123
- </IconButton>
124
137
  <IconButton
138
+ tabIndex={0}
125
139
  aria-label={t('save')}
126
140
  title={t('save')}
127
141
  size="small"
@@ -129,10 +143,21 @@ const FolderNameInput = ({ onSaveNewFolder, parentId, onCancelNewFolder, loading
129
143
  onClick={() => onSaveNewFolder(name, parentId)}>
130
144
  <Done />
131
145
  </IconButton>
146
+ <IconButton aria-label={t('close')} title={t('close')} size="small" ghostPill onClick={onCancelNewFolder}>
147
+ <Cross />
148
+ </IconButton>
132
149
  </>
133
- ) : (
134
- <Spinner size="small" margin="0" />
135
150
  )}
151
+ <div aria-live="assertive">
152
+ {loading && (
153
+ <Spinner
154
+ size="normal"
155
+ margin={spacing.small}
156
+ id={treestructureId(type, 'spinner')}
157
+ aria-label={t('loading')}
158
+ />
159
+ )}
160
+ </div>
136
161
  </Row>
137
162
  </InputWrapper>
138
163
  </NewFolderWrapper>
@@ -55,9 +55,9 @@ const NavigationLink = ({
55
55
  loading,
56
56
  folder,
57
57
  selectedFolder,
58
- focusedFolderId,
58
+ focusedFolder,
59
59
  setSelectedFolder,
60
- setFocusedId,
60
+ setFocusedFolder,
61
61
  visibleFolders,
62
62
  onOpenFolder,
63
63
  onCloseFolder,
@@ -65,30 +65,37 @@ const NavigationLink = ({
65
65
  const { id, icon, name } = folder;
66
66
  const selected = selectedFolder && selectedFolder.id === id;
67
67
  const ref = useRef<HTMLButtonElement & HTMLAnchorElement>(null);
68
- const focused = focusedFolderId === id;
68
+ const focused = focusedFolder?.id === id;
69
69
 
70
70
  const handleClick = () => {
71
71
  if (!selected) {
72
72
  setSelectedFolder(folder);
73
- setFocusedId(id);
73
+ setFocusedFolder(folder);
74
74
  }
75
75
  };
76
76
 
77
77
  useEffect(() => {
78
- if (focusedFolderId === id) {
78
+ if (focusedFolder?.id === id) {
79
79
  ref.current?.focus();
80
80
  }
81
- }, [focusedFolderId, ref, id]);
81
+ }, [focusedFolder, ref, id]);
82
82
 
83
83
  return (
84
84
  <StyledSafeLink
85
+ role="treeitem"
85
86
  ref={ref}
86
- onKeyDown={(e: KeyboardEvent<HTMLElement>) =>
87
- arrowNavigation(e, id, visibleFolders, setFocusedId, onOpenFolder, onCloseFolder)
88
- }
87
+ onKeyDown={(e: KeyboardEvent<HTMLElement>) => {
88
+ if (e.key === 'Enter') {
89
+ setSelectedFolder(folder);
90
+ setFocusedFolder(folder);
91
+ return;
92
+ }
93
+ arrowNavigation(e, id, visibleFolders, setFocusedFolder, onOpenFolder, onCloseFolder);
94
+ }}
95
+ aria-current={selected ? 'page' : undefined}
89
96
  tabIndex={selected || focused ? 0 : -1}
90
97
  selected={selected}
91
- onFocus={() => setFocusedId(id)}
98
+ onFocus={() => setFocusedFolder(folder)}
92
99
  onClick={handleClick}
93
100
  to={loading ? '' : `/minndla/${id}`}>
94
101
  <IconWrapper>{icon}</IconWrapper>