@ndla/ui 4.3.0 → 5.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 (177) hide show
  1. package/README.md +11 -11
  2. package/es/Article/Article.js +3 -7
  3. package/es/Article/ArticleByline.js +4 -4
  4. package/es/Article/ArticleNotions.js +37 -92
  5. package/es/ContentTypeBadge/ContentTypeBadge.js +10 -1
  6. package/es/FactBox/FactBox.js +36 -11
  7. package/es/Filter/FilterButtons.js +18 -17
  8. package/es/Filter/FilterCarousel.js +198 -0
  9. package/es/Masthead/Masthead.js +24 -2
  10. package/es/Notion/ConceptNotion.js +57 -0
  11. package/es/Notion/FigureNotion.js +82 -0
  12. package/es/Notion/Notion.js +122 -48
  13. package/es/Notion/NotionImage.js +47 -0
  14. package/es/Notion/NotionVisualElement.js +38 -0
  15. package/es/Notion/index.js +2 -1
  16. package/es/SearchTypeResult/ActiveFilterContent.js +17 -2
  17. package/es/SearchTypeResult/ActiveFilters.js +64 -50
  18. package/es/SearchTypeResult/PopupFilter.js +28 -125
  19. package/es/SearchTypeResult/ResultNavigation.js +41 -14
  20. package/es/SearchTypeResult/SearchFieldHeader.js +20 -41
  21. package/es/SearchTypeResult/SearchFilterContent.js +61 -0
  22. package/es/SearchTypeResult/SearchHeader.js +51 -28
  23. package/es/SearchTypeResult/SearchItem.js +64 -190
  24. package/es/SearchTypeResult/SearchItemList.js +132 -0
  25. package/es/SearchTypeResult/SearchItems.js +17 -13
  26. package/es/SearchTypeResult/SearchNotionItem.js +13 -13
  27. package/es/SearchTypeResult/SearchNotionsResult.js +16 -21
  28. package/es/SearchTypeResult/SearchTypeHeader.js +43 -26
  29. package/es/SearchTypeResult/SearchTypeResult.js +7 -5
  30. package/es/SearchTypeResult/SearchViewType.js +93 -0
  31. package/es/SearchTypeResult/components/ItemContexts.js +125 -0
  32. package/es/SearchTypeResult/components/ItemResourceHeader.js +87 -0
  33. package/es/SearchTypeResult/components/ItemTopicHeader.js +56 -0
  34. package/es/SearchTypeResult/components/SubjectFilters.js +177 -0
  35. package/es/SearchTypeResult/index.js +2 -1
  36. package/es/all.css +1 -1
  37. package/es/index-javascript.js +1 -2
  38. package/es/index.js +3 -2
  39. package/es/locale/messages-en.js +21 -6
  40. package/es/locale/messages-nb.js +21 -6
  41. package/es/locale/messages-nn.js +22 -7
  42. package/es/model/ContentType.js +2 -1
  43. package/es/shapes.js +1 -1
  44. package/lib/Article/Article.d.ts +4 -5
  45. package/lib/Article/Article.js +3 -7
  46. package/lib/Article/ArticleByline.js +4 -4
  47. package/lib/Article/ArticleNotions.d.ts +3 -8
  48. package/lib/Article/ArticleNotions.js +41 -90
  49. package/lib/ContentTypeBadge/ContentTypeBadge.d.ts +1 -0
  50. package/lib/ContentTypeBadge/ContentTypeBadge.js +14 -2
  51. package/lib/FactBox/FactBox.js +41 -8
  52. package/lib/Filter/FilterButtons.d.ts +3 -10
  53. package/lib/Filter/FilterButtons.js +19 -17
  54. package/lib/Filter/FilterCarousel.d.ts +13 -0
  55. package/lib/Filter/FilterCarousel.js +207 -0
  56. package/lib/Masthead/Masthead.js +30 -2
  57. package/lib/Notion/ConceptNotion.d.ts +25 -0
  58. package/lib/Notion/ConceptNotion.js +79 -0
  59. package/lib/Notion/FigureNotion.d.ts +23 -0
  60. package/lib/Notion/FigureNotion.js +97 -0
  61. package/lib/Notion/Notion.d.ts +24 -11
  62. package/lib/Notion/Notion.js +120 -48
  63. package/lib/Notion/NotionImage.d.ts +15 -0
  64. package/lib/Notion/NotionImage.js +63 -0
  65. package/lib/Notion/NotionVisualElement.d.ts +22 -0
  66. package/lib/Notion/NotionVisualElement.js +51 -0
  67. package/lib/Notion/index.d.ts +1 -0
  68. package/lib/Notion/index.js +8 -0
  69. package/lib/SearchTypeResult/ActiveFilterContent.js +16 -9
  70. package/lib/SearchTypeResult/ActiveFilters.d.ts +2 -1
  71. package/lib/SearchTypeResult/ActiveFilters.js +65 -50
  72. package/lib/SearchTypeResult/PopupFilter.d.ts +13 -19
  73. package/lib/SearchTypeResult/PopupFilter.js +27 -123
  74. package/lib/SearchTypeResult/ResultNavigation.d.ts +2 -2
  75. package/lib/SearchTypeResult/ResultNavigation.js +38 -14
  76. package/lib/SearchTypeResult/SearchFieldHeader.d.ts +3 -8
  77. package/lib/SearchTypeResult/SearchFieldHeader.js +18 -39
  78. package/lib/SearchTypeResult/SearchFilterContent.d.ts +16 -0
  79. package/lib/SearchTypeResult/SearchFilterContent.js +67 -0
  80. package/lib/SearchTypeResult/SearchHeader.d.ts +3 -7
  81. package/lib/SearchTypeResult/SearchHeader.js +59 -30
  82. package/lib/SearchTypeResult/SearchItem.d.ts +8 -12
  83. package/lib/SearchTypeResult/SearchItem.js +64 -187
  84. package/lib/SearchTypeResult/SearchItemList.d.ts +10 -0
  85. package/lib/SearchTypeResult/SearchItemList.js +139 -0
  86. package/lib/SearchTypeResult/SearchItems.d.ts +4 -3
  87. package/lib/SearchTypeResult/SearchItems.js +18 -13
  88. package/lib/SearchTypeResult/SearchNotionItem.js +13 -13
  89. package/lib/SearchTypeResult/SearchNotionsResult.d.ts +2 -4
  90. package/lib/SearchTypeResult/SearchNotionsResult.js +23 -23
  91. package/lib/SearchTypeResult/SearchTypeHeader.d.ts +2 -2
  92. package/lib/SearchTypeResult/SearchTypeHeader.js +40 -25
  93. package/lib/SearchTypeResult/SearchTypeResult.d.ts +7 -6
  94. package/lib/SearchTypeResult/SearchTypeResult.js +7 -5
  95. package/lib/SearchTypeResult/SearchViewType.d.ts +13 -0
  96. package/lib/SearchTypeResult/SearchViewType.js +99 -0
  97. package/lib/SearchTypeResult/components/ItemContexts.d.ts +19 -0
  98. package/lib/SearchTypeResult/components/ItemContexts.js +134 -0
  99. package/lib/SearchTypeResult/components/ItemResourceHeader.d.ts +16 -0
  100. package/lib/SearchTypeResult/components/ItemResourceHeader.js +98 -0
  101. package/lib/SearchTypeResult/components/ItemTopicHeader.d.ts +17 -0
  102. package/lib/SearchTypeResult/components/ItemTopicHeader.js +67 -0
  103. package/lib/SearchTypeResult/components/SubjectFilters.d.ts +32 -0
  104. package/lib/SearchTypeResult/components/SubjectFilters.js +192 -0
  105. package/lib/SearchTypeResult/index.d.ts +2 -1
  106. package/lib/SearchTypeResult/index.js +8 -0
  107. package/lib/all.css +1 -1
  108. package/lib/index-javascript.js +0 -20
  109. package/lib/index.d.ts +2 -1
  110. package/lib/index.js +24 -1
  111. package/lib/locale/messages-en.d.ts +16 -1
  112. package/lib/locale/messages-en.js +21 -6
  113. package/lib/locale/messages-nb.d.ts +15 -0
  114. package/lib/locale/messages-nb.js +21 -6
  115. package/lib/locale/messages-nn.d.ts +16 -1
  116. package/lib/locale/messages-nn.js +22 -7
  117. package/lib/model/ContentType.d.ts +1 -0
  118. package/lib/model/ContentType.js +4 -2
  119. package/lib/shapes.js +1 -1
  120. package/lib/types.d.ts +1 -0
  121. package/package.json +14 -15
  122. package/src/Article/Article.tsx +8 -15
  123. package/src/Article/ArticleByline.tsx +1 -1
  124. package/src/Article/ArticleNotions.tsx +13 -33
  125. package/src/ContentTypeBadge/ContentTypeBadge.tsx +8 -0
  126. package/src/ContentTypeBadge/component.content-type-badge.scss +5 -0
  127. package/src/FactBox/FactBox.tsx +22 -15
  128. package/src/Figure/component.figure.scss +1 -1
  129. package/src/Filter/FilterButtons.tsx +14 -15
  130. package/src/Filter/FilterCarousel.tsx +166 -0
  131. package/src/Masthead/Masthead.tsx +42 -18
  132. package/src/Notion/ConceptNotion.tsx +80 -0
  133. package/src/Notion/FigureNotion.tsx +86 -0
  134. package/src/Notion/Notion.tsx +205 -75
  135. package/src/Notion/NotionImage.tsx +51 -0
  136. package/src/Notion/NotionVisualElement.tsx +50 -0
  137. package/src/Notion/index.ts +1 -0
  138. package/src/SearchTypeResult/ActiveFilterContent.tsx +7 -2
  139. package/src/SearchTypeResult/ActiveFilters.tsx +72 -38
  140. package/src/SearchTypeResult/PopupFilter.tsx +73 -146
  141. package/src/SearchTypeResult/ResultNavigation.tsx +54 -16
  142. package/src/SearchTypeResult/SearchFieldHeader.tsx +15 -40
  143. package/src/SearchTypeResult/SearchFilterContent.tsx +63 -0
  144. package/src/SearchTypeResult/SearchHeader.tsx +31 -31
  145. package/src/SearchTypeResult/SearchItem.tsx +145 -233
  146. package/src/SearchTypeResult/SearchItemList.tsx +167 -0
  147. package/src/SearchTypeResult/SearchItems.tsx +26 -19
  148. package/src/SearchTypeResult/SearchNotionItem.tsx +5 -12
  149. package/src/SearchTypeResult/SearchNotionsResult.tsx +29 -22
  150. package/src/SearchTypeResult/SearchTypeHeader.tsx +51 -33
  151. package/src/SearchTypeResult/SearchTypeResult.tsx +13 -12
  152. package/src/SearchTypeResult/SearchViewType.tsx +109 -0
  153. package/src/SearchTypeResult/components/ItemContexts.tsx +138 -0
  154. package/src/SearchTypeResult/components/ItemResourceHeader.tsx +133 -0
  155. package/src/SearchTypeResult/components/ItemTopicHeader.tsx +95 -0
  156. package/src/SearchTypeResult/components/SubjectFilters.tsx +152 -0
  157. package/src/SearchTypeResult/index.ts +9 -1
  158. package/src/index-javascript.js +0 -2
  159. package/src/index.ts +3 -0
  160. package/src/locale/messages-en.ts +19 -4
  161. package/src/locale/messages-nb.ts +19 -4
  162. package/src/locale/messages-nn.ts +20 -5
  163. package/src/model/ContentType.ts +1 -0
  164. package/src/shapes.js +1 -0
  165. package/src/types.ts +1 -0
  166. package/es/Embedded/Facebook.js +0 -19
  167. package/es/Embedded/FacebookPage.js +0 -22
  168. package/es/Embedded/Twitter.js +0 -121
  169. package/es/Embedded/index.js +0 -3
  170. package/lib/Embedded/Facebook.js +0 -32
  171. package/lib/Embedded/FacebookPage.js +0 -35
  172. package/lib/Embedded/Twitter.js +0 -132
  173. package/lib/Embedded/index.js +0 -31
  174. package/src/Embedded/Facebook.jsx +0 -17
  175. package/src/Embedded/FacebookPage.jsx +0 -24
  176. package/src/Embedded/Twitter.jsx +0 -71
  177. package/src/Embedded/index.js +0 -3
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Copyright (c) 2021-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 React, { ReactNode, Fragment } from 'react';
10
+ import SafeLink from '@ndla/safelink';
11
+ import { Additional, Core } from '@ndla/icons/common';
12
+ import styled from '@emotion/styled';
13
+ import { breakpoints, colors, fonts, mq, spacing } from '@ndla/core';
14
+ import Button from '@ndla/button';
15
+ import Modal, { ModalCloseButton } from '@ndla/modal';
16
+ import { useTranslation } from 'react-i18next';
17
+
18
+ const BreadcrumbPath = styled.div`
19
+ color: ${colors.text.light};
20
+ ${fonts.sizes('14px', '20px')};
21
+
22
+ svg {
23
+ margin-left: ${spacing.small};
24
+ }
25
+ `;
26
+
27
+ const ModalButton = styled(Button)`
28
+ ${fonts.sizes('14px', '20px')};
29
+ box-shadow: none;
30
+ &:hover {
31
+ box-shadow: inset 0 -1px;
32
+ }
33
+ `;
34
+
35
+ const ModalHeader = styled.div`
36
+ display: flex;
37
+ justify-content: space-between;
38
+ align-items: center;
39
+ padding: ${spacing.normal} ${spacing.small};
40
+ ${mq.range({ from: breakpoints.tablet })} {
41
+ padding: ${spacing.normal} ${spacing.large};
42
+ }
43
+ `;
44
+
45
+ const ModalHeading = styled.h2`
46
+ margin: 0;
47
+ ${fonts.sizes('16px', '20px')};
48
+ font-weight: 600;
49
+ `;
50
+
51
+ const ModalContent = styled.div`
52
+ padding: 0 ${spacing.small} ${spacing.normal};
53
+ ${mq.range({ from: breakpoints.tablet })} {
54
+ padding: 0 ${spacing.large} ${spacing.normal};
55
+ }
56
+ `;
57
+
58
+ const ContextList = styled.ul`
59
+ margin: 0;
60
+ padding: 0;
61
+ list-style: none;
62
+ `;
63
+ const ContextListItem = styled.li`
64
+ margin-bottom: ${spacing.normal};
65
+ ${fonts.sizes('16px', '28px')};
66
+ `;
67
+
68
+ type context = {
69
+ breadcrumb: string[];
70
+ url: string;
71
+ isAdditional?: boolean;
72
+ };
73
+ export type ItemContextsType = {
74
+ contexts: context[];
75
+ id: string | number;
76
+ title: string;
77
+ };
78
+
79
+ const iconStyle = { width: '22px', height: '22px' };
80
+
81
+ const ItemContexts = ({ contexts, id, title }: ItemContextsType) => {
82
+ const { t } = useTranslation();
83
+ const mainContext = contexts[0];
84
+ const Breadcrumb = ({ breadcrumb, children }: { breadcrumb: string[]; children?: ReactNode }) => (
85
+ <BreadcrumbPath>
86
+ {breadcrumb.map((breadcrumbItem: string, i: number) => (
87
+ <Fragment key={`${breadcrumbItem}-${id}`}>
88
+ {i > 0 && <> &rsaquo; </>}
89
+ {breadcrumbItem}
90
+ </Fragment>
91
+ ))}
92
+ {children}
93
+ </BreadcrumbPath>
94
+ );
95
+
96
+ return (
97
+ <Breadcrumb breadcrumb={mainContext.breadcrumb}>
98
+ &nbsp;
99
+ {contexts.length > 1 && (
100
+ <Modal
101
+ activateButton={
102
+ <ModalButton link>
103
+ {t('searchPage.contextModal.button', {
104
+ count: contexts.length - 1,
105
+ })}
106
+ </ModalButton>
107
+ }
108
+ animation="subtle"
109
+ animationDuration={50}
110
+ backgroundColor="white"
111
+ size="medium">
112
+ {(onClose: () => void) => (
113
+ <>
114
+ <ModalHeader>
115
+ <ModalHeading>{t('searchPage.contextModal.heading')}</ModalHeading>
116
+ <ModalCloseButton onClick={onClose} title={t('searchPage.close')} />
117
+ </ModalHeader>
118
+ <ModalContent>
119
+ <ContextList>
120
+ {contexts.map((context) => (
121
+ <ContextListItem key={context.url}>
122
+ <SafeLink to={context.url}>{title}</SafeLink>
123
+ <Breadcrumb breadcrumb={context.breadcrumb}>
124
+ {context.isAdditional ? <Additional style={iconStyle} /> : <Core style={iconStyle} />}
125
+ </Breadcrumb>
126
+ </ContextListItem>
127
+ ))}
128
+ </ContextList>
129
+ </ModalContent>
130
+ </>
131
+ )}
132
+ </Modal>
133
+ )}
134
+ </Breadcrumb>
135
+ );
136
+ };
137
+
138
+ export default ItemContexts;
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Copyright (c) 2021-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 React, { Fragment } from 'react';
10
+ import { useTranslation } from 'react-i18next';
11
+ import styled from '@emotion/styled';
12
+
13
+ import { animations, fonts, spacing } from '@ndla/core';
14
+
15
+ import { ContentType } from '../SearchTypeResult';
16
+ import { resourceTypeColor, SearchItemType } from '../SearchItem';
17
+ import ContentTypeBadge from '../../ContentTypeBadge';
18
+
19
+ type ItemTypeProps = {
20
+ contentType?: ContentType;
21
+ };
22
+
23
+ const ImageElement = styled.img`
24
+ border-top-left-radius: 5px;
25
+ border-top-right-radius: 5px;
26
+ width: 100%;
27
+ height: 100%;
28
+ object-fit: cover;
29
+ flex: 1;
30
+ min-height: 40px;
31
+ `;
32
+
33
+ const NoImageElement = styled.div<ItemTypeProps>`
34
+ border-top-left-radius: 5px;
35
+ border-top-right-radius: 5px;
36
+ flex: 1;
37
+ min-height: 40px;
38
+ background: ${(props) => props.contentType && `${resourceTypeColor(props.contentType)}`};
39
+ position: relative;
40
+ transition: background-color ${animations.durations.normal} ease-in-out;
41
+ .c-content-type-badge {
42
+ transition: all ${animations.durations.normal} ease-in-out;
43
+ position: absolute;
44
+ width: 58px;
45
+ height: 58px;
46
+ left: 50%;
47
+ margin-left: -29px;
48
+ top: 50%;
49
+ margin-top: -18px;
50
+ opacity: 0.2;
51
+ z-index: 3;
52
+ svg {
53
+ width: 58px;
54
+ height: 58px;
55
+ }
56
+ }
57
+ `;
58
+
59
+ const ContentTypeWrapper = styled.div<ItemTypeProps>`
60
+ height: 48px;
61
+ background: ${(props) => props.contentType && `${resourceTypeColor(props.contentType)}`};
62
+ flex: 0 0 auto;
63
+ position: relative;
64
+ display: flex;
65
+ align-items: center;
66
+ padding: 0 ${spacing.normal};
67
+ ${fonts.sizes('12px', '16px')};
68
+ font-weight: ${fonts.weight.semibold};
69
+ transition: all ${animations.durations.fast} ease-in-out;
70
+ `;
71
+
72
+ const ContentTypeIcon = styled.span<ItemTypeProps>`
73
+ position: absolute;
74
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='78' height='23' viewBox='17 0 78 23' fill='none'%3E%3Cpath d='M35.6874 10.8284C37.0999 8.9889 38.405 7.28934 40 6C44.8452 2.08335 48.9078 0 56 0C63.0922 0 67.6548 2.5833 72.5 6.49995C74.0499 7.75284 75.2937 9.39082 76.6385 11.1617C80.0028 15.5921 83.9988 20.8545 95 23H17C27.9865 20.8573 32.1701 15.409 35.6874 10.8284ZM352' fill='${(
75
+ props,
76
+ ) => props.contentType && `${encodeURIComponent(resourceTypeColor(props.contentType))}`}'/%3E%3C/svg%3E");
77
+ background-position: top;
78
+ background-repeat: no-repeat;
79
+ left: 17px;
80
+ top: -23px;
81
+ height: 45px;
82
+ width: 78px;
83
+ display: flex;
84
+ justify-content: center;
85
+ align-items: center;
86
+ transition: all ${animations.durations.fast} ease-in-out;
87
+ z-index: 2;
88
+
89
+ svg {
90
+ transition: all ${animations.durations.fast} ease-in-out;
91
+ width: 20px;
92
+ height: 20px;
93
+ }
94
+ `;
95
+
96
+ type Props = {
97
+ labels: SearchItemType['item']['labels'];
98
+ img?: SearchItemType['item']['img'] | null;
99
+ type?: ContentType;
100
+ };
101
+
102
+ const ItemResourceHeader = ({ labels = [], img, type }: Props) => {
103
+ const { t } = useTranslation();
104
+ return (
105
+ <>
106
+ {img ? (
107
+ <ImageElement src={img.url} alt={img.alt} />
108
+ ) : (
109
+ <NoImageElement className="resource-no-image" contentType={type}>
110
+ {type && <ContentTypeBadge type={type} border={false} />}
111
+ </NoImageElement>
112
+ )}
113
+ <ContentTypeWrapper className="resource-type-wrapper" contentType={type}>
114
+ <ContentTypeIcon className="resource-icon-wrapper" contentType={type}>
115
+ {img && type && <ContentTypeBadge type={type} border={false} />}
116
+ </ContentTypeIcon>
117
+ {type && t(`contentTypes.${type}`)}
118
+ {labels.length > 0 && (
119
+ <>
120
+ {labels.map((label) => (
121
+ <Fragment key={label}>
122
+ {' '}
123
+ <>&#8226;</> {label}
124
+ </Fragment>
125
+ ))}
126
+ </>
127
+ )}
128
+ </ContentTypeWrapper>
129
+ </>
130
+ );
131
+ };
132
+
133
+ export default ItemResourceHeader;
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Copyright (c) 2021-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 React, { ReactNode } from 'react';
10
+ import { useTranslation } from 'react-i18next';
11
+ import styled from '@emotion/styled';
12
+ import { animations, colors, fonts, spacing } from '@ndla/core';
13
+
14
+ import { SearchItemType } from '../SearchItem';
15
+ import ContentTypeBadge from '../../ContentTypeBadge';
16
+ import { ContentType } from '../SearchTypeResult';
17
+
18
+ const Wrapper = styled.div`
19
+ padding: ${spacing.small} ${spacing.normal};
20
+ position: relative;
21
+ min-height: 0;
22
+ flex: 1;
23
+ `;
24
+
25
+ const Label = styled.div`
26
+ ${fonts.sizes('12px', '16px')};
27
+ font-weight: ${fonts.weight.semibold};
28
+ height: 26px;
29
+ display: flex;
30
+ align-items: center;
31
+ margin-top: ${spacing.small};
32
+ transition: all ${animations.durations.fast} ease-in-out;
33
+
34
+ .c-content-type-badge {
35
+ width: 26px;
36
+ height: 26px;
37
+ margin-right: ${spacing.xsmall};
38
+ }
39
+
40
+ svg {
41
+ transition: all ${animations.durations.fast} ease-in-out;
42
+ width: 22px;
43
+ height: 22px;
44
+ color: ${colors.text.primary} !important;
45
+ }
46
+ `;
47
+
48
+ const TopicHeaderVisualElementWrapper = styled.div`
49
+ float: right;
50
+ margin-left: ${spacing.small};
51
+ position: relative;
52
+ width: 112px;
53
+ height: 112px;
54
+ display: flex;
55
+ justify-content: center;
56
+ align-items: center;
57
+ `;
58
+
59
+ const TopicHeaderImage = styled.img`
60
+ border-radius: 50%;
61
+ width: 100%;
62
+ height: 100%;
63
+ object-fit: cover;
64
+ transition: all ${animations.durations.fast} ease-in-out;
65
+ max-width: unset;
66
+ `;
67
+
68
+ type Props = {
69
+ children: ReactNode;
70
+ image?: SearchItemType['item']['img'] | null;
71
+ type?: ContentType;
72
+ };
73
+ const ItemTopicHeader = ({ children, image, type }: Props) => {
74
+ const { t } = useTranslation();
75
+ return (
76
+ <Wrapper>
77
+ {image && (
78
+ <TopicHeaderVisualElementWrapper>
79
+ <TopicHeaderImage className="topic-header-image" src={image.url} alt={image.alt} />
80
+ </TopicHeaderVisualElementWrapper>
81
+ )}
82
+ <Label className="topic-label">
83
+ {type && (
84
+ <>
85
+ <ContentTypeBadge type={type} border={false} />
86
+ {t(`contentTypes.${type}`)}
87
+ </>
88
+ )}
89
+ </Label>
90
+ {children}
91
+ </Wrapper>
92
+ );
93
+ };
94
+
95
+ export default ItemTopicHeader;
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Copyright (c) 2021-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 React, { useEffect, useRef, useState } from 'react';
10
+ import { useTranslation } from 'react-i18next';
11
+ import styled from '@emotion/styled';
12
+ import Button from '@ndla/button';
13
+ import { Plus as PlusIcon } from '@ndla/icons/action';
14
+
15
+ import PopupFilter, { PopupFilterProps } from '../PopupFilter';
16
+ import ActiveFilters from '../ActiveFilters';
17
+ import { FilterProps } from '../ActiveFilterContent';
18
+
19
+ const FilterButtonText = styled.span`
20
+ display: inline-block;
21
+ font-weight: 600;
22
+ margin-right: 10px;
23
+ `;
24
+
25
+ type ProgrammeProps = PopupFilterProps['programmes'] & {
26
+ values: string[];
27
+ onProgrammeValuesChange: (values: string[]) => void;
28
+ };
29
+
30
+ type SubjectCategoriesProps = PopupFilterProps['subjectCategories'] & {
31
+ values: string[];
32
+ onSubjectValuesChange: (values: string[]) => void;
33
+ };
34
+
35
+ export type SubjectFilterProps = {
36
+ filters: {
37
+ programmes?: ProgrammeProps;
38
+ subjectCategories?: SubjectCategoriesProps;
39
+ };
40
+ activeFilters?: {
41
+ filters: FilterProps[];
42
+ onFilterRemove: (value: string, name: string) => void;
43
+ };
44
+ };
45
+
46
+ type Props = {
47
+ isNarrowScreen?: boolean;
48
+ };
49
+
50
+ const SubjectFilters = ({ filters, activeFilters, isNarrowScreen }: SubjectFilterProps & Props) => {
51
+ const { t } = useTranslation();
52
+ const { subjectCategories, programmes } = filters;
53
+ const [subjectValues, setSubjectValues] = useState<Array<string>>([]);
54
+ const [programmesValues, setProgrammesValues] = useState<Array<string>>([]);
55
+ const [isOpen, setIsOpen] = useState(false);
56
+
57
+ const prevIsOpenRef = useRef<boolean>();
58
+ useEffect(() => {
59
+ prevIsOpenRef.current = isOpen;
60
+ });
61
+ const prevIsOpen = prevIsOpenRef.current;
62
+
63
+ useEffect(() => {
64
+ if (isOpen && isOpen !== prevIsOpen) {
65
+ if (subjectCategories) {
66
+ setSubjectValues([...subjectCategories.values]);
67
+ }
68
+ if (programmes) {
69
+ setProgrammesValues([...programmes.values]);
70
+ }
71
+ }
72
+ }, [isOpen, prevIsOpen, subjectCategories, programmes]);
73
+
74
+ const onToggleSubject = (subjectId: string) => {
75
+ let updatedFilter = [...subjectValues];
76
+ if (updatedFilter.includes(subjectId)) {
77
+ updatedFilter = subjectValues.filter((option) => option !== subjectId);
78
+ } else {
79
+ updatedFilter.push(subjectId);
80
+ }
81
+ setSubjectValues(updatedFilter);
82
+ if (subjectCategories) {
83
+ subjectCategories.onSubjectValuesChange(updatedFilter);
84
+ }
85
+ };
86
+
87
+ const onToggleProgramme = (programmeId: string) => {
88
+ let updatedFilter = [...programmesValues];
89
+ if (updatedFilter.includes(programmeId)) {
90
+ updatedFilter = programmesValues.filter((option) => option !== programmeId);
91
+ } else {
92
+ updatedFilter.push(programmeId);
93
+ }
94
+ setProgrammesValues(updatedFilter);
95
+ if (programmes) {
96
+ programmes.onProgrammeValuesChange(updatedFilter);
97
+ }
98
+ };
99
+
100
+ const handlePopupOpen = () => {
101
+ setIsOpen(true);
102
+ };
103
+
104
+ const handleModalClose = () => {
105
+ setIsOpen(false);
106
+ };
107
+
108
+ const OpenModalButton = () => (
109
+ <Button type="button" size="normal" greyLighter borderShape="rounded" onClick={handlePopupOpen}>
110
+ <FilterButtonText>{t('searchPage.searchFilterMessages.noValuesButtonText')}</FilterButtonText>
111
+ <PlusIcon />
112
+ </Button>
113
+ );
114
+
115
+ const ActiveFiltersElement = ({ showModalButton }: { showModalButton?: boolean }) => {
116
+ const customElements = showModalButton ? [OpenModalButton()] : [];
117
+ if (!activeFilters) return null;
118
+ return (
119
+ <ActiveFilters
120
+ {...activeFilters}
121
+ showOnSmallScreen={isNarrowScreen}
122
+ onClickShowHiddenSubjects={handlePopupOpen}
123
+ customElements={customElements}
124
+ />
125
+ );
126
+ };
127
+
128
+ return (
129
+ <>
130
+ {isNarrowScreen ? (
131
+ <ActiveFiltersElement showModalButton />
132
+ ) : isNarrowScreen === false ? (
133
+ <>
134
+ <OpenModalButton />
135
+ <ActiveFiltersElement />
136
+ </>
137
+ ) : undefined}
138
+ <PopupFilter
139
+ programmes={programmes}
140
+ subjectCategories={subjectCategories}
141
+ subjectValues={subjectValues}
142
+ programmesValues={programmesValues}
143
+ onClose={handleModalClose}
144
+ onToggleSubject={onToggleSubject}
145
+ onToggleProgramme={onToggleProgramme}
146
+ isOpen={isOpen}
147
+ />
148
+ </>
149
+ );
150
+ };
151
+
152
+ export default SubjectFilters;
@@ -11,7 +11,15 @@ import SearchHeader from './SearchHeader';
11
11
  import SearchFieldHeader from './SearchFieldHeader';
