@ndla/ui 25.2.1 → 26.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/Article/ArticleByline.js +17 -7
  2. package/es/Article/ArticleSideBar.js +5 -4
  3. package/es/Breadcrumb/BreadcrumbItem.js +8 -7
  4. package/es/ErrorMessage/ErrorMessage.js +12 -6
  5. package/es/Frontpage/FrontpageHeader.js +7 -9
  6. package/es/LanguageSelector/LanguageSelector.js +12 -7
  7. package/es/LearningPaths/LearningPathInformation.js +8 -5
  8. package/es/Subject/SubjectHeader.js +5 -6
  9. package/es/TreeStructure/FolderItem.js +110 -94
  10. package/es/TreeStructure/FolderItems.js +26 -30
  11. package/es/TreeStructure/FolderNameInput.js +35 -27
  12. package/es/TreeStructure/NavigationLink.js +81 -0
  13. package/es/TreeStructure/TreeStructure.js +169 -45
  14. package/es/locale/messages-en.js +7 -22
  15. package/es/locale/messages-nb.js +8 -23
  16. package/es/locale/messages-nn.js +7 -22
  17. package/es/locale/messages-se.js +697 -712
  18. package/es/locale/messages-sma.js +8 -23
  19. package/lib/Article/ArticleByline.js +17 -7
  20. package/lib/Article/ArticleSideBar.js +5 -4
  21. package/lib/Breadcrumb/BreadcrumbItem.js +8 -7
  22. package/lib/ErrorMessage/ErrorMessage.d.ts +1 -0
  23. package/lib/ErrorMessage/ErrorMessage.js +12 -6
  24. package/lib/Frontpage/FrontpageHeader.d.ts +5 -6
  25. package/lib/Frontpage/FrontpageHeader.js +7 -11
  26. package/lib/LanguageSelector/LanguageSelector.js +13 -7
  27. package/lib/LearningPaths/LearningPathInformation.d.ts +2 -1
  28. package/lib/LearningPaths/LearningPathInformation.js +8 -5
  29. package/lib/Subject/SubjectHeader.js +14 -16
  30. package/lib/TreeStructure/FolderItem.d.ts +2 -3
  31. package/lib/TreeStructure/FolderItem.js +107 -92
  32. package/lib/TreeStructure/FolderItems.d.ts +1 -3
  33. package/lib/TreeStructure/FolderItems.js +26 -29
  34. package/lib/TreeStructure/FolderNameInput.d.ts +2 -1
  35. package/lib/TreeStructure/FolderNameInput.js +33 -26
  36. package/lib/TreeStructure/NavigationLink.d.ts +15 -0
  37. package/lib/TreeStructure/NavigationLink.js +100 -0
  38. package/lib/TreeStructure/TreeStructure.d.ts +1 -2
  39. package/lib/TreeStructure/TreeStructure.js +163 -45
  40. package/lib/TreeStructure/types.d.ts +4 -1
  41. package/lib/locale/messages-en.d.ts +4 -19
  42. package/lib/locale/messages-en.js +7 -22
  43. package/lib/locale/messages-nb.d.ts +4 -19
  44. package/lib/locale/messages-nb.js +8 -23
  45. package/lib/locale/messages-nn.d.ts +4 -19
  46. package/lib/locale/messages-nn.js +7 -22
  47. package/lib/locale/messages-se.d.ts +4 -19
  48. package/lib/locale/messages-se.js +697 -712
  49. package/lib/locale/messages-sma.d.ts +4 -19
  50. package/lib/locale/messages-sma.js +8 -23
  51. package/package.json +14 -14
  52. package/src/Article/ArticleByline.tsx +10 -3
  53. package/src/Article/ArticleSideBar.tsx +1 -0
  54. package/src/Breadcrumb/BreadcrumbItem.tsx +1 -1
  55. package/src/ErrorMessage/ErrorMessage.tsx +6 -0
  56. package/src/Frontpage/FrontpageHeader.tsx +5 -6
  57. package/src/LanguageSelector/LanguageSelector.tsx +4 -1
  58. package/src/LearningPaths/LearningPathInformation.tsx +3 -2
  59. package/src/Subject/SubjectHeader.tsx +1 -2
  60. package/src/TreeStructure/FolderItem.tsx +126 -104
  61. package/src/TreeStructure/FolderItems.tsx +51 -43
  62. package/src/TreeStructure/FolderNameInput.tsx +43 -28
  63. package/src/TreeStructure/NavigationLink.tsx +100 -0
  64. package/src/TreeStructure/TreeStructure.tsx +187 -61
  65. package/src/TreeStructure/types.ts +5 -1
  66. package/src/locale/messages-en.ts +9 -22
  67. package/src/locale/messages-nb.ts +10 -23
  68. package/src/locale/messages-nn.ts +9 -22
  69. package/src/locale/messages-se.ts +724 -738
  70. package/src/locale/messages-sma.ts +10 -23
  71. package/es/TreeStructure/TreeStructureWrapper.js +0 -13
  72. package/lib/TreeStructure/TreeStructureWrapper.d.ts +0 -12
  73. package/lib/TreeStructure/TreeStructureWrapper.js +0 -24
  74. package/src/TreeStructure/TreeStructureWrapper.tsx +0 -31
