@ndla/ui 22.3.0 → 24.1.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 (148) hide show
  1. package/es/Article/ArticleAuthorContent.js +2 -4
  2. package/es/Article/ArticleFavoritesButton.js +2 -2
  3. package/es/AuthorInfo/AuthorInfo.js +29 -16
  4. package/es/ContentCard/ContentCard.js +66 -25
  5. package/es/FileList/File.js +34 -8
  6. package/es/FileList/FileList.js +29 -3
  7. package/es/InfoBox/InfoBox.js +10 -3
  8. package/es/InfoWidget/InfoWidget.js +67 -22
  9. package/es/MyNdla/Resource/FolderInput.js +19 -6
  10. package/es/NoContentBox/NoContentBox.js +1 -6
  11. package/es/Portrait/Portrait.js +19 -13
  12. package/es/Resource/BlockResource.js +7 -6
  13. package/es/Resource/ListResource.js +8 -7
  14. package/es/ResourceBox/ResourceBox.js +7 -35
  15. package/es/Search/ActiveFilterContent.js +4 -14
  16. package/es/Search/ActiveFilters.js +8 -19
  17. package/es/Search/SearchField.js +31 -52
  18. package/es/Search/SearchResult.js +113 -136
  19. package/es/Search/ToggleSearchButton.js +34 -43
  20. package/es/Search/index.js +2 -8
  21. package/es/TreeStructure/FolderItems.js +3 -3
  22. package/es/TreeStructure/FolderNameInput.js +32 -14
  23. package/es/all.css +1 -1
  24. package/es/index-javascript.js +0 -1
  25. package/es/index.js +2 -1
  26. package/es/locale/messages-en.js +3 -3
  27. package/es/locale/messages-nb.js +3 -3
  28. package/es/locale/messages-nn.js +3 -3
  29. package/es/locale/messages-se.js +3 -3
  30. package/es/locale/messages-sma.js +3 -3
  31. package/lib/Article/ArticleAuthorContent.js +9 -4
  32. package/lib/Article/ArticleFavoritesButton.js +2 -2
  33. package/lib/AuthorInfo/AuthorInfo.d.ts +1 -11
  34. package/lib/AuthorInfo/AuthorInfo.js +36 -20
  35. package/lib/ContentCard/ContentCard.d.ts +1 -15
  36. package/lib/ContentCard/ContentCard.js +60 -28
  37. package/lib/FileList/File.js +36 -12
  38. package/lib/FileList/FileList.js +28 -5
  39. package/lib/InfoBox/InfoBox.js +12 -5
  40. package/lib/InfoWidget/InfoWidget.js +61 -25
  41. package/lib/MyNdla/Resource/FolderInput.js +18 -5
  42. package/lib/NoContentBox/NoContentBox.js +1 -8
  43. package/lib/Portrait/Portrait.js +19 -14
  44. package/lib/Resource/BlockResource.js +7 -6
  45. package/lib/Resource/ListResource.js +8 -7
  46. package/lib/ResourceBox/ResourceBox.d.ts +1 -4
  47. package/lib/ResourceBox/ResourceBox.js +15 -35
  48. package/lib/Search/ActiveFilterContent.d.ts +13 -0
  49. package/lib/Search/ActiveFilterContent.js +4 -15
  50. package/lib/Search/ActiveFilters.d.ts +13 -0
  51. package/lib/Search/ActiveFilters.js +8 -20
  52. package/lib/Search/SearchField.d.ts +19 -0
  53. package/lib/Search/SearchField.js +32 -56
  54. package/lib/Search/SearchResult.d.ts +36 -0
  55. package/lib/Search/SearchResult.js +116 -159
  56. package/lib/Search/ToggleSearchButton.d.ts +16 -0
  57. package/lib/Search/ToggleSearchButton.js +36 -46
  58. package/lib/Search/index.d.ts +12 -0
  59. package/lib/Search/index.js +0 -54
  60. package/lib/SearchTypeResult/SearchTypeHeader.d.ts +1 -1
  61. package/lib/TreeStructure/FolderItems.js +3 -3
  62. package/lib/TreeStructure/FolderNameInput.js +34 -14
  63. package/lib/TreeStructure/types.d.ts +1 -1
  64. package/lib/all.css +1 -1
  65. package/lib/index-javascript.js +0 -74
  66. package/lib/index.d.ts +1 -0
  67. package/lib/index.js +38 -1
  68. package/lib/locale/messages-en.js +3 -3
  69. package/lib/locale/messages-nb.js +3 -3
  70. package/lib/locale/messages-nn.js +3 -3
  71. package/lib/locale/messages-se.js +3 -3
  72. package/lib/locale/messages-sma.js +3 -3
  73. package/package.json +10 -10
  74. package/src/Article/ArticleAuthorContent.tsx +1 -1
  75. package/src/Article/ArticleFavoritesButton.tsx +2 -2
  76. package/src/AuthorInfo/AuthorInfo.tsx +53 -19
  77. package/src/ContentCard/ContentCard.tsx +127 -35
  78. package/src/FileList/File.tsx +47 -17
  79. package/src/FileList/FileList.tsx +37 -8
  80. package/src/InfoBox/InfoBox.tsx +24 -4
  81. package/src/InfoWidget/InfoWidget.tsx +83 -34
  82. package/src/MyNdla/Resource/FolderInput.tsx +18 -3
  83. package/src/NoContentBox/NoContentBox.tsx +2 -7
  84. package/src/Portrait/Portrait.tsx +25 -10
  85. package/src/Resource/BlockResource.tsx +1 -1
  86. package/src/Resource/ListResource.tsx +3 -1
  87. package/src/ResourceBox/ResourceBox.tsx +1 -20
  88. package/src/Search/{ActiveFilterContent.jsx → ActiveFilterContent.tsx} +11 -12
  89. package/src/Search/{ActiveFilters.jsx → ActiveFilters.tsx} +20 -17
  90. package/src/Search/{SearchField.jsx → SearchField.tsx} +58 -68
  91. package/src/Search/SearchResult.tsx +360 -0
  92. package/src/Search/ToggleSearchButton.tsx +73 -0
  93. package/src/Search/component.search.scss +0 -4
  94. package/src/Search/index.ts +16 -0
  95. package/src/SectionHeading/SectionHeading.tsx +1 -0
  96. package/src/TreeStructure/FolderItems.tsx +1 -1
  97. package/src/TreeStructure/FolderNameInput.tsx +33 -9
  98. package/src/TreeStructure/types.ts +1 -1
  99. package/src/all.scss +0 -1
  100. package/src/index-javascript.js +0 -15
  101. package/src/index.ts +2 -0
  102. package/src/locale/messages-en.ts +3 -3
  103. package/src/locale/messages-nb.ts +3 -3
  104. package/src/locale/messages-nn.ts +3 -3
  105. package/src/locale/messages-se.ts +3 -3
  106. package/src/locale/messages-sma.ts +3 -3
  107. package/src/main.scss +0 -8
  108. package/es/BackgroundImage/BackgroundImage.js +0 -27
  109. package/es/BackgroundImage/index.js +0 -2
  110. package/es/Search/SearchFilter.js +0 -72
  111. package/es/Search/SearchFilterList.js +0 -115
  112. package/es/Search/SearchOverlay.js +0 -39
  113. package/es/Search/SearchPage.js +0 -178
  114. package/es/Search/SearchPopoverFilter.js +0 -152
  115. package/es/Search/SearchResultAuthor.js +0 -51
  116. package/lib/BackgroundImage/BackgroundImage.d.ts +0 -12
  117. package/lib/BackgroundImage/BackgroundImage.js +0 -40
  118. package/lib/BackgroundImage/index.d.ts +0 -2
  119. package/lib/BackgroundImage/index.js +0 -13
  120. package/lib/Search/SearchFilter.js +0 -88
  121. package/lib/Search/SearchFilterList.js +0 -137
  122. package/lib/Search/SearchOverlay.js +0 -62
  123. package/lib/Search/SearchPage.js +0 -207
  124. package/lib/Search/SearchPopoverFilter.js +0 -172
  125. package/lib/Search/SearchResultAuthor.js +0 -60
  126. package/src/AuthorInfo/component.author-info.scss +0 -54
  127. package/src/BackgroundImage/BackgroundImage.tsx +0 -32
  128. package/src/BackgroundImage/component.background-image.scss +0 -52
  129. package/src/BackgroundImage/index.ts +0 -3
  130. package/src/ContentCard/component.content-card.scss +0 -109
  131. package/src/FileList/component.file-list.scss +0 -102
  132. package/src/InfoBox/component.info-box.scss +0 -21
  133. package/src/InfoWidget/component.info-widget.scss +0 -52
  134. package/src/NoContentBox/component.no-content-box.scss +0 -17
  135. package/src/Portrait/component.portrait.scss +0 -29
  136. package/src/Search/SearchFilter.jsx +0 -82
  137. package/src/Search/SearchFilterList.jsx +0 -110
  138. package/src/Search/SearchOverlay.jsx +0 -38
  139. package/src/Search/SearchPage.jsx +0 -178
  140. package/src/Search/SearchPopoverFilter.jsx +0 -109
  141. package/src/Search/SearchResult.jsx +0 -239
  142. package/src/Search/SearchResultAuthor.jsx +0 -54
  143. package/src/Search/ToggleSearchButton.jsx +0 -64
  144. package/src/Search/component.search-filter.scss +0 -67
  145. package/src/Search/component.search-overlay.scss +0 -103
  146. package/src/Search/component.search-page.scss +0 -125
  147. package/src/Search/component.search-result-author.scss +0 -65
  148. package/src/Search/index.js +0 -34