12
12
  import SearchNotionsResult from './SearchNotionsResult';
13
13
  import SearchSubjectResult from './SearchSubjectResult';
14
+ import SearchFilterContent from './SearchFilterContent';
14
15
 
15
16
  export type { ContentType } from './SearchTypeResult';
16
17
 
17
- export { SearchTypeResult, SearchHeader, SearchFieldHeader, SearchNotionsResult, SearchSubjectResult };
18
+ export {
19
+ SearchTypeResult,
20
+ SearchHeader,
21
+ SearchFieldHeader,
22
+ SearchNotionsResult,
23
+ SearchSubjectResult,
24
+ SearchFilterContent,
25
+ };
@@ -32,5 +32,3 @@ export {
32
32
  CompetenceGoal,
33
33
  CompetenceGoalsDialog,
34
34
  } from './CompetenceGoals';
35
-
36
- export { EmbeddedTwitter, EmbeddedFacebook, EmbeddedFacebookPage } from './Embedded';
package/src/index.ts CHANGED
@@ -134,6 +134,7 @@ export {
134
134
  SearchFieldHeader,
135
135
  SearchNotionsResult,
136
136
  SearchSubjectResult,
137
+ SearchFilterContent,
137
138
  } from './SearchTypeResult';
138
139
 
139
140
  export { default as constants } from './model';
