@ndla/ui 24.2.1 → 25.0.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.
Files changed (117) hide show
  1. package/es/CompetenceGoals/CompetenceGoalsDialog.js +15 -24
  2. package/es/CompetenceGoals/index.js +1 -4
  3. package/es/Filter/FilterButtons.js +10 -11
  4. package/es/Filter/FilterList.js +82 -152
  5. package/es/Filter/FilterListPhone.js +180 -272
  6. package/es/Filter/ToggleItem.js +6 -25
  7. package/es/Frontpage/FrontpageAllSubjects.js +8 -9
  8. package/es/Frontpage/FrontpageSearch.js +3 -4
  9. package/es/LearningPaths/LearningPathMenuModalWrapper.js +1 -2
  10. package/es/Masthead/Masthead.js +65 -41
  11. package/es/Masthead/MastheadSearchModal.js +4 -5
  12. package/es/Masthead/SkipToMainContent.js +24 -0
  13. package/es/Masthead/index.js +2 -1
  14. package/es/SearchTypeResult/PopupFilter.js +8 -9
  15. package/es/SearchTypeResult/SearchFilterContent.js +3 -4
  16. package/es/SectionHeading/SectionHeading.js +24 -17
  17. package/es/Subject/index.js +0 -1
  18. package/es/User/UserInfo.js +11 -3
  19. package/es/all.css +1 -1
  20. package/es/index-javascript.js +1 -4
  21. package/es/index.js +5 -2
  22. package/es/locale/messages-en.js +1 -0
  23. package/es/locale/messages-nb.js +1 -0
  24. package/es/locale/messages-nn.js +1 -0
  25. package/es/locale/messages-se.js +1 -0
  26. package/es/locale/messages-sma.js +1 -0
  27. package/lib/CompetenceGoals/CompetenceGoalsDialog.d.ts +19 -0
  28. package/lib/CompetenceGoals/CompetenceGoalsDialog.js +13 -25
  29. package/lib/CompetenceGoals/index.d.ts +1 -0
  30. package/lib/CompetenceGoals/index.js +1 -33
  31. package/lib/Filter/FilterButtons.js +9 -9
  32. package/lib/Filter/FilterList.d.ts +25 -0
  33. package/lib/Filter/FilterList.js +85 -155
  34. package/lib/Filter/FilterListPhone.d.ts +32 -0
  35. package/lib/Filter/FilterListPhone.js +176 -270
  36. package/lib/Filter/ToggleItem.d.ts +15 -0
  37. package/lib/Filter/ToggleItem.js +6 -32
  38. package/lib/Filter/filterClasses.d.ts +2 -0
  39. package/lib/Filter/index.d.ts +12 -0
  40. package/lib/Frontpage/FrontpageAllSubjects.js +7 -7
  41. package/lib/Frontpage/FrontpageSearch.js +2 -3
  42. package/lib/LearningPaths/LearningPathMenuModalWrapper.js +1 -2
  43. package/lib/Masthead/Masthead.d.ts +4 -8
  44. package/lib/Masthead/Masthead.js +65 -49
  45. package/lib/Masthead/MastheadSearchModal.js +3 -4
  46. package/lib/Masthead/SkipToMainContent.d.ts +6 -0
  47. package/lib/Masthead/SkipToMainContent.js +38 -0
  48. package/lib/Masthead/index.d.ts +2 -1
  49. package/lib/Masthead/index.js +10 -0
  50. package/lib/SearchTypeResult/PopupFilter.js +7 -7
  51. package/lib/SearchTypeResult/SearchFilterContent.js +2 -2
  52. package/lib/SectionHeading/SectionHeading.d.ts +1 -13
  53. package/lib/SectionHeading/SectionHeading.js +23 -19
  54. package/lib/Subject/index.d.ts +0 -1
  55. package/lib/Subject/index.js +0 -8
  56. package/lib/User/UserInfo.js +11 -3
  57. package/lib/all.css +1 -1
  58. package/lib/index-javascript.js +1 -63
  59. package/lib/index.d.ts +5 -2
  60. package/lib/index.js +48 -7
  61. package/lib/locale/messages-en.d.ts +1 -0
  62. package/lib/locale/messages-en.js +1 -0
  63. package/lib/locale/messages-nb.d.ts +1 -0
  64. package/lib/locale/messages-nb.js +1 -0
  65. package/lib/locale/messages-nn.d.ts +1 -0
  66. package/lib/locale/messages-nn.js +1 -0
  67. package/lib/locale/messages-se.d.ts +1 -0
  68. package/lib/locale/messages-se.js +1 -0
  69. package/lib/locale/messages-sma.d.ts +1 -0
  70. package/lib/locale/messages-sma.js +1 -0
  71. package/package.json +11 -11
  72. package/src/.DS_Store +0 -0
  73. package/src/CompetenceGoals/{CompetenceGoalsDialog.jsx → CompetenceGoalsDialog.tsx} +34 -27
  74. package/src/CompetenceGoals/index.ts +1 -0
  75. package/src/Filter/FilterButtons.tsx +0 -1
  76. package/src/Filter/FilterList.tsx +135 -0
  77. package/src/Filter/FilterListPhone.tsx +275 -0
  78. package/src/Filter/ToggleItem.tsx +58 -0
  79. package/src/Filter/{filterClasses.js → filterClasses.ts} +0 -0
  80. package/src/Filter/{index.js → index.ts} +0 -0
  81. package/src/Frontpage/FrontpageAllSubjects.tsx +0 -1
  82. package/src/Frontpage/FrontpageSearch.tsx +0 -1
  83. package/src/LearningPaths/LearningPathMenuModalWrapper.tsx +0 -1
  84. package/src/Masthead/Masthead.tsx +85 -45
  85. package/src/Masthead/MastheadSearchModal.tsx +0 -1
  86. package/src/Masthead/SkipToMainContent.tsx +48 -0
  87. package/src/Masthead/index.ts +2 -1
  88. package/src/SearchTypeResult/PopupFilter.tsx +0 -1
  89. package/src/SearchTypeResult/SearchFilterContent.tsx +0 -1
  90. package/src/SectionHeading/SectionHeading.tsx +29 -16
  91. package/src/Subject/index.ts +0 -1
  92. package/src/User/UserInfo.tsx +4 -4
  93. package/src/index-javascript.js +0 -10
  94. package/src/index.ts +7 -2
  95. package/src/locale/messages-en.ts +1 -0
  96. package/src/locale/messages-nb.ts +1 -0
  97. package/src/locale/messages-nn.ts +1 -0
  98. package/src/locale/messages-se.ts +1 -0
  99. package/src/locale/messages-sma.ts +1 -0
  100. package/src/main.scss +0 -3
  101. package/es/CompetenceGoals/CompetenceGoalList.js +0 -58
  102. package/es/CompetenceGoals/CompetenceGoals.js +0 -159
  103. package/es/Subject/SubjectFilter.js +0 -42
  104. package/lib/CompetenceGoals/CompetenceGoalList.js +0 -78
  105. package/lib/CompetenceGoals/CompetenceGoals.js +0 -184
  106. package/lib/Subject/SubjectFilter.d.ts +0 -27
  107. package/lib/Subject/SubjectFilter.js +0 -58
  108. package/src/CompetenceGoals/CompetenceGoalList.jsx +0 -51
  109. package/src/CompetenceGoals/CompetenceGoals.jsx +0 -152
  110. package/src/CompetenceGoals/component.competence-goals.scss +0 -161
  111. package/src/CompetenceGoals/index.js +0 -6
  112. package/src/Filter/FilterList.jsx +0 -167
  113. package/src/Filter/FilterListPhone.jsx +0 -329
  114. package/src/Filter/ToggleItem.jsx +0 -71
  115. package/src/Masthead/component.masthead.scss +0 -146
  116. package/src/SectionHeading/component.section-heading.scss +0 -17
  117. package/src/Subject/SubjectFilter.tsx +0 -48