@@ -1,11 +1,36 @@
1
+ import styled from '@emotion/styled';
2
+ import { breakpoints, colors, fonts, mq, spacing } from '@ndla/core';
1
3
  import { Download } from '@ndla/icons/common';
2
4
  import SafeLink from '@ndla/safelink';
3
5
  import Tooltip from '@ndla/tooltip';
4
6
  import React from 'react';
5
- import BEMHelper from 'react-bem-helper';
6
7
  import { FileFormat, FileType } from './FileList';
7
8
 
8
- const classes = BEMHelper('c-file-list');
9
+ const LinkText = styled.span`
10
+ & > span {
11
+ box-shadow: inset 0 -1px;
12
+ }
13
+ `;
14
+ const FileLink = styled(SafeLink)`
15
+ box-shadow: none;
16
+ position: relative;
17
+ color: ${colors.brand.primary};
18
+ margin-right: ${spacing.normal};
19
+ display: flex;
20
+ align-items: center;
21
+
22
+ &:last-child {
23
+ margin-right: 0;
24
+ }
25
+
26
+ &:hover,
27
+ &:focus,
28
+ &:active {
29
+ ${LinkText} {
30
+ box-shadow: none;
31
+ }
32
+ }
33
+ `;
9
34
 
10
35
  const renderFormat = (format: FileFormat, title: string, isPrimary: boolean, id: string, isDeadLink: boolean) => {
11
36
  const titleWithFormat = `${title} (${format.fileType.toUpperCase()})`;
@@ -22,20 +47,14 @@ const renderFormat = (format: FileFormat, title: string, isPrimary: boolean, id:
22
47
  }
23
48
 
24
49
  return (
25
- <SafeLink
26
- {...classes('link')}
27
- key={format.url}
28
- to={format.url}
29
- target="_blank"
30
- aria-label={titleWithFormat}
31
- aria-describedby={formatId}>
50
+ <FileLink key={format.url} to={format.url} target="_blank" aria-label={titleWithFormat} aria-describedby={formatId}>
32
51
  <Download />
33
52
  <Tooltip tooltip={format.tooltip}>
34
- <span {...classes('link-text')}>
53
+ <LinkText>
35
54
  <span>{isPrimary ? titleWithFormat : `(${format.fileType.toUpperCase()})`}</span>
36
- </span>
55
+ </LinkText>
37
56
  </Tooltip>
38
- </SafeLink>
57
+ </FileLink>
39
58
  );
40
59
  };
