@eeacms/volto-eea-design-system 1.19.0 → 1.21.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 (52) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/cypress.config.js +3 -3
  3. package/package.json +3 -3
  4. package/src/ui/Banner/Banner.jsx +7 -13
  5. package/src/ui/Card/Card.stories.jsx +177 -57
  6. package/src/ui/Card/Card.stories.test.jsx +206 -0
  7. package/src/ui/Footer/Footer.jsx +2 -2
  8. package/src/ui/Header/Header.jsx +71 -71
  9. package/src/ui/Header/Header.stories.js +58 -51
  10. package/src/ui/Header/Header.test.jsx +102 -4
  11. package/src/ui/Header/HeaderMenuPopUp.js +2 -3
  12. package/src/ui/InpageNavigation/InpageNavigation.jsx +5 -1
  13. package/src/ui/Item/Item.stories.test.js +2 -2
  14. package/src/ui/Item/ItemGroupWithIcons.stories.js +28 -18
  15. package/src/ui/Message/Message.stories.js +6 -6
  16. package/src/ui/Message/Message.stories.test.js +11 -5
  17. package/src/ui/Tab/Tab.stories.js +21 -5
  18. package/src/ui/Tab/Tab.stories.test.js +10 -0
  19. package/src/ui/TagList/TagList.stories.jsx +1 -1
  20. package/theme/themes/eea/collections/breadcrumb.overrides +4 -3
  21. package/theme/themes/eea/collections/menu.overrides +201 -15
  22. package/theme/themes/eea/collections/menu.variables +43 -28
  23. package/theme/themes/eea/collections/message.overrides +1 -1
  24. package/theme/themes/eea/collections/table.overrides +9 -15
  25. package/theme/themes/eea/elements/button.overrides +2 -2
  26. package/theme/themes/eea/elements/container.overrides +39 -34
  27. package/theme/themes/eea/elements/divider.overrides +13 -9
  28. package/theme/themes/eea/elements/image.overrides +5 -6
  29. package/theme/themes/eea/elements/label.overrides +16 -16
  30. package/theme/themes/eea/elements/list.overrides +3 -1
  31. package/theme/themes/eea/elements/segment.overrides +4 -4
  32. package/theme/themes/eea/extras/custom.overrides +43 -22
  33. package/theme/themes/eea/extras/header.less +2 -15
  34. package/theme/themes/eea/extras/header.variables +1 -3
  35. package/theme/themes/eea/extras/tag.less +5 -0
  36. package/theme/themes/eea/extras/tag.variables +1 -1
  37. package/theme/themes/eea/globals/site.overrides +63 -35
  38. package/theme/themes/eea/globals/site.variables +2 -5
  39. package/theme/themes/eea/globals/utilities.less +21 -1
  40. package/theme/themes/eea/modules/accordion.overrides +36 -24
  41. package/theme/themes/eea/modules/accordion.variables +4 -4
  42. package/theme/themes/eea/modules/checkbox.overrides +2 -2
  43. package/theme/themes/eea/modules/modal.overrides +2 -2
  44. package/theme/themes/eea/modules/search.overrides +4 -0
  45. package/theme/themes/eea/modules/tab.overrides +6 -12
  46. package/theme/themes/eea/modules/tab.variables +2 -1
  47. package/theme/themes/eea/tokens/colors.less +2 -2
  48. package/theme/themes/eea/views/card.overrides +50 -35
  49. package/theme/themes/eea/views/card.variables +7 -2
  50. package/theme/themes/eea/views/item.overrides +23 -27
  51. package/theme/themes/eea/views/item.variables +3 -5
  52. package/theme/themes/eea/views/statistic.overrides +13 -3
@@ -1,14 +1,48 @@
1
1
  import React from 'react';
2
2
  import { render, fireEvent } from '@testing-library/react';
3
+ import '@testing-library/jest-dom/extend-expect';
3
4
  import {
4
5
  TeaserCardGrid,
5
6
  CarouselCards,
6
7
  FluidGrid,
8
+ ImageGrid,
7
9
  CardGrid,
8
10
  Default,
9
11
  } from './Card.stories';
10
12
 