@@ -0,0 +1,275 @@
1
+ /*
2
+ * Copyright (c) 2016-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
+ * FRI OG BEGRENSET
7
+ */
8
+
9
+ import React, { ChangeEvent, useEffect, useState } from 'react';
10
+ import { ChevronDown, ChevronUp } from '@ndla/icons/common';
11
+ import Modal, { ModalHeader, ModalBody, ModalCloseButton } from '@ndla/modal';
12
+ import Button from '@ndla/button';
13
+ import { debounce } from 'lodash';
14
+ import { classes } from './filterClasses';
15
+ import ToggleItem from './ToggleItem';
16
+ import ActiveFilters from '../Search/ActiveFilters';
17
+
18
+ interface Option {
19
+ title: string;
20
+ value: string;
21
+ noResults?: boolean;
22
+ disabled?: boolean;
23
+ }
24
+
25
+ interface Props {
26
+ preid: string;
27
+ label?: string;
28
+ labelNotVisible?: boolean;
29
+ modifiers?: string;
30
+ onChange: (values: string[], value: string) => void;
31
+ options: Option[] | Option[][];
32
+ values?: string[];
33
+ defaultVisibleCount?: number;
34
+ showLabel?: string;
35
+ hideLabel?: string;
36
+ alignedGroup?: boolean;
37
+ collapseMobile?: boolean;
38
+ activeFiltersNarrow?: boolean;
39
+ messages: {
40
+ useFilter: string;
41
+ openFilter: string;
42
+ closeFilter: string;
43
+ };
44
+ viewMode?: 'inlineDesktop' | 'allModal';
45
+ isGroupedOptions?: boolean;
46
+ showActiveFiltersOnSmallScreen?: boolean;
47
+ }
48
+
49
+ const is2DArray = (options: Option[] | Option[][]): options is Option[][] => {
50
+ return Array.isArray(options[0]);
51
+ };
52
+
53
+ const FilterListPhone = ({
54
+ preid,
55
+ label,
56
+ labelNotVisible,
57
+ modifiers = '',
58
+ onChange,
59
+ options,
60
+ values = [],
61
+ defaultVisibleCount,
62
+ showLabel,
63
+ hideLabel,
64
+ alignedGroup = false,
65
+ collapseMobile = true,
66
+ activeFiltersNarrow,
67
+ messages,
68
+ viewMode = 'inlineDesktop',
69
+ isGroupedOptions,
70
+ showActiveFiltersOnSmallScreen,
71
+ }: Props) => {
72
+ const [isNarrowScreen, setIsNarrowScreen] = useState(false);
73
+ const [visibleCount, setVisibleCount] = useState(defaultVisibleCount);
74
+
75
+ useEffect(() => {
76
+ setScreenSize(true);
77
+ const debounced = debounce(() => setScreenSize(false), 50);
78
+ window.addEventListener('resize', debounced);
79
+
80
+ return () => {
81
+ debounced.cancel();
82
+ window.removeEventListener('resize', debounced);
83
+ };
84
+ }, []);
85
+
86
+ const setScreenSize = (initial = false) => {
87
+ const newIsNarrowScreen = (window.innerWidth || document.documentElement.clientWidth) < 768;
88
+
89
+ /* eslint react/no-did-mount-set-state: 0 */
90
+ if ((initial && newIsNarrowScreen) || !initial) {
91
+ setIsNarrowScreen(newIsNarrowScreen);
92
+ }
93
+ /* eslint react/no-did-mount-set-state: 1 */
94
+ };
95
+
96
+ const handleChange = (event: ChangeEvent<HTMLInputElement>, option: Option) => {
97
+ let newValues = null;
98
+ if (event.currentTarget.checked) {
99
+ newValues = [...values, option.value];
100
+ } else {
101
+ newValues = values.filter((value) => value !== option.value);
102
+ }
103
+ if (onChange) {
104
+ onChange(newValues, option.value);
105
+ }
106
+ };
107
+
108
+ const showAll = defaultVisibleCount === undefined || options.length <= defaultVisibleCount;
109
+ const labelModifiers: string[] = [];
110
+
111
+ if (labelNotVisible) {
112
+ labelModifiers.push('hidden');
113
+ }
114
+
115
+ const groupedOptions = is2DArray(options) ? options : [options];
116
+
117
+ if (isNarrowScreen || viewMode === 'allModal') {
118
+ let currentlyActiveFilters: Option[] = [];
119
+ groupedOptions.forEach((options) => {
120
+ const activeFilters = options.filter((option) => values.some((value) => value === option.value));
121
+ currentlyActiveFilters = [...currentlyActiveFilters, ...activeFilters];
122
+ });
123
+
124
+ const wrapperClassName =
125
+ activeFiltersNarrow || viewMode === 'allModal' ? classes('narrow-active-filters').className : '';
126
+ return (
127
+ <div className={wrapperClassName}>
128
+ {currentlyActiveFilters.length > 0 && (
129
+ <ActiveFilters
130
+ filters={currentlyActiveFilters}
131
+ showOnSmallScreen={showActiveFiltersOnSmallScreen}
132
+ onFilterRemove={(value) => {
133
+ onChange(
134
+ values.filter((option) => option !== value),
135
+ value,
136
+ );
137
+ }}
138
+ />
139
+ )}
140
+ <Modal
141
+ size="fullscreen"
142
+ backgroundColor="grey"
143
+ activateButton={
144
+ <Button outline {...classes('modal-button')}>
145
+ {messages.openFilter}
146
+ </Button>
147
+ }>
148
+ {(onClose) => (
149
+ <>
150
+ <ModalHeader modifier={['left-align']}>
151
+ <div {...classes('modal-header')}>
152
+ <div {...classes('modal-heading')}>
153
+ {!isNarrowScreen && label && <h1 {...classes('label')}>{label}</h1>}
154
+ <Button outline onClick={onClose}>
155
+ {messages.useFilter}
156
+ </Button>
157
+ </div>
158
+ <ModalCloseButton title={messages.closeFilter} onClick={onClose} />
159
+ </div>
160
+ </ModalHeader>
161
+ <ModalBody modifier="no-side-padding-mobile">
162
+ {isNarrowScreen && label && <h1 {...classes('label')}>{label}</h1>}
163
+ {groupedOptions.map((options, index) => (
164
+ <ul
165
+ key={index}
166
+ {...classes('item-wrapper', {
167
+ 'aligned-grouping': !!alignedGroup,
168
+ 'collapse-mobile': !!collapseMobile,
169
+ 'grouped-options': !!isGroupedOptions,
170
+ })}>
171
+ {options.map((option) => {
172
+ const itemModifiers = [];
173
+
174
+ const checked = values.some((value) => value === option.value);
175
+
176
+ if (option.noResults) {
177
+ itemModifiers.push('no-results');
178
+ }
179
+
180
+ if (option.disabled) {
181
+ itemModifiers.push('disabled');
182
+ }
183
+ return (
184
+ <ToggleItem
185
+ key={option.value}
186
+ id={preid + option.value}
187
+ value={option.value}
188
+ checked={checked}
189
+ onChange={(event) => handleChange(event, option)}
190
+ label={option.title}
191
+ disabled={option.disabled}
192
+ modifiers={itemModifiers}
193
+ />
194
+ );
195
+ })}
196
+ </ul>
197
+ ))}
198
+
199
+ <div {...classes('usefilter-wrapper')}>
200
+ <Button outline onClick={onClose}>
201
+ {messages.useFilter}
202
+ </Button>
203
+ </div>
204
+ </ModalBody>
205
+ </>
206
+ )}
207
+ </Modal>
208
+ </div>
209
+ );
210
+ }
211
+
212
+ return (
213
+ <>
214
+ {isGroupedOptions && label && <h2 {...classes('label', labelModifiers)}>{label}</h2>}
215
+ {groupedOptions.map((options, index) => (
216
+ <section key={index} {...classes('list', modifiers)}>
217
+ {!isGroupedOptions && label && <h1 {...classes('label', labelModifiers)}>{label}</h1>}
218
+ <ul {...classes('item-wrapper')}>
219
+ {options.map((option, index) => {
220
+ const itemModifiers = [];
221
+
222
+ const checked = values.some((value) => value === option.value);
223
+
224
+ if (!showAll && !checked && !!visibleCount && index + 1 > visibleCount) {
225
+ itemModifiers.push('hidden');
226
+ }
227
+
228
+ if (option.noResults) {
229
+ itemModifiers.push('no-results');
230
+ }
231
+
232
+ if (option.disabled) {
233
+ itemModifiers.push('disabled');
234
+ }
235
+
236
+ return (
237
+ <ToggleItem
238
+ key={option.value}
239
+ id={preid + option.value}
240
+ value={option.value}
241
+ tabIndex={option.noResults ? -1 : 0}
242
+ checked={checked}
243
+ onChange={(event) => handleChange(event, option)}
244
+ label={option.title}
245
+ modifiers={itemModifiers}
246
+ disabled={option.disabled}
247
+ />
248
+ );
249
+ })}
250
+ </ul>
251
+ {!showAll && (
252
+ <button
253
+ {...classes('expand')}
254
+ type="button"
255
+ onClick={() => {
256
+ setVisibleCount((prev) => (prev === defaultVisibleCount ? options.length : defaultVisibleCount));
257
+ }}>
258
+ {visibleCount === defaultVisibleCount ? (
259
+ <>
260
+ <span>{showLabel}</span> <ChevronDown />
261
+ </>
262
+ ) : (
263
+ <>
264
+ <span>{hideLabel}</span> <ChevronUp />
265
+ </>
266
+ )}
267
+ </button>
268
+ )}
269
+ </section>
270
+ ))}
271
+ </>
272
+ );
273
+ };
274
+
275
+ export default FilterListPhone;
@@ -0,0 +1,58 @@
1
+ /*
2
+ * Copyright (c) 2017-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, { ChangeEvent, ElementType } from 'react';
10
+ import { classes } from './filterClasses';
11
+
12
+ interface Props {
13
+ id: string;
14
+ label: string;
15
+ onChange: (event: ChangeEvent<HTMLInputElement>) => void;
16
+ checked?: boolean;
17
+ disabled?: boolean;
18
+ tabIndex?: number;
19
+ modifiers?: string[] | string;
20
+ value?: string;
21
+ component?: ElementType;
22
+ hits?: number;
23
+ }
24
+
25
+ const ToggleItem = ({
26
+ id,
27
+ checked = false,
28
+ modifiers,
29
+ label,
30
+ component: Component = 'li',
31
+ onChange,
32
+ tabIndex,
33
+ value,
34
+ disabled,
35
+ hits,
36
+ }: Props) => (
37
+ <Component {...classes('item', modifiers)}>
38
+ <input
39
+ {...classes('input')}
40
+ type="checkbox"
41
+ id={id}
42
+ value={value}
43
+ disabled={disabled}
44
+ tabIndex={tabIndex}
45
+ checked={checked}
46
+ onChange={onChange}
47
+ />
48
+ <label htmlFor={id}>
49
+ <span {...classes('item-checkbox')} />
50
+ <span {...classes('text')}>
51
+ {label}
52
+ {hits !== undefined && ` (${hits})`}
53
+ </span>
54
+ </label>
55
+ </Component>
56
+ );
57
+
58
+ export default ToggleItem;
File without changes
@@ -5,7 +5,6 @@ import Tabs from '@ndla/tabs';
5
5
  import SafeLink from '@ndla/safelink';
6
6
  import { colors, fonts, mq, breakpoints } from '@ndla/core';
7
7
  import { MessageBox } from '../Messages';
8
- // @ts-ignore
9
8
  import { ToggleItem } from '../Filter';
10
9
 
11
10
  const StyledWrapper = styled.nav`
@@ -4,7 +4,6 @@ import styled from '@emotion/styled';
4
4
  import { colors, spacing, mq, breakpoints, animations } from '@ndla/core';
5
5
  import { noScroll } from '@ndla/util';
6
6
  import { useTranslation } from 'react-i18next';
7
- // @ts-ignore
8
7
  import { SearchField } from '../Search';
9
8
  import { SearchFieldForm } from '../Search/SearchFieldForm';
10
9
  import SearchResultSleeve from '../Search/SearchResultSleeve';
@@ -40,7 +40,6 @@ const ModalWrapperComponent = ({ innerWidth, children }: ModalWrapperProps) => {
40
40
  return (
41
41
  <Modal
42
42
  backgroundColor="grey"
43
- animation="slide-up"
44
43
  animationDuration={200}
45
44
  size="fullscreen"
46
45
  activateButton={
@@ -6,39 +6,98 @@
6
6
  *
7
7
  */