41
60
 
@@ -44,16 +63,27 @@ interface Props {
44
63
  file: FileType;
45
64
  }
46
65
 
66
+ const FileListItem = styled.li`
67
+ ${fonts.sizes('18px', '26px')};
68
+ font-weight: ${fonts.weight.semibold};
69
+ min-height: 60px;
70
+ background: ${colors.brand.greyLighter};
71
+ display: flex;
72
+ align-items: center;
73
+ flex-wrap: wrap;
74
+ margin-bottom: ${spacing.xsmall};
75
+ padding: ${spacing.small};
76
+ ${mq.range({ from: breakpoints.tablet })} {
77
+ padding: ${spacing.small} ${spacing.normal};
78
+ }
79
+ `;
80
+
47
81
  const File = ({ file, id }: Props) => {
48
82
  const formatLinks = file.formats.map((format, index) =>
49
83
  renderFormat(format, file.title, index === 0, id, !file.fileExists),
50
84
  );
51
85
 
52
- return (
53
- <li {...classes('item')} key={file.title}>
54
- {formatLinks}
55
- </li>
56
- );
86
+ return <FileListItem key={file.title}>{formatLinks}</FileListItem>;
57
87
  };
58
88
 
59
89
  export default File;
@@ -1,9 +1,8 @@
1
1
  import React from 'react';