11
13
  describe('TeaserCardGrid component', () => {
14
+ it('renders correctly', () => {
15
+ const { container } = render(<TeaserCardGrid {...TeaserCardGrid.args} />);
16
+
17
+ expect(container.querySelector('.image')).toBeInTheDocument();
18
+ expect(container.querySelector('.header')).toBeInTheDocument();
19
+ expect(container.querySelector('.description')).toBeInTheDocument();
20
+
21
+ expect(
22
+ container.querySelector(`.ui.card.u-card.${TeaserCardGrid.args.variant}`),
23
+ ).toBeInTheDocument();
24
+ expect(
25
+ container.querySelector(
26
+ `.ui.card.u-card.max-${TeaserCardGrid.args.maxLines}-lines`,
27
+ ),
28
+ ).toBeInTheDocument();
29
+ expect(
30
+ container.querySelector(
31
+ `.ui.card.u-card.title-max-${TeaserCardGrid.args.maxLines}-lines`,
32
+ ),
33
+ ).toBeInTheDocument();
34
+ expect(
35
+ container.querySelector(
36
+ `.ui.card.u-card.has--object-fit--${TeaserCardGrid.args.objectFit}`,
37
+ ),
38
+ ).toBeInTheDocument();
39
+ expect(
40
+ container.querySelector(
41
+ `.ui.card.u-card.has--object-position--${TeaserCardGrid.args.objectPosition}`,
42
+ ),
43
+ ).toBeInTheDocument();
44
+ });
45
+
12
46
  it('renders the teaser card grid with correct number of cards', () => {
13
47
  const { container } = render(<TeaserCardGrid {...TeaserCardGrid.args} />);
14
48
  const teaserCards = container.querySelectorAll('.column.grid-block-teaser');
@@ -26,6 +60,39 @@ describe('TeaserCardGrid component', () => {
26
60
  });
27
61
 
28
62
  describe('CarouselCards component', () => {
63
+ it('renders correctly', () => {
64
+ const { container } = render(<CarouselCards {...CarouselCards.args} />);
65
+
66
+ expect(container.querySelector('.image')).toBeInTheDocument();
67
+ expect(container.querySelector('.header')).toBeInTheDocument();
68
+ expect(container.querySelector('.meta')).toBeInTheDocument();
69
+ expect(container.querySelector('.description')).toBeInTheDocument();
70
+
71
+ expect(
72
+ container.querySelector(`.ui.card.u-card.${CarouselCards.args.variant}`),
73
+ ).toBeInTheDocument();
74
+ expect(
75
+ container.querySelector(
76
+ `.ui.card.u-card.max-${CarouselCards.args.maxLines}-lines`,
77
+ ),
78
+ ).toBeInTheDocument();
79
+ expect(
80
+ container.querySelector(
81
+ `.ui.card.u-card.title-max-${CarouselCards.args.maxLines}-lines`,
82
+ ),
83
+ ).toBeInTheDocument();
84
+ expect(
85
+ container.querySelector(
86
+ `.ui.card.u-card.has--object-fit--${CarouselCards.args.objectFit}`,
87
+ ),
88
+ ).toBeInTheDocument();
89
+ expect(
90
+ container.querySelector(
91
+ `.ui.card.u-card.has--object-position--${CarouselCards.args.objectPosition}`,
92
+ ),
93
+ ).toBeInTheDocument();
94
+ });
95
+
29
96
  it('renders the carousel with correct number of cards', () => {
30
97
  const { container } = render(<CarouselCards {...CarouselCards.args} />);
31
98
  const carouselCards = container.querySelectorAll(
@@ -46,7 +113,50 @@ describe('CarouselCards component', () => {
46
113
  });
47
114
  });
48
115
 
116
+ describe('ImageGrid component', () => {
117
+ it('renders the image grid with correct number of cards', () => {
118
+ const { container } = render(<ImageGrid {...ImageGrid.args} />);
119
+ const imageGridCards = container.querySelectorAll(
120
+ '.imageCard-items .ui.card a.image',
121
+ );
122
+ expect(imageGridCards.length).toBe(5);
123
+ });
124
+ });
125
+
49
126
  describe('FluidGrid component', () => {
127
+ it('renders correctly', () => {
128
+ const { container } = render(<FluidGrid {...FluidGrid.args} />);
129
+
130
+ expect(container.querySelector('.image')).toBeInTheDocument();
131
+ expect(container.querySelector('.header')).toBeInTheDocument();
132
+ expect(container.querySelector('.meta')).toBeInTheDocument();
133
+ expect(container.querySelector('.description')).toBeInTheDocument();
134
+
135
+ expect(
136
+ container.querySelector(`.ui.card.u-card.${FluidGrid.args.variant}`),
137
+ ).toBeInTheDocument();
138
+ expect(
139
+ container.querySelector(
140
+ `.ui.card.u-card.max-${FluidGrid.args.maxLines}-lines`,
141
+ ),
142
+ ).toBeInTheDocument();
143
+ expect(
144
+ container.querySelector(
145
+ `.ui.card.u-card.title-max-${FluidGrid.args.maxLines}-lines`,
146
+ ),
147
+ ).toBeInTheDocument();
148
+ expect(
149
+ container.querySelector(
150
+ `.ui.card.u-card.has--object-fit--${FluidGrid.args.objectFit}`,
151
+ ),
152
+ ).toBeInTheDocument();
153
+ expect(
154
+ container.querySelector(
155
+ `.ui.card.u-card.has--object-position--${FluidGrid.args.objectPosition}`,
156
+ ),
157
+ ).toBeInTheDocument();
158
+ });
159
+
50
160
  it('renders fluid grid with correct number of cards', () => {
51
161
  const { container } = render(<FluidGrid {...FluidGrid.args} />);
52
162
  const fluidGridCards = container.querySelectorAll(
@@ -58,6 +168,39 @@ describe('FluidGrid component', () => {
58
168
  });
59
169
 
60
170
  describe('CardGrid component', () => {
171
+ it('renders correctly', () => {
172
+ const { container } = render(<CardGrid {...CardGrid.args} />);
173
+
174
+ expect(container.querySelector('.image')).toBeInTheDocument();
175
+ expect(container.querySelector('.header')).toBeInTheDocument();
176
+ expect(container.querySelector('.meta')).toBeInTheDocument();
177
+ expect(container.querySelector('.description')).toBeInTheDocument();
178
+
179
+ expect(
180
+ container.querySelector(`.ui.card.u-card.${CardGrid.args.variant}`),
181
+ ).toBeInTheDocument();
182
+ expect(
183
+ container.querySelector(
184
+ `.ui.card.u-card.max-${CardGrid.args.maxLines}-lines`,
185
+ ),
186
+ ).toBeInTheDocument();
187
+ expect(
188
+ container.querySelector(
189
+ `.ui.card.u-card.title-max-${CardGrid.args.maxLines}-lines`,
190
+ ),
191
+ ).toBeInTheDocument();
192
+ expect(
193
+ container.querySelector(
194
+ `.ui.card.u-card.has--object-fit--${CardGrid.args.objectFit}`,
195
+ ),
196
+ ).toBeInTheDocument();
197
+ expect(
198
+ container.querySelector(
199
+ `.ui.card.u-card.has--object-position--${CardGrid.args.objectPosition}`,
200
+ ),
201
+ ).toBeInTheDocument();
202
+ });
203
+
61
204
  it('renders the card grid with correct number of cards', () => {
62
205
  const { container } = render(<CardGrid {...CardGrid.args} />);
63
206
  const cardGridCards = container.querySelectorAll(
@@ -67,7 +210,70 @@ describe('CardGrid component', () => {
67
210
  });
68
211
  });
69
212
 
213
+ describe('ImageGrid component', () => {
214
+ it('renders correctly', () => {
215
+ const { container } = render(<ImageGrid {...ImageGrid.args} />);
216
+
217
+ expect(container.querySelector('.ui.image')).toBeInTheDocument();
218
+
219
+ expect(
220
+ container.querySelector(`.ui.card.u-card.${ImageGrid.args.variant}`),
221
+ ).toBeInTheDocument();
222
+ expect(
223
+ container.querySelector(
224
+ `.ui.card.u-card.has--object-fit--${ImageGrid.args.objectFit}`,
225
+ ),
226
+ ).toBeInTheDocument();
227
+ expect(
228
+ container.querySelector(
229
+ `.ui.card.u-card.has--object-position--${ImageGrid.args.objectPosition}`,
230
+ ),
231
+ ).toBeInTheDocument();
232
+ });
233
+
234
+ it('renders the image grid with correct number of cards', () => {
235
+ const { container } = render(<ImageGrid {...ImageGrid.args} />);
236
+ const imageGridCards = container.querySelectorAll(
237
+ '.ui.card.u-card.default .ui.image',
238
+ );
239
+ expect(imageGridCards.length).toBe(5);
240
+ });
241
+ });
242
+
70
243
  describe('Default component', () => {
244
+ it('renders correctly', () => {
245
+ const { container } = render(<Default {...Default.args} />);
246
+
247
+ expect(container.querySelector('.image')).toBeInTheDocument();
248
+ expect(container.querySelector('.header')).toBeInTheDocument();
249
+ expect(container.querySelector('.meta')).toBeInTheDocument();
250
+ expect(container.querySelector('.description')).toBeInTheDocument();
251
+
252
+ expect(
253
+ container.querySelector(`.ui.card.u-card.${Default.args.variant}`),
254
+ ).toBeInTheDocument();
255
+ expect(
256
+ container.querySelector(
257
+ `.ui.card.u-card.max-${Default.args.maxLines}-lines`,
258
+ ),
259
+ ).toBeInTheDocument();
260
+ expect(
261
+ container.querySelector(
262
+ `.ui.card.u-card.title-max-${Default.args.maxLines}-lines`,
263
+ ),
264
+ ).toBeInTheDocument();
265
+ expect(
266
+ container.querySelector(
267
+ `.ui.card.u-card.has--object-fit--${Default.args.objectFit}`,
268
+ ),
269
+ ).toBeInTheDocument();
270
+ expect(
271
+ container.querySelector(
272
+ `.ui.card.u-card.has--object-position--${Default.args.objectPosition}`,
273
+ ),
274
+ ).toBeInTheDocument();
275
+ });
276
+
71
277
  it('renders the default with correct number of cards', () => {
72
278
  const { container } = render(<Default {...Default.args} />);
73
279
  const defaultCards = container.querySelectorAll(
@@ -18,7 +18,7 @@ import Description from '@eeacms/volto-eea-design-system/ui/Footer/Description';
18
18
  /**
19
19
  * Component to display the footer.
20
20
  * @function Footer
21
- * @param {Object} intl Intl object
21
+ * @param {Object} props object
22
22
  * @returns {string} Markup of the component
23
23
  */
24
24
 
@@ -28,7 +28,7 @@ const Footer = (props) => {
28
28
  const bgImgRef = React.useRef();
29
29
  const onScreen = useFirstVisited(bgImgRef);
30
30
  return (
31
- <footer id={'footer'}>
31
+ <footer id={'footer'} aria-label={'Footer'}>
32
32
  <div
33
33
  ref={bgImgRef}
34
34
  className={onScreen ? 'footer-wrapper' : 'footer-wrapper-nobg'}
@@ -15,7 +15,7 @@ import burgerIcon from '@eeacms/volto-eea-design-system/../theme/themes/eea/asse
15
15
  import HeaderSearchPopUp from './HeaderSearchPopUp';
16
16
  import HeaderMenuPopUp from './HeaderMenuPopUp';
17
17
  import PropTypes from 'prop-types';
18
-
18
+ import _ from 'lodash';
19
19
  import { isInternalURL } from '@plone/volto/helpers';
20
20
  import config from '@plone/volto/registry';
21
21
 
@@ -25,7 +25,11 @@ Header.propTypes = {
25
25
  };
26
26
 
27
27
  function Header({ children }) {
28
- return <div className="eea header">{children}</div>;
28
+ return (
29
+ <header className="eea header" aria-label={'Site'}>
30
+ {children}
31
+ </header>
32
+ );
29
33
  }
30
34
 
31
35
  const TopHeader = ({ children }) => (
@@ -44,15 +48,15 @@ const onKeyDownHandler = (event) => {
44
48
  if (event.key === 'Enter') {
45
49
  event.preventDefault();
46
50
  event.target.click();
47
- //event.target.focus();
48
51
  }
49
52
  };
50
53
 
51
54
  const TopDropdownMenu = ({
52
55
  children,
56
+ ariaLabel,
53
57
  className,
58
+ classNameHeader,
54
59
  icon,
55
- hasLanguageDropdown = false,
56
60
  id,
57
61
  tabletText,
58
62
  mobileText,
@@ -63,45 +67,38 @@ const TopDropdownMenu = ({
63
67
  const isMobile = viewportWidth < 767;
64
68
 
65
69
  const Component = ({ mobileText }) => {
70
+ const headerText = mobileText || text;
71
+ const label = ariaLabel || headerText;
72
+ const dropdownRef = React.useRef(null);
73
+
66
74
  return (
67
75
  <>
68
- {children.props['aria-label'] === 'language switcher' ? (
69
- hasLanguageDropdown && (
70
- <Dropdown
71
- id={id}
72
- className={className}
73
- text={mobileText || text}
74
- icon={icon || 'chevron down'}
75
- aria-label="dropdown"
76
- role="dropdown"
77
- lazyLoad
78
- closeOnChange={true}
79
- closeOnBlur={false}
80
- closeOnEscape={true}
81
- openOnFocus={false}
82
- onKeyDown={onKeyDownHandler}
83
- >
84
- <Dropdown.Menu role="option">{children}</Dropdown.Menu>
85
- </Dropdown>
86
- )
87
- ) : (
88
- <Dropdown
89
- id={id}
90
- className={className}
91
- text={mobileText || text}
92
- icon={icon || 'chevron down'}
93
- role="dropdown"
94
- aria-label="dropdown"
95
- lazyLoad
96
- closeOnChange={true}
97
- closeOnBlur={false}
98
- closeOnEscape={true}
99
- openOnFocus={false}
100
- onKeyDown={onKeyDownHandler}
101
- >
102
- <Dropdown.Menu role="option">{children}</Dropdown.Menu>
103
- </Dropdown>
104
- )}
76
+ <Dropdown
77
+ id={id}
78
+ className={className}
79
+ text={() => (
80
+ <div className={`divider text ${classNameHeader}`}>
81
+ {headerText}
82
+ </div>
83
+ )}
84
+ ref={dropdownRef}
85
+ icon={icon || 'chevron down'}
86
+ aria-label={label}
87
+ closeOnChange={true}
88
+ closeOnBlur={false}
89
+ closeOnEscape={true}
90
+ openOnFocus={true}
91
+ onBlur={(e) => {
92
+ const dropdown = dropdownRef.current;
93
+ const ref = dropdown.ref.current;
94
+ if (e.target !== ref && !ref.contains(e.relatedTarget)) {
95
+ dropdown.close();
96
+ }
97
+ }}
98
+ onKeyDown={onKeyDownHandler}
99
+ >
100
+ <Dropdown.Menu role="option">{children}</Dropdown.Menu>
101
+ </Dropdown>
105
102
  </>
106
103
  );
107
104
  };
@@ -288,7 +285,7 @@ const Main = ({
288
285
  className={`main bar ${transparency ? 'transparency' : ''}`}
289
286
  ref={node}
290
287
  >
291
- <Container>
288
+ <Container className={'main-bar-container'}>
292
289
  <Grid>
293
290
  <Grid.Column mobile={8} tablet={8} computer={4}>
294
291
  {logo}
@@ -296,52 +293,57 @@ const Main = ({
296
293
  <Grid.Column mobile={4} tablet={4} computer={8}>
297
294
  <div className={inverted ? 'main-menu inverted' : 'main-menu'}>
298
295
  {menuItems && (
299
- <ul
300
- className="ui text eea-main-menu tablet or lower hidden menu"
301
- ref={desktopMenuRef}
302
- id={'navigation'}
303
- >
304
- {menuItems.map((item) => (
305
- <Menu.Item
306
- name={item['@id'] || item.url}
307
- key={item['@id'] || item.url}
308
- as={'li'}
309
- active={
310
- activeItem.indexOf(item['@id']) !== -1 ||
311
- activeItem.indexOf(item.url) !== -1
312
- }
313
- >
314
- {renderGlobalMenuItem(item, {
315
- onClick: menuOnClick,
316
- })}
317
- </Menu.Item>
318
- ))}
319
- </ul>
296
+ <nav aria-label={'Main'}>
297
+ <ul
298
+ className="ui text eea-main-menu tablet or lower hidden menu"
299
+ ref={desktopMenuRef}
300
+ id={'navigation'}
301
+ >
302
+ {menuItems.map((item) => (
303
+ <Menu.Item
304
+ name={item['@id'] || item.url}
305
+ key={item['@id'] || item.url}
306
+ as={'li'}
307
+ active={
308
+ activeItem.indexOf(item['@id']) !== -1 ||
309
+ activeItem.indexOf(item.url) !== -1
310
+ }
311
+ aria-expanded={
312
+ activeItem.indexOf(item['@id']) !== -1 ||
313
+ activeItem.indexOf(item.url) !== -1
314
+ }
315
+ >
316
+ {renderGlobalMenuItem(item, {
317
+ onClick: menuOnClick,
318
+ })}
319
+ </Menu.Item>
320
+ ))}
321
+ </ul>
322
+ </nav>
320
323
  )}
321
324
  {!hideSearch && (
322
325
  <button
323
326
  className="search-action"
324
327
  onClick={searchOnClick}
325
- tabIndex="0"
326
- aria-pressed="false"
327
- aria-haspopup="true"
328
+ aria-expanded={searchIsActive}
328
329
  ref={searchButtonRef}
329
330
  >
330
331
  {/* <Icon name={!state.activeSearch ? 'search' : 'close'} /> */}
331
332
  <Image
332
333
  src={!searchIsActive ? `${searchIcon}` : `${closeIcon}`}
333
- alt="search button open/close"
334
+ alt="Global search"
334
335
  />
335
336
  </button>
336
337
  )}
337
338
  <Header.BurgerAction
338
339
  className={`mobile ${burger}`}
339
340
  onClick={mobileBurgerOnClick}
341
+ aria-expanded={menuIsActive}
340
342
  ref={mobileMenuBurgerRef}
341
343
  >
342
344
  <Image
343
345
  src={burger === 'open' ? `${closeIcon}` : `${burgerIcon}`}
344
- alt="menu icon open/close"
346
+ alt="Menu navigation"
345
347
  />
346
348
  </Header.BurgerAction>
347
349
  </div>
@@ -374,9 +376,7 @@ const BurgerAction = React.forwardRef((props, ref) => (
374
376
  <button
375
377
  ref={ref}
376
378
  className={`burger-action ${props.className}`}
377
- tabIndex="0"
378
- aria-pressed="false"
379
- aria-haspopup="true"
379
+ {..._.omit(props, ['onClick', 'children', 'className', 'ref'])}
380
380
  onClick={props.onClick}
381
381
  >
382
382
  {props.children}
@@ -58,16 +58,20 @@ const logoProps = {
58
58
  };
59
59
 
60
60
  const links = [
61
- { title: 'EEA Main Portal', href: '/#' },
62
- { title: 'Biodiversity Information System for Europe', href: '/#' },
63
- { title: 'Climate Adaptation Platform', href: '/#' },
64
- { title: 'Copernicus in situ component', href: '/#' },
65
- { title: 'Copernicus land monitoring', href: '/#' },
61
+ { title: 'European Environment Agency website', href: '/#' },
62
+ { title: 'WISE marine - Marine information system for Europe', href: '/#' },
63
+ {
64
+ title: 'WISE freshwater - Freshwater information system for Europe',
65
+ href: '/#',
66
+ },
67
+ { title: 'BISE - Biodiversity information system for Europe', href: '/#' },
68
+ { title: 'FISE - Forest information system for Europe', href: '/#' },
69
+ { title: 'European Climate and health observatory', href: '/#' },
70
+ { title: 'ClimateADAPT', href: '/#' },
66
71
  { title: 'European Industrial Emissions Portal', href: '/#' },
67
- { title: 'Forest Information System for Europe', href: '/#' },
68
- { title: 'Information Platform for Chemical Monitoring', href: '/#' },
69
- { title: 'Marine Water Information System for Europe', href: '/#' },
70
- { title: 'Fresh Water Information System for Europe', href: '/#' },
72
+ { title: 'Climate and Energy in the EU', href: '/#' },
73
+ { title: 'Copernicus Land Monitoring Service', href: '/#' },
74
+ { title: 'Copernicus InSitu', href: '/#' },
71
75
  ];
72
76
 
73
77
  const languages = [
@@ -1234,7 +1238,7 @@ const Template = (args) => {
1234
1238
  <Header>
1235
1239
  <Header.TopHeader>
1236
1240
  <Header.TopItem className="official-union">
1237
- <Image src={eeaFlag} alt="eea flag"></Image>
1241
+ <Image src={eeaFlag} alt="European Union flag"></Image>
1238
1242
  <Header.TopDropdownMenu
1239
1243
  text="An official website of the European Union | How do you know?"
1240
1244
  tabletText="An official website of the European Union"
@@ -1242,6 +1246,7 @@ const Template = (args) => {
1242
1246
  icon="chevron down"
1243
1247
  aria-label="dropdown"
1244
1248
  className=""
1249
+ classNameHeader="mobile-sr-only"
1245
1250
  viewportWidth={viewportWidth}
1246
1251
  >
1247
1252
  <div
@@ -1276,7 +1281,7 @@ const Template = (args) => {
1276
1281
  mobileText={mobileLinksMenuTitle}
1277
1282
  viewportWidth={viewportWidth}
1278
1283
  >
1279
- <div className="wrapper">
1284
+ <div className="wrapper" tabIndex={0} role={'presentation'}>
1280
1285
  {links.map((item, index) => (
1281
1286
  <Dropdown.Item key={index}>
1282
1287
  <a
@@ -1293,47 +1298,49 @@ const Template = (args) => {
1293
1298
  </Header.TopDropdownMenu>
1294
1299
  </Header.TopItem>
1295
1300
 
1296
- <Header.TopDropdownMenu
1297
- id="language-switcher"
1298
- hasLanguageDropdown={hasLanguageDropdown}
1299
- className="item"
1300
- text={`${language.toUpperCase()}`}
1301
- mobileText={`${language.toUpperCase()}`}
1302
- icon={
1303
- <Image
1304
- role="option"
1305
- src={globeIcon}
1306
- alt="language dropdown globe icon"
1307
- ></Image>
1308
- }
1309
- viewportWidth={viewportWidth}
1310
- >
1311
- <ul
1312
- className="wrapper language-list"
1313
- role="listbox"
1314
- aria-label="language switcher"
1301
+ {hasLanguageDropdown && (
1302
+ <Header.TopDropdownMenu
1303
+ id="language-switcher"
1304
+ className="item"
1305
+ ariaLabel={'language switcher'}
1306
+ text={`${language.toUpperCase()}`}
1307
+ mobileText={`${language.toUpperCase()}`}
1308
+ icon={
1309
+ <Image
1310
+ role="option"
1311
+ src={globeIcon}
1312
+ alt="language dropdown globe icon"
1313
+ ></Image>
1314
+ }
1315
+ viewportWidth={viewportWidth}
1315
1316
  >
1316
- {languages.map((item, index) => (
1317
- <Dropdown.Item
1318
- as="li"
1319
- key={index}
1320
- text={
1321
- <a
1322
- href={'/' + item.code}
1323
- onClick={() => setLanguage(item.code)}
1324
- tabIndex={0}
1325
- className={'language-link'}
1326
- >
1327
- {item.name}
1328
- <span className="country-code">
1329
- {item.code.toUpperCase()}
1330
- </span>
1331
- </a>
1332
- }
1333
- ></Dropdown.Item>
1334
- ))}
1335
- </ul>
1336
- </Header.TopDropdownMenu>
1317
+ <ul
1318
+ className="wrapper language-list"
1319
+ role="listbox"
1320
+ aria-label="language switcher"
1321
+ >
1322
+ {languages.map((item, index) => (
1323
+ <Dropdown.Item
1324
+ as="li"
1325
+ key={index}
1326
+ text={
1327
+ <a
1328
+ href={'/' + item.code}
1329
+ onClick={() => setLanguage(item.code)}
1330
+ tabIndex={0}
1331
+ className={'language-link'}
1332
+ >
1333
+ {item.name}
1334
+ <span className="country-code">
1335
+ {item.code.toUpperCase()}
1336
+ </span>
1337
+ </a>
1338
+ }
1339
+ ></Dropdown.Item>
1340
+ ))}
1341
+ </ul>
1342
+ </Header.TopDropdownMenu>
1343
+ )}
1337
1344
  </Header.TopHeader>
1338
1345
  <Header.Main
1339
1346
  transparency={args.transparency}