@ndla/ui 30.8.3 → 31.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 (91) hide show
  1. package/es/Article/Article.js +3 -3
  2. package/es/Article/ArticleSideBar.js +5 -5
  3. package/es/AudioPlayer/Controls.js +26 -26
  4. package/es/AudioPlayer/SpeechControl.js +1 -1
  5. package/es/Frontpage/FrontpageSearch.js +2 -9
  6. package/es/MediaList/MediaList.js +1 -0
  7. package/es/Messages/MessageBox.js +10 -10
  8. package/es/NDLAFilm/AllMoviesAlphabetically.js +11 -11
  9. package/es/NDLAFilm/FilmSlideshow.js +7 -7
  10. package/es/NDLAFilm/MovieGrid.js +4 -4
  11. package/es/Programme/Programme.js +5 -5
  12. package/es/ResourceGroup/ResourceItem.js +12 -13
  13. package/es/ResourceGroup/ResourceList.js +3 -4
  14. package/es/Search/SearchField.js +2 -3
  15. package/es/Subject/SubjectNewContent.js +10 -10
  16. package/es/TreeStructure/AddFolderButton.js +9 -9
  17. package/es/TreeStructure/ComboboxButton.js +30 -5
  18. package/es/TreeStructure/FolderItem.js +11 -10
  19. package/es/TreeStructure/FolderItems.js +28 -37
  20. package/es/TreeStructure/TreeStructure.js +9 -7
  21. package/es/i18n/index.js +1 -2
  22. package/es/index.js +1 -1
  23. package/lib/Article/Article.js +3 -3
  24. package/lib/Article/ArticleSideBar.js +5 -5
  25. package/lib/AudioPlayer/Controls.js +26 -26
  26. package/lib/AudioPlayer/SpeechControl.js +1 -1
  27. package/lib/Frontpage/FrontpageSearch.js +2 -9
  28. package/lib/MediaList/MediaList.js +1 -0
  29. package/lib/Messages/MessageBox.js +10 -10
  30. package/lib/NDLAFilm/AllMoviesAlphabetically.js +11 -11
  31. package/lib/NDLAFilm/FilmSlideshow.js +7 -7
  32. package/lib/NDLAFilm/MovieGrid.js +4 -4
  33. package/lib/Programme/Programme.js +5 -5
  34. package/lib/ResourceGroup/ResourceItem.js +11 -11
  35. package/lib/ResourceGroup/ResourceList.js +2 -2
  36. package/lib/Search/SearchField.js +2 -3
  37. package/lib/Subject/SubjectNewContent.js +10 -10
  38. package/lib/TreeStructure/AddFolderButton.d.ts +4 -3
  39. package/lib/TreeStructure/AddFolderButton.js +9 -9
  40. package/lib/TreeStructure/ComboboxButton.d.ts +8 -6
  41. package/lib/TreeStructure/ComboboxButton.js +29 -3
  42. package/lib/TreeStructure/FolderItem.d.ts +5 -3
  43. package/lib/TreeStructure/FolderItem.js +11 -10
  44. package/lib/TreeStructure/FolderItems.d.ts +4 -3
  45. package/lib/TreeStructure/FolderItems.js +28 -36
  46. package/lib/TreeStructure/TreeStructure.d.ts +3 -2
  47. package/lib/TreeStructure/TreeStructure.js +9 -7
  48. package/lib/TreeStructure/arrowNavigation.d.ts +2 -2
  49. package/lib/TreeStructure/helperFunctions.d.ts +3 -2
  50. package/lib/TreeStructure/index.d.ts +0 -1
  51. package/lib/TreeStructure/types.d.ts +5 -9
  52. package/lib/i18n/index.d.ts +0 -1
  53. package/lib/i18n/index.js +1 -8
  54. package/lib/index.d.ts +2 -2
  55. package/lib/index.js +0 -7
  56. package/package.json +10 -12
  57. package/src/Article/Article.tsx +1 -1
  58. package/src/Article/ArticleSideBar.tsx +1 -1
  59. package/src/AudioPlayer/Controls.tsx +1 -0
  60. package/src/AudioPlayer/SpeechControl.tsx +1 -0
  61. package/src/Frontpage/FrontpageSearch.tsx +0 -7
  62. package/src/MediaList/MediaList.tsx +1 -0
  63. package/src/Messages/MessageBox.tsx +1 -1
  64. package/src/NDLAFilm/AllMoviesAlphabetically.tsx +1 -1
  65. package/src/NDLAFilm/FilmSlideshow.tsx +1 -1
  66. package/src/NDLAFilm/MovieGrid.tsx +2 -1
  67. package/src/Programme/Programme.tsx +1 -2
  68. package/src/ResourceGroup/ResourceItem.tsx +1 -2
  69. package/src/ResourceGroup/ResourceList.tsx +1 -2
  70. package/src/Search/SearchField.tsx +1 -2
  71. package/src/Subject/SubjectNewContent.tsx +2 -2
  72. package/src/TreeStructure/AddFolderButton.tsx +18 -19
  73. package/src/TreeStructure/ComboboxButton.tsx +52 -26
  74. package/src/TreeStructure/FolderItem.tsx +10 -7
  75. package/src/TreeStructure/FolderItems.tsx +31 -36
  76. package/src/TreeStructure/TreeStructure.tsx +8 -6
  77. package/src/TreeStructure/arrowNavigation.ts +5 -5
  78. package/src/TreeStructure/helperFunctions.ts +4 -3
  79. package/src/TreeStructure/index.ts +0 -1
  80. package/src/TreeStructure/types.ts +5 -10
  81. package/src/i18n/index.ts +0 -1
  82. package/src/index.ts +2 -2
  83. package/es/TreeStructure/NavigationLink.js +0 -91
  84. package/es/i18n/formatMessage.js +0 -45
  85. package/lib/TreeStructure/NavigationLink.d.ts +0 -14
  86. package/lib/TreeStructure/NavigationLink.js +0 -93
  87. package/lib/i18n/formatMessage.d.ts +0 -14
  88. package/lib/i18n/formatMessage.js +0 -53
  89. package/src/TreeStructure/NavigationLink.tsx +0 -107
  90. package/src/i18n/__tests__/formatMessage-test.ts +0 -34
  91. package/src/i18n/formatMessage.ts +0 -61