2
- import BEMHelper from 'react-bem-helper';
2
+ import styled from '@emotion/styled';
3
+ import { colors, fonts, spacing } from '@ndla/core';
3
4
  import File from './File';
4
5
 
5
- const classes = BEMHelper('c-file-list');
6
-
7
6
  export interface FileType {
8
7
  title: string;
9
8
  formats: FileFormat[];
@@ -22,15 +21,45 @@ interface Props {
22
21
  files: FileType[];
23
22
  }
24
23
 
24
+ const FileListSection = styled.section`
25
+ margin: ${spacing.large} 0;
26
+ padding: ${spacing.small} 0 ${spacing.normal} ${spacing.normal};
27
+ border-left: 2px solid ${colors.brand.greyLightest};
28
+ font-family: ${fonts.sans};
29
+
30
+ .c-icon {
31
+ margin-top: 3px;
32
+ flex-shrink: 0;
33
+ margin-right: ${spacing.small};
34
+ height: 18px;
35
+ width: 18px;
36
+ }
37
+ `;
38
+
39
+ const FileListHeading = styled.h1`
40
+ ${fonts.sizes('16px', '18px')};
41
+ letter-spacing: 0.05em;
42
+ margin: 0 0 ${spacing.xsmall} 0;
43
+ padding-bottom: ${spacing.xsmall};
44
+ border-bottom: 2px solid ${colors.brand.greyLight};
45
+ font-weight: ${fonts.weight.bold};
46
+ text-transform: uppercase;
47
+ `;
48
+
49
+ const FilesList = styled.ul`
50
+ margin: 0;
51
+ padding: 0;
52
+ `;
53
+
25
54
  const FileList = ({ files, heading, id }: Props) => (
26
- <section {...classes()}>
27
- <h1 {...classes('heading')}>{heading}</h1>
28
- <ul {...classes('files')}>
55
+ <FileListSection>
56
+ <FileListHeading>{heading}</FileListHeading>
57
+ <FilesList>
29
58
  {files.map((file) => (
30
59
  <File key={`file-${id}-${file.title}`} file={file} id={id} />
31
60
  ))}
32
- </ul>
33
- </section>
61
+ </FilesList>
62
+ </FileListSection>
34
63
  );
35
64
 
36
65
  export default FileList;
@@ -1,11 +1,31 @@
1
+ import styled from '@emotion/styled';
2
+ import { breakpoints, colors, fonts, mq, spacing } from '@ndla/core';
1
3
  import React, { ReactNode } from 'react';
2
- import BEMHelper from 'react-bem-helper';
3
-
4
- const classes = BEMHelper('c-info-box');
5
4
 
6
5
  interface Props {
7
6
  children?: ReactNode;
8
7
  }
9
- const InfoBox = ({ children }: Props) => <div {...classes()}>{children}</div>;
8
+
9
+ const StyledInfoBox = styled.div`
10
+ background: ${colors.support.yellowLight};
11
+ padding: ${spacing.small} ${spacing.normal};
12
+ font-family: ${fonts.sans};
13
+ ${fonts.sizes('14px', '18px')};
14
+ margin-bottom: ${spacing.medium};
15
+ ${mq.range({ from: breakpoints.tablet })} {
16
+ ${fonts.sizes('16px', '20px')};
17
+ padding: ${spacing.normal};
18
+ }
19
+
20
+ & > *:first-child {
21
+ margin-top: 0;
22
+ }
23
+
24
+ & > *:last-child {
25
+ margin-bottom: 0;
26
+ }
27
+ `;
28
+
29
+ const InfoBox = ({ children }: Props) => <StyledInfoBox>{children}</StyledInfoBox>;
10
30
 
11
31
  export default InfoBox;
@@ -1,13 +1,67 @@
1
+ import styled from '@emotion/styled';
2
+ import { css } from '@emotion/core';
1
3
  import React, { ReactNode } from 'react';
2
- import BEMHelper from 'react-bem-helper';
3
4
  import { Forward } from '@ndla/icons/common';
4
-
5
5
  import SafeLink from '@ndla/safelink';
6
+ import { colors, fonts, spacing } from '@ndla/core';
6
7
 
7
8
  import SectionHeading from '../SectionHeading';
8
9
 
9
- const classes = BEMHelper('c-info-widget');
10
+ interface InfoWidgetSectionProps {
11
+ center?: boolean;
12
+ }
13
+ const InfoWidgetSection = styled.section<InfoWidgetSectionProps>`
14
+ max-width: 600px;
15
+ margin: ${(p) => p.center && '0 auto'};
16
+ `;
17
+
18
+ const InfoWidgetDescription = styled.div`
19
+ padding: ${spacing.normal};
20
+ background: ${colors.brand.lighter};
21
+ color: ${colors.brand.dark};
22
+ ${fonts.sizes('18px', '26px')};
23
+
24
+ p {
25
+ margin-top: 0;
26
+ &:last-child {
27
+ margin-bottom: 0;
28
+ }
29
+ }
30
+ `;
31
+
32
+ const InfoWidgetLinksContainer = styled.div`
33
+ display: flex;
34
+ align-items: center;
35
+ justify-content: flex-end;
36
+ `;
37
+
38
+ const iconLinkStyle = css`
39
+ width: 47px;
40
+ height: 47px;
41
+ border-radius: 100%;
42
+ background: ${colors.brand.lighter};
43
+ color: ${colors.brand.dark};
44
+ box-shadow: none;
45
+ display: flex;
46
+ justify-content: center;
47
+ align-items: center;
48
+ margin-right: ${spacing.small};
10
49
 
50
+ .c-icon {
51
+ width: 20px;
52
+ height: 20px;
53
+ }
54
+ `;
55
+
56
+ const IconSafeLink = styled(SafeLink)(iconLinkStyle);
57
+
58
+ const IconAnchor = styled.a(iconLinkStyle);
59
+ const IconSpan = styled.span(iconLinkStyle);
60
+
61
+ const StyledMainLink = styled.a`
62
+ color: ${colors.brand.dark};
63
+ ${fonts.sizes('16px', '24px')};
64
+ `;
11
65
  interface Props {
12
66
  heading: string;
13
67
  description: string;
@@ -26,36 +80,33 @@ interface Props {
26
80
  }
27
81
 
28
82
  const InfoWidget = ({ heading, description, mainLink, iconLinks, center = false }: Props) => (
29
- <section {...classes('', { center })}>
30
- <SectionHeading large className={classes('heading').className}>
31
- {heading}
32
- </SectionHeading>
33
- <div {...classes('description')}>
83
+ <InfoWidgetSection center={center}>
84
+ <SectionHeading large>{heading}</SectionHeading>
85
+ <InfoWidgetDescription>
34
86
  <p>{description}</p>
35
- </div>
36
- <div {...classes('links')}>
37
- {iconLinks &&
38
- iconLinks.map((link) => {
39
- if (link.url) {
40
- return (
41
- <SafeLink key={link.url} {...classes('icon-link')} to={link.url} aria-label={link.name}>
42
- {link.icon}
43
- </SafeLink>
44
- );
45
- }
46
- if (link.href) {
47
- return (
48
- <a key={link.href} href={link.href} {...classes('icon-link')} aria-label={link.name}>
49
- {link.icon}
50
- </a>
51
- );
52
- }
87
+ </InfoWidgetDescription>
88
+ <InfoWidgetLinksContainer>
89
+ {iconLinks?.map((link) => {
90
+ if (link.url) {
91
+ return (
92
+ <IconSafeLink key={link.url} to={link.url} aria-label={link.name}>
93
+ {link.icon}
94
+ </IconSafeLink>
95
+ );
96
+ }
97
+ if (link.href) {
53
98
  return (
54
- <span key={link.name} {...classes('icon-link')} aria-label={link.name}>
99
+ <IconAnchor key={link.href} href={link.href} aria-label={link.name}>
55
100
  {link.icon}
56
- </span>
101
+ </IconAnchor>
57
102
  );
58
- })}
103
+ }
104
+ return (
105
+ <IconSpan key={link.name} aria-label={link.name}>
106
+ {link.icon}
107
+ </IconSpan>
108
+ );
109
+ })}
59
110
  {mainLink.url ? (
60
111
  <div className="o-text-link__wrapper o-text-link__wrapper--right">
61
112
  <SafeLink className="o-text-link" to={mainLink.url}>
@@ -63,12 +114,10 @@ const InfoWidget = ({ heading, description, mainLink, iconLinks, center = false
63
114
  </SafeLink>
64
115
  </div>
65
116
  ) : (
66
- <a {...classes('main-link')} href={mainLink.href}>
67
- {mainLink.name}
68
- </a>
117
+ <StyledMainLink href={mainLink.href}>{mainLink.name}</StyledMainLink>
69
118
  )}
70
- </div>
71
- </section>
119
+ </InfoWidgetLinksContainer>
120
+ </InfoWidgetSection>
72
121
  );
73
122
 
74
123
  export default InfoWidget;
@@ -15,6 +15,7 @@ import { useTranslation } from 'react-i18next';
15
15
  import { colors, spacing } from '@ndla/core';
16
16
  import { Input } from '@ndla/forms';
17
17
  import { css } from '@emotion/core';
18
+ import { Done } from '@ndla/icons/editor';
18
19
 
19
20
  // Source: https://kovart.github.io/dashed-border-generator/
20
21
  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(
@@ -40,6 +41,9 @@ const inputWrapperStyle = css`
40
41
 
41
42
  const StyledInput = styled(Input)`
42
43
  && {
44
+ flex-grow: 1;
45
+ flex-basis: 0;
46
+ min-width: 0;
43
47
  line-height: 1.75em;
44
48
  color: ${colors.brand.primary};
45
49
  ::selection {
@@ -48,6 +52,12 @@ const StyledInput = styled(Input)`
48
52
  }
49
53
  `;
50
54
 
55
+ const Row = styled.div`
56
+ display: flex;
57
+ gap: ${spacing.xsmall};
58
+ padding-right: ${spacing.small};
59
+ `;
60
+
51
61
  interface Props {
52
62
  onAddFolder: (name: string) => void;
53
63
  onClose: () => void;
@@ -90,9 +100,14 @@ const FolderInput = ({ onAddFolder, onClose, autoSelect }: Props) => {
90
100
  </StyledFolderIcon>
91
101
  }
92
102
  iconRight={
93
- <IconButton aria-label={t('close')} size="small" ghostPill onClick={onClose}>
94
- <Cross />
95
- </IconButton>
103
+ <Row>
104
+ <IconButton aria-label={t('close')} size="small" ghostPill onClick={onClose}>
105
+ <Cross />
106
+ </IconButton>
107
+ <IconButton aria-label={t('save')} size="small" ghostPill onClick={() => onAddFolder(input)}>
108
+ <Done />
109
+ </IconButton>
110
+ </Row>
96
111
  }
97
112
  />
98
113
  );
@@ -7,21 +7,16 @@
7
7
  */
8
8
 
9
9
  import React from 'react';
10
- import BEMHelper from 'react-bem-helper';
11
10
  import Button from '@ndla/button';
12
11
 
13
- const classes = new BEMHelper({
14
- name: 'topic-resource',
15
- prefix: 'c-',
16
- });
17
-
18
12
  interface Props {
19
13
  onClick?: () => void;
20
14
  buttonText?: string;
21
15
  text: string;
22
16
  }
17
+
23
18
  export const NoContentBox = ({ buttonText, text, onClick }: Props) => (
24
- <div {...classes('additional-resources-trigger')}>
19
+ <div>
25
20
  <span>
26
21
  <div>
27
22
  <p>{text}</p>
@@ -6,13 +6,25 @@
6
6
  *
7
7
  */
8
8
 
9
+ import styled from '@emotion/styled';
9
10
  import React from 'react';
10
- import BEMHelper from 'react-bem-helper';
11
11
 
12
- const classes = new BEMHelper({
13
- name: 'portrait',
14
- prefix: 'c-',
15
- });
12
+ interface PortraitWrapperProps {
13
+ size: string;
14
+ }
15
+ const PortraitWrapper = styled.div<PortraitWrapperProps>`
16
+ width: ${(p) => p.size};
17
+ height: ${(p) => p.size};
18
+ span {
19
+ display: block;
20
+ border-radius: 50%;
21
+ background-color: rgba(0, 0, 0, 0.16);
22
+ background-size: cover;
23
+ background-position: center center;
24
+ width: ${(p) => p.size};
25
+ height: ${(p) => p.size};
26
+ }
27
+ `;
16
28
 
17
29
  interface Props {
18
30
  src: string;
@@ -21,10 +33,13 @@ interface Props {
21
33
  modifier?: 'small' | 'large';
22
34
  }
23
35
 
24
- const Portrait = ({ src, alt, modifier, className }: Props) => (
25
- <div {...classes('', modifier, className)}>
26
- <span role="img" aria-label={alt} style={{ backgroundImage: `url(${src})` }} />
27
- </div>
28
- );
36
+ const Portrait = ({ src, alt, modifier, className }: Props) => {
37
+ const size = modifier === 'small' ? '4rem' : modifier === 'large' ? '10rem' : '7rem';
38
+ return (
39
+ <PortraitWrapper size={size} className={className}>
40
+ <span role="img" aria-label={alt} style={{ backgroundImage: `url(${src})` }} />
41
+ </PortraitWrapper>
42
+ );
43
+ };
29
44
 
30
45
  export default Portrait;
@@ -94,7 +94,7 @@ const BlockImage = ({ image, loading }: BlockImageProps) => {
94
94
  </ContentLoader>
95
95
  );
96
96
  }
97
- return <Image alt={image.alt} src={image.src} />;
97
+ return <Image alt={image.alt} src={image.src} fallbackWidth={300} />;
98
98
  };
99
99
 
100
100
  interface BlockTitleProps {
@@ -127,7 +127,9 @@ interface ListResourceImageProps {
127
127
 
128
128
  const ListResourceImage = ({ resourceImage, loading, type }: ListResourceImageProps) => {
129
129
  if (!loading) {
130
- return <StyledImage alt={resourceImage.alt} src={resourceImage.src} />;
130
+ return (
131
+ <StyledImage alt={resourceImage.alt} src={resourceImage.src} fallbackWidth={type === 'compact' ? 56 : 136} />
132
+ );
131
133
  }
132
134
  return (
133
135
  <ContentLoader height={'100%'} width={'100%'} viewBox={null} preserveAspectRatio="none">
@@ -10,7 +10,6 @@ import React from 'react';
10
10
  import { breakpoints, fonts, mq, colors, spacing } from '@ndla/core';
11
11
  import { useTranslation } from 'react-i18next';
12
12
  import { Launch } from '@ndla/icons/common';
13
- import { LicenseByline } from '@ndla/licenses';
14
13
  import { SafeLinkButton } from '@ndla/safelink';
15
14
  import styled from '@emotion/styled';
16
15
  import Image from '../Image';
@@ -81,12 +80,6 @@ const StyledImage = styled(Image)`
81
80
  }
82
81
  `;
83
82
 
84
- const LincenseWrapper = styled.div`
85
- position: absolute;
86
- top: 9px;
87
- right: 0;
88
- `;
89
-
90
83
  interface ImageMeta {
91
84
  src: string;
92
85
  alt: string;
@@ -96,25 +89,13 @@ interface Props {
96
89
  image: ImageMeta;
97
90
  title: string;
98
91
  caption: string;
99
- licenseRights: string[];
100
- authors?: string[];
101
- locale?: string;
102
92
  url: string;
103
93
  }
104
94
 
105
- export const ResourceBox = ({ image, title, caption, licenseRights, locale, authors, url }: Props) => {
95
+ export const ResourceBox = ({ image, title, caption, url }: Props) => {
106
96
  const { t } = useTranslation();
107
97
  return (
108
98
  <ResourceBoxContainer>
109
- <LincenseWrapper>
110
- <LicenseByline licenseRights={licenseRights} locale={locale} marginRight color={colors.brand.tertiary}>
111
- {authors && authors.length > 0 && (
112
- <div className="c-figure__byline-author-buttons">
113
- <span className="c-figure__byline-authors">{authors.join(' ')}</span>
114
- </div>
115
- )}
116
- </LicenseByline>
117
- </LincenseWrapper>
118
99
  <StyledImage src={image.src} alt={image.alt} />
119
100
  <ContentWrapper>
120
101
  <Title>{title}</Title>
@@ -1,5 +1,4 @@
1
1
  import React from 'react';
2
- import PropTypes from 'prop-types';
3
2
  import styled from '@emotion/styled';
4
3
  import { Cross } from '@ndla/icons/action';
5
4
  import { spacing, mq, breakpoints, colors, misc, fonts } from '@ndla/core';
@@ -32,7 +31,17 @@ export const StyledActiveFilterTitle = styled('span')`
32
31
  padding-right: ${spacing.small};
33
32
  `;
34
33
 
35
- const ActiveFilterContent = ({ filter, onFilterRemove, ariaLabel }) => (
34
+ interface Props {
35
+ filter: {
36
+ value: string;
37
+ title: string;
38
+ filterName?: string;
39
+ };
40
+ onFilterRemove: (value: string, filterName?: string) => void;
41
+ ariaLabel: string;
42
+ }
43
+
44
+ const ActiveFilterContent = ({ filter, onFilterRemove, ariaLabel }: Props) => (
36
45
  <StyledActiveFilter
37
46
  aria-label={ariaLabel}
38
47
  type="button"
@@ -42,14 +51,4 @@ const ActiveFilterContent = ({ filter, onFilterRemove, ariaLabel }) => (
42
51
  </StyledActiveFilter>
43
52
  );
44
53
 
45
- ActiveFilterContent.propTypes = {
46
- filter: PropTypes.shape({
47
- value: PropTypes.string.isRequired,
48
- title: PropTypes.string.isRequired,
49
- filterName: PropTypes.string,
50
- }),
51
- onFilterRemove: PropTypes.func.isRequired,
52
- ariaLabel: PropTypes.string.isRequired,
53
- };
54
-
55
54
  export default ActiveFilterContent;
@@ -1,5 +1,4 @@
1
1
  import React from 'react';
2
- import PropTypes from 'prop-types';
3
2
  import styled from '@emotion/styled';
4
3
  import { css } from '@emotion/core';
5
4
  import { spacing, mq, breakpoints } from '@ndla/core';
@@ -7,7 +6,12 @@ import Tooltip from '@ndla/tooltip';
7
6
  import { useTranslation } from 'react-i18next';
8
7
  import ActiveFilterContent, { StyledActiveFilterTitle } from './ActiveFilterContent';
9
8
 
10
- const StyledActiveFilters = styled('ul')`
9
+ interface StyledActiveFiltersProps {
10
+ showOnSmallScreen?: boolean;
11
+ filterLength?: number;
12
+ }
13
+
14
+ const StyledActiveFilters = styled('ul')<StyledActiveFiltersProps>`
11
15
  margin: 0;
12
16
  padding: 0;
13
17
  flex-direction: column;
@@ -70,10 +74,22 @@ const StyledActiveFilterWrapper = styled('li')`
70
74
  }
71
75
  `;
72
76
 
73
- const getFilterLength = (filters) =>
77
+ const getFilterLength = (filters: Filter[]) =>
74
78
  filters.filter((filter) => filter.filterName === 'filter_subjects' && filter.title).length;
75
79
 
76
- const ActiveFilters = ({ filters, onFilterRemove, showOnSmallScreen }) => {
80
+ interface Filter {
81
+ title: string;
82
+ value: string;
83
+ filterName?: string;
84
+ }
85
+
86
+ interface Props {
87
+ filters: Filter[];
88
+ onFilterRemove: (value: string, filterName?: string) => void;
89
+ showOnSmallScreen?: boolean;
90
+ }
91
+
92
+ const ActiveFilters = ({ filters, onFilterRemove, showOnSmallScreen }: Props) => {
77
93
  const { t } = useTranslation();
78
94
  if (filters && filters.length > 0) {
79
95
  const filterLength = getFilterLength(filters);
@@ -90,7 +106,6 @@ const ActiveFilters = ({ filters, onFilterRemove, showOnSmallScreen }) => {
90
106
  })}>
91
107
  <ActiveFilterContent
92
108
  filter={filter}
93
- filterLength={filterLength}
94
109
  ariaLabel={t('searchPage.searchFilterMessages.removeFilter', {
95
110
  filterName: filter.title,
96
111
  })}
@@ -100,7 +115,6 @@ const ActiveFilters = ({ filters, onFilterRemove, showOnSmallScreen }) => {
100
115
  ) : (
101
116
  <ActiveFilterContent
102
117
  filter={filter}
103
- filterLength={filterLength}
104
118
  onFilterRemove={onFilterRemove}
105
119
  ariaLabel={t('searchPage.searchFilterMessages.removeFilter', {
106
120
  filterName: filter.title,
@@ -121,15 +135,4 @@ const ActiveFilters = ({ filters, onFilterRemove, showOnSmallScreen }) => {
121
135
  return null;
122
136
  };
123
137
 
124
- ActiveFilters.propTypes = {
125
- filters: PropTypes.arrayOf(
126
- PropTypes.shape({
127
- value: PropTypes.string.isRequired,
128
- title: PropTypes.string.isRequired,
129
- filterName: PropTypes.string,
130
- }),
131
- ),
132
- onFilterRemove: PropTypes.func.isRequired,
133
- };
134
-
135
138
  export default ActiveFilters;