8
8
 
9
+ import { css } from '@emotion/core';
10
+ import styled from '@emotion/styled';
11
+ import { breakpoints, colors, fonts, mq, spacing } from '@ndla/core';
9
12
  import React, { ReactNode, useEffect, useRef } from 'react';
10
- import BEMHelper from 'react-bem-helper';
11
- import { WithTranslation, withTranslation } from 'react-i18next';
12
- import { DisplayOnPageYOffset } from '../Animation';
13
13
  import { MessageBanner } from '../Messages';
14
-
15
- const classes = new BEMHelper({
16
- name: 'masthead',
17
- prefix: 'c-',
18
- });
14
+ import SkipToMainContent from './SkipToMainContent';
19
15
 
20
16
  interface MastheadItemProps {
21
17
  children?: ReactNode;
22
- className?: string;
23
18
  right?: boolean;
24
19
  left?: boolean;
25
20
  }
26
- export const MastheadItem = ({ children, className, left = false, right = false }: MastheadItemProps) => {
27
- const itemClassName = left ? 'left' : right ? 'right' : undefined;
28
- const itemClassNames = itemClassName ? classes(itemClassName).className : undefined;
29
21
 
30
- return <div className={itemClassNames}>{children}</div>;
22
+ const LeftMastheadItem = styled.div`
23
+ display: flex;
24
+ button {
25
+ white-space: nowrap;
26
+ }
27
+ > div:last-child {
28
+ flex-grow: 1;
29
+ }
30
+ ${mq.range({ from: breakpoints.desktop })} {
31
+ flex-grow: 1;
32
+ text-align: left;
33
+ }
34
+ `;
35
+
36
+ const RightMastheadItem = styled.div`
37
+ display: flex;
38
+ align-items: center;
39
+ justify-content: flex-end;
40
+ ${mq.range({ from: breakpoints.tablet })} {
41
+ padding: ${spacing.small} 0;
42
+ padding: 0;
43
+ }
44
+ `;
45
+
46
+ export const MastheadItem = ({ children, left = false, right = false }: MastheadItemProps) => {
47
+ const Wrapper = left ? LeftMastheadItem : right ? RightMastheadItem : 'div';
48
+ return <Wrapper>{children}</Wrapper>;
31
49
  };
32
50
 
33
- interface MastheadInfoProps {
34
- children?: ReactNode;
51
+ const MastheadContent = styled.div`
52
+ justify-content: center;
53
+ align-items: center;
54
+ text-align: center;
55
+ padding: ${spacing.small};
56
+ font-weight: ${fonts.weight.normal};
57
+ display: flex;
58
+ height: 84px;
59
+ justify-content: space-between;
60
+ ${mq.range({ from: breakpoints.tablet })} {
61
+ flex-direction: row;
62
+ padding: ${spacing.small} ${spacing.normal};
63
+ }
64
+ ${mq.range({ from: breakpoints.desktop })} {
65
+ padding: ${spacing.small} ${spacing.large};
66
+ }
67
+ `;
68
+
69
+ interface StyledMastheadProps {
70
+ fixed: boolean;
71
+ ndlaFilm: boolean;
35
72
  }
36
73
 
37
- const MastheadInfo = ({ children }: MastheadInfoProps) => (
38
- <div {...classes('info')}>
39
- <div {...classes('info-content')}>{children}</div>
40
- </div>
41
- );
74
+ const StyledMasthead = styled.div<StyledMastheadProps>`
75
+ z-index: 99;
76
+ position: relative;
77
+ background: white;
78
+ border-bottom: 1px solid ${colors.brand.greyLighter};
79
+ min-height: 84px;
80
+ display: flex;
81
+ flex-flow: column;
82
+ justify-content: flex-end;
83
+ ${(p) =>
84
+ p.fixed &&
85
+ css`
86
+ top: 0;
87
+ position: sticky;
88
+ @media print {
89
+ position: relative;
90
+ }
91
+ `};
92
+ ${(p) =>
93
+ p.ndlaFilm &&
94
+ css`
95
+ background: ${colors.ndlaFilm.filmColorLight};
96
+ background-image: linear-gradient(0deg, ${colors.ndlaFilm.filmColorLight}, ${colors.ndlaFilm.filmColor});
97
+ border: 0;
98
+ border-bottom: 1px solid #18334c;
99
+ `};
100
+ `;
42
101
 
43
102
  interface Alert {
44
103
  content: string;
@@ -49,23 +108,13 @@ interface Alert {
49
108
  interface Props {
50
109
  children?: ReactNode;
51
110
  fixed?: boolean;
52
- infoContent?: ReactNode;
53
111
  ndlaFilm?: boolean;
54
112
  skipToMainContentId?: string;
55
113
  messages?: Alert[];
56
114
  onCloseAlert?: (id: number) => void;
57
115
  }
58
116
 
59
- export const Masthead = ({
60
- children,
61
- fixed,
62
- infoContent,
63
- ndlaFilm,
64
- skipToMainContentId,
65
- messages,
66
- onCloseAlert,
67
- t,
68
- }: Props & WithTranslation) => {
117
+ export const Masthead = ({ children, fixed, ndlaFilm, skipToMainContentId, messages, onCloseAlert }: Props) => {
69
118
  const mastheadRef = useRef<HTMLDivElement>(null);
70
119
  const focusHandler = (evt: FocusEvent) => {
71
120
  const mastheadHeight = (mastheadRef.current && mastheadRef.current.offsetHeight) || 0;
@@ -88,12 +137,8 @@ export const Masthead = ({
88
137
 
89
138
  return (
90
139
  <>
91
- {skipToMainContentId && (
92
- <a tabIndex={0} href={`#${skipToMainContentId}`} {...classes('skip-to-main-content')}>
93
- {t('masthead.skipToContent')}
94
- </a>
95
- )}
96
- <div id="masthead" {...classes('', { fixed: !!fixed, infoContent: !!infoContent, ndlaFilm: !!ndlaFilm })}>
140
+ {skipToMainContentId && <SkipToMainContent skipToMainContentId={skipToMainContentId} />}
141
+ <StyledMasthead fixed={!!fixed} ndlaFilm={!!ndlaFilm} id="masthead">
97
142
  {messages?.map((message) => (
98
143
  <MessageBanner
99
144
  key={message.number}
@@ -102,15 +147,10 @@ export const Masthead = ({
102
147
  {message.content}
103
148
  </MessageBanner>
104
149
  ))}
105
- {infoContent && (
106
- <DisplayOnPageYOffset yOffsetMin={0} yOffsetMax={90}>
107
- <MastheadInfo>{infoContent}</MastheadInfo>
108
- </DisplayOnPageYOffset>
109
- )}
110
- <div className={`u-1/1 ${classes('content').className}`}>{children}</div>
111
- </div>
150
+ <MastheadContent className="u-1/1">{children}</MastheadContent>
151
+ </StyledMasthead>
112
152
  </>
113
153
  );
114
154
  };
115
155
 
116
- export default withTranslation()(Masthead);
156
+ export default Masthead;
@@ -7,7 +7,6 @@ import styled from '@emotion/styled';
7
7
  import { css } from '@emotion/core';
8
8
  import { spacing, mq, breakpoints, colors, shadows } from '@ndla/core';
9
9
  import { WithTranslation, withTranslation } from 'react-i18next';
10
- // @ts-ignore
11
10
  import ToggleSearchButton from '../Search/ToggleSearchButton';
12
11
 
13
12
  interface Props {
@@ -0,0 +1,48 @@
1
+ import React from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import styled from '@emotion/styled';
4
+ import { colors } from '@ndla/core';
5
+
6
+ interface Props {
7
+ skipToMainContentId: string;
8
+ }
9
+
10
+ const StyledSkipToMainContent = styled.a`
11
+ left: -999px;
12
+ position: absolute;
13
+ top: auto;
14
+ width: 1px;
15
+ height: 1px;
16
+ overflow: hidden;
17
+ z-index: -999;
18
+ &:focus,
19
+ &:active {
20
+ color: ${colors.white};
21
+ background: ${colors.brand.primary};
22
+ left: auto;
23
+ top: auto;
24
+ width: 30%;
25
+ height: auto;
26
+ overflow: auto;
27
+ margin: 10px 35%;
28
+ padding: 5px;
29
+ border-radius: 15px;
30
+ border: 4px solid ${colors.brand.tertiary};
31
+ text-align: center;
32
+ font-size: 1.2em;
33
+ z-index: 9999;
34
+ animation-name: fadeIn;
35
+ animation-duration: 0.3s;
36
+ }
37
+ `;
38
+
39
+ const SkipToMainContent = ({ skipToMainContentId }: Props) => {
40
+ const { t } = useTranslation();
41
+ return (
42
+ <StyledSkipToMainContent tabIndex={0} href={`#${skipToMainContentId}`}>
43
+ {t('masthead.skipToContent')}
44
+ </StyledSkipToMainContent>
45
+ );
46
+ };
47
+
48
+ export default SkipToMainContent;
@@ -9,7 +9,8 @@
9
9
  import Masthead, { MastheadItem } from './Masthead';
10
10
 
11
11
  import { getMastheadHeight, useMastheadHeight } from './utils';
12
+ import { default as SkipToMainContent } from './SkipToMainContent';
12
13
 
13
- export { MastheadItem, getMastheadHeight, useMastheadHeight };
14
+ export { MastheadItem, getMastheadHeight, useMastheadHeight, SkipToMainContent };
14
15
 
15
16
  export default Masthead;
@@ -12,7 +12,6 @@ import Modal, { ModalCloseButton, ModalBody } from '@ndla/modal';
12
12
  import { breakpoints, fonts, mq, spacing } from '@ndla/core';
13
13
  import Button from '@ndla/button';
14
14
  import { useTranslation } from 'react-i18next';
15
- // @ts-ignore
16
15
  import { ToggleItem } from '../Filter';
17
16
 
18
17
  import FrontpageAllSubjects, { subjectsProps } from '../Frontpage/FrontpageAllSubjects';
@@ -12,7 +12,6 @@ import styled from '@emotion/styled';
12
12
  import { spacing } from '@ndla/core';
13
13
 
14
14
  import SearchViewType, { SearchViewTypeProps } from './SearchViewType';
15
- // @ts-ignore
16
15
  import { FilterButtons } from '../Filter';
17
16
  import { FilterButtonsProps } from '../Filter/FilterButtons';
18
17
 
@@ -1,8 +1,32 @@
1
1
  import React, { ElementType, ReactNode } from 'react';
2
- import PropTypes from 'prop-types';
3
- import BEMHelper from 'react-bem-helper';
2
+ import { css } from '@emotion/core';
3
+ import { breakpoints, fonts, mq, spacing } from '@ndla/core';
4
+ import styled from '@emotion/styled';
4
5
 
5
- const classes = BEMHelper('c-section-heading');
6
+ interface StyledWrapperProps {
7
+ large?: boolean;
8
+ }
9
+
10
+ const StyledWrapper = styled.h2<StyledWrapperProps>`
11
+ font-weight: ${fonts.weight.bold};
12
+ text-transform: uppercase;
13
+ letter-spacing: 0.05em;
14
+ ${fonts.sizes('18px', '24px')};
15
+ ${mq.range({ from: breakpoints.tablet })} {
16
+ ${fonts.sizes('20px', '26px')};
17
+ }
18
+ ${(p) =>
19
+ p.large &&
20
+ css`
21
+ margin: 0 0 ${spacing.small} 0;
22
+ ${fonts.sizes('16px', '32px')};
23
+ ${mq.range({ from: breakpoints.tablet })} {
24
+ ${fonts.sizes('22px')};
25
+ }
26
+ `};
27
+ `;
28
+
29
+ const LargeStyledWrapper = StyledWrapper.withComponent('h2');
6
30
 
7
31
  interface Props {
8
32
  children: ReactNode;
@@ -11,19 +35,8 @@ interface Props {
11
35
  }
12
36
 
13
37
  const SectionHeading = ({ children, large = false, className }: Props) => {
14
- const Wrapper: ElementType = large ? 'h1' : 'h2';
15
- return <Wrapper {...classes('', { large: !!large }, className)}>{children}</Wrapper>;
38
+ const Wrapper: ElementType = large ? LargeStyledWrapper : StyledWrapper;
39
+ return <Wrapper className={className}>{children}</Wrapper>;
16
40
  };
17
41
 
18
42
  export default SectionHeading;
19
-
20
- SectionHeading.propTypes = {
21
- children: PropTypes.node.isRequired,
22
- large: PropTypes.bool,
23
- className: PropTypes.string,
24
- };
25
-
26
- SectionHeading.defaultProps = {
27
- large: false,
28
- className: null,
29
- };