@ndla/ui 12.0.1 → 13.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 (59) hide show
  1. package/es/Breadcrumb/Breadcrumb.js +101 -24
  2. package/es/Breadcrumb/BreadcrumbItem.js +70 -18
  3. package/es/Breadcrumb/HeaderBreadcrumb.js +63 -0
  4. package/es/Breadcrumb/HomeBreadcrumb.js +117 -0
  5. package/es/Breadcrumb/index.js +2 -1
  6. package/es/Breadcrumblist/index.js +1 -2
  7. package/es/all.css +1 -1
  8. package/es/index.js +1 -1
  9. package/es/locale/messages-en.js +59 -18
  10. package/es/locale/messages-nb.js +58 -17
  11. package/es/locale/messages-nn.js +59 -18
  12. package/es/locale/messages-se.js +56 -15
  13. package/es/locale/messages-sma.js +56 -15
  14. package/lib/Breadcrumb/Breadcrumb.d.ts +10 -12
  15. package/lib/Breadcrumb/Breadcrumb.js +104 -32
  16. package/lib/Breadcrumb/BreadcrumbItem.d.ts +15 -7
  17. package/lib/Breadcrumb/BreadcrumbItem.js +68 -20
  18. package/lib/Breadcrumb/HeaderBreadcrumb.d.ts +14 -0
  19. package/lib/Breadcrumb/HeaderBreadcrumb.js +76 -0
  20. package/lib/Breadcrumb/HomeBreadcrumb.d.ts +15 -0
  21. package/lib/Breadcrumb/HomeBreadcrumb.js +127 -0
  22. package/lib/Breadcrumb/index.d.ts +3 -1
  23. package/lib/Breadcrumb/index.js +11 -3
  24. package/lib/Breadcrumblist/index.js +0 -1
  25. package/lib/all.css +1 -1
  26. package/lib/index.d.ts +2 -1
  27. package/lib/index.js +10 -3
  28. package/lib/locale/messages-en.d.ts +43 -2
  29. package/lib/locale/messages-en.js +59 -18
  30. package/lib/locale/messages-nb.d.ts +43 -2
  31. package/lib/locale/messages-nb.js +58 -17
  32. package/lib/locale/messages-nn.d.ts +43 -2
  33. package/lib/locale/messages-nn.js +59 -18
  34. package/lib/locale/messages-se.d.ts +43 -2
  35. package/lib/locale/messages-se.js +56 -15
  36. package/lib/locale/messages-sma.d.ts +44 -3
  37. package/lib/locale/messages-sma.js +56 -15
  38. package/package.json +10 -10
  39. package/src/Breadcrumb/Breadcrumb.tsx +76 -40
  40. package/src/Breadcrumb/BreadcrumbItem.tsx +82 -18
  41. package/src/Breadcrumb/HeaderBreadcrumb.tsx +67 -0
  42. package/src/Breadcrumb/HomeBreadcrumb.tsx +88 -0
  43. package/src/Breadcrumb/index.ts +5 -1
  44. package/src/Breadcrumblist/index.tsx +0 -1
  45. package/src/index.ts +2 -1
  46. package/src/locale/__tests__/translations-test.ts +10 -0
  47. package/src/locale/messages-en.ts +55 -14
  48. package/src/locale/messages-nb.ts +54 -13
  49. package/src/locale/messages-nn.ts +56 -15
  50. package/src/locale/messages-se.ts +52 -11
  51. package/src/locale/messages-sma.ts +53 -12
  52. package/src/main.scss +0 -2
  53. package/es/Breadcrumb/BreadcrumbBlock.js +0 -90
  54. package/lib/Breadcrumb/BreadcrumbBlock.d.ts +0 -15
  55. package/lib/Breadcrumb/BreadcrumbBlock.js +0 -105
  56. package/src/.DS_Store +0 -0
  57. package/src/Breadcrumb/BreadcrumbBlock.tsx +0 -80
  58. package/src/Breadcrumb/component.breadcrumb-block.scss +0 -98
  59. package/src/Breadcrumb/component.breadcrumb.scss +0 -104