@@ -214,3 +215,5 @@ export {
214
215
  export { default as ContentCard } from './ContentCard';
215
216
 
216
217
  export { default as CopyParagraphButton } from './CopyParagraphButton';
218
+
219
+ export { Notion, ConceptNotion } from './Notion';
@@ -116,6 +116,7 @@ const messages = {
116
116
  useFilter: 'Use filter',
117
117
  closeFilter: 'Close filter',
118
118
  removeFilter: 'Remove filter {{filterName}}',
119
+ additionalSubjectFilters: '+ {{count}} subjects',
119
120
  coreRelevance: 'Core content',
120
121
  supplementaryRelevance: 'Supplementary content',
121
122
  resourceTypeFilter: {
@@ -124,20 +125,25 @@ const messages = {
124
125
  },
125
126
  },
126
127
  resultType: {
127
- showing: 'Showing {{fromCount}} to {{toCount}} of {{totalCount}}',
128
+ showing: 'Showing {{count}} of {{totalCount}} {{contentType}}',
129
+ showingAll: 'Showing all',
128
130
  showMore: 'Show more',
129
131
  showAll: 'Show all',
132
+ toTopOfPage: 'To top of page',
130
133
  toSubjectPageLabel: 'Go to subject page',
131
134
  all: 'All',
135
+ allContentTypes: 'All content-types',
132
136
  hits: '{{count}} hits',
133
- showingSearchPhrase: 'Showing results for:',
137
+ showingSearchPhrase: 'Showing hits for',
138
+ searchPhraseSuggestion: 'Search instead for',
134
139
  showingCompetenceGoalSearchPhrase: 'Showing results for competence goals {text}',
135
- searchPhraseSuggestion: 'Search instead for:',
136
140
  notionLabels: 'Used in',
137
141
  notionsHeading: 'Explanations',
138
142
  notionsRemove: 'Remove',
139
143
  showVideo: 'Watch video',
140
144
  showNotion: 'Show notion',
145
+ gridView: 'Grid-view',
146
+ listView: 'List-view',
141
147
  },
142
148
  contextModal: {
143
149
  button: '+ {{count}} more contexts',
@@ -454,7 +460,7 @@ const messages = {
454
460
  heading: 'How to use other content from the article',
455
461
  description: 'You will find guidelines for use of other content in the asset',
456
462
  itemImage: {
457
- ariaLabel: 'Open video in new window',
463
+ ariaLabel: 'Open in new window',
458
464
  },
459
465
  },
460
466
  h5p: {
@@ -548,6 +554,7 @@ const messages = {
548
554
  'source-material': 'Source material',
549
555
  'assessment-resources': 'Assessment resource',
550
556
  topic: 'Topic',
557
+ 'multidisciplinary-topic': 'Multidisciplinary case',
551
558
  },
552
559
  modal: {
553
560
  closeModal: 'Close',
@@ -834,6 +841,9 @@ const messages = {
834
841
  },
835
842
  readMoreDescriptionLabel: 'show more',
836
843
  },
844
+ h5p: {
845
+ reuse: 'Use H5P',
846
+ },
837
847
  video: {
838
848
  download: 'Download video',
839
849
  reuse: 'Use video',
@@ -845,6 +855,7 @@ const messages = {
845
855
  },
846
856
  concept: {
847
857
  showDescription: 'Show concept description',
858
+ reuse: 'Use concept',
848
859
  error: {
849
860
  title: 'An error occurred',
850
861
  content: 'Could not load the description of the concept.',
@@ -895,6 +906,10 @@ const messages = {
895
906
  open: 'Open menu',
896
907
  close: 'Close menu',
897
908
  },
909
+ factbox: {
910
+ open: 'Open fact box',
911
+ close: 'Close fact box',
912
+ },
898
913
  };
899
914
 
900
915
  export default messages;