@@ -92,6 +92,8 @@ declare const messages: {
92
92
  myPage: string;
93
93
  deleteAccount: string;
94
94
  logout: string;
95
+ loginText: string;
96
+ loginTextLink: string;
95
97
  loginTerms: string;
96
98
  loginResourcePitch: string;
97
99
  loginWelcome: string;
@@ -167,6 +169,8 @@ declare const messages: {
167
169
  edit: string;
168
170
  delete: string;
169
171
  };
172
+ hideFolders: string;
173
+ showFolders: string;
170
174
  createFolder: string;
171
175
  maxFoldersAlreadyAdded: string;
172
176
  newFolder: {
@@ -392,13 +396,6 @@ declare const messages: {
392
396
  name: string;
393
397
  };
394
398
  };
395
- category: {
396
- fellesfag: string;
397
- yrkesfag: string;
398
- studiespesialiserende: string;
399
- imported: string;
400
- heading: string;
401
- };
402
399
  errorDescription: string;
403
400
  film: {
404
401
  header: string;
@@ -955,18 +952,6 @@ declare const messages: {
955
952
  text: string;
956
953
  };
957
954
  };
958
- fagfornyelse: {
959
- frontpage: {
960
- heading: string;
961
- text: string;
962
- blogHeading: string;
963
- };
964
- badge: {
965
- heading: string;
966
- text: string;
967
- linkText: string;
968
- };
969
- };
970
955
  frontPageToolbox: {
971
956
  heading: string;
972
957
  text: string;
@@ -33,6 +33,8 @@ var messages = _objectSpread(_objectSpread({
33
33
  edit: 'Endre mappenavn',
34
34
  "delete": 'Slett'
35
35
  },
36
+ hideFolders: 'Skjul alle mapper',
37
+ showFolders: 'Vis alle mapper',
36
38
  createFolder: 'Lag mappe',
37
39
  maxFoldersAlreadyAdded: 'Maks nivå av undermapper nådd',
38
40
  newFolder: {
@@ -248,13 +250,6 @@ var messages = _objectSpread(_objectSpread({
248
250
  name: 'Følg oss'
249
251
  }
250
252
  },
251
- category: {
252
- fellesfag: 'Fellesfag',
253
- yrkesfag: 'Yrkesfag',
254
- studiespesialiserende: 'Studieforberedende',
255
- imported: 'Spolte fag',
256
- heading: 'Hva lærer du?'
257
- },
258
253
  errorDescription: 'Beklager, en feil oppstod under lasting av fagene.',
259
254
  film: {
260
255
  header: 'NDLA film',
@@ -787,18 +782,6 @@ var messages = _objectSpread(_objectSpread({
787
782
  text: 'er utarbeidet av'
788
783
  }
789
784
  },
790
- fagfornyelse: {
791
- frontpage: {
792
- heading: 'Velkommen til sniktitt på Fagfornyelsen i NDLA',
793
- text: 'Høsten 2020 og 2021 vil de nye læreplanene tre i kraft. I NDLA har vi startet med dette arbeidet allerede. Våre innholdsansvarlige lager hver dag nye supre læringsressurser som er tilrettelagt for de nye planene. På denne siden kan du se dem allerede nå.',
794
- blogHeading: 'Vil du vite mer?'
795
- },
796
- badge: {
797
- heading: 'Denne siden er tilrettelagt for fagfornyelsen 2020/2021',
798
- text: 'Innholdet er under arbeid. Ikke på jakt etter dette?',
799
- linkText: 'Gå til ndla.no for dagens innhold'
800
- }
801
- },
802
785
  frontPageToolbox: {
803
786
  heading: 'Verktøykassa',
804
787
  text: 'Har du lyst til å bli god til å presentere, eller vil du lære å studere smartere ved hjelp av riktig studieteknikk? Trenger du råd om hvordan du leser mest mulig effektivt til eksamen? I verktøykassa til NDLA finner du masse gode tips og råd!',
@@ -970,14 +953,14 @@ var messages = _objectSpread(_objectSpread({
970
953
  folders_plural: '{{count}} mapper',
971
954
  folder: {
972
955
  folder: 'Mappe',
973
- "delete": 'Slett',
974
- edit: 'Rediger',
956
+ "delete": 'Slett mappe',
957
+ edit: 'Rediger mappe',
975
958
  missingName: 'Skriv navn på mappe',
976
959
  folderDeleted: '"{{folderName}}" er slettet'
977
960
  },
978
961
  tags: '{{count}} emneknagg',
979
962
  tags_plural: '{{count}} emneknagger',
980
- confirmDeleteFolder: 'Er du sikker på at du vil slette mappen? Denne handlingen kan ikke endres.',
963
+ confirmDeleteFolder: 'Er du sikker på at du vil slette mappa? Dersom mappa har undermappar vil disse også slettes. Denne handlinga kan ikkje endrast.',
981
964
  confirmDeleteTag: 'Er du sikker på at du vil slette emneknagg? Denne handlingen kan ikke endres.',
982
965
  myFolders: 'Mine mapper',
983
966
  myTags: 'Mine emneknagger',
@@ -999,6 +982,8 @@ var messages = _objectSpread(_objectSpread({
999
982
  myPage: 'Min side',
1000
983
  deleteAccount: 'Slett Min NDLA',
1001
984
  logout: 'Logg ut av Min NDLA',
985
+ loginText: 'Logg på med Feide for å få tilgang til Min NDLA. Vi ber om at du ikkje skriver noko støtende, personsensitiv informasjon eller andre persondata i tekstfelt. Les vår ',
986
+ loginTextLink: 'personvernerklæring her',
1002
987
  loginTerms: 'Logg på med Feide for å få tilgang. Ved å logge på godkjenner du våre vilkår for bruk',
1003
988
  loginResourcePitch: 'Ønsker du å favorittmerke denne siden?',
1004
989
  loginWelcome: 'Velkommen til NDLA! Her kan du organisere fagstoffet på <i>din</i> måte!',
@@ -1043,7 +1028,7 @@ var messages = _objectSpread(_objectSpread({
1043
1028
  linkCopied: 'Kopiert til utklippstavle',
1044
1029
  addToMyNdla: 'Legg i Min NDLA',
1045
1030
  addedToMyNdla: 'Lagt i Min NDLA',
1046
- addedToFolder: 'Ressurs er lagt i "{{folderName}}"',
1031
+ addedToFolder: 'Ressurs er lagt i ',
1047
1032
  removedFromFolder: 'Fjernet fra "{{folderName}}"',
1048
1033
  titleUpdated: 'Tittel oppdatert',
1049
1034
  tagsUpdated: 'Emneknagger oppdatert',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ndla/ui",
3
- "version": "25.2.1",
3
+ "version": "26.0.0",
4
4
  "description": "UI component library for NDLA.",
5
5
  "license": "GPL-3.0",
6
6
  "main": "lib/index.js",
@@ -31,22 +31,22 @@
31
31
  "types"
32
32
  ],
33
33
  "dependencies": {
34
- "@ndla/article-scripts": "^3.0.1",
35
- "@ndla/button": "^3.4.0",
36
- "@ndla/carousel": "^1.2.20",
34
+ "@ndla/article-scripts": "^3.0.2",
35
+ "@ndla/button": "^3.5.0",
36
+ "@ndla/carousel": "^1.2.22",
37
37
  "@ndla/core": "^2.3.5",
38
- "@ndla/forms": "^3.1.9",
39
- "@ndla/hooks": "^1.1.5",
40
- "@ndla/icons": "^1.11.8",
41
- "@ndla/licenses": "^5.0.14",
42
- "@ndla/modal": "^1.3.4",
43
- "@ndla/notion": "^3.1.38",
44
- "@ndla/safelink": "^2.2.12",
38
+ "@ndla/forms": "^3.2.1",
39
+ "@ndla/hooks": "^1.1.6",
40
+ "@ndla/icons": "^1.12.1",
41
+ "@ndla/licenses": "^5.0.16",
42
+ "@ndla/modal": "^1.3.6",
43
+ "@ndla/notion": "^3.1.40",
44
+ "@ndla/safelink": "^2.2.14",
45
45
  "@ndla/switch": "^0.1.13",
46
- "@ndla/tabs": "^1.1.19",
46
+ "@ndla/tabs": "^1.1.21",
47
47
  "@ndla/tooltip": "^2.2.1",
48
48
  "@ndla/types-learningpath-api": "^0.0.13",
49
- "@ndla/util": "^3.0.1",
49
+ "@ndla/util": "^3.1.0",
50
50
  "@reach/menu-button": "^0.16.2",
51
51
  "@reach/slider": "^0.16.0",
52
52
  "focus-trap-react": "^8.9.2",
@@ -85,5 +85,5 @@
85
85
  "publishConfig": {
86
86
  "access": "public"
87
87
  },
88
- "gitHead": "49b8d92c63e8aac6fb8891404d0262383c6523cb"
88
+ "gitHead": "e2029bce0a42c485f247a60ee895b2320fddc6ba"
89
89
  }
@@ -75,8 +75,11 @@ const getSuppliersText = (suppliers: SupplierProps[], t: TFunction) => {
75
75
  return '';
76
76
  }
77
77
  return suppliers.length > 1
78
- ? t('article.multipleSuppliersLabel', { names: renderContributors(suppliers, t) })
79
- : t('article.supplierLabel', { name: renderContributors(suppliers, t) });
78
+ ? t('article.multipleSuppliersLabel', {
79
+ names: renderContributors(suppliers, t),
80
+ interpolation: { escapeValue: false },
81
+ })
82
+ : t('article.supplierLabel', { name: renderContributors(suppliers, t), interpolation: { escapeValue: false } });
80
83
  };
81
84
 
82
85
  const ArticleByline = ({
@@ -114,7 +117,10 @@ const ArticleByline = ({
114
117
  {showPrimaryContributors && (
115
118
  <PrimaryContributorsWrapper>
116
119
  {authors.length > 0
117
- ? t('article.authorsLabel', { names: renderContributors(authors, t) })
120
+ ? t('article.authorsLabel', {
121
+ names: renderContributors(authors, t),
122
+ interpolation: { escapeValue: false },
123
+ })
118
124
  : getSuppliersText(suppliers, t)}
119
125
  </PrimaryContributorsWrapper>
120
126
  )}
@@ -150,6 +156,7 @@ const ArticleByline = ({
150
156
  size="small"
151
157
  borderShape="rounded"
152
158
  outline
159
+ aria-live="assertive"
153
160
  data-copy-string={copyPageUrlLink}
154
161
  copyNode={t('article.copyPageLinkCopied')}>
155
162
  {t('article.copyPageLink')}
@@ -89,6 +89,7 @@ const ArticleSideBar = ({
89
89
  size="small"
90
90
  width="full"
91
91
  outline
92
+ aria-live="assertive"
92
93
  copyNode={t('article.copyPageLinkCopied')}
93
94
  data-copy-string={copyPageUrlLink}>
94
95
  {t('article.copyPageLink')}
@@ -92,7 +92,7 @@ const BreadcrumbItem = forwardRef<any, Props>(
92
92
  const { to, name, index } = item;
93
93
  const isLast = index === totalCount - 1;
94
94
  return (
95
- <StyledListItem ref={liRef} autoCollapse={autoCollapse}>
95
+ <StyledListItem ref={liRef} autoCollapse={autoCollapse} aria-current={isLast ? 'page' : undefined}>
96
96
  <CollapseContainer autoCollapse={autoCollapse}>
97
97
  {renderItem ? (
98
98
  renderItem(item, totalCount)
@@ -47,6 +47,7 @@ interface Props {
47
47
  linksTitle?: string;
48
48
  back?: string;
49
49
  goToFrontPage?: string;
50
+ logInFailed?: string;
50
51
  };
51
52
  illustration?: {
52
53
  url: string;
@@ -78,6 +79,11 @@ export const ErrorMessage = ({ children, messages, illustration, illustrationEle
78
79
  <SafeLink to="/">{messages.goToFrontPage}</SafeLink>
79
80
  </div>
80
81
  )}
82
+ {messages.logInFailed && (
83
+ <div css={{ marginTop: spacing.xsmall }}>
84
+ <SafeLink to="/minndla">{messages.logInFailed}</SafeLink>
85
+ </div>
86
+ )}
81
87
  {children}
82
88
  </StyledErrorMessage>
83
89
  );
@@ -2,7 +2,6 @@ import React, { ReactNode } from 'react';
2
2
  import styled from '@emotion/styled';
3
3
  import { colors, spacing, spacingUnit, mq, breakpoints } from '@ndla/core';
4
4
  import SafeLink from '@ndla/safelink';
5
- import { WithTranslation, withTranslation } from 'react-i18next';
6
5
  import FrontpageHeaderIllustration from './illustrations/FrontpageHeaderIllustration';
7
6
  import SvgLogo from '../Logo/SvgLogo';
8
7
 
@@ -48,16 +47,16 @@ const HeaderIllustrationWrapper = styled.div`
48
47
  }
49
48
  `;
50
49
 
51
- export type FrontPageHeaderProps = {
50
+ interface FrontPageHeaderProps {
52
51
  locale: string;
53
52
  showHeader: boolean;
54
53
  children?: ReactNode;
55
- };
54
+ }
56
55
 
57
- const FrontpageHeader = ({ locale, showHeader = true, children, t }: FrontPageHeaderProps & WithTranslation) => (
56
+ const FrontpageHeader = ({ locale, showHeader = true, children }: FrontPageHeaderProps) => (
58
57
  <StyledHeaderWrapper>
59
58
  <StyledHeader>
60
- <StyledLogo to="/" aria-label={t('logo.altText')}>
59
+ <StyledLogo to="/" aria-hidden="true">
61
60
  <SvgLogo locale={locale} />
62
61
  </StyledLogo>
63
62
  {showHeader && (
@@ -70,4 +69,4 @@ const FrontpageHeader = ({ locale, showHeader = true, children, t }: FrontPageHe
70
69
  </StyledHeaderWrapper>
71
70
  );
72
71
 
73
- export default withTranslation()(FrontpageHeader);
72
+ export default FrontpageHeader;
@@ -115,7 +115,9 @@ interface StyledButtonProps {
115
115
  inverted?: boolean;
116
116
  }
117
117
 
118
- const StyledButton = styled(Button)<StyledButtonProps>`
118
+ const shouldForwardProp = (name: string) => name !== 'outline';
119
+
120
+ const StyledButton = styled(Button, { shouldForwardProp })<StyledButtonProps>`
119
121
  border-color: ${({ inverted, outline }) =>
120
122
  outline ? (inverted ? colors.white : colors.brand.primary) : 'transparent'};
121
123
  `;
@@ -139,6 +141,7 @@ const LanguageSelector = ({ currentLanguage, outline, center, inverted, alwaysVi
139
141
  </StyledSpan>
140
142
  <ChevronDown />
141
143
  </StyledButton>
144
+
142
145
  {isOpen && (
143
146
  <FocusTrapReact
144
147
  active
@@ -66,18 +66,19 @@ interface Props {
66
66
  description?: string;
67
67
  title: string;
68
68
  invertedStyle?: boolean;
69
+ id?: string;
69
70
  license?: {
70
71
  license: string;
71
72
  };
72
73
  }
73
74
 
74
- export const LearningPathInformation = ({ description, title, license, invertedStyle }: Props) => {
75
+ export const LearningPathInformation = ({ description, title, license, invertedStyle, id }: Props) => {
75
76
  const { rights } = getLicenseByAbbreviation(license?.license || '', 'nb');
76
77
  return (
77
78
  <section className="o-wrapper">
78
79
  <StyledWrapper invertedStyle={invertedStyle} className="c-article">
79
80
  <LicenseWrapper>
80
- <StyledHeader>{title}</StyledHeader>
81
+ <StyledHeader id={id}>{title}</StyledHeader>
81
82
  <LicenseByline licenseRights={rights} color={colors.brand.tertiary} />
82
83
  </LicenseWrapper>
83
84
  {description && <div dangerouslySetInnerHTML={{ __html: description }} />}
@@ -1,8 +1,7 @@
1
1
  import React from 'react';
2
2
  import styled from '@emotion/styled';
3
3
  import { css } from '@emotion/core';
4
- import { breakpoints } from '@ndla/util';
5
- import { colors, fonts, mq, spacing, spacingUnit } from '@ndla/core';
4
+ import { colors, fonts, mq, spacing, spacingUnit, breakpoints } from '@ndla/core';
6
5
  import OneColumn from '../Layout/OneColumn';
7
6
 
8
7
  type Types = 'mobile' | 'tablet' | 'desktop' | 'wide';
@@ -6,40 +6,38 @@
6
6
  *
7
7
  */
8
8
 
9
- import React, { KeyboardEvent, MouseEvent, useEffect, useRef } from 'react';
9
+ import React, { KeyboardEvent, useEffect, useRef } from 'react';
10
10
  import { useTranslation } from 'react-i18next';
11
11
  import styled from '@emotion/styled';
12
- import { ArrowDropDown } from '@ndla/icons/common';
12
+ import { ArrowDropDownRounded } from '@ndla/icons/common';
13
13
  import { Done } from '@ndla/icons/editor';
14
- import { MenuButton } from '@ndla/button';
15
- import { FolderOutlined } from '@ndla/icons/contentType';
16
- import { colors, spacing, misc, animations } from '@ndla/core';
14
+ import { ButtonV2 as Button } from '@ndla/button';
15
+ import { colors, spacing, animations, spacingUnit, misc, fonts } from '@ndla/core';
17
16
  import SafeLink from '@ndla/safelink';
18
17
  import { CommonFolderItemsProps, FolderType } from './types';
19
18
  import { arrowNavigation } from './arrowNavigation';
20
19
 
21
- const OpenButton = styled.button<{ isOpen: boolean }>`
22
- background: transparent;
23
- border: 0;
24
- transform: rotate(${({ isOpen }) => (isOpen ? '0' : '-90')}deg);
25
- padding: ${spacing.xsmall};
20
+ const OpenButton = styled.span<{ isOpen: boolean }>`
26
21
  display: flex;
27
- margin: 0;
28
- color: ${colors.brand.secondary};
22
+ align-items: center;
23
+ justify-content: center;
24
+ align-self: stretch;
25
+ color: ${colors.brand.tertiary};
26
+ ${misc.transition.default};
29
27
  cursor: pointer;
30
28
  &:hover {
31
29
  color: ${colors.brand.primary};
32
30
  }
33
31
  svg {
34
- width: 16px;
35
- height: 16px;
36
- transform: ${({ isOpen }) => (isOpen ? 'translateX(3px)' : 'translateY(3px)')};
32
+ width: 24px;
33
+ height: 24px;
34
+ transform: rotate(${({ isOpen }) => (isOpen ? '0' : '-90')}deg);
37
35
  }
38
36
  `;
39
37
 
40
- const FolderItemWrapper = styled.div`
41
- display: flex;
42
- align-items: center;
38
+ const StyledName = styled.span`
39
+ grid-column-start: 2;
40
+ text-align: left;
43
41
  `;
44
42
 
45
43
  const WrapperForFolderChild = styled.div`
@@ -47,44 +45,39 @@ const WrapperForFolderChild = styled.div`
47
45
  flex-direction: row;
48
46
  align-items: center;
49
47
  gap: ${spacing.xsmall};
48
+ margin-left: auto;
50
49
  `;
51
50
 
52
- const shouldForwardProp = (name: string) => !['selected', 'noArrow', 'fullWidth'].includes(name);
51
+ const shouldForwardProp = (name: string) => !['selected', 'noArrow', 'fullWidth', 'level'].includes(name);
53
52
 
54
53
  interface FolderNameProps {
55
54
  selected?: boolean;
56
55
  noArrow?: boolean;
57
56
  fullWidth?: boolean;
57
+ level: number;
58
+ isCreatingFolder?: boolean;
58
59
  }
59
60
 
60
- const FolderName = styled('button', { shouldForwardProp })<FolderNameProps>`
61
- cursor: pointer;
62
- padding: ${spacing.xsmall};
63
- margin: 0;
64
- outline-offset: -2px;
65
- outline-color: ${colors.brand.primary};
66
- margin-left: ${({ noArrow }) => (noArrow ? `29px` : `0px`)};
67
- flex-grow: ${({ fullWidth }) => fullWidth && 1};
61
+ const FolderName = styled(Button, { shouldForwardProp })<FolderNameProps>`
68
62
  display: grid;
69
- grid-template-columns: auto 1fr auto;
70
- align-items: center;
63
+ grid-template-columns: ${spacing.medium} 1fr auto;
64
+
65
+ padding-left: ${({ level }) => 0.75 * spacingUnit * level}px;
71
66
  gap: ${spacing.xxsmall};
72
- border: 0;
73
- border-radius: ${misc.borderRadius};
74
- box-shadow: none;
75
- background: ${({ selected }) => (selected ? colors.brand.lighter : 'transparent')};
76
- color: ${colors.text.primary};
67
+ border: none;
68
+ outline: none;
69
+ background: ${({ selected, isCreatingFolder }) => selected && !isCreatingFolder && colors.brand.lighter};
70
+ color: ${({ isCreatingFolder, selected }) =>
71
+ isCreatingFolder && selected ? colors.brand.primary : colors.text.primary};
77
72
  transition: ${animations.durations.superFast};
78
- text-align: left;
79
73
  line-height: 1;
80
74
  word-break: break-word;
81
75
  &:hover,
82
76
  &:focus {
77
+ box-shadow: none;
78
+ outline: none;
83
79
  background: ${({ selected }) => (selected ? colors.brand.light : colors.brand.lightest)};
84
- color: ${colors.brand.primary};
85
- + ${WrapperForFolderChild} {
86
- opacity: 1;
87
- }
80
+ color: ${colors.text.primary};
88
81
  }
89
82
  `;
90
83
 
@@ -92,25 +85,42 @@ const StyledDone = styled(Done)`
92
85
  color: ${colors.support.green};
93
86
  `;
94
87
 
95
- const FolderNameLink = FolderName.withComponent(SafeLink);
88
+ const FolderNameLink = styled(SafeLink, { shouldForwardProp })<FolderNameProps>`
89
+ display: grid;
90
+ align-items: center;
91
+ grid-template-columns: ${spacing.medium} 1fr auto;
92
+ padding: ${spacing.small} ${spacing.xxsmall};
93
+ margin-left: ${({ level }) => 0.75 * spacingUnit * level}px;
94
+ gap: ${spacing.xxsmall};
95
+ cursor: pointer;
96
+
97
+ border: none;
98
+ box-shadow: none;
99
+ color: ${({ selected }) => (selected ? colors.brand.primary : colors.text.primary)};
100
+ font-weight: ${({ selected }) => selected && fonts.weight.semibold};
101
+ font-size: ${fonts.sizes('16px')};
102
+ transition: ${animations.durations.superFast};
103
+ line-height: 1;
104
+ word-break: break-word;
105
+ &:hover,
106
+ &:focus {
107
+ color: ${colors.brand.primary};
108
+ }
109
+ `;
96
110
 
97
111
  interface Props extends CommonFolderItemsProps {
98
- hideArrow?: boolean;
99
112
  isOpen: boolean;
100
113
  folder: FolderType;
101
- noPaddingWhenArrowIsHidden?: boolean;
114
+ isCreatingFolder?: boolean;
102
115
  }
103
116
 
104
117
  const FolderItem = ({
105
118
  focusedFolderId,
106
- menuItems,
107
- hideArrow,
108
119
  folder,
109
120
  isOpen,
110
121
  level,
111
122
  loading,
112
123
  selectedFolder,
113
- noPaddingWhenArrowIsHidden,
114
124
  onCloseFolder,
115
125
  onOpenFolder,
116
126
  onSelectFolder,
@@ -120,9 +130,11 @@ const FolderItem = ({
120
130
  targetResource,
121
131
  visibleFolders,
122
132
  framed,
133
+ maxLevel,
134
+ isCreatingFolder,
123
135
  }: Props) => {
124
136
  const { t } = useTranslation();
125
- const { id, icon, name } = folder;
137
+ const { id, name } = folder;
126
138
  const ref = useRef<HTMLButtonElement & HTMLAnchorElement>(null);
127
139
  const selected = selectedFolder && selectedFolder.id === id;
128
140
  const focused = focusedFolderId === id;
@@ -145,79 +157,89 @@ const FolderItem = ({
145
157
  };
146
158
 
147
159
  useEffect(() => {
148
- if (focusedFolderId === id) {
160
+ if (focusedFolderId === id && !isCreatingFolder) {
149
161
  ref.current?.focus();
150
162
  }
151
- }, [focusedFolderId, ref, id]);
152
-
153
- const actions = menuItems?.map((item) => {
154
- const { onClick } = item;
155
- return {
156
- ...item,
157
- onClick: (e?: MouseEvent<HTMLDivElement>) => onClick(e, folder),
158
- };
159
- });
163
+ }, [focusedFolderId, ref, id, isCreatingFolder]);
160
164
 
161
165
  const linkPath = `/minndla${level > 0 ? '/folders' : ''}/${id}`;
162
166
 
163
167
  const containsResource =
164
168
  targetResource && folder.resources.some((resource) => resource.resourceId === targetResource.resourceId);
165
169
 
166
- return (
167
- <FolderItemWrapper>
170
+ const emptyFolder = folder.subfolders.length === 0;
171
+
172
+ const isMaxDepth = level > maxLevel;
173
+
174
+ const hideArrow = isMaxDepth || emptyFolder;
175
+
176
+ return onSelectFolder ? (
177
+ <FolderName
178
+ variant="ghost"
179
+ shape="sharp"
180
+ fontWeight="normal"
181
+ colorTheme="light"
182
+ ref={ref}
183
+ level={level}
184
+ fullWidth={framed}
185
+ onKeyDown={(e) => arrowNavigation(e, id, visibleFolders, setFocusedId, onOpenFolder, onCloseFolder)}
186
+ noArrow={hideArrow}
187
+ tabIndex={selected || focused ? 0 : -1}
188
+ selected={selected}
189
+ disabled={loading}
190
+ onFocus={() => setFocusedId(id)}
191
+ onClick={handleClickFolder}
192
+ isCreatingFolder={isCreatingFolder}>
168
193
  {!hideArrow && (
169
194
  <OpenButton
170
195
  tabIndex={-1}
171
196
  isOpen={isOpen}
172
- disabled={loading}
173
- onClick={() => (isOpen ? onCloseFolder(id) : onOpenFolder(id))}>
174
- <ArrowDropDown />
197
+ onClick={() => {
198
+ ref.current?.focus();
199
+ if (isOpen) {
200
+ onCloseFolder(id);
201
+ } else {
202
+ onOpenFolder(id);
203
+ }
204
+ }}>
205
+ <ArrowDropDownRounded />
175
206
  </OpenButton>
176
207
  )}
177
- {onSelectFolder ? (
178
- <>
179
- <FolderName
180
- fullWidth={framed}
181
- ref={ref}
182
- onKeyDown={(e) => arrowNavigation(e, id, visibleFolders, setFocusedId, onOpenFolder, onCloseFolder)}
183
- noArrow={hideArrow && !noPaddingWhenArrowIsHidden}
184
- tabIndex={selected || focused ? 0 : -1}
185
- selected={selected}
186
- disabled={loading}
187
- onFocus={() => setFocusedId(id)}
188
- onClick={handleClickFolder}>
189
- {icon || <FolderOutlined />}
190
- {name}
191
- <WrapperForFolderChild>
192
- {containsResource && <StyledDone title={t('myNdla.alreadyInFolder')} />}
193
- {actions && (
194
- <MenuButton
195
- onClick={(e) => e.stopPropagation()}
196
- size="xsmall"
197
- menuItems={actions}
198
- tabIndex={selected || id === focusedFolderId ? 0 : -1}
199
- />
200
- )}
201
- </WrapperForFolderChild>
202
- </FolderName>
203
- </>
204
- ) : (
205
- <FolderNameLink
206
- ref={ref}
207
- onKeyDown={(e: KeyboardEvent<HTMLElement>) =>
208
- arrowNavigation(e, id, visibleFolders, setFocusedId, onOpenFolder, onCloseFolder)
209
- }
210
- noArrow={hideArrow}
211
- to={loading ? '' : linkPath}
212
- tabIndex={selected || focused || level === 0 ? 0 : -1}
213
- selected={selected}
214
- onFocus={() => setFocusedId(id)}
215
- onClick={handleClickFolder}>
216
- {icon || <FolderOutlined />}
217
- {name}
218
- </FolderNameLink>
208
+ <StyledName>{name}</StyledName>
209
+ <WrapperForFolderChild>
210
+ {containsResource && <StyledDone title={t('myNdla.alreadyInFolder')} />}
211
+ </WrapperForFolderChild>
212
+ </FolderName>
213
+ ) : (
214
+ <FolderNameLink
215
+ ref={ref}
216
+ 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
+ selected={selected}
224
+ onFocus={() => setFocusedId(id)}
225
+ onClick={handleClickFolder}>
226
+ {(!hideArrow || level === 0) && (
227
+ <OpenButton
228
+ tabIndex={-1}
229
+ isOpen={isOpen}
230
+ onClick={() => {
231
+ ref.current?.focus();
232
+ if (isOpen) {
233
+ onCloseFolder(id);
234
+ } else {
235
+ onOpenFolder(id);
236
+ }
237
+ }}>
238
+ <ArrowDropDownRounded />
239
+ </OpenButton>
219
240
  )}
220
- </FolderItemWrapper>
241
+ <StyledName>{name}</StyledName>
242
+ </FolderNameLink>
221
243
  );
222
244
  };
223
245