@@ -52,6 +52,7 @@ const SpeechControl = ({ src, title }: Props) => {
52
52
  };
53
53
  return (
54
54
  <div>
55
+ {/* eslint-disable-next-line jsx-a11y/media-has-caption */}
55
56
  <audio ref={audioRef} src={src} title={title} preload="metadata" />
56
57
  <SpeechPlayButton type="button" onClick={togglePlay}>
57
58
  <VolumeUp role="img" aria-label="play" title="play" />
@@ -146,13 +146,6 @@ const FrontpageSearch = ({
146
146
  }, [inputHasFocus]);
147
147
 
148
148
  const onBlur = () => {
149
- setTimeout(() => {
150
- if (searchFieldRef.current) {
151
- if (!searchFieldRef.current.contains(document.activeElement)) {
152
- onInputBlur();
153
- }
154
- }
155
- }, 0);
156
149
  // This is needed when user tabs out of field
157
150
  if (!searchFieldValue) {
158
151
  onInputBlur();
@@ -147,6 +147,7 @@ export const MediaListItemMeta = ({ items = [] }: MediaListItemMetaProps) => {
147
147
 
148
148
  return (
149
149
  //@ts-ignore
150
+ // eslint-disable-next-line react/no-unknown-property
150
151
  <ul {...cClasses('actions')} property="cc:attributionName" content={attributionMeta}>
151
152
  {items.map((item) => (
152
153
  <li key={uuid()} className="c-medialist__meta-item">
@@ -138,7 +138,7 @@ export const MessageBox = ({ type, children = '', links, showCloseButton, onClos
138
138
  {links && (
139
139
  <LinkWrapper>
140
140
  {links.map((x) => (
141
- <Link href={x.href}>
141
+ <Link href={x.href} key={x.href}>
142
142
  <span>{x.text}</span>
143
143
  <Forward />
144
144
  </Link>
@@ -170,7 +170,7 @@ const hasForEachPolyfill = () => {
170
170
  if ('NodeList' in window && !NodeList.prototype.forEach) {
171
171
  NodeList.prototype.forEach = function (callback, thisArg) {
172
172
  thisArg = thisArg || window;
173
- for (var i = 0; i < this.length; i++) {
173
+ for (let i = 0; i < this.length; i++) {
174
174
  callback.call(thisArg, this[i], i, this);
175
175
  }
176
176
  };
@@ -217,7 +217,7 @@ const FilmSlideshow = ({ autoSlide = false, slideshow = [], slideInterval = 5000
217
217
  const [animationComplete, setAnimationComplete] = useState(true);
218
218
  const slideRef = useRef<HTMLDivElement>(null);
219
219
  const slideText = useRef<HTMLDivElement>(null);
220
- let timer = useRef<ReturnType<typeof setTimeout> | null>(null);
220
+ const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
221
221
 
222
222
  const gotoSlide = useCallback((indexTarget: number, useAnimation = false) => {
223
223
  setSwipeDistance(0);
@@ -66,8 +66,9 @@ const MovieGrid = ({
66
66
  <MovieListing marginLeft={autoSizedProps.margin}>
67
67
  {fetchingMoviesByType && <LoadingPlaceholder height={loadingPlaceholderHeight} />}
68
68
  {!fetchingMoviesByType &&
69
- moviesByType.map((movie) => (
69
+ moviesByType.map((movie, index) => (
70
70
  <FilmContentCard
71
+ key={index}
71
72
  hideTags
72
73
  movie={movie}
73
74
  columnWidth={autoSizedProps.columnWidth}
@@ -3,8 +3,7 @@ import styled from '@emotion/styled';
3
3
  import { breakpoints, mq, spacing } from '@ndla/core';
4
4
  import { useTranslation } from 'react-i18next';
5
5
  import LayoutItem, { OneColumn } from '../Layout';
6
- import ProgrammeSubjects from './ProgrammeSubjects';
7
- import { GradesProps } from './ProgrammeSubjects';
6
+ import ProgrammeSubjects, { GradesProps } from './ProgrammeSubjects';
8
7
  import MessageBox from '../Messages/MessageBox';
9
8
  import { NavigationHeading } from '..';
10
9
  const StyledWrapper = styled.div`
@@ -9,8 +9,7 @@
9
9
  import React, { ReactNode } from 'react';
10
10
  import { useTranslation } from 'react-i18next';
11
11
  import styled from '@emotion/styled';
12
- import { css } from '@emotion/react';
13
- import { keyframes } from '@emotion/react';
12
+ import { css, keyframes } from '@emotion/react';
14
13
  import SafeLink from '@ndla/safelink';
15
14
  import { Additional, Core, HumanMaleBoard } from '@ndla/icons/common';
16
15
  import { breakpoints, colors, fonts, mq, spacing } from '@ndla/core';
@@ -8,8 +8,7 @@
8
8
 
9
9
  import React, { ReactNode } from 'react';
10
10
  import styled from '@emotion/styled';
11
- import { css } from '@emotion/react';
12
- import { keyframes } from '@emotion/react';
11
+ import { css, keyframes } from '@emotion/react';
13
12
  import { useTranslation } from 'react-i18next';
14
13
  import NoContentBox from '../NoContentBox';
15
14
  import ResourceItem from './ResourceItem';
@@ -134,8 +134,7 @@ const SearchField = ({
134
134
  onChange('');
135
135
  onFocus?.();
136
136
  inputRef?.current?.focus();
137
- }}
138
- onBlur={onBlur}>
137
+ }}>
139
138
  {t('welcomePage.resetSearch')}
140
139
  </button>
141
140
  )}
@@ -91,8 +91,8 @@ const SubjectNewContent = ({ heading, content }: Props) => (
91
91
  <StyledSubjectSectionTitle>{heading}</StyledSubjectSectionTitle>
92
92
  <nav>
93
93
  <StyledUl>
94
- {content.map((item) => (
95
- <StyledListItem>
94
+ {content.map((item, index) => (
95
+ <StyledListItem key={index}>
96
96
  <LeftWrapper>
97
97
  <ContentTypeBadge type={item.contentType} size="x-small" background border />
98
98
  <ContentLinkWrapper>
@@ -12,13 +12,14 @@ import Tooltip from '@ndla/tooltip';
12
12
  import styled from '@emotion/styled';
13
13
  import { ButtonV2 as Button } from '@ndla/button';
14
14
  import { Plus } from '@ndla/icons/action';
15
- import { FolderType } from './types';
15
+ import { IFolder } from '@ndla/types-learningpath-api';
16
16
 
17
17
  interface AddFolderButtonProps {
18
18
  canAddFolder: boolean;
19
- focusedFolder?: FolderType;
19
+ focusedFolder?: IFolder;
20
20
  setNewFolderParentId: (id?: string) => void;
21
21
  setShowTree: (value: boolean) => void;
22
+ loading?: boolean;
22
23
  }
23
24
 
24
25
  const StyledAddFolderButton = styled(Button)`
@@ -33,30 +34,28 @@ const StyledPlus = styled(Plus)`
33
34
  width: 24px;
34
35
  `;
35
36
 
36
- const AddFolderButton = ({ canAddFolder, setNewFolderParentId, focusedFolder, setShowTree }: AddFolderButtonProps) => {
37
+ const AddFolderButton = ({
38
+ canAddFolder,
39
+ loading,
40
+ setNewFolderParentId,
41
+ focusedFolder,
42
+ setShowTree,
43
+ }: AddFolderButtonProps) => {
37
44
  const { t } = useTranslation();
38
45
  const ref = useRef<HTMLButtonElement>(null);
46
+ const tooltip = loading
47
+ ? t('loading')
48
+ : canAddFolder
49
+ ? t('myNdla.newFolderUnder', { folderName: focusedFolder?.name })
50
+ : t('treeStructure.maxFoldersAlreadyAdded');
39
51
  return (
40
- <Tooltip
41
- tooltip={
42
- canAddFolder
43
- ? t('myNdla.newFolderUnder', {
44
- folderName: focusedFolder?.name,
45
- })
46
- : t('treeStructure.maxFoldersAlreadyAdded')
47
- }>
52
+ <Tooltip tooltip={tooltip}>
48
53
  <StyledAddFolderButton
49
54
  ref={ref}
50
55
  variant="outline"
51
56
  shape="pill"
52
- disabled={!canAddFolder}
53
- aria-label={
54
- canAddFolder
55
- ? t('myNdla.newFolderUnder', {
56
- folderName: focusedFolder?.name,
57
- })
58
- : t('treeStructure.maxFoldersAlreadyAdded')
59
- }
57
+ disabled={loading || !canAddFolder}
58
+ aria-label={tooltip}
60
59
  onMouseDown={(e) => {
61
60
  e.preventDefault();
62
61
  e.stopPropagation();
@@ -6,16 +6,17 @@
6
6
  *
7
7
  */
8
8
 
9
- import React, { KeyboardEvent } from 'react';
9
+ import React, { KeyboardEvent, forwardRef } from 'react';
10
10
  import styled from '@emotion/styled';
11
11
  import { useForwardedRef } from '@ndla/util';
12
12
  import { breakpoints, colors, mq, spacing } from '@ndla/core';
13
13
  import { ChevronUp, ChevronDown } from '@ndla/icons/common';
14
- import { forwardRef } from 'react';
14
+ import { IFolder } from '@ndla/types-learningpath-api';
15
15
  import { ButtonV2 as Button, IconButtonV2 as IconButton } from '@ndla/button';
16
16
  import { treestructureId } from './helperFunctions';
17
- import { FolderType, TreeStructureType } from './types';
17
+ import { TreeStructureType } from './types';
18
18
  import { arrowNavigation } from './arrowNavigation';
19
+ import ContentLoader from '../ContentLoader';
19
20
 
20
21
  interface StyledRowProps {
21
22
  isOpen: boolean;
@@ -52,14 +53,15 @@ interface Props {
52
53
  showTree: boolean;
53
54
  type: TreeStructureType;
54
55
  label?: string;
55
- focusedFolder?: FolderType;
56
- selectedFolder?: FolderType;
57
- setSelectedFolder: (folder: FolderType) => void;
56
+ focusedFolder?: IFolder;
57
+ selectedFolder?: IFolder;
58
+ setSelectedFolder: (folder: IFolder) => void;
59
+ loading?: boolean;
58
60
  onToggleTree: (open: boolean) => void;
59
- flattenedFolders: FolderType[];
61
+ flattenedFolders: IFolder[];
60
62
  onOpenFolder: (id: string) => void;
61
63
  onCloseFolder: (id: string) => void;
62
- setFocusedFolder: (folder: FolderType) => void;
64
+ setFocusedFolder: (folder: IFolder) => void;
63
65
  ariaDescribedby?: string;
64
66
  }
65
67
 
@@ -77,6 +79,7 @@ const ComboboxButton = forwardRef<HTMLButtonElement, Props>(
77
79
  setFocusedFolder,
78
80
  onOpenFolder,
79
81
  onCloseFolder,
82
+ loading,
80
83
  ariaDescribedby,
81
84
  },
82
85
  ref,
@@ -116,25 +119,48 @@ const ComboboxButton = forwardRef<HTMLButtonElement, Props>(
116
119
  innerRef.current?.focus();
117
120
  }
118
121
  }}>
119
- <StyledSelectedFolder
120
- ref={innerRef}
121
- tabIndex={0}
122
- id={treestructureId(type, 'combobox')}
123
- role="combobox"
124
- aria-controls={treestructureId(type, 'popup')}
125
- aria-haspopup="tree"
126
- aria-expanded={showTree}
127
- aria-labelledby={label ? treestructureId(type, 'label') : undefined}
128
- aria-activedescendant={focusedFolder ? treestructureId(type, focusedFolder.id) : undefined}
129
- aria-describedby={ariaDescribedby}
122
+ {loading && (
123
+ <ContentLoader width={1000} height={40}>
124
+ <rect x="15" y="0" width="1000" rx="3" ry="3" r="15" height="40" />
125
+ </ContentLoader>
126
+ )}
127
+ {!loading && (
128
+ <StyledSelectedFolder
129
+ ref={innerRef}
130
+ tabIndex={0}
131
+ id={treestructureId(type, 'combobox')}
132
+ role="combobox"
133
+ aria-controls={treestructureId(type, 'popup')}
134
+ aria-haspopup="tree"
135
+ aria-expanded={showTree}
136
+ aria-labelledby={label ? treestructureId(type, 'label') : undefined}
137
+ aria-activedescendant={focusedFolder ? treestructureId(type, focusedFolder.id) : undefined}
138
+ aria-describedby={ariaDescribedby}
139
+ variant="ghost"
140
+ colorTheme="light"
141
+ fontWeight="normal"
142
+ shape="sharp"
143
+ onKeyDown={onKeyDown}
144
+ onClick={() => {
145
+ innerRef.current?.focus();
146
+ onToggleTree(!showTree);
147
+ }}>
148
+ {selectedFolder?.name}
149
+ </StyledSelectedFolder>
150
+ )}
151
+ <IconButton
152
+ disabled={loading}
153
+ aria-busy={loading}
154
+ aria-hidden
155
+ aria-label=""
156
+ tabIndex={-1}
130
157
  variant="ghost"
131
- colorTheme="light"
132
- fontWeight="normal"
133
- shape="sharp"
134
- onKeyDown={onKeyDown}>
135
- {selectedFolder?.name}
136
- </StyledSelectedFolder>
137
- <IconButton aria-hidden aria-label="" tabIndex={-1} variant="ghost" colorTheme="greyLighter" size="small">
158
+ colorTheme="greyLighter"
159
+ size="small"
160
+ onClick={() => {
161
+ innerRef.current?.focus();
162
+ onToggleTree(!showTree);
163
+ }}>
138
164
  {showTree ? <ChevronUp /> : <ChevronDown />}
139
165
  </IconButton>
140
166
  </StyledRow>
@@ -14,7 +14,8 @@ import { Done } from '@ndla/icons/editor';
14
14
  import { ButtonV2 as Button } from '@ndla/button';
15
15
  import { colors, spacing, animations, spacingUnit, misc, fonts } from '@ndla/core';
16
16
  import SafeLink from '@ndla/safelink';
17
- import { CommonFolderItemsProps, FolderType } from './types';
17
+ import { IFolder } from '@ndla/types-learningpath-api';
18
+ import { CommonFolderItemsProps } from './types';
18
19
  import { arrowNavigation } from './arrowNavigation';
19
20
  import { treestructureId } from './helperFunctions';
20
21
 
@@ -104,8 +105,9 @@ const FolderNameLink = styled(SafeLink, { shouldForwardProp })<FolderNameProps>`
104
105
 
105
106
  interface Props extends CommonFolderItemsProps {
106
107
  isOpen: boolean;
107
- folder: FolderType;
108
+ folder: IFolder;
108
109
  isCreatingFolder?: boolean;
110
+ index: number;
109
111
  }
110
112
 
111
113
  const FolderItem = ({
@@ -125,9 +127,10 @@ const FolderItem = ({
125
127
  isCreatingFolder,
126
128
  type,
127
129
  closeTree,
130
+ index,
128
131
  }: Props) => {
129
132
  const { t } = useTranslation();
130
- const { id, name, isNavigation } = folder;
133
+ const { id, name } = folder;
131
134
  const ref = useRef<HTMLButtonElement & HTMLAnchorElement>(null);
132
135
  const selected = selectedFolder ? selectedFolder.id === id : false;
133
136
 
@@ -159,17 +162,17 @@ const FolderItem = ({
159
162
  }
160
163
  }, [focusedFolder, ref, id, isCreatingFolder, type]);
161
164
 
162
- const linkPath = `/minndla${!isNavigation ? '/folders' : ''}/${id}`;
165
+ const linkPath = `/minndla/folders/${id}`;
163
166
 
164
167
  const containsResource =
165
168
  targetResource && folder.resources.some((resource) => resource.resourceId === targetResource.resourceId);
166
169
 
167
170
  const emptyFolder = folder.subfolders.length === 0;
168
-
169
171
  const isMaxDepth = level > maxLevel;
170
-
171
172
  const hideArrow = isMaxDepth || emptyFolder;
172
173
 
174
+ const tabable = selected || focused || (!focusedFolder && !folder.parentId && index === 0);
175
+
173
176
  return type === 'navigation' ? (
174
177
  <FolderNameLink
175
178
  role="treeitem"
@@ -187,7 +190,7 @@ const FolderItem = ({
187
190
  arrowNavigation(e, id, visibleFolders, setFocusedFolder, onOpenFolder, onCloseFolder);
188
191
  }}
189
192
  to={loading ? '' : linkPath}
190
- tabIndex={selected || focused ? 0 : -1}
193
+ tabIndex={tabable ? 0 : -1}
191
194
  selected={selected}
192
195
  onFocus={() => setFocusedFolder(folder)}
193
196
  onClick={handleClickFolder}>
@@ -9,9 +9,9 @@
9
9
  import React, { ReactNode } from 'react';
10
10
  import styled from '@emotion/styled';
11
11
  import { animations } from '@ndla/core';
12
+ import { IFolder } from '@ndla/types-learningpath-api';
12
13
  import FolderItem from './FolderItem';
13
- import { CommonFolderItemsProps, FolderType, NewFolderInputFunc, OnCreatedFunc, TreeStructureType } from './types';
14
- import NavigationLink from './NavigationLink';
14
+ import { CommonFolderItemsProps, NewFolderInputFunc, OnCreatedFunc, TreeStructureType } from './types';
15
15
  import { treestructureId } from './helperFunctions';
16
16
 
17
17
  const StyledUL = styled.ul`
@@ -38,11 +38,11 @@ const StyledLI = styled.li<StyledLiProps>`
38
38
  `;
39
39
 
40
40
  export interface FolderItemsProps extends CommonFolderItemsProps {
41
- folders: FolderType[];
41
+ folders: IFolder[];
42
42
  newFolderParentId: string | undefined;
43
43
  onCancelNewFolder: () => void;
44
44
  openFolders: string[];
45
- parentFolder?: FolderType;
45
+ parentFolder?: IFolder;
46
46
  children?: ReactNode;
47
47
  onCreate: OnCreatedFunc;
48
48
  newFolderInput?: NewFolderInputFunc;
@@ -74,44 +74,39 @@ const FolderItems = ({
74
74
  aria-labelledby={level === 0 && type === 'picker' ? treestructureId(type, 'label') : undefined}
75
75
  role={level === 0 ? 'tree' : 'group'}>
76
76
  {children}
77
- {folders.map((folder) => {
77
+ {folders.map((folder, index) => {
78
78
  const { subfolders, id } = folder;
79
79
  const isOpen = openFolders?.includes(id);
80
80
 
81
81
  return (
82
82
  <StyledLI key={id} tabIndex={-1} role="none" type={type}>
83
- {folder.isNavigation ? (
84
- <NavigationLink folder={folder} isOpen={isOpen} level={level} type={type} loading={loading} {...rest} />
85
- ) : (
86
- <>
87
- <FolderItem
88
- folder={folder}
89
- isOpen={isOpen}
90
- level={level}
91
- loading={loading}
92
- type={type}
93
- isCreatingFolder={!!newFolderParentId}
94
- {...rest}
95
- />
96
- {((subfolders && isOpen) || newFolderParentId === id) && (
97
- <FolderItems
98
- parentFolder={folder}
99
- folders={subfolders}
100
- level={level + 1}
101
- loading={loading}
102
- type={type}
103
- newFolderParentId={newFolderParentId}
104
- onCancelNewFolder={onCancelNewFolder}
105
- openFolders={openFolders}
106
- newFolderInput={newFolderInput}
107
- onCreate={onCreate}
108
- {...rest}>
109
- {newFolderParentId === id && (
110
- <li role="none">{newFolderInput?.({ parentId: id, onClose: onCancelNewFolder, onCreate })}</li>
111
- )}
112
- </FolderItems>
83
+ <FolderItem
84
+ index={index}
85
+ folder={folder}
86
+ isOpen={isOpen}
87
+ level={level}
88
+ loading={loading}
89
+ type={type}
90
+ isCreatingFolder={!!newFolderParentId}
91
+ {...rest}
92
+ />
93
+ {((subfolders && isOpen) || newFolderParentId === id) && (
94
+ <FolderItems
95
+ parentFolder={folder}
96
+ folders={subfolders}
97
+ level={level + 1}
98
+ loading={loading}
99
+ type={type}
100
+ newFolderParentId={newFolderParentId}
101
+ onCancelNewFolder={onCancelNewFolder}
102
+ openFolders={openFolders}
103
+ newFolderInput={newFolderInput}
104
+ onCreate={onCreate}
105
+ {...rest}>
106
+ {newFolderParentId === id && (
107
+ <li role="none">{newFolderInput?.({ parentId: id, onClose: onCancelNewFolder, onCreate })}</li>
113
108
  )}
114
- </>
109
+ </FolderItems>
115
110
  )}
116
111
  </StyledLI>
117
112
  );
@@ -14,7 +14,7 @@ import { uniq } from 'lodash';
14
14
  import { IFolder } from '@ndla/types-learningpath-api';
15
15
  import FolderItems from './FolderItems';
16
16
  import { flattenFolders, treestructureId } from './helperFunctions';
17
- import { CommonTreeStructureProps, FolderType, NewFolderInputFunc, TreeStructureType } from './types';
17
+ import { CommonTreeStructureProps, NewFolderInputFunc, TreeStructureType } from './types';
18
18
  import ComboboxButton from './ComboboxButton';
19
19
  import AddFolderButton from './AddFolderButton';
20
20
 
@@ -69,7 +69,7 @@ const ScrollableDiv = styled.div<ScrollableDivProps>`
69
69
 
70
70
  export interface TreeStructureProps extends CommonTreeStructureProps {
71
71
  defaultOpenFolders?: string[];
72
- folders: FolderType[];
72
+ folders: IFolder[];
73
73
  label?: string;
74
74
  maxLevel?: number;
75
75
  newFolderInput?: NewFolderInputFunc;
@@ -96,8 +96,8 @@ const TreeStructure = ({
96
96
  const [openFolders, setOpenFolders] = useState<string[]>(defaultOpenFolders || []);
97
97
 
98
98
  const [newFolderParentId, setNewFolderParentId] = useState<string | undefined>();
99
- const [focusedFolder, _setFocusedFolder] = useState<FolderType | undefined>();
100
- const [selectedFolder, _setSelectedFolder] = useState<FolderType | undefined>();
99
+ const [focusedFolder, _setFocusedFolder] = useState<IFolder | undefined>();
100
+ const [selectedFolder, _setSelectedFolder] = useState<IFolder | undefined>();
101
101
  const [showTree, setShowTree] = useState(type === 'navigation');
102
102
 
103
103
  const flattenedFolders = useMemo(() => flattenFolders(folders, openFolders), [folders, openFolders]);
@@ -133,12 +133,12 @@ const TreeStructure = ({
133
133
  }
134
134
  };
135
135
 
136
- const setSelectedFolder = (folder: FolderType) => {
136
+ const setSelectedFolder = (folder: IFolder) => {
137
137
  _setSelectedFolder(folder);
138
138
  onSelectFolder?.(folder.id);
139
139
  };
140
140
 
141
- const setFocusedFolder = (folder: FolderType) => {
141
+ const setFocusedFolder = (folder: IFolder) => {
142
142
  _setFocusedFolder(folder);
143
143
  setNewFolderParentId(undefined);
144
144
 
@@ -189,6 +189,7 @@ const TreeStructure = ({
189
189
  {label && <StyledLabel id={treestructureId(type, 'label')}>{label}</StyledLabel>}
190
190
  {type === 'picker' && (
191
191
  <AddFolderButton
192
+ loading={loading}
192
193
  canAddFolder={!!canAddFolder}
193
194
  focusedFolder={focusedFolder}
194
195
  setNewFolderParentId={setNewFolderParentId}
@@ -203,6 +204,7 @@ const TreeStructure = ({
203
204
  showTree={showTree}
204
205
  type={type}
205
206
  label={label}
207
+ loading={loading}
206
208
  focusedFolder={focusedFolder}
207
209
  selectedFolder={selectedFolder}
208
210
  setSelectedFolder={setSelectedFolder}
@@ -6,13 +6,13 @@
6
6
  *
7
7
  */
8
8
 
9
+ import { IFolder } from '@ndla/types-learningpath-api';
9
10
  import { KeyboardEvent } from 'react';
10
- import { FolderType } from './types';
11
11
 
12
12
  const navigateVertical = (
13
- visibleFolders: FolderType[],
13
+ visibleFolders: IFolder[],
14
14
  folderId: string,
15
- setFocusedFolderId: (id: FolderType) => void,
15
+ setFocusedFolderId: (id: IFolder) => void,
16
16
  direction: 1 | -1,
17
17
  ) => {
18
18
  const currentIndex = visibleFolders.findIndex((folder) => folder.id === folderId);
@@ -27,8 +27,8 @@ const arrowKeys = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Enter'];
27
27
  export const arrowNavigation = (
28
28
  e: KeyboardEvent<HTMLElement>,
29
29
  id: string,
30
- visibleFolders: FolderType[],
31
- setFocusedFolderId: (id: FolderType) => void,
30
+ visibleFolders: IFolder[],
31
+ setFocusedFolderId: (id: IFolder) => void,
32
32
  onOpen: (id: string) => void,
33
33
  onClose: (id: string) => void,
34
34
  ) => {
@@ -1,12 +1,13 @@
1
- import { FolderType, TreeStructureType } from './types';
1
+ import { IFolder } from '@ndla/types-learningpath-api';
2
+ import { TreeStructureType } from './types';
2
3
 
3
- export const flattenFolders = (folders: FolderType[], openFolders?: string[]): FolderType[] => {
4
+ export const flattenFolders = (folders: IFolder[], openFolders?: string[]): IFolder[] => {
4
5
  return folders.reduce((acc, { subfolders, id, ...rest }) => {
5
6
  if (!subfolders || (openFolders && !openFolders.includes(id))) {
6
7
  return acc.concat({ subfolders, id, ...rest });
7
8
  }
8
9
  return acc.concat({ subfolders, id, ...rest }, flattenFolders(subfolders, openFolders));
9
- }, [] as FolderType[]);
10
+ }, [] as IFolder[]);
10
11
  };
11
12
 
12
13
  export const treestructureId = (type: TreeStructureType, modifier: string) => {
@@ -7,6 +7,5 @@
7
7
  */
8
8
 
9
9
  import TreeStructure from './TreeStructure';
10
- export type { FolderType } from './types';
11
10
  export type { TreeStructureProps } from './TreeStructure';
12
11
  export { TreeStructure };
@@ -9,11 +9,6 @@
9
9
  import { ReactNode } from 'react';
10
10
  import { IFolder, IResource } from '@ndla/types-learningpath-api';
11
11
 
12
- export interface FolderType extends IFolder {
13
- icon?: ReactNode;
14
- isNavigation?: boolean;
15
- }
16
-
17
12
  export type TreeStructureType = 'navigation' | 'picker';
18
13
 
19
14
  export type OnCreatedFunc = (folder: IFolder | undefined, parentId: string) => void;
@@ -35,14 +30,14 @@ export interface CommonTreeStructureProps {
35
30
  }
36
31
 
37
32
  export interface CommonFolderItemsProps extends CommonTreeStructureProps {
38
- focusedFolder?: FolderType;
33
+ focusedFolder?: IFolder;
39
34
  level: number;
40
35
  maxLevel: number;
41
- selectedFolder?: FolderType;
36
+ selectedFolder?: IFolder;
42
37
  onCloseFolder: (id: string) => void;
43
38
  onOpenFolder: (id: string) => void;
44
- setFocusedFolder: (folder: FolderType) => void;
45
- setSelectedFolder: (folder: FolderType) => void;
46
- visibleFolders: FolderType[];
39
+ setFocusedFolder: (folder: IFolder) => void;
40
+ setSelectedFolder: (folder: IFolder) => void;
41
+ visibleFolders: IFolder[];
47
42
  closeTree: () => void;
48
43
  }
package/src/i18n/index.ts CHANGED
@@ -8,4 +8,3 @@
8
8
 
9
9
  export { i18nInstance } from './i18n';
10
10
  export { formatNestedMessages } from './formatNestedMessages';
11
- export { formatMessage } from './formatMessage';
package/src/index.ts CHANGED
@@ -174,7 +174,7 @@ export { default as Breadcrumb, HeaderBreadcrumb, HomeBreadcrumb, ActionBreadcru
174
174
  export type { SimpleBreadcrumbItem, IndexedBreadcrumbItem } from './Breadcrumb';
175
175
 
176
176
  export type { BreadcrumbItemProps } from './Breadcrumblist';
177
- export { i18nInstance, formatNestedMessages, formatMessage } from './i18n';
177
+ export { i18nInstance, formatNestedMessages } from './i18n';
178
178
  export { default as ResourceGroup } from './ResourceGroup';
179
179
 
180
180
  export { default as LayoutItem, OneColumn, PageContainer, Content } from './Layout';
@@ -251,7 +251,7 @@ export { SnackbarProvider, useSnack, BaseSnack, DefaultSnackbar } from './SnackB
251
251
  export type { Snack, SnackContext } from './SnackBar';
252
252
  export { InfoBlock } from './InfoBlock';
253
253
  export { TreeStructure } from './TreeStructure';
254
- export type { FolderType, TreeStructureProps } from './TreeStructure';
254
+ export type { TreeStructureProps } from './TreeStructure';
255
255
 
256
256
  export { SearchField, SearchResultList, SearchResultItem, ActiveFilters, ToggleSearchButton } from './Search';
257
257
  export { default as LetterFilter } from './LetterFilter';