@@ -411,7 +411,7 @@ var messages = _objectSpread(_objectSpread({
411
411
  rules: 'Regler for bruk av bildet:'
412
412
  },
413
413
  images: {
414
- heading: 'Slik bruker du bilder fra artikkelen',
414
+ heading: 'Slik gjenbruker du bilder',
415
415
  description: 'Husk å kopiere teksten som skal legges ved bildet der du bruker det.',
416
416
  rules: 'Regler for bruk av bildet:',
417
417
  itemImage: {
@@ -425,18 +425,18 @@ var messages = _objectSpread(_objectSpread({
425
425
  title: 'Tittel'
426
426
  },
427
427
  text: {
428
- heading: 'Slik bruker du tekst fra artikkelen',
428
+ heading: 'Slik gjenbruker du teksten',
429
429
  description: 'Husk å henvise til kilden når du gjenbruker tekst.',
430
430
  rules: 'Regler for bruk av teksten:',
431
431
  published: 'Publiseringsdato'
432
432
  },
433
433
  audio: {
434
- heading: 'Slik bruker du lydfiler',
434
+ heading: 'Slik gjenbruker du lydfiler',
435
435
  description: 'Husk å kopiere teksten som skal legges ved lydfilen der du bruker den.',
436
436
  rules: 'Regler for bruk av lydfilen:'
437
437
  },
438
438
  video: {
439
- heading: 'Slik bruker du video fra artikkelen',
439
+ heading: 'Slik gjenbruker du videoer',
440
440
  description: 'Husk å kopiere teksten som skal legges ved videoen der du bruker den.',
441
441
  rules: 'Regler for bruk av videoen:',
442
442
  itemImage: {
@@ -444,31 +444,42 @@ var messages = _objectSpread(_objectSpread({
444
444
  }
445
445
  },
446
446
  other: {
447
- heading: 'Slik bruker du annet innhold fra artikkelen',
447
+ heading: 'Slik gjenbruker du annet innhold',
448
448
  description: 'Du finner retningslinjene for bruk av innholdet i innholdselementet',
449
449
  itemImage: {
450
450
  ariaLabel: 'Åpne i nytt vindu'
451
451
  }
452
452
  },
453
453
  h5p: {
454
- heading: 'Slik bruker du H5P-innhold fra artikkelen',
454
+ heading: 'Slik gjenbruker du H5P-innhold',
455
455
  description: 'Du finner retningslinjene for bruk av innholdet i H5P-elementet',
456
456
  rules: 'Regler for bruk av H5P:'
457
457
  },
458
458
  concept: {
459
- heading: 'Slik bruker du forklaringer fra artikkelen',
459
+ embedlink: {
460
+ heading: 'Slik viser du forklaringa i anna innhald',
461
+ description: 'Denne lenka viser forklaringa utan kontekst (meny og botntekst)',
462
+ copyTitle: 'Kopier innbyggingslenke',
463
+ hasCopiedTitle: 'Innbyggingslenke kopiert'
464
+ },
465
+ heading: 'Slik gjenbruker du forklaringer',
460
466
  description: 'Du finner retningslinjene for bruk av innholdet i forklaring-elementet',
461
467
  rules: 'Regler for bruk av forklaring:',
462
468
  title: 'Tittel'
463
469
  },
464
470
  files: {
465
- heading: 'Slik bruker du filer fra artikkelen',
471
+ heading: 'Slik gjenbruker du filer',
466
472
  description: 'Husk å kopier teksten som skal legges ved filen der du bruker den.',
467
473
  rules: 'Regler for bruk av filen:',
468
474
  itemImage: {
469
475
  ariaLabel: 'Åpne i nytt vindu'
470
476
  }
471
- }
477
+ },
478
+ title: 'Tittel',
479
+ originator: 'Opphavar',
480
+ rightsholder: 'Rettshavar',
481
+ source: 'Kjelde',
482
+ published: 'Publiseringsdato'
472
483
  },
473
484
  errorMessage: {
474
485
  title: 'Ops, noe gikk galt',
@@ -499,8 +510,10 @@ var messages = _objectSpread(_objectSpread({
499
510
  newsletterAria: 'Meld deg på vårt nyhetsbrev',
500
511
  youtube: 'NDLA på YouTube',
501
512
  youtubeAria: 'NDLA på YouTube',
502
- twitter: 'NDLA på Twitter',
503
- twitterAria: 'Besøk NDLA på Twitter',
513
+ linkedin: 'NDLA på LinkedIn',
514
+ linkedinAria: 'Besøk NDLA på LinkedIn',
515
+ instagram: 'NDLA på Instagram',
516
+ instagramAria: 'Besøk NDLA på Instagram',
504
517
  sharePage: 'Del denne siden',
505
518
  sharePageAria: 'Del denne siden'
506
519
  },
@@ -563,6 +576,10 @@ var messages = _objectSpread(_objectSpread({
563
576
  search: {
564
577
  placeholder: 'Søk'
565
578
  },
579
+ embedlink: {
580
+ copyTitle: 'Kopier innbyggingskode',
581
+ hasCopiedTitle: 'Innbyggingskode kopiert'
582
+ },
566
583
  filters: {
567
584
  subject: {
568
585
  useFilter: 'Bruk filter',
@@ -578,7 +595,8 @@ var messages = _objectSpread(_objectSpread({
578
595
  useFilter: 'Bruk filter',
579
596
  openFilter: 'Filtrer',
580
597
  closeFilter: 'Lukk filter',
581
- heading: 'Filter'
598
+ heading: 'Filter',
599
+ filteredBy: 'Filtrert på'
582
600
  },
583
601
  alphabet: {
584
602
  letterFilter: 'Vis innhald på (bokstaven) {{letter}}.'
@@ -728,7 +746,15 @@ var messages = _objectSpread(_objectSpread({
728
746
  },
729
747
  createdBy: {
730
748
  content: 'Ressursen',
731
- text: 'er hentet fra'
749
+ text: 'er hentet fra',
750
+ concept: {
751
+ content: 'Forklaringa',
752
+ text: 'er utarbeida av'
753
+ },
754
+ listing: {
755
+ content: 'Lista',
756
+ text: 'er utarbeida av'
757
+ }
732
758
  },
733
759
  fagfornyelse: {
734
760
  frontpage: {
@@ -761,7 +787,7 @@ var messages = _objectSpread(_objectSpread({
761
787
  frontpageMenu: {
762
788
  program: 'Utdanningsprogram',
763
789
  allsubjects: 'Alle fag',
764
- cursorText: 'Se smakebiter fra fag som kommer høsten 2022.'
790
+ cursorText: 'Se smakebiter fra fag under utvikling.'
765
791
  },
766
792
  navigation: {
767
793
  showLongerDescription: 'Vis hele emnebeskrivelsen',
@@ -782,7 +808,7 @@ var messages = _objectSpread(_objectSpread({
782
808
  title: 'Tittel',
783
809
  image: {
784
810
  altText: 'Alt-tekst',
785
- imageText: 'Bilettekst',
811
+ caption: 'Bilettekst',
786
812
  type: 'Filtype',
787
813
  width: 'Breidde',
788
814
  height: 'Høgde',
@@ -901,6 +927,21 @@ var messages = _objectSpread(_objectSpread({
901
927
  myAccount: 'Min konto',
902
928
  favourites: 'Favoritter',
903
929
  help: 'Hjelp'
930
+ },
931
+ labels: {
932
+ category: 'Kategori',
933
+ subject: 'Fag',
934
+ other: 'Anna'
935
+ },
936
+ listingPage: {
937
+ or: 'eller',
938
+ noFilters: 'Har ikkje noko å filtrera',
939
+ loadMore: 'Last meir'
940
+ },
941
+ siteNav: {
942
+ search: 'Søk',
943
+ contact: 'Kontakt',
944
+ help: 'Hjelp'
904
945
  }
905
946
  });
906
947
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ndla/ui",
3
- "version": "12.0.1",
3
+ "version": "13.0.0",
4
4
  "description": "UI component library for NDLA.",
5
5
  "license": "GPL-3.0",
6
6
  "main": "lib/index.js",
@@ -31,17 +31,17 @@
31
31
  "types"
32
32
  ],
33
33
  "dependencies": {
34
- "@ndla/button": "^2.2.0",
35
- "@ndla/carousel": "^1.2.6",
34
+ "@ndla/button": "^2.2.1",
35
+ "@ndla/carousel": "^1.2.7",
36
36
  "@ndla/core": "^2.1.1",
37
37
  "@ndla/hooks": "^1.1.4",
38
- "@ndla/icons": "^1.7.0",
39
- "@ndla/licenses": "^4.1.1",
40
- "@ndla/modal": "^1.2.7",
41
- "@ndla/notion": "^3.1.6",
42
- "@ndla/safelink": "^2.0.0",
38
+ "@ndla/icons": "^1.7.1",
39
+ "@ndla/licenses": "^4.1.2",
40
+ "@ndla/modal": "^1.2.8",
41
+ "@ndla/notion": "^3.1.8",
42
+ "@ndla/safelink": "^2.0.2",
43
43
  "@ndla/switch": "^0.1.5",
44
- "@ndla/tabs": "^1.1.5",
44
+ "@ndla/tabs": "^1.1.6",
45
45
  "@ndla/tooltip": "^0.3.5",
46
46
  "@ndla/util": "^3.0.0",
47
47
  "@reach/menu-button": "^0.16.2",
@@ -81,5 +81,5 @@
81
81
  "publishConfig": {
82
82
  "access": "public"
83
83
  },
84
- "gitHead": "aaadfb10e26a439688e1059890c1425756504093"
84
+ "gitHead": "e8c0504caecab0d3f093d57ccb007f529d838cd4"
85
85
  }
@@ -6,47 +6,83 @@
6
6
  *
7
7
  */
8
8
 
9
- import React, { ReactNode } from 'react';
10
- import BEMHelper, { ReturnObject } from 'react-bem-helper';
9
+ import React, { ReactNode, useRef } from 'react';
10
+ import { useComponentSize, useIsomorphicLayoutEffect } from '@ndla/hooks';
11
11
  import { uuid } from '@ndla/util';
12
- import { withTranslation, WithTranslation } from 'react-i18next';
13
- import { Home } from '@ndla/icons/common';
14
- import BreadcrumbItem from './BreadcrumbItem';
15
-
16
- const classes: BEMHelper<ReturnObject> = BEMHelper({
17
- name: 'breadcrumb',
18
- prefix: 'c-',
19
- });
20
-
21
- export interface BreadcrumbItemI {
22
- to: string;
23
- name: string;
24
- }
12
+ import styled from '@emotion/styled';
13
+ import { useTranslation } from 'react-i18next';
14
+ import BreadcrumbItem, { IndexedBreadcrumbItem, SimpleBreadcrumbItem } from './BreadcrumbItem';
25
15
 
26
- interface Props extends WithTranslation {
27
- children?: ReactNode;
28
- items: BreadcrumbItemI[];
29
- invertedStyle: boolean;
16
+ interface Props {
17
+ items: SimpleBreadcrumbItem[];
18
+ autoCollapse?: boolean;
19
+ collapseFirst?: boolean;
20
+ renderItem?: (item: IndexedBreadcrumbItem, totalCount: number) => ReactNode;
21
+ renderSeparator?: (item: IndexedBreadcrumbItem, totalCount: number) => ReactNode;
30
22
  }
31
23
 
32
- const Breadcrumb = ({ children, items, invertedStyle, t }: Props) => (
33
- <nav aria-label={t('breadcrumb.breadcrumb')}>
34
- {children}
35
- <ol {...classes('list')}>
36
- {items.map((item, i) => (
37
- <BreadcrumbItem
38
- invertedStyle={invertedStyle}
39
- classes={classes}
40
- home={i === 0}
41
- key={uuid()}
42
- isCurrent={i === items.length - 1}
43
- to={item.to}
44
- name={item.name}>
45
- {i === 0 ? <Home className="c-icon--20" title={item.name} /> : item.name}
46
- </BreadcrumbItem>
47
- ))}
48
- </ol>
49
- </nav>
50
- );
51
-
52
- export default withTranslation()(Breadcrumb);
24
+ const BreadcrumbNav = styled.nav``;
25
+
26
+ const StyledList = styled.ol`
27
+ display: inline-block;
28
+ padding-left: 0;
29
+ margin-bottom: 0;
30
+ margin-top: 0;
31
+ list-style: none;
32
+ `;
33
+
34
+ const Breadcrumb = ({ items, autoCollapse, renderItem, renderSeparator, collapseFirst }: Props) => {
35
+ const { t } = useTranslation();
36
+ const olRef = useRef<any>();
37
+ const containerRef = useRef<HTMLDivElement>(null);
38
+ // No idiomatic way of dealing with sets of refs yet
39
+ // See: https://github.com/facebook/react/issues/14072#issuecomment-446777406
40
+ const breadcrumbItemRefs = useRef(new Map()).current;
41
+ const size = useComponentSize(containerRef);
42
+
43
+ useIsomorphicLayoutEffect(() => {
44
+ if (!autoCollapse) {
45
+ return;
46
+ }
47
+ // Create an array of all breadcrumb item refs
48
+ const items = Array.from(breadcrumbItemRefs).map(([key, value]) => value);
49
+
50
+ // Clear max width on all items
51
+ items.forEach((el) => {
52
+ el.setMaxWidth('none');
53
+ });
54
+
55
+ // Set maxWidth on breadcrumb text items iteratively until
56
+ // the ordered list fits on a single line. It's on a single line
57
+ // if the height of the list is less then 70.
58
+ items.forEach((el) => {
59
+ if (olRef.current.offsetHeight > 60) {
60
+ el.setMaxWidth('40px');
61
+ }
62
+ });
63
+ }, [size]);
64
+
65
+ return (
66
+ <BreadcrumbNav ref={containerRef} aria-label={t('breadcrumb.breadcrumb')}>
67
+ <StyledList ref={olRef}>
68
+ {items.map((item, index) => (
69
+ <BreadcrumbItem
70
+ autoCollapse={autoCollapse}
71
+ renderItem={renderItem}
72
+ renderSeparator={renderSeparator}
73
+ ref={(element) =>
74
+ element === null || (!collapseFirst && index === 0) // skip first item which is never truncated
75
+ ? breadcrumbItemRefs.delete(item.to)
76
+ : breadcrumbItemRefs.set(item.to, element)
77
+ }
78
+ key={uuid()}
79
+ totalCount={items.length}
80
+ item={{ ...item, index }}
81
+ />
82
+ ))}
83
+ </StyledList>
84
+ </BreadcrumbNav>
85
+ );
86
+ };
87
+
88
+ export default Breadcrumb;
@@ -9,37 +9,101 @@
9
9
  import React, { useRef, useImperativeHandle, ReactNode, forwardRef } from 'react';
10
10
  import { ChevronRight } from '@ndla/icons/common';
11
11
  import SafeLink from '@ndla/safelink';
12
- import BEMHelper, { ReturnObject } from 'react-bem-helper';
12
+ import styled from '@emotion/styled';
13
+ import { mq, spacing, breakpoints } from '@ndla/core';
14
+ import { css } from '@emotion/core';
13
15
 
14
- interface Props {
15
- classes: BEMHelper<ReturnObject>;
16
- isCurrent: boolean;
17
- children: ReactNode;
16
+ export interface SimpleBreadcrumbItem {
18
17
  to: string | Partial<Location>;
19
- home: boolean;
20
18
  name: string;
21
- invertedStyle: boolean;
19
+ }
20
+
21
+ export interface IndexedBreadcrumbItem extends SimpleBreadcrumbItem {
22
+ index: number;
23
+ }
24
+
25
+ export interface BreadcrumbRenderProps {
26
+ item: IndexedBreadcrumbItem;
27
+ totalCount: number;
28
+ }
29
+
30
+ interface AutoCollapseProps {
31
+ autoCollapse?: boolean;
32
+ }
33
+
34
+ const StyledListItem = styled.li<AutoCollapseProps>`
35
+ margin-bottom: 0;
36
+ margin-left: 0;
37
+ display: inline-flex;
38
+ align-items: center;
39
+ :before {
40
+ display: none;
41
+ }
42
+
43
+ ${({ autoCollapse }) =>
44
+ !autoCollapse &&
45
+ css`
46
+ ${mq.range({ until: breakpoints.tablet })} {
47
+ display: block;
48
+ }
49
+ `}
50
+ `;
51
+
52
+ const CollapseContainer = styled.div<AutoCollapseProps>`
53
+ display: inline-block;
54
+ ${({ autoCollapse }) =>
55
+ autoCollapse &&
56
+ css`
57
+ text-overflow: ellipsis;
58
+ white-space: nowrap;
59
+ overflow: hidden;
60
+ display: inline-block;
61
+ `}
62
+ `;
63
+
64
+ const StyledChevron = styled(ChevronRight)`
65
+ margin: ${spacing.xxsmall};
66
+ `;
67
+
68
+ const StyledSafeLink = styled(SafeLink)`
69
+ color: inherit;
70
+ `;
71
+
72
+ interface Props {
73
+ item: IndexedBreadcrumbItem;
74
+ autoCollapse?: boolean;
75
+ totalCount: number;
76
+ renderItem?: (item: IndexedBreadcrumbItem, totalCount: number) => ReactNode;
77
+ renderSeparator?: (item: IndexedBreadcrumbItem, totalCount: number) => ReactNode;
22
78
  }
23
79
 
24
80
  const BreadcrumbItem = forwardRef<any, Props>(
25
- ({ to, children, classes, isCurrent, home, invertedStyle, name }, ref) => {
81
+ ({ renderItem, renderSeparator, item, totalCount, autoCollapse }, ref) => {
26
82
  const liRef = useRef<any>();
83
+
27
84
  useImperativeHandle(ref, () => ({
28
85
  setMaxWidth: (maxWidth: number) => {
29
86
  liRef.current.children[0].style.maxWidth = maxWidth;
30
87
  },
31
88
  }));
89
+
90
+ const { to, name, index } = item;
91
+ const isLast = index === totalCount - 1;
32
92
  return (
33
- <li {...classes('item', { home, invertedStyle })} ref={liRef}>
34
- {isCurrent ? (
35
- <span>{children}</span>
36
- ) : (
37
- <SafeLink to={to} aria-label={home ? name : undefined}>
38
- {children}
39
- </SafeLink>
40
- )}
41
- {!home && <ChevronRight />}
42
- </li>
93
+ <StyledListItem ref={liRef} autoCollapse={autoCollapse}>
94
+ <CollapseContainer autoCollapse={autoCollapse}>
95
+ {renderItem ? (
96
+ renderItem(item, totalCount)
97
+ ) : isLast ? (
98
+ <span>{name}</span>
99
+ ) : (
100
+ <StyledSafeLink to={to}>
101
+ <span>{name}</span>
102
+ </StyledSafeLink>
103
+ )}
104
+ </CollapseContainer>
105
+ {renderSeparator ? renderSeparator(item, totalCount) : !isLast && <StyledChevron />}
106
+ </StyledListItem>
43
107
  );
44
108
  },
45
109
  );
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Copyright (c) 2022-present, NDLA.
3
+ *
4
+ * This source code is licensed under the GPLv3 license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ import styled from '@emotion/styled';
10
+ import { colors, fonts, spacing } from '@ndla/core';
11
+ import { ChevronRight } from '@ndla/icons/common';
12
+ import SafeLink from '@ndla/safelink';
13
+ import React from 'react';
14
+ import Breadcrumb from './Breadcrumb';
15
+ import { IndexedBreadcrumbItem, SimpleBreadcrumbItem } from './BreadcrumbItem';
16
+
17
+ const StyledHeaderSafeLink = styled(SafeLink)`
18
+ ${fonts.sizes(14)};
19
+ font-weight: ${fonts.weight.bold};
20
+ color: ${colors.brand.primary};
21
+ `;
22
+
23
+ const StyledBlueRightChevron = styled(ChevronRight)`
24
+ color: ${colors.brand.primary};
25
+ margin: ${spacing.xxsmall};
26
+ `;
27
+ const StyledBlueSpan = styled.span`
28
+ color: ${colors.brand.primary};
29
+ `;
30
+ const StyledBlueSafeLink = styled(SafeLink)`
31
+ color: ${colors.brand.primary};
32
+ `;
33
+
34
+ interface Props {
35
+ items: SimpleBreadcrumbItem[];
36
+ }
37
+
38
+ const HeaderBreadcrumb = ({ items }: Props) => {
39
+ const renderItem = (item: IndexedBreadcrumbItem, totalCount: number) => {
40
+ if (item.index === totalCount - 1) {
41
+ return <StyledBlueSpan>{item.name}</StyledBlueSpan>;
42
+ }
43
+ return <StyledBlueSafeLink to={item.to}>{item.name}</StyledBlueSafeLink>;
44
+ };
45
+
46
+ const renderSeparator = (item: IndexedBreadcrumbItem, totalCount: number) => {
47
+ if (item.index === totalCount - 1) {
48
+ return null;
49
+ }
50
+ return <StyledBlueRightChevron />;
51
+ };
52
+
53
+ return (
54
+ <div>
55
+ <StyledHeaderSafeLink to={items[0]?.to}>{items[0]?.name}</StyledHeaderSafeLink>
56
+ <Breadcrumb
57
+ items={items.slice(1)}
58
+ renderItem={renderItem}
59
+ renderSeparator={renderSeparator}
60
+ autoCollapse
61
+ collapseFirst
62
+ />
63
+ </div>
64
+ );
65
+ };
66
+
67
+ export default HeaderBreadcrumb;
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Copyright (c) 2022-present, NDLA.
3
+ *
4
+ * This source code is licensed under the GPLv3 license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ import styled from '@emotion/styled';
10
+ import { breakpoints, colors, fonts, mq, spacing } from '@ndla/core';
11
+ import { ChevronRight, Home } from '@ndla/icons/common';
12
+ import SafeLink from '@ndla/safelink';
13
+ import React from 'react';
14
+ import Breadcrumb from './Breadcrumb';
15
+ import { IndexedBreadcrumbItem, SimpleBreadcrumbItem } from './BreadcrumbItem';
16
+
17
+ interface ThemeProps {
18
+ light: boolean | undefined;
19
+ }
20
+
21
+ const StyledSeparator = styled.div<ThemeProps>`
22
+ ${fonts.sizes('14px')};
23
+ margin: 0 ${spacing.small};
24
+ user-select: none;
25
+ color: ${({ light }) => (light ? colors.white : colors.text.primary)};
26
+ ${mq.range({ until: breakpoints.tablet })} {
27
+ display: none;
28
+ }
29
+ `;
30
+
31
+ const StyledIconSafeLink = styled(SafeLink)`
32
+ box-shadow: none;
33
+ border-bottom: none;
34
+ `;
35
+ const StyledHome = styled(Home)<ThemeProps>`
36
+ width: 20px;
37
+ height: 20px;
38
+ color: ${({ light }) => (light ? colors.white : colors.text.primary)};
39
+ `;
40
+ const StyledRightChevron = styled(ChevronRight)<ThemeProps>`
41
+ color: ${({ light }) => (light ? colors.white : colors.text.primary)};
42
+ margin: ${spacing.xxsmall};
43
+ `;
44
+ const StyledSpan = styled.span<ThemeProps>`
45
+ color: ${({ light }) => (light ? colors.white : colors.text.primary)};
46
+ `;
47
+ const StyledSafeLink = styled(SafeLink)<ThemeProps>`
48
+ color: ${({ light }) => (light ? colors.white : colors.text.primary)};
49
+ `;
50
+
51
+ interface Props {
52
+ items: SimpleBreadcrumbItem[];
53
+ light?: boolean;
54
+ }
55
+
56
+ const HomeBreadcrumb = ({ items, light }: Props) => {
57
+ const renderItem = (item: IndexedBreadcrumbItem, totalCount: number) => {
58
+ if (item.index === totalCount - 1) {
59
+ return <StyledSpan light={light}>{item.name}</StyledSpan>;
60
+ }
61
+ if (item.index === 0) {
62
+ return (
63
+ <StyledIconSafeLink aria-label={item.name} to={item.to}>
64
+ <StyledHome title={item.name} light={light} />
65
+ </StyledIconSafeLink>
66
+ );
67
+ }
68
+ return (
69
+ <StyledSafeLink light={light} to={item.to}>
70
+ {item.name}
71
+ </StyledSafeLink>
72
+ );
73
+ };
74
+
75
+ const renderSeparator = (item: IndexedBreadcrumbItem, totalCount: number) => {
76
+ if (item.index === totalCount - 1) {
77
+ return null;
78
+ }
79
+ if (item.index === 0) {
80
+ return <StyledSeparator light={light}>|</StyledSeparator>;
81
+ }
82
+ return <StyledRightChevron light={light} />;
83
+ };
84
+
85
+ return <Breadcrumb items={items} renderItem={renderItem} renderSeparator={renderSeparator} />;
86
+ };
87
+
88
+ export default HomeBreadcrumb;
@@ -8,6 +8,10 @@
8
8
 
9
9
  import Breadcrumb from './Breadcrumb';
10
10
 
11
- export { default as BreadcrumbBlock } from './BreadcrumbBlock';
11
+ export type { SimpleBreadcrumbItem, IndexedBreadcrumbItem } from './BreadcrumbItem';
12
+
13
+ export { default as HeaderBreadcrumb } from './HeaderBreadcrumb';
14
+
15
+ export { default as HomeBreadcrumb } from './HomeBreadcrumb';
12
16
 
13
17
  export default Breadcrumb;
@@ -1,4 +1,3 @@
1
1
  import Breadcrumblist from './Breadcrumblist';
2
2
 
3
- // export { default as BreadcrumbBlock } from './BreadcrumbBlock';
4
3
  export default Breadcrumblist;
package/src/index.ts CHANGED
@@ -151,7 +151,8 @@ export type { TopicProps } from './Topic';
151
151
  export { default as Aside } from './Aside';
152
152
  export { default as AuthorInfo } from './AuthorInfo';
153
153
 
154
- export { default as Breadcrumb, BreadcrumbBlock } from './Breadcrumb';
154
+ export { default as Breadcrumb, HeaderBreadcrumb, HomeBreadcrumb } from './Breadcrumb';
155
+ export type { SimpleBreadcrumbItem, IndexedBreadcrumbItem } from './Breadcrumb';
155
156
 
156
157
  export type { BreadcrumbItemProps } from './Breadcrumblist/Breadcrumblist';
157
158
  export { i18nInstance, formatNestedMessages, formatMessage } from './i18n';
@@ -10,6 +10,8 @@ import { validateTranslationFiles } from '@ndla/util';
10
10
  import messagesNB from '../messages-nb';
11
11
  import messagesNN from '../messages-nn';
12
12
  import messagesEN from '../messages-en';
13
+ import messagesSE from '../messages-se';
14
+ import messagesSMA from '../messages-sma';
13
15
 
14
16
  test('That all translations has all language keys', () => {
15
17
  const anyMissing = validateTranslationFiles(
@@ -26,6 +28,14 @@ test('That all translations has all language keys', () => {
26
28
  languageName: 'English',
27
29
  translationObject: messagesEN,
28
30
  },
31
+ {
32
+ languageName: 'Nordsamisk',
33
+ translationObject: messagesSE,
34
+ },
35
+ {
36
+ languageName: 'Sørsamisk',
37
+ translationObject: messagesSMA,
38
+ },
29
39
  ],
30
40
  'only-on-error',
31
41
  );