@ndla/ui 42.0.6 → 42.1.1
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.
- package/es/Article/ArticleByline.js +9 -6
- package/es/CompetenceGoalTab/CompetenceGoalItem.js +7 -81
- package/es/CompetenceGoalTab/CompetenceGoalTab.js +2 -11
- package/es/CompetenceGoalTab/CompetenceItem.js +26 -51
- package/es/CompetenceGoalTab/CoreElementItem.js +43 -0
- package/es/CompetenceGoalTab/SearchButton.js +43 -0
- package/es/CompetenceGoalTab/styles.js +57 -0
- package/es/FrontpageArticle/FrontpageArticle.js +15 -3
- package/es/Hero/Hero.js +2 -2
- package/es/Layout/OneColumn.js +15 -4
- package/es/MyNdla/Resource/FolderInput.js +8 -4
- package/es/SearchTypeResult/SearchHeader.js +34 -17
- package/es/TreeStructure/FolderItem.js +13 -12
- package/es/index.js +2 -1
- package/es/locale/messages-en.js +2 -1
- package/es/locale/messages-nb.js +2 -1
- package/es/locale/messages-nn.js +2 -1
- package/es/locale/messages-se.js +2 -1
- package/es/locale/messages-sma.js +2 -1
- package/lib/Article/ArticleByline.d.ts +3 -1
- package/lib/Article/ArticleByline.js +9 -6
- package/lib/CompetenceGoalTab/CompetenceGoalItem.js +20 -92
- package/lib/CompetenceGoalTab/CompetenceGoalTab.js +9 -18
- package/lib/CompetenceGoalTab/CompetenceItem.d.ts +2 -6
- package/lib/CompetenceGoalTab/CompetenceItem.js +25 -50
- package/lib/CompetenceGoalTab/CoreElementItem.d.ts +10 -0
- package/lib/CompetenceGoalTab/CoreElementItem.js +50 -0
- package/lib/CompetenceGoalTab/SearchButton.d.ts +14 -0
- package/lib/CompetenceGoalTab/SearchButton.js +50 -0
- package/lib/CompetenceGoalTab/styles.d.ts +28 -0
- package/lib/CompetenceGoalTab/styles.js +67 -0
- package/lib/FrontpageArticle/FrontpageArticle.d.ts +2 -1
- package/lib/FrontpageArticle/FrontpageArticle.js +15 -3
- package/lib/Hero/Hero.js +2 -2
- package/lib/Layout/OneColumn.d.ts +3 -3
- package/lib/Layout/OneColumn.js +16 -6
- package/lib/MyNdla/Resource/FolderInput.js +8 -4
- package/lib/SearchTypeResult/SearchHeader.d.ts +3 -2
- package/lib/SearchTypeResult/SearchHeader.js +34 -17
- package/lib/TreeStructure/FolderItem.js +13 -12
- package/lib/index.d.ts +1 -0
- package/lib/index.js +7 -0
- package/lib/locale/messages-en.d.ts +1 -0
- package/lib/locale/messages-en.js +2 -1
- package/lib/locale/messages-nb.d.ts +1 -0
- package/lib/locale/messages-nb.js +2 -1
- package/lib/locale/messages-nn.d.ts +1 -0
- package/lib/locale/messages-nn.js +2 -1
- package/lib/locale/messages-se.d.ts +1 -0
- package/lib/locale/messages-se.js +2 -1
- package/lib/locale/messages-sma.d.ts +1 -0
- package/lib/locale/messages-sma.js +2 -1
- package/lib/types.d.ts +9 -2
- package/package.json +11 -11
- package/src/Article/ArticleByline.tsx +12 -2
- package/src/CompetenceGoalTab/CompetenceGoalItem.tsx +7 -56
- package/src/CompetenceGoalTab/CompetenceGoalTab.tsx +2 -2
- package/src/CompetenceGoalTab/CompetenceItem.tsx +26 -26
- package/src/CompetenceGoalTab/CoreElementItem.tsx +38 -0
- package/src/CompetenceGoalTab/SearchButton.tsx +51 -0
- package/src/CompetenceGoalTab/styles.ts +36 -0
- package/src/FrontpageArticle/FrontpageArticle.tsx +17 -2
- package/src/Hero/Hero.tsx +4 -1
- package/src/Layout/OneColumn.tsx +8 -4
- package/src/MyNdla/Resource/FolderInput.tsx +5 -1
- package/src/SearchTypeResult/SearchHeader.tsx +24 -9
- package/src/TreeStructure/FolderItem.tsx +2 -1
- package/src/index.ts +2 -0
- package/src/locale/messages-en.ts +2 -1
- package/src/locale/messages-nb.ts +2 -1
- package/src/locale/messages-nn.ts +2 -1
- package/src/locale/messages-se.ts +2 -1
- package/src/locale/messages-sma.ts +2 -1
- package/src/types.ts +11 -2
package/lib/types.d.ts
CHANGED
|
@@ -87,9 +87,11 @@ export type elementRectType = {
|
|
|
87
87
|
fromY: number;
|
|
88
88
|
fromScale: number;
|
|
89
89
|
};
|
|
90
|
-
|
|
90
|
+
interface GrepCode {
|
|
91
91
|
id: string;
|
|
92
92
|
title: string;
|
|
93
|
+
}
|
|
94
|
+
export interface CompetenceGoalsItemType extends GrepCode {
|
|
93
95
|
goals: {
|
|
94
96
|
id: string;
|
|
95
97
|
text: string;
|
|
@@ -97,7 +99,12 @@ export type CompetenceGoalsItemType = {
|
|
|
97
99
|
}[];
|
|
98
100
|
selected?: boolean;
|
|
99
101
|
isOembed?: boolean;
|
|
100
|
-
}
|
|
102
|
+
}
|
|
103
|
+
export interface CoreElementsItemType extends GrepCode {
|
|
104
|
+
text?: string;
|
|
105
|
+
url?: string;
|
|
106
|
+
isOembed?: boolean;
|
|
107
|
+
}
|
|
101
108
|
export type NotionMedia = {
|
|
102
109
|
type: 'video' | 'other';
|
|
103
110
|
element: ReactNode;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ndla/ui",
|
|
3
|
-
"version": "42.
|
|
3
|
+
"version": "42.1.1",
|
|
4
4
|
"description": "UI component library for NDLA.",
|
|
5
5
|
"license": "GPL-3.0",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -31,21 +31,21 @@
|
|
|
31
31
|
"types"
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@ndla/accordion": "^2.2.
|
|
34
|
+
"@ndla/accordion": "^2.2.13",
|
|
35
35
|
"@ndla/article-scripts": "^3.0.18",
|
|
36
|
-
"@ndla/button": "^10.1.
|
|
37
|
-
"@ndla/carousel": "^3.1.
|
|
36
|
+
"@ndla/button": "^10.1.12",
|
|
37
|
+
"@ndla/carousel": "^3.1.10",
|
|
38
38
|
"@ndla/core": "^4.1.4",
|
|
39
|
-
"@ndla/forms": "^4.3.
|
|
39
|
+
"@ndla/forms": "^4.3.13",
|
|
40
40
|
"@ndla/hooks": "^2.0.7",
|
|
41
|
-
"@ndla/icons": "^
|
|
41
|
+
"@ndla/icons": "^4.0.0",
|
|
42
42
|
"@ndla/licenses": "^7.1.1",
|
|
43
|
-
"@ndla/modal": "^3.0.
|
|
44
|
-
"@ndla/notion": "^5.0.
|
|
45
|
-
"@ndla/safelink": "^4.1.
|
|
43
|
+
"@ndla/modal": "^3.0.10",
|
|
44
|
+
"@ndla/notion": "^5.0.11",
|
|
45
|
+
"@ndla/safelink": "^4.1.12",
|
|
46
46
|
"@ndla/switch": "^1.1.8",
|
|
47
47
|
"@ndla/tabs": "^3.0.4",
|
|
48
|
-
"@ndla/tooltip": "^4.1.
|
|
48
|
+
"@ndla/tooltip": "^4.1.12",
|
|
49
49
|
"@ndla/util": "^3.1.13",
|
|
50
50
|
"@radix-ui/react-dropdown-menu": "^2.0.5",
|
|
51
51
|
"@radix-ui/react-popover": "^1.0.6",
|
|
@@ -83,5 +83,5 @@
|
|
|
83
83
|
"publishConfig": {
|
|
84
84
|
"access": "public"
|
|
85
85
|
},
|
|
86
|
-
"gitHead": "
|
|
86
|
+
"gitHead": "4c4276e39a7529a8043ef1dcc3c1e9858f2ddca8"
|
|
87
87
|
}
|
|
@@ -51,6 +51,8 @@ type SupplierProps = {
|
|
|
51
51
|
name: string;
|
|
52
52
|
};
|
|
53
53
|
|
|
54
|
+
type AccordionHeaderVariants = 'white' | 'blue';
|
|
55
|
+
|
|
54
56
|
type Props = {
|
|
55
57
|
authors?: AuthorProps[];
|
|
56
58
|
suppliers?: SupplierProps[];
|
|
@@ -59,6 +61,7 @@ type Props = {
|
|
|
59
61
|
licenseBox?: ReactNode;
|
|
60
62
|
locale?: string;
|
|
61
63
|
footnotes?: FootNote[];
|
|
64
|
+
accordionHeaderVariant?: AccordionHeaderVariants;
|
|
62
65
|
};
|
|
63
66
|
|
|
64
67
|
const renderContributors = (contributors: SupplierProps[] | AuthorProps[], t: TFunction) => {
|
|
@@ -85,7 +88,7 @@ const getSuppliersText = (suppliers: SupplierProps[], t: TFunction) => {
|
|
|
85
88
|
const LicenseWrapper = styled.div`
|
|
86
89
|
display: flex;
|
|
87
90
|
gap: ${spacing.small};
|
|
88
|
-
padding-right: ${spacing.xsmall}
|
|
91
|
+
padding-right: ${spacing.xsmall};
|
|
89
92
|
`;
|
|
90
93
|
|
|
91
94
|
const StyledAccordionHeader = styled(AccordionHeader)`
|
|
@@ -93,6 +96,10 @@ const StyledAccordionHeader = styled(AccordionHeader)`
|
|
|
93
96
|
border: 1px solid ${colors.brand.tertiary};
|
|
94
97
|
font-size: ${fonts.sizes('16px', '29px')};
|
|
95
98
|
font-weight: ${fonts.weight.semibold};
|
|
99
|
+
|
|
100
|
+
&[data-background-color='white'] {
|
|
101
|
+
background-color: ${colors.background.default};
|
|
102
|
+
}
|
|
96
103
|
`;
|
|
97
104
|
|
|
98
105
|
const refRegexp = /note\d/;
|
|
@@ -106,6 +113,7 @@ const ArticleByline = ({
|
|
|
106
113
|
licenseBox,
|
|
107
114
|
published,
|
|
108
115
|
locale,
|
|
116
|
+
accordionHeaderVariant = 'blue',
|
|
109
117
|
}: Props) => {
|
|
110
118
|
const { t } = useTranslation();
|
|
111
119
|
const [openAccordions, setOpenAccordions] = useState<string[]>([]);
|
|
@@ -157,7 +165,9 @@ const ArticleByline = ({
|
|
|
157
165
|
<AccordionRoot type="multiple" onValueChange={setOpenAccordions} value={openAccordions}>
|
|
158
166
|
{licenseBox && (
|
|
159
167
|
<AccordionItem value="rulesForUse">
|
|
160
|
-
<StyledAccordionHeader headingLevel="h2"
|
|
168
|
+
<StyledAccordionHeader headingLevel="h2" data-background-color={accordionHeaderVariant}>
|
|
169
|
+
{t('article.useContent')}
|
|
170
|
+
</StyledAccordionHeader>
|
|
161
171
|
<AccordionContent>{licenseBox}</AccordionContent>
|
|
162
172
|
</AccordionItem>
|
|
163
173
|
)}
|
|
@@ -7,58 +7,10 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import React from 'react';
|
|
10
|
-
import styled from '@emotion/styled';
|
|
11
|
-
import { fonts, spacing, mq, breakpoints } from '@ndla/core';
|
|
12
|
-
import { Search } from '@ndla/icons/common';
|
|
13
|
-
import { SafeLinkButton } from '@ndla/safelink';
|
|
14
10
|
import { useTranslation } from 'react-i18next';
|
|
15
11
|
import { CompetenceGoalsItemType } from '../types';
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
margin: ${spacing.medium} 0;
|
|
19
|
-
border-bottom: 1px solid #d1d6db;
|
|
20
|
-
`;
|
|
21
|
-
|
|
22
|
-
const GoalsHeading = styled.h3`
|
|
23
|
-
margin-top: 0;
|
|
24
|
-
`;
|
|
25
|
-
|
|
26
|
-
const StyledSearch = styled(Search)`
|
|
27
|
-
height: 24px;
|
|
28
|
-
width: 24px;
|
|
29
|
-
min-width: 24px;
|
|
30
|
-
`;
|
|
31
|
-
|
|
32
|
-
const GoalList = styled.ul`
|
|
33
|
-
padding: 0;
|
|
34
|
-
`;
|
|
35
|
-
|
|
36
|
-
const GoalText = styled.p`
|
|
37
|
-
margin: 0;
|
|
38
|
-
`;
|
|
39
|
-
|
|
40
|
-
const ListItemContent = styled.div`
|
|
41
|
-
display: flex;
|
|
42
|
-
justify-content: space-between;
|
|
43
|
-
${fonts.sizes('22px', '32px')};
|
|
44
|
-
${mq.range({ until: breakpoints.tabletWide })} {
|
|
45
|
-
flex-direction: column;
|
|
46
|
-
}
|
|
47
|
-
`;
|
|
48
|
-
|
|
49
|
-
const GoalSearchWrapper = styled.div`
|
|
50
|
-
margin-left: ${spacing.normal};
|
|
51
|
-
flex: 0 0 289px;
|
|
52
|
-
span {
|
|
53
|
-
text-align: left;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
${mq.range({ until: breakpoints.tabletWide })} {
|
|
57
|
-
margin-left: 0;
|
|
58
|
-
margin-top: ${spacing.normal};
|
|
59
|
-
flex-basis: auto;
|
|
60
|
-
}
|
|
61
|
-
`;
|
|
12
|
+
import SearchButton from './SearchButton';
|
|
13
|
+
import { GoalItem, GoalList, GoalText, GoalsHeading, ListItemContent } from './styles';
|
|
62
14
|
|
|
63
15
|
const CompetenceGoalItem = ({ title, goals, isOembed }: CompetenceGoalsItemType) => {
|
|
64
16
|
const { t } = useTranslation();
|
|
@@ -71,12 +23,11 @@ const CompetenceGoalItem = ({ title, goals, isOembed }: CompetenceGoalsItemType)
|
|
|
71
23
|
<ListItemContent>
|
|
72
24
|
<GoalText>{goal.text}</GoalText>
|
|
73
25
|
{goal.url && (
|
|
74
|
-
<
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
</GoalSearchWrapper>
|
|
26
|
+
<SearchButton
|
|
27
|
+
url={goal.url}
|
|
28
|
+
isOembed={isOembed}
|
|
29
|
+
searchText={t('competenceGoals.competenceGoalResourceSearchText', { code: goal.id })}
|
|
30
|
+
/>
|
|
80
31
|
)}
|
|
81
32
|
</ListItemContent>
|
|
82
33
|
</li>
|
|
@@ -30,8 +30,8 @@ const ButtonWrapper = styled.div`
|
|
|
30
30
|
`;
|
|
31
31
|
|
|
32
32
|
const LicenseIconsTextWrapper = styled.span`
|
|
33
|
-
padding-bottom:
|
|
34
|
-
margin-left:
|
|
33
|
+
padding-bottom: ${spacing.xxsmall};
|
|
34
|
+
margin-left: ${spacing.xxsmall};
|
|
35
35
|
`;
|
|
36
36
|
|
|
37
37
|
const CompetenceGoalTab = ({ list, isOembed }: CompetenceProps) => {
|
|
@@ -9,25 +9,26 @@
|
|
|
9
9
|
import React from 'react';
|
|
10
10
|
import styled from '@emotion/styled';
|
|
11
11
|
import { MenuBook } from '@ndla/icons/action';
|
|
12
|
-
import { spacing } from '@ndla/core';
|
|
12
|
+
import { colors, spacing } from '@ndla/core';
|
|
13
13
|
import { useTranslation } from 'react-i18next';
|
|
14
14
|
import CompetenceGoalItem from './CompetenceGoalItem';
|
|
15
|
-
import { CompetenceGoalsItemType } from '../types';
|
|
15
|
+
import { CompetenceGoalsItemType, CoreElementsItemType } from '../types';
|
|
16
|
+
import CoreElementItem from './CoreElementItem';
|
|
16
17
|
|
|
17
|
-
const
|
|
18
|
-
margin:
|
|
18
|
+
const GroupedElementWrapper = styled.div`
|
|
19
|
+
margin: ${spacing.normal} 0 ${spacing.large};
|
|
19
20
|
`;
|
|
20
21
|
|
|
21
22
|
const GroupedGoalsTitleWrapper = styled.div`
|
|
22
|
-
border-bottom: 1px solid
|
|
23
|
+
border-bottom: 1px solid ${colors.brand.neutral7};
|
|
23
24
|
`;
|
|
24
25
|
const GroupedGoalsTitle = styled.h2`
|
|
25
26
|
display: flex;
|
|
26
27
|
align-items: center;
|
|
27
28
|
gap: ${spacing.xsmall};
|
|
28
29
|
svg {
|
|
29
|
-
width:
|
|
30
|
-
height:
|
|
30
|
+
width: ${spacing.normal};
|
|
31
|
+
height: ${spacing.normal};
|
|
31
32
|
}
|
|
32
33
|
`;
|
|
33
34
|
|
|
@@ -42,10 +43,6 @@ const GoalList = styled.ul`
|
|
|
42
43
|
list-style-image: none;
|
|
43
44
|
`;
|
|
44
45
|
|
|
45
|
-
const GroupedCoreItemsWrapper = styled.div`
|
|
46
|
-
margin: 24px 0 52px;
|
|
47
|
-
`;
|
|
48
|
-
|
|
49
46
|
export type CompetenceTypeProps = 'competenceGoals' | 'coreElement';
|
|
50
47
|
export type CompetenceGoals = {
|
|
51
48
|
title: string;
|
|
@@ -53,11 +50,7 @@ export type CompetenceGoals = {
|
|
|
53
50
|
};
|
|
54
51
|
export type CoreElementItems = {
|
|
55
52
|
title?: string;
|
|
56
|
-
elements:
|
|
57
|
-
id: string;
|
|
58
|
-
name: string;
|
|
59
|
-
text: string;
|
|
60
|
-
}[];
|
|
53
|
+
elements: CoreElementsItemType[];
|
|
61
54
|
};
|
|
62
55
|
export type ListItemProp = {
|
|
63
56
|
id: string;
|
|
@@ -74,12 +67,13 @@ export type ListItemProps = {
|
|
|
74
67
|
const CompetenceItem = ({ item, isOembed }: ListItemProps) => {
|
|
75
68
|
const { t } = useTranslation();
|
|
76
69
|
const { type, groupedCompetenceGoals, groupedCoreElementItems } = item;
|
|
70
|
+
|
|
77
71
|
switch (type) {
|
|
78
72
|
case 'competenceGoals':
|
|
79
73
|
return (
|
|
80
74
|
<>
|
|
81
75
|
{groupedCompetenceGoals?.map((group) => (
|
|
82
|
-
<
|
|
76
|
+
<GroupedElementWrapper key={group.title}>
|
|
83
77
|
<GroupedGoalsTitleWrapper>
|
|
84
78
|
<hgroup>
|
|
85
79
|
<GroupedGoalsTitle>
|
|
@@ -102,7 +96,7 @@ const CompetenceItem = ({ item, isOembed }: ListItemProps) => {
|
|
|
102
96
|
))}
|
|
103
97
|
</GoalList>
|
|
104
98
|
)}
|
|
105
|
-
</
|
|
99
|
+
</GroupedElementWrapper>
|
|
106
100
|
))}
|
|
107
101
|
</>
|
|
108
102
|
);
|
|
@@ -110,18 +104,24 @@ const CompetenceItem = ({ item, isOembed }: ListItemProps) => {
|
|
|
110
104
|
return (
|
|
111
105
|
<>
|
|
112
106
|
{groupedCoreElementItems?.map((group) => (
|
|
113
|
-
<
|
|
107
|
+
<GroupedElementWrapper key={group.title}>
|
|
114
108
|
<GroupedGoalsTitle>
|
|
115
109
|
<MenuBook />
|
|
116
110
|
{group.title}
|
|
117
111
|
</GroupedGoalsTitle>
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
<
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
112
|
+
<GoalList>
|
|
113
|
+
{group.elements.map((coreItem) => (
|
|
114
|
+
<CoreElementItem
|
|
115
|
+
key={coreItem.id}
|
|
116
|
+
id={coreItem.id}
|
|
117
|
+
title={coreItem.title}
|
|
118
|
+
text={coreItem.text}
|
|
119
|
+
url={coreItem.url}
|
|
120
|
+
isOembed={isOembed}
|
|
121
|
+
/>
|
|
122
|
+
))}
|
|
123
|
+
</GoalList>
|
|
124
|
+
</GroupedElementWrapper>
|
|
125
125
|
))}
|
|
126
126
|
</>
|
|
127
127
|
);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2023-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 from 'react';
|
|
10
|
+
import { useTranslation } from 'react-i18next';
|
|
11
|
+
import { CoreElementsItemType } from '../types';
|
|
12
|
+
import SearchButton from './SearchButton';
|
|
13
|
+
import { GoalItem, GoalList, GoalText, GoalsHeading, ListItemContent } from './styles';
|
|
14
|
+
|
|
15
|
+
const CoreElementItem = ({ title, text, url, id, isOembed }: CoreElementsItemType) => {
|
|
16
|
+
const { t } = useTranslation();
|
|
17
|
+
return (
|
|
18
|
+
<GoalItem>
|
|
19
|
+
<GoalsHeading>{title}</GoalsHeading>
|
|
20
|
+
<GoalList>
|
|
21
|
+
<li>
|
|
22
|
+
<ListItemContent>
|
|
23
|
+
<GoalText>{text}</GoalText>
|
|
24
|
+
{url && (
|
|
25
|
+
<SearchButton
|
|
26
|
+
url={url}
|
|
27
|
+
isOembed={isOembed}
|
|
28
|
+
searchText={t('competenceGoals.coreResourceSearchText', { code: id })}
|
|
29
|
+
/>
|
|
30
|
+
)}
|
|
31
|
+
</ListItemContent>
|
|
32
|
+
</li>
|
|
33
|
+
</GoalList>
|
|
34
|
+
</GoalItem>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export default CoreElementItem;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2023-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 from 'react';
|
|
10
|
+
import styled from '@emotion/styled';
|
|
11
|
+
import { spacing, mq, breakpoints } from '@ndla/core';
|
|
12
|
+
import { Search } from '@ndla/icons/common';
|
|
13
|
+
import { SafeLinkButton } from '@ndla/safelink';
|
|
14
|
+
|
|
15
|
+
const StyledSearch = styled(Search)`
|
|
16
|
+
height: ${spacing.normal};
|
|
17
|
+
width: ${spacing.normal};
|
|
18
|
+
min-width: ${spacing.normal};
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
const GoalSearchWrapper = styled.div`
|
|
22
|
+
margin-left: ${spacing.normal};
|
|
23
|
+
flex: 0 0 30%;
|
|
24
|
+
span {
|
|
25
|
+
text-align: left;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
${mq.range({ until: breakpoints.tabletWide })} {
|
|
29
|
+
margin-left: 0;
|
|
30
|
+
margin-top: ${spacing.normal};
|
|
31
|
+
flex-basis: auto;
|
|
32
|
+
}
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
interface SearchButtonProps {
|
|
36
|
+
url: string;
|
|
37
|
+
searchText: string;
|
|
38
|
+
isOembed?: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const SearchButton = ({ url, isOembed, searchText }: SearchButtonProps) => {
|
|
42
|
+
return (
|
|
43
|
+
<GoalSearchWrapper>
|
|
44
|
+
<SafeLinkButton variant="outline" to={url} target={isOembed ? '_blank' : '_self'}>
|
|
45
|
+
<StyledSearch size="large" />
|
|
46
|
+
<span>{searchText}</span>
|
|
47
|
+
</SafeLinkButton>
|
|
48
|
+
</GoalSearchWrapper>
|
|
49
|
+
);
|
|
50
|
+
};
|
|
51
|
+
export default SearchButton;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2023-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
|
+
|
|
12
|
+
export const ListItemContent = styled.div`
|
|
13
|
+
display: flex;
|
|
14
|
+
justify-content: space-between;
|
|
15
|
+
${fonts.sizes('22px', '32px')};
|
|
16
|
+
${mq.range({ until: breakpoints.tabletWide })} {
|
|
17
|
+
flex-direction: column;
|
|
18
|
+
}
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
export const GoalItem = styled.li`
|
|
22
|
+
margin: ${spacing.medium} 0;
|
|
23
|
+
border-bottom: 1px solid ${colors.brand.neutral7};
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
export const GoalsHeading = styled.h3`
|
|
27
|
+
margin-top: 0;
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
export const GoalList = styled.ul`
|
|
31
|
+
padding: 0;
|
|
32
|
+
`;
|
|
33
|
+
|
|
34
|
+
export const GoalText = styled.p`
|
|
35
|
+
margin: 0;
|
|
36
|
+
`;
|
|
@@ -7,17 +7,19 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { ReactNode } from 'react';
|
|
10
|
-
import { breakpoints, fonts, mq, spacing
|
|
10
|
+
import { breakpoints, fonts, mq, spacing } from '@ndla/core';
|
|
11
11
|
import styled from '@emotion/styled';
|
|
12
12
|
import { Article } from '../types';
|
|
13
13
|
import LayoutItem from '../Layout';
|
|
14
14
|
import { Heading } from '../Typography';
|
|
15
|
+
import { ArticleByline } from '../Article';
|
|
15
16
|
|
|
16
17
|
interface Props {
|
|
17
18
|
article: Article;
|
|
18
19
|
children?: ReactNode;
|
|
19
20
|
id: string;
|
|
20
21
|
isWide?: boolean;
|
|
22
|
+
licenseBox?: ReactNode;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
const StyledArticle = styled.article`
|
|
@@ -39,7 +41,7 @@ const StyledIntroduction = styled.div`
|
|
|
39
41
|
}
|
|
40
42
|
`;
|
|
41
43
|
|
|
42
|
-
export const FrontpageArticle = ({ article, id, isWide }: Props) => {
|
|
44
|
+
export const FrontpageArticle = ({ article, id, isWide, licenseBox }: Props) => {
|
|
43
45
|
const { title, introduction, content } = article;
|
|
44
46
|
|
|
45
47
|
if (isWide) {
|
|
@@ -50,6 +52,10 @@ export const FrontpageArticle = ({ article, id, isWide }: Props) => {
|
|
|
50
52
|
);
|
|
51
53
|
}
|
|
52
54
|
|
|
55
|
+
const authors =
|
|
56
|
+
article.copyright.creators.length || article.copyright.rightsholders.length
|
|
57
|
+
? article.copyright.creators
|
|
58
|
+
: article.copyright.processors;
|
|
53
59
|
return (
|
|
54
60
|
<StyledArticle>
|
|
55
61
|
<LayoutItem>
|
|
@@ -59,6 +65,15 @@ export const FrontpageArticle = ({ article, id, isWide }: Props) => {
|
|
|
59
65
|
<StyledIntroduction>{introduction}</StyledIntroduction>
|
|
60
66
|
</LayoutItem>
|
|
61
67
|
<LayoutItem>{content}</LayoutItem>
|
|
68
|
+
<ArticleByline
|
|
69
|
+
authors={authors}
|
|
70
|
+
suppliers={article.copyright.rightsholders}
|
|
71
|
+
license={article.copyright.license?.license!}
|
|
72
|
+
published={article.published}
|
|
73
|
+
footnotes={article.footNotes}
|
|
74
|
+
accordionHeaderVariant={'white'}
|
|
75
|
+
licenseBox={licenseBox}
|
|
76
|
+
/>
|
|
62
77
|
</StyledArticle>
|
|
63
78
|
);
|
|
64
79
|
};
|
package/src/Hero/Hero.tsx
CHANGED
|
@@ -56,6 +56,10 @@ const StyledDiv = styled.div`
|
|
|
56
56
|
background-color: ${colors.brand.primary};
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
&[data-contenttype='frontpage-article'] {
|
|
60
|
+
background-color: ${colors.background.lightBlue};
|
|
61
|
+
}
|
|
62
|
+
|
|
59
63
|
&[data-contenttype='ndla-film has-image'],
|
|
60
64
|
&[data-contenttype='ndla-film'] {
|
|
61
65
|
background: ${colors.ndlaFilm.filmColor};
|
|
@@ -112,7 +116,6 @@ interface HeroProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
112
116
|
export const Hero = ({ children, contentType }: HeroProps) => (
|
|
113
117
|
<StyledDiv data-contenttype={contentType}>{children || null}</StyledDiv>
|
|
114
118
|
);
|
|
115
|
-
|
|
116
119
|
interface Props {
|
|
117
120
|
children?: ReactNode;
|
|
118
121
|
hasImage?: boolean;
|
package/src/Layout/OneColumn.tsx
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import React, { ReactNode } from 'react';
|
|
9
|
+
import React, { HTMLAttributes, ReactNode } from 'react';
|
|
10
10
|
import BEMHelper from 'react-bem-helper';
|
|
11
11
|
|
|
12
12
|
const classes = BEMHelper({
|
|
@@ -15,7 +15,7 @@ const classes = BEMHelper({
|
|
|
15
15
|
outputIsString: true,
|
|
16
16
|
});
|
|
17
17
|
|
|
18
|
-
interface Props {
|
|
18
|
+
interface Props extends HTMLAttributes<HTMLDivElement> {
|
|
19
19
|
children?: ReactNode;
|
|
20
20
|
className?: string;
|
|
21
21
|
cssModifier?: string;
|
|
@@ -24,7 +24,7 @@ interface Props {
|
|
|
24
24
|
extraPadding?: boolean;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export const OneColumn = ({ children, className, cssModifier, wide, noPadding, extraPadding }: Props) => {
|
|
27
|
+
export const OneColumn = ({ children, className, cssModifier, wide, noPadding, extraPadding, ...rest }: Props) => {
|
|
28
28
|
const modifiers = [];
|
|
29
29
|
|
|
30
30
|
if (cssModifier) {
|
|
@@ -43,7 +43,11 @@ export const OneColumn = ({ children, className, cssModifier, wide, noPadding, e
|
|
|
43
43
|
modifiers.push('extra-padding');
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
return
|
|
46
|
+
return (
|
|
47
|
+
<div className={`${classes('', modifiers)} ${className}`} {...rest}>
|
|
48
|
+
{children}
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
47
51
|
};
|
|
48
52
|
|
|
49
53
|
export default OneColumn;
|
|
@@ -24,6 +24,10 @@ interface Props extends ComponentProps<typeof InputV2> {
|
|
|
24
24
|
onSave: () => void;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
const StyledSpinner = styled(Spinner)`
|
|
28
|
+
margin: ${spacing.small};
|
|
29
|
+
`;
|
|
30
|
+
|
|
27
31
|
// Source: https://kovart.github.io/dashed-border-generator/
|
|
28
32
|
const borderStyle = (error?: boolean) =>
|
|
29
33
|
`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(
|
|
@@ -102,7 +106,7 @@ const FolderInput = forwardRef<HTMLInputElement, Props>(({ loading, error, onClo
|
|
|
102
106
|
</>
|
|
103
107
|
)}
|
|
104
108
|
<div aria-live="assertive">
|
|
105
|
-
{loading && <
|
|
109
|
+
{loading && <StyledSpinner size="normal" id="folder-spinner" aria-label={t('loading')} />}
|
|
106
110
|
</div>
|
|
107
111
|
</Row>
|
|
108
112
|
}
|
|
@@ -13,9 +13,10 @@ import { ButtonV2 } from '@ndla/button';
|
|
|
13
13
|
import { Spinner } from '@ndla/icons';
|
|
14
14
|
|
|
15
15
|
import SearchFieldHeader from './SearchFieldHeader';
|
|
16
|
-
import { CompetenceGoalsItemType } from '../types';
|
|
16
|
+
import { CompetenceGoalsItemType, CoreElementsItemType } from '../types';
|
|
17
17
|
import CompetenceGoalItem from '../CompetenceGoalTab/CompetenceGoalItem';
|
|
18
18
|
import SubjectFilters, { SubjectFilterProps } from './components/SubjectFilters';
|
|
19
|
+
import CoreElementItem from '../CompetenceGoalTab/CoreElementItem';
|
|
19
20
|
|
|
20
21
|
const Wrapper = styled.div`
|
|
21
22
|
margin-top: ${spacing.normal};
|
|
@@ -36,13 +37,13 @@ const PhraseWrapper = styled.div`
|
|
|
36
37
|
margin: ${spacing.normal} 0 ${spacing.medium};
|
|
37
38
|
`;
|
|
38
39
|
|
|
39
|
-
const
|
|
40
|
+
const GrepCodesWrapper = styled.div`
|
|
40
41
|
font-size: 16px;
|
|
41
42
|
width: 100%;
|
|
42
43
|
margin-top: ${spacing.normal};
|
|
43
44
|
`;
|
|
44
45
|
|
|
45
|
-
const
|
|
46
|
+
const GrepCodesList = styled.ul`
|
|
46
47
|
padding: 0;
|
|
47
48
|
margin: 0;
|
|
48
49
|
li {
|
|
@@ -56,7 +57,7 @@ const PhraseText = styled.div`
|
|
|
56
57
|
`;
|
|
57
58
|
const PhraseSuggestionText = styled.div``;
|
|
58
59
|
|
|
59
|
-
const
|
|
60
|
+
const GrepCodesLabel = styled.div`
|
|
60
61
|
${fonts.sizes('16px', '32px')};
|
|
61
62
|
text-transform: uppercase;
|
|
62
63
|
`;
|
|
@@ -69,6 +70,7 @@ type Props = {
|
|
|
69
70
|
filters: SubjectFilterProps['filters'];
|
|
70
71
|
activeFilters?: SubjectFilterProps['activeFilters'];
|
|
71
72
|
competenceGoals?: CompetenceGoalsItemType[];
|
|
73
|
+
coreElements?: CoreElementsItemType[];
|
|
72
74
|
onSearchValueChange: (value: string) => void;
|
|
73
75
|
onSubmit: (event: FormEvent<HTMLFormElement>) => void;
|
|
74
76
|
noResults?: boolean;
|
|
@@ -85,6 +87,7 @@ const SearchHeader = ({
|
|
|
85
87
|
activeFilters,
|
|
86
88
|
filters,
|
|
87
89
|
competenceGoals,
|
|
90
|
+
coreElements,
|
|
88
91
|
noResults,
|
|
89
92
|
loading,
|
|
90
93
|
}: Props) => {
|
|
@@ -143,18 +146,30 @@ const SearchHeader = ({
|
|
|
143
146
|
</PhraseSuggestionText>
|
|
144
147
|
)}
|
|
145
148
|
{!!competenceGoals?.length && (
|
|
146
|
-
<
|
|
149
|
+
<GrepCodesWrapper>
|
|
147
150
|
{competenceGoals?.length && (
|
|
148
151
|
<>
|
|
149
|
-
<
|
|
150
|
-
<
|
|
152
|
+
<GrepCodesLabel>{t('competenceGoals.competenceGoalItem.title')}</GrepCodesLabel>
|
|
153
|
+
<GrepCodesList>
|
|
151
154
|
{competenceGoals.map((e) => (
|
|
152
155
|
<CompetenceGoalItem key={e.id} id={e.id} title={e.title} goals={e.goals} />
|
|
153
156
|
))}
|
|
154
|
-
</
|
|
157
|
+
</GrepCodesList>
|
|
155
158
|
</>
|
|
156
159
|
)}
|
|
157
|
-
</
|
|
160
|
+
</GrepCodesWrapper>
|
|
161
|
+
)}
|
|
162
|
+
{!!coreElements?.length && (
|
|
163
|
+
<GrepCodesWrapper>
|
|
164
|
+
<>
|
|
165
|
+
<GrepCodesLabel>{t('competenceGoals.competenceTabCorelabel')}</GrepCodesLabel>
|
|
166
|
+
<GrepCodesList>
|
|
167
|
+
{coreElements.map((e) => (
|
|
168
|
+
<CoreElementItem key={e.id} id={e.id} title={e.title} text={e.text} url={e.url} />
|
|
169
|
+
))}
|
|
170
|
+
</GrepCodesList>
|
|
171
|
+
</>
|
|
172
|
+
</GrepCodesWrapper>
|
|
158
173
|
)}
|
|
159
174
|
</PhraseWrapper>
|
|
160
175
|
{isNarrowScreen && (
|