@ndla/ui 22.0.2 → 22.2.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.
- package/es/Article/ArticleByline.js +7 -4
- package/es/Article/ArticleNotions.js +10 -6
- package/es/CompetenceGoalTab/CompetenceGoalItem.js +12 -10
- package/es/CompetenceGoalTab/CompetenceGoalTab.js +11 -9
- package/es/CompetenceGoalTab/CompetenceItem.js +14 -12
- package/es/CompetenceGoalTab/SearchButton.js +7 -4
- package/es/CompetenceGoals/CompetenceGoalsDialog.js +8 -4
- package/es/Filter/FilterButtons.js +10 -9
- package/es/Footer/FooterPrivacy.js +3 -2
- package/es/Masthead/MastheadSearchModal.js +4 -3
- package/es/Resource/BlockResource.js +9 -5
- package/es/Resource/ListResource.js +8 -6
- package/es/Resource/resourceComponents.js +36 -25
- package/es/ResourcesWrapper/ResourcesTopicTitle.js +7 -4
- package/es/SearchTypeResult/PopupFilter.js +12 -8
- package/es/SearchTypeResult/components/ItemContexts.js +8 -7
- package/es/Topic/Topic.js +21 -20
- package/es/TreeStructure/FolderItem.js +34 -29
- package/es/TreeStructure/TreeStructure.js +5 -3
- package/es/TreeStructure/TreeStructureWrapper.js +2 -2
- package/es/User/AuthModal.js +9 -8
- package/es/locale/messages-en.js +12 -4
- package/es/locale/messages-nb.js +12 -4
- package/es/locale/messages-nn.js +12 -4
- package/es/locale/messages-se.js +12 -4
- package/es/locale/messages-sma.js +12 -4
- package/lib/Article/ArticleByline.js +7 -4
- package/lib/Article/ArticleNotions.js +10 -6
- package/lib/CompetenceGoalTab/CompetenceGoalItem.d.ts +1 -1
- package/lib/CompetenceGoalTab/CompetenceGoalItem.js +12 -10
- package/lib/CompetenceGoalTab/CompetenceGoalTab.d.ts +2 -1
- package/lib/CompetenceGoalTab/CompetenceGoalTab.js +11 -9
- package/lib/CompetenceGoalTab/CompetenceItem.d.ts +2 -1
- package/lib/CompetenceGoalTab/CompetenceItem.js +14 -12
- package/lib/CompetenceGoalTab/SearchButton.d.ts +2 -1
- package/lib/CompetenceGoalTab/SearchButton.js +7 -4
- package/lib/CompetenceGoals/CompetenceGoalsDialog.js +8 -4
- package/lib/Filter/FilterButtons.js +10 -9
- package/lib/Footer/FooterPrivacy.js +3 -2
- package/lib/Masthead/MastheadSearchModal.js +4 -3
- package/lib/Resource/BlockResource.d.ts +2 -1
- package/lib/Resource/BlockResource.js +9 -5
- package/lib/Resource/ListResource.d.ts +2 -1
- package/lib/Resource/ListResource.js +8 -6
- package/lib/Resource/resourceComponents.d.ts +4 -2
- package/lib/Resource/resourceComponents.js +38 -19
- package/lib/ResourcesWrapper/ResourcesTopicTitle.js +7 -4
- package/lib/SearchTypeResult/PopupFilter.js +12 -8
- package/lib/SearchTypeResult/components/ItemContexts.js +8 -7
- package/lib/Topic/Topic.js +21 -20
- package/lib/TreeStructure/FolderItem.d.ts +1 -1
- package/lib/TreeStructure/FolderItem.js +34 -29
- package/lib/TreeStructure/TreeStructure.d.ts +0 -1
- package/lib/TreeStructure/TreeStructure.js +5 -3
- package/lib/TreeStructure/TreeStructureWrapper.js +2 -2
- package/lib/TreeStructure/types.d.ts +1 -0
- package/lib/User/AuthModal.js +9 -8
- package/lib/locale/messages-en.d.ts +8 -0
- package/lib/locale/messages-en.js +12 -4
- package/lib/locale/messages-nb.d.ts +8 -0
- package/lib/locale/messages-nb.js +12 -4
- package/lib/locale/messages-nn.d.ts +8 -0
- package/lib/locale/messages-nn.js +12 -4
- package/lib/locale/messages-se.d.ts +8 -0
- package/lib/locale/messages-se.js +12 -4
- package/lib/locale/messages-sma.d.ts +8 -0
- package/lib/locale/messages-sma.js +12 -4
- package/lib/types.d.ts +1 -0
- package/package.json +8 -7
- package/src/Article/ArticleByline.tsx +4 -1
- package/src/Article/ArticleNotions.tsx +4 -1
- package/src/CompetenceGoalTab/CompetenceGoalItem.tsx +6 -2
- package/src/CompetenceGoalTab/CompetenceGoalTab.tsx +5 -4
- package/src/CompetenceGoalTab/CompetenceItem.tsx +9 -2
- package/src/CompetenceGoalTab/SearchButton.tsx +3 -2
- package/src/CompetenceGoals/CompetenceGoalsDialog.jsx +5 -2
- package/src/Filter/FilterButtons.tsx +1 -0
- package/src/Footer/FooterPrivacy.tsx +4 -1
- package/src/Masthead/MastheadSearchModal.tsx +1 -0
- package/src/Resource/BlockResource.tsx +4 -2
- package/src/Resource/ListResource.tsx +3 -1
- package/src/Resource/resourceComponents.tsx +25 -9
- package/src/ResourcesWrapper/ResourcesTopicTitle.tsx +5 -1
- package/src/SearchTypeResult/PopupFilter.tsx +3 -1
- package/src/SearchTypeResult/components/ItemContexts.tsx +1 -0
- package/src/Topic/Topic.tsx +1 -0
- package/src/TreeStructure/FolderItem.tsx +7 -2
- package/src/TreeStructure/TreeStructure.tsx +2 -1
- package/src/TreeStructure/TreeStructureWrapper.tsx +9 -12
- package/src/TreeStructure/types.ts +1 -0
- package/src/User/AuthModal.tsx +2 -1
- package/src/locale/messages-en.ts +8 -0
- package/src/locale/messages-nb.ts +8 -0
- package/src/locale/messages-nn.ts +8 -0
- package/src/locale/messages-se.ts +8 -0
- package/src/locale/messages-sma.ts +8 -0
- package/src/types.ts +1 -0
- package/src/.DS_Store +0 -0
|
@@ -29,6 +29,8 @@ declare const messages: {
|
|
|
29
29
|
collectedInfo: string;
|
|
30
30
|
general: string;
|
|
31
31
|
topic: string;
|
|
32
|
+
isAuth: string;
|
|
33
|
+
isNotAuth: string;
|
|
32
34
|
};
|
|
33
35
|
resource: {
|
|
34
36
|
accessDenied: string;
|
|
@@ -311,6 +313,7 @@ declare const messages: {
|
|
|
311
313
|
contextModal: {
|
|
312
314
|
button: string;
|
|
313
315
|
heading: string;
|
|
316
|
+
ariaLabel: string;
|
|
314
317
|
};
|
|
315
318
|
};
|
|
316
319
|
subjectPage: {
|
|
@@ -356,6 +359,7 @@ declare const messages: {
|
|
|
356
359
|
articleErrorDescription: string;
|
|
357
360
|
topic: string;
|
|
358
361
|
topics: string;
|
|
362
|
+
imageModal: string;
|
|
359
363
|
};
|
|
360
364
|
welcomePage: {
|
|
361
365
|
search: string;
|
|
@@ -413,6 +417,7 @@ declare const messages: {
|
|
|
413
417
|
toFrontpage: string;
|
|
414
418
|
subjectOverview: string;
|
|
415
419
|
title: string;
|
|
420
|
+
modalLabel: string;
|
|
416
421
|
subjectPage: string;
|
|
417
422
|
backToSubjectFrontpage: string;
|
|
418
423
|
openFilter: string;
|
|
@@ -523,6 +528,7 @@ declare const messages: {
|
|
|
523
528
|
competenceGoals: {
|
|
524
529
|
competenceGoal: string;
|
|
525
530
|
title: string;
|
|
531
|
+
modalText: string;
|
|
526
532
|
closeCompetenceGoals: string;
|
|
527
533
|
showCompetenceGoals: string;
|
|
528
534
|
openCompentenceGoalsFilter: string;
|
|
@@ -546,6 +552,8 @@ declare const messages: {
|
|
|
546
552
|
competenceGoalItem: {
|
|
547
553
|
title: string;
|
|
548
554
|
};
|
|
555
|
+
licenseData: string;
|
|
556
|
+
licenseFrom: string;
|
|
549
557
|
};
|
|
550
558
|
subject: {
|
|
551
559
|
associatedTopics: string;
|
|
@@ -172,7 +172,8 @@ var messages = _objectSpread(_objectSpread({
|
|
|
172
172
|
},
|
|
173
173
|
contextModal: {
|
|
174
174
|
button: '+ {{count}} flere steder',
|
|
175
|
-
heading: 'Ressursen er brukt flere steder'
|
|
175
|
+
heading: 'Ressursen er brukt flere steder',
|
|
176
|
+
ariaLabel: 'Se flere kontekster'
|
|
176
177
|
}
|
|
177
178
|
},
|
|
178
179
|
subjectPage: {
|
|
@@ -217,7 +218,8 @@ var messages = _objectSpread(_objectSpread({
|
|
|
217
218
|
topicPage: {
|
|
218
219
|
articleErrorDescription: 'Beklager, en feil oppstod under lasting av emnebeskrivelsen.',
|
|
219
220
|
topic: 'EMNE',
|
|
220
|
-
topics: 'Emner'
|
|
221
|
+
topics: 'Emner',
|
|
222
|
+
imageModal: 'Se bildet i full størrelse'
|
|
221
223
|
},
|
|
222
224
|
welcomePage: {
|
|
223
225
|
search: 'Søk',
|
|
@@ -275,6 +277,7 @@ var messages = _objectSpread(_objectSpread({
|
|
|
275
277
|
toFrontpage: 'Til forsiden',
|
|
276
278
|
subjectOverview: 'Alle fag',
|
|
277
279
|
title: 'Innhold',
|
|
280
|
+
modalLabel: 'Velg innhold',
|
|
278
281
|
subjectPage: 'Fagforside',
|
|
279
282
|
backToSubjectFrontpage: 'Tilbake til fagforsiden',
|
|
280
283
|
openFilter: 'Filter',
|
|
@@ -361,6 +364,7 @@ var messages = _objectSpread(_objectSpread({
|
|
|
361
364
|
competenceGoals: {
|
|
362
365
|
competenceGoal: 'kompetansemål',
|
|
363
366
|
title: 'Kompetansemål og læreplan',
|
|
367
|
+
modalText: 'Utforsk læreplankoblinger',
|
|
364
368
|
closeCompetenceGoals: 'Lukk kompetansemål',
|
|
365
369
|
showCompetenceGoals: 'Vis kompetansemål',
|
|
366
370
|
openCompentenceGoalsFilter: 'Filtrer kompetansemål',
|
|
@@ -383,7 +387,9 @@ var messages = _objectSpread(_objectSpread({
|
|
|
383
387
|
competenceTabCorelabel: 'Kjerneelement',
|
|
384
388
|
competenceGoalItem: {
|
|
385
389
|
title: 'Kompetansemål og vurdering'
|
|
386
|
-
}
|
|
390
|
+
},
|
|
391
|
+
licenseData: 'Inneholder data under',
|
|
392
|
+
licenseFrom: 'tilgjengeliggjort på'
|
|
387
393
|
},
|
|
388
394
|
subject: {
|
|
389
395
|
associatedTopics: 'Tilhørende emner'
|
|
@@ -923,7 +929,9 @@ var messages = _objectSpread(_objectSpread({
|
|
|
923
929
|
modal: {
|
|
924
930
|
collectedInfo: 'Vi har hentet følgende informasjon om deg fra Feide:',
|
|
925
931
|
general: 'Ressursene som krever pålogging med Feide, vises med ikonet',
|
|
926
|
-
topic: 'Logg inn med Feide for å få tilgang til dette emnet.'
|
|
932
|
+
topic: 'Logg inn med Feide for å få tilgang til dette emnet.',
|
|
933
|
+
isAuth: 'Brukerinfo',
|
|
934
|
+
isNotAuth: 'Logg inn med Feide'
|
|
927
935
|
},
|
|
928
936
|
resource: {
|
|
929
937
|
accessDenied: 'Vi beklager, men denne ressursen er bare for lærere innlogget med Feide.'
|
package/lib/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ndla/ui",
|
|
3
|
-
"version": "22.0
|
|
3
|
+
"version": "22.2.0",
|
|
4
4
|
"description": "UI component library for NDLA.",
|
|
5
5
|
"license": "GPL-3.0",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -31,16 +31,16 @@
|
|
|
31
31
|
"types"
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@ndla/button": "^3.
|
|
34
|
+
"@ndla/button": "^3.3.0",
|
|
35
35
|
"@ndla/carousel": "^1.2.15",
|
|
36
36
|
"@ndla/core": "^2.3.3",
|
|
37
37
|
"@ndla/forms": "^3.1.3",
|
|
38
38
|
"@ndla/hooks": "^1.1.4",
|
|
39
39
|
"@ndla/icons": "^1.11.3",
|
|
40
40
|
"@ndla/licenses": "^5.0.6",
|
|
41
|
-
"@ndla/modal": "^1.2.
|
|
42
|
-
"@ndla/notion": "^3.1.
|
|
43
|
-
"@ndla/safelink": "^2.2.
|
|
41
|
+
"@ndla/modal": "^1.2.17",
|
|
42
|
+
"@ndla/notion": "^3.1.29",
|
|
43
|
+
"@ndla/safelink": "^2.2.6",
|
|
44
44
|
"@ndla/switch": "^0.1.10",
|
|
45
45
|
"@ndla/tabs": "^1.1.14",
|
|
46
46
|
"@ndla/tooltip": "^2.1.5",
|
|
@@ -68,7 +68,8 @@
|
|
|
68
68
|
"i18next": "^20.3.1",
|
|
69
69
|
"prop-types": "^15.8.1",
|
|
70
70
|
"react": "^16.8.4 || ^17.0.0",
|
|
71
|
-
"react-i18next": "^11.11.0"
|
|
71
|
+
"react-i18next": "^11.11.0",
|
|
72
|
+
"react-router-dom": "^6.3.0"
|
|
72
73
|
},
|
|
73
74
|
"devDependencies": {
|
|
74
75
|
"@babel/plugin-proposal-optional-chaining": "^7.11.0",
|
|
@@ -83,5 +84,5 @@
|
|
|
83
84
|
"publishConfig": {
|
|
84
85
|
"access": "public"
|
|
85
86
|
},
|
|
86
|
-
"gitHead": "
|
|
87
|
+
"gitHead": "12ed2984919fd75fbb7f1b1e55c1d5903fd51d51"
|
|
87
88
|
}
|
|
@@ -102,6 +102,8 @@ const ArticleByline = ({
|
|
|
102
102
|
const showPrimaryContributors = suppliers.length > 0 || authors.length > 0;
|
|
103
103
|
const showSecondaryContributors = suppliers.length > 0 && authors.length > 0;
|
|
104
104
|
|
|
105
|
+
const buttonId = 'popupUseContent';
|
|
106
|
+
|
|
105
107
|
return (
|
|
106
108
|
<Wrapper>
|
|
107
109
|
<div>
|
|
@@ -124,8 +126,9 @@ const ArticleByline = ({
|
|
|
124
126
|
<ButtonWrapper>
|
|
125
127
|
{licenseBox && (
|
|
126
128
|
<Modal
|
|
129
|
+
labelledBy={buttonId}
|
|
127
130
|
activateButton={
|
|
128
|
-
<Button size="small" borderShape="rounded" outline>
|
|
131
|
+
<Button id={buttonId} size="small" borderShape="rounded" outline>
|
|
129
132
|
{t('article.useContent')}
|
|
130
133
|
</Button>
|
|
131
134
|
}
|
|
@@ -165,14 +165,17 @@ type ArticleNotionsProps = {
|
|
|
165
165
|
export const ArticleNotions = ({ notions, relatedContent = [], buttonOffsetRight, type }: ArticleNotionsProps) => {
|
|
166
166
|
const { t } = useTranslation();
|
|
167
167
|
const leftOffset = `${buttonOffsetRight - 32}px`;
|
|
168
|
+
const headingId = 'popupNotionHeading';
|
|
169
|
+
|
|
168
170
|
return (
|
|
169
171
|
<ArticleNotionsContainer>
|
|
170
172
|
<Modal
|
|
173
|
+
labelledBy={headingId}
|
|
171
174
|
activateButton={
|
|
172
175
|
<NotionsTrigger role="button" aria-label={t('article.notionsPrompt')} style={{ left: leftOffset }}>
|
|
173
176
|
<NotionFlip />
|
|
174
177
|
<Explanation />
|
|
175
|
-
<span>{t('article.notionsPrompt')}</span>
|
|
178
|
+
<span id={headingId}>{t('article.notionsPrompt')}</span>
|
|
176
179
|
</NotionsTrigger>
|
|
177
180
|
}
|
|
178
181
|
size="large"
|
|
@@ -60,7 +60,7 @@ const GoalSearchWrapper = styled.div`
|
|
|
60
60
|
}
|
|
61
61
|
`;
|
|
62
62
|
|
|
63
|
-
const CompetenceGoalItem = ({ title, goals }: CompetenceGoalsItemType) => {
|
|
63
|
+
const CompetenceGoalItem = ({ title, goals, isOembed }: CompetenceGoalsItemType) => {
|
|
64
64
|
const { t } = useTranslation();
|
|
65
65
|
return (
|
|
66
66
|
<GoalItem>
|
|
@@ -73,7 +73,11 @@ const CompetenceGoalItem = ({ title, goals }: CompetenceGoalsItemType) => {
|
|
|
73
73
|
<GoalListInnerTextWrapper>{goal.text}</GoalListInnerTextWrapper>
|
|
74
74
|
{goal.url && goal.type !== 'LK06' && (
|
|
75
75
|
<GoalSearchWrapper>
|
|
76
|
-
<SearchButton
|
|
76
|
+
<SearchButton
|
|
77
|
+
to={goal.url}
|
|
78
|
+
text={t('competenceGoals.competenceGoalResourceSearchText')}
|
|
79
|
+
target={isOembed ? '_blank' : '_self'}
|
|
80
|
+
/>
|
|
77
81
|
</GoalSearchWrapper>
|
|
78
82
|
)}
|
|
79
83
|
</GoalListElementInnerWrapper>
|
|
@@ -19,6 +19,7 @@ import CompetenceItem, { ListItemProp } from './CompetenceItem';
|
|
|
19
19
|
type CompetenceProps = {
|
|
20
20
|
list: ListItemProp[];
|
|
21
21
|
highlightSearchBox?: boolean;
|
|
22
|
+
isOembed?: boolean;
|
|
22
23
|
};
|
|
23
24
|
|
|
24
25
|
const Wrapper = styled.div`
|
|
@@ -79,7 +80,7 @@ const HighlightText = styled.span`
|
|
|
79
80
|
font-family: 'Shadows Into Light Two', cursive;
|
|
80
81
|
`;
|
|
81
82
|
|
|
82
|
-
const CompetenceGoalTab = ({ list, highlightSearchBox }: CompetenceProps) => {
|
|
83
|
+
const CompetenceGoalTab = ({ list, highlightSearchBox, isOembed }: CompetenceProps) => {
|
|
83
84
|
const [currentTabItem, setCurrentTab] = useState(list[0]);
|
|
84
85
|
const { t } = useTranslation();
|
|
85
86
|
|
|
@@ -106,15 +107,15 @@ const CompetenceGoalTab = ({ list, highlightSearchBox }: CompetenceProps) => {
|
|
|
106
107
|
<ArrowFeatureTips />
|
|
107
108
|
</HighlightWrapper>
|
|
108
109
|
)}
|
|
109
|
-
<CompetenceItem item={currentTabItem} />
|
|
110
|
+
<CompetenceItem item={currentTabItem} isOembed={isOembed} />
|
|
110
111
|
<LicenseByline licenseRights={[CC, BY]}>
|
|
111
112
|
<LicenseIconsTextWrapper>UDIR</LicenseIconsTextWrapper>
|
|
112
113
|
</LicenseByline>
|
|
113
|
-
|
|
114
|
+
{`${t('competenceGoals.licenseData')} `}
|
|
114
115
|
<SafeLink to="https://data.norge.no/nlod/no" target="_blank">
|
|
115
116
|
NLOD
|
|
116
117
|
</SafeLink>
|
|
117
|
-
|
|
118
|
+
{`, ${t('competenceGoals.licenseFrom')} `}
|
|
118
119
|
<SafeLink to="https://data.udir.no/" target="_blank">
|
|
119
120
|
data.udir.no
|
|
120
121
|
</SafeLink>
|
|
@@ -79,9 +79,10 @@ export type ListItemProp = {
|
|
|
79
79
|
};
|
|
80
80
|
export type ListItemProps = {
|
|
81
81
|
item: ListItemProp;
|
|
82
|
+
isOembed?: boolean;
|
|
82
83
|
};
|
|
83
84
|
|
|
84
|
-
const CompetenceItem = ({ item }: ListItemProps) => {
|
|
85
|
+
const CompetenceItem = ({ item, isOembed }: ListItemProps) => {
|
|
85
86
|
const { t } = useTranslation();
|
|
86
87
|
const { type, groupedCompetenceGoals, groupedCoreElementItems } = item;
|
|
87
88
|
switch (type) {
|
|
@@ -105,7 +106,13 @@ const CompetenceItem = ({ item }: ListItemProps) => {
|
|
|
105
106
|
{group.elements.length > 0 && (
|
|
106
107
|
<Goals>
|
|
107
108
|
{group.elements.map((goal) => (
|
|
108
|
-
<CompetenceGoalItem
|
|
109
|
+
<CompetenceGoalItem
|
|
110
|
+
key={goal.id}
|
|
111
|
+
id={goal.id}
|
|
112
|
+
title={goal.title}
|
|
113
|
+
goals={goal.goals}
|
|
114
|
+
isOembed={isOembed}
|
|
115
|
+
/>
|
|
109
116
|
))}
|
|
110
117
|
</Goals>
|
|
111
118
|
)}
|
|
@@ -37,11 +37,12 @@ const IconWrapper = styled.span`
|
|
|
37
37
|
type Props = {
|
|
38
38
|
to: string;
|
|
39
39
|
text: string;
|
|
40
|
+
target?: string;
|
|
40
41
|
};
|
|
41
42
|
|
|
42
|
-
const SearchButton = ({ to, text }: Props) => (
|
|
43
|
+
const SearchButton = ({ to, text, target = '_self' }: Props) => (
|
|
43
44
|
<Wrapper>
|
|
44
|
-
<SafeLink to={to}>
|
|
45
|
+
<SafeLink to={to} target={target}>
|
|
45
46
|
<IconWrapper>
|
|
46
47
|
<Search style={{ width: '24px', height: '24px' }} />
|
|
47
48
|
</IconWrapper>
|
|
@@ -33,8 +33,11 @@ const HeadingWrapper = styled.h2`
|
|
|
33
33
|
|
|
34
34
|
export const CompetenceGoalsDialog = ({ children, isOpen, onClose, subjectName, modalProps }) => {
|
|
35
35
|
const { t } = useTranslation();
|
|
36
|
+
const iconId = 'popupCompetenceGoals';
|
|
37
|
+
|
|
36
38
|
return (
|
|
37
39
|
<Modal
|
|
40
|
+
labelledBy={iconId}
|
|
38
41
|
{...modalProps}
|
|
39
42
|
controllable
|
|
40
43
|
isOpen={isOpen}
|
|
@@ -48,8 +51,8 @@ export const CompetenceGoalsDialog = ({ children, isOpen, onClose, subjectName,
|
|
|
48
51
|
<ModalHeader modifier="menu">
|
|
49
52
|
<HeaderWrapper>
|
|
50
53
|
<HeadingWrapper>
|
|
51
|
-
<FooterHeaderIcon size="24px" style={{ marginRight: '20px' }} />
|
|
52
|
-
{'
|
|
54
|
+
<FooterHeaderIcon id={iconId} size="24px" style={{ marginRight: '20px' }} />
|
|
55
|
+
{t('competenceGoals.modalText')} {subjectName && ` \u2022 ${subjectName}`}
|
|
53
56
|
</HeadingWrapper>
|
|
54
57
|
<ModalCloseButton onClick={close} title={t('competenceGoals.competenceGoalClose')} />
|
|
55
58
|
</HeaderWrapper>
|
|
@@ -128,6 +128,7 @@ export const FilterButtons = ({ heading, items, onFilterToggle, onRemoveAllFilte
|
|
|
128
128
|
</StyledButtonElementWrapper>
|
|
129
129
|
))}
|
|
130
130
|
<Modal
|
|
131
|
+
label={t('searchPage.searchFilterMessages.resourceTypeFilter.button')}
|
|
131
132
|
size="fullscreen"
|
|
132
133
|
animation="subtle"
|
|
133
134
|
backgroundColor="white"
|
|
@@ -69,7 +69,10 @@ const StyledFooterText = styled.div`
|
|
|
69
69
|
|
|
70
70
|
const FooterPrivacy = ({ lang, label }: FooterPrivacyProps) => (
|
|
71
71
|
<StyledFooterText>
|
|
72
|
-
<Modal
|
|
72
|
+
<Modal
|
|
73
|
+
label={label}
|
|
74
|
+
activateButton={<StyledPrivacyButton type="button">{label}</StyledPrivacyButton>}
|
|
75
|
+
size="fullscreen">
|
|
73
76
|
{(onClose: () => void) => (
|
|
74
77
|
<OneColumn cssModifier="medium">
|
|
75
78
|
<ModalHeader>
|
|
@@ -17,6 +17,7 @@ import ContentLoader from '../ContentLoader';
|
|
|
17
17
|
|
|
18
18
|
interface BlockResourceProps {
|
|
19
19
|
link: string;
|
|
20
|
+
tagLinkPrefix?: string;
|
|
20
21
|
title: string;
|
|
21
22
|
resourceImage: ResourceImageProps;
|
|
22
23
|
topics: string[];
|
|
@@ -47,7 +48,7 @@ const BlockDescription = styled.p`
|
|
|
47
48
|
overflow: hidden;
|
|
48
49
|
text-overflow: ellipsis;
|
|
49
50
|
transition: height 0.2s ease-out;
|
|
50
|
-
${() => BlockElementWrapper}:hover & {
|
|
51
|
+
${() => BlockElementWrapper}:hover &, ${() => BlockElementWrapper}:focus & {
|
|
51
52
|
// Unfortunate css needed for multi-line text overflow ellipsis.
|
|
52
53
|
height: 3.1em;
|
|
53
54
|
-webkit-line-clamp: 2;
|
|
@@ -132,6 +133,7 @@ const BlockTopicList = ({ topics, loading }: BlockTopicListProps) => {
|
|
|
132
133
|
|
|
133
134
|
const BlockResource = ({
|
|
134
135
|
link,
|
|
136
|
+
tagLinkPrefix,
|
|
135
137
|
title,
|
|
136
138
|
tags,
|
|
137
139
|
resourceImage,
|
|
@@ -152,7 +154,7 @@ const BlockResource = ({
|
|
|
152
154
|
<BlockTopicList topics={topics} loading={isLoading} />
|
|
153
155
|
<BlockDescription>{description}</BlockDescription>
|
|
154
156
|
<RightRow>
|
|
155
|
-
{tags && <CompressedTagList tags={tags} />}
|
|
157
|
+
{tags && <CompressedTagList tagLinkPrefix={tagLinkPrefix} tags={tags} />}
|
|
156
158
|
{menuItems && menuItems.length > 0 && <MenuButton alignRight size="small" menuItems={menuItems} />}
|
|
157
159
|
</RightRow>
|
|
158
160
|
</BlockInfoWrapper>
|
|
@@ -109,6 +109,7 @@ interface StyledImageProps {
|
|
|
109
109
|
|
|
110
110
|
export interface ListResourceProps {
|
|
111
111
|
link: string;
|
|
112
|
+
tagLinkPrefix?: string;
|
|
112
113
|
title: string;
|
|
113
114
|
resourceImage: ResourceImageProps;
|
|
114
115
|
topics: string[];
|
|
@@ -184,6 +185,7 @@ const ResourceDescription = ({ description, loading }: ResourceDescriptionProps)
|
|
|
184
185
|
|
|
185
186
|
const ListResource = ({
|
|
186
187
|
link,
|
|
188
|
+
tagLinkPrefix,
|
|
187
189
|
title,
|
|
188
190
|
tags,
|
|
189
191
|
resourceImage,
|
|
@@ -205,7 +207,7 @@ const ListResource = ({
|
|
|
205
207
|
</TopicAndTitleWrapper>
|
|
206
208
|
{showDescription && <ResourceDescription description={description} loading={isLoading} />}
|
|
207
209
|
<TagsandActionMenu>
|
|
208
|
-
{tags && <CompressedTagList tags={tags} />}
|
|
210
|
+
{tags && <CompressedTagList tagLinkPrefix={tagLinkPrefix} tags={tags} />}
|
|
209
211
|
{menuItems && menuItems.length > 0 && <MenuButton alignRight size="small" menuItems={menuItems} />}
|
|
210
212
|
</TagsandActionMenu>
|
|
211
213
|
</ResourceWrapper>
|
|
@@ -6,11 +6,13 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import React from 'react';
|
|
10
9
|
import styled from '@emotion/styled';
|
|
11
|
-
import {
|
|
10
|
+
import { colors, fonts, spacing } from '@ndla/core';
|
|
11
|
+
import React, { MouseEvent } from 'react';
|
|
12
12
|
|
|
13
13
|
import { MenuButton } from '@ndla/button';
|
|
14
|
+
import SafeLink from '@ndla/safelink';
|
|
15
|
+
import { useNavigate } from 'react-router-dom';
|
|
14
16
|
|
|
15
17
|
export interface ResourceImageProps {
|
|
16
18
|
alt: string;
|
|
@@ -42,9 +44,13 @@ const StyledTagList = styled.ul`
|
|
|
42
44
|
`;
|
|
43
45
|
|
|
44
46
|
const StyledTagListElement = styled.li`
|
|
45
|
-
color: ${colors.brand.grey};
|
|
46
47
|
margin: 0;
|
|
47
48
|
${fonts.sizes(14)};
|
|
49
|
+
`;
|
|
50
|
+
|
|
51
|
+
const StyledSafeLink = styled(SafeLink)`
|
|
52
|
+
box-shadow: none;
|
|
53
|
+
color: ${colors.brand.grey};
|
|
48
54
|
::before {
|
|
49
55
|
content: '#';
|
|
50
56
|
}
|
|
@@ -90,14 +96,20 @@ const TagCounterWrapper = styled.p`
|
|
|
90
96
|
|
|
91
97
|
interface TagListProps {
|
|
92
98
|
tags?: string[];
|
|
99
|
+
tagLinkPrefix?: string;
|
|
93
100
|
}
|
|
94
|
-
|
|
95
|
-
export const TagList = ({ tags }: TagListProps) => {
|
|
101
|
+
export const TagList = ({ tags, tagLinkPrefix }: TagListProps) => {
|
|
96
102
|
if (!tags) return null;
|
|
97
103
|
return (
|
|
98
104
|
<StyledTagList>
|
|
99
105
|
{tags.map((tag, i) => (
|
|
100
|
-
<StyledTagListElement key={`tag-${i}`}>
|
|
106
|
+
<StyledTagListElement key={`tag-${i}`}>
|
|
107
|
+
<StyledSafeLink
|
|
108
|
+
onClick={(e: MouseEvent<HTMLAnchorElement | HTMLElement>) => e.stopPropagation()}
|
|
109
|
+
to={`${tagLinkPrefix ? tagLinkPrefix : ''}/${tag}`}>
|
|
110
|
+
{tag}
|
|
111
|
+
</StyledSafeLink>
|
|
112
|
+
</StyledTagListElement>
|
|
101
113
|
))}
|
|
102
114
|
</StyledTagList>
|
|
103
115
|
);
|
|
@@ -105,20 +117,24 @@ export const TagList = ({ tags }: TagListProps) => {
|
|
|
105
117
|
|
|
106
118
|
interface CompressedTagListProps {
|
|
107
119
|
tags: string[];
|
|
120
|
+
tagLinkPrefix?: string;
|
|
108
121
|
}
|
|
109
122
|
|
|
110
|
-
export const CompressedTagList = ({ tags }: CompressedTagListProps) => {
|
|
123
|
+
export const CompressedTagList = ({ tags, tagLinkPrefix }: CompressedTagListProps) => {
|
|
124
|
+
const navigate = useNavigate();
|
|
111
125
|
const visibleTags = tags.slice(0, 3);
|
|
112
126
|
const remainingTags = tags.slice(3, tags.length).map((tag) => {
|
|
113
127
|
return {
|
|
114
128
|
text: '#' + tag,
|
|
115
|
-
onClick: () => {
|
|
129
|
+
onClick: () => {
|
|
130
|
+
navigate(`${tagLinkPrefix ? tagLinkPrefix : ''}/${tag}`);
|
|
131
|
+
},
|
|
116
132
|
};
|
|
117
133
|
});
|
|
118
134
|
|
|
119
135
|
return (
|
|
120
136
|
<>
|
|
121
|
-
<TagList tags={visibleTags} />
|
|
137
|
+
<TagList tagLinkPrefix={tagLinkPrefix} tags={visibleTags} />
|
|
122
138
|
{remainingTags.length > 0 && (
|
|
123
139
|
<MenuButton hideMenuIcon={true} menuItems={remainingTags}>
|
|
124
140
|
<TagCounterWrapper>{`+${remainingTags.length}`}</TagCounterWrapper>
|
|
@@ -66,6 +66,9 @@ const ResourcesTopicTitle = ({
|
|
|
66
66
|
} else {
|
|
67
67
|
heading = <h1 {...classes('topic-title', 'single')}>{messages.label}</h1>;
|
|
68
68
|
}
|
|
69
|
+
|
|
70
|
+
const tooltipId = 'popupDialogTooltip';
|
|
71
|
+
|
|
69
72
|
return (
|
|
70
73
|
<header {...classes('topic-title-wrapper', { invertedStyle })}>
|
|
71
74
|
<div>
|
|
@@ -82,6 +85,7 @@ const ResourcesTopicTitle = ({
|
|
|
82
85
|
css={invertedStyle ? invertedSwitchCSS : switchCSS}
|
|
83
86
|
/>
|
|
84
87
|
<Modal
|
|
88
|
+
labelledBy={tooltipId}
|
|
85
89
|
narrow
|
|
86
90
|
wrapperFunctionForButton={(activateButton: ReactNode) => (
|
|
87
91
|
<TooltipWrapper>
|
|
@@ -89,7 +93,7 @@ const ResourcesTopicTitle = ({
|
|
|
89
93
|
</TooltipWrapper>
|
|
90
94
|
)}
|
|
91
95
|
activateButton={
|
|
92
|
-
<TooltipButton aria-label={t('resource.dialogTooltip')}>
|
|
96
|
+
<TooltipButton id={tooltipId} aria-label={t('resource.dialogTooltip')}>
|
|
93
97
|
<HelpIcon invertedStyle={invertedStyle} />
|
|
94
98
|
</TooltipButton>
|
|
95
99
|
}>
|
|
@@ -92,9 +92,11 @@ const PopupFilter = ({
|
|
|
92
92
|
}: PopupFilterProps) => {
|
|
93
93
|
const { t } = useTranslation();
|
|
94
94
|
const [selectedMenu, setSelectedMenu] = useState(MENU_ALL_SUBJECTS);
|
|
95
|
+
const headingId = 'popupFilterHeading';
|
|
95
96
|
|
|
96
97
|
return (
|
|
97
98
|
<Modal
|
|
99
|
+
labelledBy={headingId}
|
|
98
100
|
controllable
|
|
99
101
|
backgroundColor="white"
|
|
100
102
|
animation="subtle"
|
|
@@ -107,7 +109,7 @@ const PopupFilter = ({
|
|
|
107
109
|
<ModalWrapper>
|
|
108
110
|
<ModalContent>
|
|
109
111
|
<ModalHeaderWrapper>
|
|
110
|
-
<ModalHeading>{t('searchPage.searchFilterMessages.filterLabel')}</ModalHeading>
|
|
112
|
+
<ModalHeading id={headingId}>{t('searchPage.searchFilterMessages.filterLabel')}</ModalHeading>
|
|
111
113
|
<ModalCloseButton onClick={() => onClose()} title={t('searchPage.close')} />
|
|
112
114
|
</ModalHeaderWrapper>
|
|
113
115
|
{subjectCategories && programmes && (
|
package/src/Topic/Topic.tsx
CHANGED
|
@@ -49,19 +49,22 @@ const WrapperForFolderChild = styled.div`
|
|
|
49
49
|
gap: ${spacing.xsmall};
|
|
50
50
|
`;
|
|
51
51
|
|
|
52
|
-
const shouldForwardProp = (name: string) => !['selected', 'noArrow'].includes(name);
|
|
52
|
+
const shouldForwardProp = (name: string) => !['selected', 'noArrow', 'fullWidth'].includes(name);
|
|
53
53
|
|
|
54
54
|
interface FolderNameProps {
|
|
55
55
|
selected?: boolean;
|
|
56
56
|
noArrow?: boolean;
|
|
57
|
+
fullWidth?: boolean;
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
const FolderName = styled('button', { shouldForwardProp })<FolderNameProps>`
|
|
60
61
|
cursor: pointer;
|
|
61
62
|
padding: ${spacing.xsmall};
|
|
62
63
|
margin: 0;
|
|
64
|
+
outline-offset: -2px;
|
|
65
|
+
outline-color: ${colors.brand.primary};
|
|
63
66
|
margin-left: ${({ noArrow }) => (noArrow ? `29px` : `0px`)};
|
|
64
|
-
flex-grow: 1;
|
|
67
|
+
flex-grow: ${({ fullWidth }) => fullWidth && 1};
|
|
65
68
|
display: grid;
|
|
66
69
|
grid-template-columns: auto 1fr auto;
|
|
67
70
|
align-items: center;
|
|
@@ -116,6 +119,7 @@ const FolderItem = ({
|
|
|
116
119
|
setSelectedFolder,
|
|
117
120
|
targetResource,
|
|
118
121
|
visibleFolders,
|
|
122
|
+
framed,
|
|
119
123
|
}: Props) => {
|
|
120
124
|
const { t } = useTranslation();
|
|
121
125
|
const { id, icon, name } = folder;
|
|
@@ -173,6 +177,7 @@ const FolderItem = ({
|
|
|
173
177
|
{onSelectFolder ? (
|
|
174
178
|
<>
|
|
175
179
|
<FolderName
|
|
180
|
+
fullWidth={framed}
|
|
176
181
|
ref={ref}
|
|
177
182
|
onKeyDown={(e) => arrowNavigation(e, id, visibleFolders, setFocusedId, onOpenFolder, onCloseFolder)}
|
|
178
183
|
noArrow={hideArrow && !noPaddingWhenArrowIsHidden}
|
|
@@ -34,7 +34,6 @@ export interface TreeStructureProps extends CommonTreeStructureProps {
|
|
|
34
34
|
defaultOpenFolders?: string[];
|
|
35
35
|
folders: FolderType[];
|
|
36
36
|
editable?: boolean;
|
|
37
|
-
framed?: boolean;
|
|
38
37
|
label?: string;
|
|
39
38
|
maximumLevelsOfFoldersAllowed?: number;
|
|
40
39
|
onNewFolder?: (name: string, parentId: string) => Promise<IFolder>;
|
|
@@ -118,6 +117,7 @@ const TreeStructure = ({
|
|
|
118
117
|
onNewFolder?.(name, parentId).then((newFolder) => {
|
|
119
118
|
if (newFolder) {
|
|
120
119
|
setSelectedFolder(newFolder);
|
|
120
|
+
onSelectFolder?.(newFolder.id);
|
|
121
121
|
setFocusedId(newFolder.id);
|
|
122
122
|
setOpenFolders(uniq(openFolders.concat(parentId)));
|
|
123
123
|
}
|
|
@@ -156,6 +156,7 @@ const TreeStructure = ({
|
|
|
156
156
|
setSelectedFolder={setSelectedFolder}
|
|
157
157
|
targetResource={targetResource}
|
|
158
158
|
visibleFolders={visibleFolderIds}
|
|
159
|
+
framed={framed}
|
|
159
160
|
/>
|
|
160
161
|
</TreeStructureStyledWrapper>
|
|
161
162
|
{editable && (
|
|
@@ -13,18 +13,15 @@ import { colors, misc, spacing } from '@ndla/core';
|
|
|
13
13
|
const TreeStructureWrapper = styled.div<{ framed?: boolean }>`
|
|
14
14
|
padding: ${spacing.xsmall};
|
|
15
15
|
${({ framed }) =>
|
|
16
|
-
framed
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
: css`
|
|
26
|
-
margin-left: -${spacing.medium};
|
|
27
|
-
`}
|
|
16
|
+
framed &&
|
|
17
|
+
css`
|
|
18
|
+
border: 1px solid ${colors.brand.neutral7};
|
|
19
|
+
border-radius: ${misc.borderRadius};
|
|
20
|
+
max-height: 400px;
|
|
21
|
+
overflow-y: scroll;
|
|
22
|
+
scroll-behavior: smooth;
|
|
23
|
+
padding: ${spacing.small};
|
|
24
|
+
`}
|
|
28
25
|
transition: ${misc.transition.default};
|
|
29
26
|
&:focus-within {
|
|
30
27
|
border-color: ${colors.brand.primary};
|