@eeacms/volto-eea-design-system 1.50.2 → 1.60.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/CHANGELOG.md +17 -0
- package/README.md +4 -0
- package/package.json +1 -1
- package/src/ui/Header/Header.jsx +8 -1
- package/src/ui/Header/Header.stories.test.js +11 -3
- package/src/ui/Header/Header.test.jsx +55 -32
- package/src/ui/Header/HeaderSearchPopUp.js +54 -25
- package/src/ui/Header/HeaderSearchPopUp.test.js +119 -98
- package/theme/themes/eea/assets/images/Header/ai-search.svg +6 -5
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,23 @@ All notable changes to this project will be documented in this file. Dates are d
|
|
|
4
4
|
|
|
5
5
|
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
6
6
|
|
|
7
|
+
### [1.60.0](https://github.com/eea/volto-eea-design-system/compare/1.50.2...1.60.0) - 17 April 2026
|
|
8
|
+
|
|
9
|
+
#### :house: Documentation changes
|
|
10
|
+
|
|
11
|
+
- docs: Update README [Alin Voinea - [`06e6ce7`](https://github.com/eea/volto-eea-design-system/commit/06e6ce7553bb50dea0d000f990f19c568e658b9f)]
|
|
12
|
+
|
|
13
|
+
#### :hammer_and_wrench: Others
|
|
14
|
+
|
|
15
|
+
- update readme [Miu Razvan - [`268086f`](https://github.com/eea/volto-eea-design-system/commit/268086fa17f9f68283dc7528ddbcd71105b676da)]
|
|
16
|
+
- Release 1.60.0 [Alin Voinea - [`c74e3ac`](https://github.com/eea/volto-eea-design-system/commit/c74e3acb79cce153067984a234401e3d16ac0b05)]
|
|
17
|
+
- update version [Miu Razvan - [`5753c9f`](https://github.com/eea/volto-eea-design-system/commit/5753c9f98c80a8699889a55a3fe1abecf3853b77)]
|
|
18
|
+
- fix tests [Miu Razvan - [`32b5e42`](https://github.com/eea/volto-eea-design-system/commit/32b5e42c6f458f7391f930165852700daa758790)]
|
|
19
|
+
- fix tests [Miu Razvan - [`dca166a`](https://github.com/eea/volto-eea-design-system/commit/dca166aa44a9bc3c123a17e78333b850ef401fb6)]
|
|
20
|
+
- fix tests [Miu Razvan - [`0e876ca`](https://github.com/eea/volto-eea-design-system/commit/0e876caf4e172b84dfbc30a16c58009ee4481d94)]
|
|
21
|
+
- fix tests [Miu Razvan - [`86f657a`](https://github.com/eea/volto-eea-design-system/commit/86f657a70a357a412257e9d8cb63f9050ca718e4)]
|
|
22
|
+
- Make use of eea_settings [Miu Razvan - [`78ab3c5`](https://github.com/eea/volto-eea-design-system/commit/78ab3c59a4a7232b09812b3967ddd2b93cdd7fad)]
|
|
23
|
+
- Bump version + add upgrade step [Miu Razvan - [`a1dbbd5`](https://github.com/eea/volto-eea-design-system/commit/a1dbbd557f66174f05ce87ed21073aecefa1f507)]
|
|
7
24
|
### [1.50.2](https://github.com/eea/volto-eea-design-system/compare/1.50.1...1.50.2) - 9 April 2026
|
|
8
25
|
|
|
9
26
|
#### :bug: Bug Fixes
|
package/README.md
CHANGED
|
@@ -25,6 +25,10 @@ This package provides the following features:
|
|
|
25
25
|
2. A repository for the design system source files and mockups
|
|
26
26
|
3. A website to showcase the design system
|
|
27
27
|
|
|
28
|
+
## Upgrade
|
|
29
|
+
|
|
30
|
+
- Version `^1.60.0` requires `^eea.volto.policy@12.0` to allow customizing the header search box.
|
|
31
|
+
|
|
28
32
|
### Try the design system website
|
|
29
33
|
|
|
30
34
|
Checkout the docs found within the [website](https://github.com/eea/volto-eea-design-system/blob/develop/website/README.md) folder
|
package/package.json
CHANGED
package/src/ui/Header/Header.jsx
CHANGED
|
@@ -5,11 +5,13 @@
|
|
|
5
5
|
|
|
6
6
|
import React from 'react'; // , { Component }
|
|
7
7
|
import { useHistory } from 'react-router-dom';
|
|
8
|
+
import { useSelector } from 'react-redux';
|
|
8
9
|
import cx from 'classnames';
|
|
9
10
|
import { Container, Image, Menu, Grid, Dropdown } from 'semantic-ui-react'; // Dropdown,
|
|
10
11
|
|
|
11
12
|
import closeIcon from '@eeacms/volto-eea-design-system/../theme/themes/eea/assets/images/Header/close-line.svg';
|
|
12
|
-
import
|
|
13
|
+
import searchSVG from '@eeacms/volto-eea-design-system/../theme/themes/eea/assets/images/Header/search-line.svg';
|
|
14
|
+
import aiSearchIcon from '@eeacms/volto-eea-design-system/../theme/themes/eea/assets/images/Header/ai-search.svg';
|
|
13
15
|
import burgerIcon from '@eeacms/volto-eea-design-system/../theme/themes/eea/assets/images/Header/menu-line.svg';
|
|
14
16
|
|
|
15
17
|
import HeaderSearchPopUp from './HeaderSearchPopUp';
|
|
@@ -159,6 +161,11 @@ const Main = ({
|
|
|
159
161
|
const [burger, setBurger] = React.useState('');
|
|
160
162
|
const searchInputRef = React.useRef(null);
|
|
161
163
|
const [isClient, setIsClient] = React.useState();
|
|
164
|
+
const headerSettings = useSelector(
|
|
165
|
+
(state) => state.eeaSettings?.data?.header || {},
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
const searchIcon = headerSettings.useAISearchIcon ? aiSearchIcon : searchSVG;
|
|
162
169
|
|
|
163
170
|
const itemsLayouts =
|
|
164
171
|
menuItemsLayouts || config.settings?.menuItemsLayouts || {};
|
|
@@ -2,9 +2,15 @@ import React from 'react';
|
|
|
2
2
|
import { createMemoryHistory } from 'history';
|
|
3
3
|
import { Router } from 'react-router-dom';
|
|
4
4
|
import { render, fireEvent, act } from '@testing-library/react';
|
|
5
|
+
import { Provider } from 'react-redux';
|
|
6
|
+
import { createStore } from 'redux';
|
|
5
7
|
import { Default } from './Header.stories';
|
|
6
8
|
import '@testing-library/jest-dom';
|
|
7
9
|
|
|
10
|
+
const mockStore = createStore(() => ({
|
|
11
|
+
eeaSettings: { data: { header: {} } },
|
|
12
|
+
}));
|
|
13
|
+
|
|
8
14
|
global.ResizeObserver = require('resize-observer-polyfill');
|
|
9
15
|
|
|
10
16
|
const history = createMemoryHistory();
|
|
@@ -129,9 +135,11 @@ describe('Default component', () => {
|
|
|
129
135
|
|
|
130
136
|
it('renders the default with correct number of cards', () => {
|
|
131
137
|
const { container, getByText } = render(
|
|
132
|
-
<
|
|
133
|
-
<
|
|
134
|
-
|
|
138
|
+
<Provider store={mockStore}>
|
|
139
|
+
<Router history={history}>
|
|
140
|
+
<Default {...args} />
|
|
141
|
+
</Router>
|
|
142
|
+
</Provider>,
|
|
135
143
|
);
|
|
136
144
|
const dropdown = container.querySelector('.official-union .content');
|
|
137
145
|
fireEvent.click(dropdown);
|
|
@@ -4,8 +4,29 @@ import '@testing-library/jest-dom';
|
|
|
4
4
|
|
|
5
5
|
import { createMemoryHistory } from 'history';
|
|
6
6
|
import { Router } from 'react-router-dom';
|
|
7
|
+
import { Provider } from 'react-redux';
|
|
7
8
|
import '@testing-library/jest-dom';
|
|
8
9
|
|
|
10
|
+
const mockState = {
|
|
11
|
+
eeaSettings: {
|
|
12
|
+
data: {
|
|
13
|
+
header: {
|
|
14
|
+
useAISearchIcon: false,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const mockStore = {
|
|
21
|
+
getState: () => mockState,
|
|
22
|
+
subscribe: () => () => {},
|
|
23
|
+
dispatch: () => {},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const renderWithProvider = (component) => {
|
|
27
|
+
return render(<Provider store={mockStore}>{component}</Provider>);
|
|
28
|
+
};
|
|
29
|
+
|
|
9
30
|
// Mock subcomponents that pull in ESM helpers not transformed by Jest here
|
|
10
31
|
jest.doMock('./HeaderSearchPopUp', () => () => null);
|
|
11
32
|
jest.doMock('./HeaderMenuPopUp', () => () => null);
|
|
@@ -26,16 +47,16 @@ describe('Header component', () => {
|
|
|
26
47
|
});
|
|
27
48
|
|
|
28
49
|
it('renders without crashing', () => {
|
|
29
|
-
|
|
50
|
+
renderWithProvider(<Header />);
|
|
30
51
|
});
|
|
31
52
|
|
|
32
53
|
it('renders children correctly', () => {
|
|
33
|
-
|
|
54
|
+
renderWithProvider(<Header>Test Children</Header>);
|
|
34
55
|
expect(screen.getByText('Test Children')).toBeInTheDocument();
|
|
35
56
|
});
|
|
36
57
|
|
|
37
58
|
it('renders without crashing', () => {
|
|
38
|
-
|
|
59
|
+
renderWithProvider(
|
|
39
60
|
<Header>
|
|
40
61
|
<Header.TopHeader>
|
|
41
62
|
<Header.TopItem>
|
|
@@ -83,7 +104,7 @@ describe('Header component', () => {
|
|
|
83
104
|
});
|
|
84
105
|
|
|
85
106
|
it('renders without crashing', () => {
|
|
86
|
-
|
|
107
|
+
renderWithProvider(
|
|
87
108
|
<Header>
|
|
88
109
|
<Header.TopHeader>
|
|
89
110
|
<Header.TopItem>
|
|
@@ -129,7 +150,7 @@ describe('Header component', () => {
|
|
|
129
150
|
});
|
|
130
151
|
|
|
131
152
|
it('renders without crashing', () => {
|
|
132
|
-
const { getByText, getAllByText, container } =
|
|
153
|
+
const { getByText, getAllByText, container } = renderWithProvider(
|
|
133
154
|
<Router history={history}>
|
|
134
155
|
<Header>
|
|
135
156
|
<Header.TopHeader>
|
|
@@ -427,7 +448,7 @@ test('marks only the best-matching top-level item active (sibling collision case
|
|
|
427
448
|
},
|
|
428
449
|
];
|
|
429
450
|
|
|
430
|
-
const { container, getByText } =
|
|
451
|
+
const { container, getByText } = renderWithProvider(
|
|
431
452
|
<Router history={history}>
|
|
432
453
|
<Header>
|
|
433
454
|
<Header.Main
|
|
@@ -476,7 +497,7 @@ test('exact match wins: parent item active only on its own URL', async () => {
|
|
|
476
497
|
},
|
|
477
498
|
];
|
|
478
499
|
|
|
479
|
-
const { container, getByText } =
|
|
500
|
+
const { container, getByText } = renderWithProvider(
|
|
480
501
|
<Router history={history}>
|
|
481
502
|
<Header>
|
|
482
503
|
<Header.Main
|
|
@@ -515,7 +536,7 @@ test('mobile burger menu interactions', async () => {
|
|
|
515
536
|
},
|
|
516
537
|
];
|
|
517
538
|
|
|
518
|
-
const { container } =
|
|
539
|
+
const { container } = renderWithProvider(
|
|
519
540
|
<Router history={history}>
|
|
520
541
|
<Header>
|
|
521
542
|
<Header.Main
|
|
@@ -559,7 +580,7 @@ test('search interactions and state management', async () => {
|
|
|
559
580
|
},
|
|
560
581
|
];
|
|
561
582
|
|
|
562
|
-
const { container } =
|
|
583
|
+
const { container } = renderWithProvider(
|
|
563
584
|
<Router history={history}>
|
|
564
585
|
<Header>
|
|
565
586
|
<Header.Main
|
|
@@ -603,7 +624,7 @@ test('escape key handling', async () => {
|
|
|
603
624
|
},
|
|
604
625
|
];
|
|
605
626
|
|
|
606
|
-
const { container } =
|
|
627
|
+
const { container } = renderWithProvider(
|
|
607
628
|
<Router history={history}>
|
|
608
629
|
<Header>
|
|
609
630
|
<Header.Main
|
|
@@ -650,7 +671,7 @@ test('multilingual pathname handling', async () => {
|
|
|
650
671
|
},
|
|
651
672
|
];
|
|
652
673
|
|
|
653
|
-
const { rerender } =
|
|
674
|
+
const { rerender } = renderWithProvider(
|
|
654
675
|
<Router history={history}>
|
|
655
676
|
<Header>
|
|
656
677
|
<Header.Main
|
|
@@ -674,25 +695,27 @@ test('multilingual pathname handling', async () => {
|
|
|
674
695
|
|
|
675
696
|
// Test with empty multilingual path
|
|
676
697
|
rerender(
|
|
677
|
-
<
|
|
678
|
-
<
|
|
679
|
-
<Header
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
{item.title}
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
{item.
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
698
|
+
<Provider store={mockStore}>
|
|
699
|
+
<Router history={history}>
|
|
700
|
+
<Header>
|
|
701
|
+
<Header.Main
|
|
702
|
+
pathname="/en/"
|
|
703
|
+
isMultilingual={true}
|
|
704
|
+
menuItems={menuItems}
|
|
705
|
+
renderGlobalMenuItem={(item) => (
|
|
706
|
+
<a href={item.url} title={item.title}>
|
|
707
|
+
{item.title}
|
|
708
|
+
</a>
|
|
709
|
+
)}
|
|
710
|
+
renderMenuItem={(item, options) => (
|
|
711
|
+
<a href={item.url || '/'} {...(options || {})}>
|
|
712
|
+
{item.title}
|
|
713
|
+
</a>
|
|
714
|
+
)}
|
|
715
|
+
/>
|
|
716
|
+
</Header>
|
|
717
|
+
</Router>
|
|
718
|
+
</Provider>,
|
|
696
719
|
);
|
|
697
720
|
});
|
|
698
721
|
|
|
@@ -707,11 +730,11 @@ test('menu click outside behavior with different active states', async () => {
|
|
|
707
730
|
},
|
|
708
731
|
];
|
|
709
732
|
|
|
710
|
-
const { container } =
|
|
733
|
+
const { container } = renderWithProvider(
|
|
711
734
|
<Router history={history}>
|
|
712
735
|
<Header>
|
|
713
736
|
<Header.Main
|
|
714
|
-
pathname="/
|
|
737
|
+
pathname="/test"
|
|
715
738
|
menuItems={menuItems}
|
|
716
739
|
renderGlobalMenuItem={(item) => (
|
|
717
740
|
<a href={item.url} title={item.title}>
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import React, { useEffect } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { useSelector } from 'react-redux';
|
|
3
|
+
import { Container, Input, List, Image } from 'semantic-ui-react';
|
|
3
4
|
import { withRouter, Link } from 'react-router-dom';
|
|
4
5
|
import { useClickOutside } from '@eeacms/volto-eea-design-system/helpers';
|
|
5
6
|
import { handleEnterKeyPress } from '@eeacms/volto-eea-design-system/helpers';
|
|
6
7
|
|
|
8
|
+
import searchSVG from '@eeacms/volto-eea-design-system/../theme/themes/eea/assets/images/Header/search-line.svg';
|
|
9
|
+
import aiSearchIcon from '@eeacms/volto-eea-design-system/../theme/themes/eea/assets/images/Header/ai-search.svg';
|
|
10
|
+
|
|
7
11
|
const getRandomItems = (arr, max) => {
|
|
8
12
|
return (
|
|
9
13
|
arr?.slice(0, max).map(function () {
|
|
@@ -12,36 +16,57 @@ const getRandomItems = (arr, max) => {
|
|
|
12
16
|
);
|
|
13
17
|
};
|
|
14
18
|
|
|
19
|
+
function ActionItem({ action }) {
|
|
20
|
+
if (action.type === 'info') {
|
|
21
|
+
return <div>{action.content}</div>;
|
|
22
|
+
}
|
|
23
|
+
if (action.type === 'button-link') {
|
|
24
|
+
return (
|
|
25
|
+
<a
|
|
26
|
+
href={action.url}
|
|
27
|
+
className="ui button white inverted"
|
|
28
|
+
title={action.title}
|
|
29
|
+
>
|
|
30
|
+
{action.title}
|
|
31
|
+
</a>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
15
37
|
function HeaderSearchPopUp({
|
|
16
38
|
history,
|
|
17
|
-
location,
|
|
18
39
|
onClose,
|
|
19
40
|
searchInputRef,
|
|
20
41
|
headerSearchBox,
|
|
21
42
|
triggerRefs = [],
|
|
22
43
|
}) {
|
|
23
44
|
const nodeRef = React.useRef();
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
45
|
+
|
|
46
|
+
const searchInput =
|
|
47
|
+
headerSearchBox.filter((v) => v.type === 'search-input')[0] || {};
|
|
48
|
+
const actions =
|
|
49
|
+
headerSearchBox.filter((v) => ['info', 'button-link'].includes(v.type)) ||
|
|
50
|
+
[];
|
|
30
51
|
|
|
31
52
|
const {
|
|
32
53
|
path = '',
|
|
33
|
-
buttonTitle,
|
|
34
|
-
buttonUrl,
|
|
35
|
-
description,
|
|
36
54
|
placeholder = 'Search',
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
55
|
+
suggestionsTitle,
|
|
56
|
+
maxToShow,
|
|
57
|
+
suggestions,
|
|
58
|
+
} = searchInput;
|
|
40
59
|
|
|
41
60
|
const [visibleSuggestions, setVisibileSuggestions] = React.useState(
|
|
42
61
|
getRandomItems(suggestions, maxToShow),
|
|
43
62
|
);
|
|
44
63
|
|
|
64
|
+
const headerSettings = useSelector(
|
|
65
|
+
(state) => state.eeaSettings?.data?.header || {},
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const searchIcon = headerSettings.useAISearchIcon ? aiSearchIcon : searchSVG;
|
|
69
|
+
|
|
45
70
|
useEffect(() => {
|
|
46
71
|
setVisibileSuggestions(getRandomItems(suggestions, maxToShow));
|
|
47
72
|
}, [maxToShow, suggestions]);
|
|
@@ -77,18 +102,27 @@ function HeaderSearchPopUp({
|
|
|
77
102
|
ref={searchInputRef}
|
|
78
103
|
className="icon search"
|
|
79
104
|
action={{
|
|
80
|
-
className: 'icon
|
|
105
|
+
className: 'icon',
|
|
81
106
|
'aria-label': 'Submit search',
|
|
82
107
|
onClick: onSubmit,
|
|
83
108
|
onKeyDown: (event) => {
|
|
84
109
|
handleEnterKeyPress(event, onSubmit);
|
|
85
110
|
},
|
|
111
|
+
children: (
|
|
112
|
+
<Image
|
|
113
|
+
src={searchIcon}
|
|
114
|
+
alt="Global search"
|
|
115
|
+
height={45}
|
|
116
|
+
width={45}
|
|
117
|
+
className="header-search-icon"
|
|
118
|
+
/>
|
|
119
|
+
),
|
|
86
120
|
}}
|
|
87
121
|
placeholder={placeholder}
|
|
88
122
|
fluid
|
|
89
123
|
/>
|
|
90
124
|
</form>
|
|
91
|
-
{
|
|
125
|
+
{suggestions?.length > 0 && (
|
|
92
126
|
<div className="search-suggestions">
|
|
93
127
|
{suggestionsTitle && <h4>{suggestionsTitle}</h4>}
|
|
94
128
|
|
|
@@ -109,17 +143,12 @@ function HeaderSearchPopUp({
|
|
|
109
143
|
</div>
|
|
110
144
|
)}
|
|
111
145
|
</Container>
|
|
112
|
-
{
|
|
146
|
+
{actions?.length > 0 && (
|
|
113
147
|
<div className="advanced-search">
|
|
114
148
|
<Container>
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
className="ui button white inverted"
|
|
119
|
-
title="Advanced search"
|
|
120
|
-
>
|
|
121
|
-
{buttonTitle}
|
|
122
|
-
</a>
|
|
149
|
+
{actions.map((action, i) => (
|
|
150
|
+
<ActionItem key={i} action={action} />
|
|
151
|
+
))}
|
|
123
152
|
</Container>
|
|
124
153
|
</div>
|
|
125
154
|
)}
|
|
@@ -2,41 +2,53 @@ import React from 'react';
|
|
|
2
2
|
import { render, screen, fireEvent } from '@testing-library/react';
|
|
3
3
|
import { createMemoryHistory } from 'history';
|
|
4
4
|
import { Router } from 'react-router-dom';
|
|
5
|
+
import { Provider } from 'react-redux';
|
|
5
6
|
import HeaderSearchPopUp from './HeaderSearchPopUp';
|
|
6
7
|
import '@testing-library/jest-dom';
|
|
7
8
|
|
|
9
|
+
const mockState = {
|
|
10
|
+
eeaSettings: {
|
|
11
|
+
data: {
|
|
12
|
+
header: {
|
|
13
|
+
useAISearchIcon: false,
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const mockStore = {
|
|
20
|
+
getState: () => mockState,
|
|
21
|
+
subscribe: () => () => {},
|
|
22
|
+
dispatch: () => {},
|
|
23
|
+
};
|
|
24
|
+
|
|
8
25
|
describe('HeaderSearchPopUp', () => {
|
|
9
26
|
let history;
|
|
10
27
|
const mockOnClose = jest.fn();
|
|
11
28
|
const sampleHeaderSearchBox = [
|
|
12
29
|
{
|
|
30
|
+
type: 'search-input',
|
|
13
31
|
path: '/search',
|
|
14
|
-
buttonTitle: 'Advanced Search',
|
|
15
|
-
buttonUrl: '/advanced-search',
|
|
16
|
-
description: 'Sample description',
|
|
17
32
|
placeholder: 'Search',
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
33
|
+
suggestionsTitle: 'Suggestions Title',
|
|
34
|
+
suggestions: ['suggestion 1', 'suggestion 2', 'suggestion 3'],
|
|
35
|
+
maxToShow: 3,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
type: 'button-link',
|
|
39
|
+
title: 'Advanced Search',
|
|
40
|
+
url: '/advanced-search',
|
|
24
41
|
},
|
|
25
42
|
];
|
|
26
43
|
|
|
27
44
|
const sampleHeaderSearchBoxWithMatchpath = [
|
|
28
45
|
{
|
|
46
|
+
type: 'search-input',
|
|
29
47
|
matchpath: '/search',
|
|
30
|
-
buttonTitle: 'Advanced Search',
|
|
31
|
-
buttonUrl: undefined,
|
|
32
|
-
description: 'Sample description',
|
|
33
48
|
placeholder: 'Search',
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
maxToShow: 3,
|
|
38
|
-
},
|
|
39
|
-
isDefault: true,
|
|
49
|
+
suggestionsTitle: 'Suggestions Title',
|
|
50
|
+
suggestions: ['suggestion 1', 'suggestion 2', 'suggestion 3'],
|
|
51
|
+
maxToShow: 3,
|
|
40
52
|
},
|
|
41
53
|
];
|
|
42
54
|
|
|
@@ -50,26 +62,30 @@ describe('HeaderSearchPopUp', () => {
|
|
|
50
62
|
|
|
51
63
|
it('should render HeaderSearchPopUp', () => {
|
|
52
64
|
render(
|
|
53
|
-
<
|
|
54
|
-
<
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
65
|
+
<Provider store={mockStore}>
|
|
66
|
+
<Router history={history}>
|
|
67
|
+
<HeaderSearchPopUp
|
|
68
|
+
headerSearchBox={sampleHeaderSearchBox}
|
|
69
|
+
onClose={mockOnClose}
|
|
70
|
+
triggerRefs={[]}
|
|
71
|
+
/>
|
|
72
|
+
</Router>
|
|
73
|
+
</Provider>,
|
|
60
74
|
);
|
|
61
75
|
expect(screen.getByPlaceholderText('Search')).toBeInTheDocument();
|
|
62
76
|
});
|
|
63
77
|
|
|
64
78
|
it('should update search text on change', () => {
|
|
65
79
|
render(
|
|
66
|
-
<
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
80
|
+
<Provider store={mockStore}>
|
|
81
|
+
<Router history={history}>
|
|
82
|
+
<HeaderSearchPopUp
|
|
83
|
+
headerSearchBox={sampleHeaderSearchBox}
|
|
84
|
+
onClose={mockOnClose}
|
|
85
|
+
triggerRefs={[]}
|
|
86
|
+
/>
|
|
87
|
+
</Router>
|
|
88
|
+
</Provider>,
|
|
73
89
|
);
|
|
74
90
|
const input = screen.getByPlaceholderText('Search');
|
|
75
91
|
fireEvent.change(input, { target: { value: 'New text' } });
|
|
@@ -80,32 +96,35 @@ describe('HeaderSearchPopUp', () => {
|
|
|
80
96
|
window.searchContext = { resetSearch: jest.fn() };
|
|
81
97
|
|
|
82
98
|
const { container } = render(
|
|
83
|
-
<
|
|
84
|
-
<
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
99
|
+
<Provider store={mockStore}>
|
|
100
|
+
<Router history={history}>
|
|
101
|
+
<HeaderSearchPopUp
|
|
102
|
+
headerSearchBox={sampleHeaderSearchBox}
|
|
103
|
+
onClose={mockOnClose}
|
|
104
|
+
triggerRefs={[]}
|
|
105
|
+
/>
|
|
106
|
+
</Router>
|
|
107
|
+
</Provider>,
|
|
90
108
|
);
|
|
91
109
|
const input = screen.getByPlaceholderText('Search');
|
|
92
110
|
fireEvent.change(input, { target: { value: 'Search text' } });
|
|
93
111
|
fireEvent.submit(container.querySelector('form'));
|
|
94
112
|
fireEvent.keyDown(input, { key: 'Enter', code: 'Enter' });
|
|
95
113
|
expect(history.location.pathname).toBe('/search');
|
|
96
|
-
// expect(history.location.search).toBe('?q=Search text');
|
|
97
114
|
});
|
|
98
115
|
|
|
99
116
|
it('should navigate to the suggestion when a suggestion is clicked', () => {
|
|
100
117
|
window.searchContext = { resetSearch: jest.fn() };
|
|
101
118
|
|
|
102
119
|
render(
|
|
103
|
-
<
|
|
104
|
-
<
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
120
|
+
<Provider store={mockStore}>
|
|
121
|
+
<Router history={history}>
|
|
122
|
+
<HeaderSearchPopUp
|
|
123
|
+
headerSearchBox={sampleHeaderSearchBoxWithMatchpath}
|
|
124
|
+
onClose={mockOnClose}
|
|
125
|
+
/>
|
|
126
|
+
</Router>
|
|
127
|
+
</Provider>,
|
|
109
128
|
);
|
|
110
129
|
fireEvent.click(screen.getByText('suggestion 1'));
|
|
111
130
|
expect(history.location.pathname).toBe('/');
|
|
@@ -114,13 +133,15 @@ describe('HeaderSearchPopUp', () => {
|
|
|
114
133
|
|
|
115
134
|
it('should handle keydown events on search action button', () => {
|
|
116
135
|
const { container } = render(
|
|
117
|
-
<
|
|
118
|
-
<
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
136
|
+
<Provider store={mockStore}>
|
|
137
|
+
<Router history={history}>
|
|
138
|
+
<HeaderSearchPopUp
|
|
139
|
+
headerSearchBox={sampleHeaderSearchBox}
|
|
140
|
+
onClose={mockOnClose}
|
|
141
|
+
triggerRefs={[]}
|
|
142
|
+
/>
|
|
143
|
+
</Router>
|
|
144
|
+
</Provider>,
|
|
124
145
|
);
|
|
125
146
|
|
|
126
147
|
const searchActionButton = container.querySelector(
|
|
@@ -128,25 +149,23 @@ describe('HeaderSearchPopUp', () => {
|
|
|
128
149
|
);
|
|
129
150
|
expect(searchActionButton).toBeInTheDocument();
|
|
130
151
|
|
|
131
|
-
// Test Enter key press on search action button
|
|
132
152
|
fireEvent.keyDown(searchActionButton, { key: 'Enter', code: 'Enter' });
|
|
133
|
-
|
|
134
|
-
// Test other key press (should not trigger action)
|
|
135
153
|
fireEvent.keyDown(searchActionButton, { key: 'Tab', code: 'Tab' });
|
|
136
154
|
});
|
|
137
155
|
|
|
138
156
|
it('should handle search without searchContext', () => {
|
|
139
|
-
// Remove searchContext to test the fallback
|
|
140
157
|
delete window.searchContext;
|
|
141
158
|
|
|
142
159
|
const { container } = render(
|
|
143
|
-
<
|
|
144
|
-
<
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
160
|
+
<Provider store={mockStore}>
|
|
161
|
+
<Router history={history}>
|
|
162
|
+
<HeaderSearchPopUp
|
|
163
|
+
headerSearchBox={sampleHeaderSearchBox}
|
|
164
|
+
onClose={mockOnClose}
|
|
165
|
+
triggerRefs={[]}
|
|
166
|
+
/>
|
|
167
|
+
</Router>
|
|
168
|
+
</Provider>,
|
|
150
169
|
);
|
|
151
170
|
|
|
152
171
|
const input = screen.getByPlaceholderText('Search');
|
|
@@ -157,16 +176,17 @@ describe('HeaderSearchPopUp', () => {
|
|
|
157
176
|
});
|
|
158
177
|
|
|
159
178
|
it('should handle suggestion click without searchContext', () => {
|
|
160
|
-
// Remove searchContext to test the fallback
|
|
161
179
|
delete window.searchContext;
|
|
162
180
|
|
|
163
181
|
render(
|
|
164
|
-
<
|
|
165
|
-
<
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
182
|
+
<Provider store={mockStore}>
|
|
183
|
+
<Router history={history}>
|
|
184
|
+
<HeaderSearchPopUp
|
|
185
|
+
headerSearchBox={sampleHeaderSearchBox}
|
|
186
|
+
onClose={mockOnClose}
|
|
187
|
+
/>
|
|
188
|
+
</Router>
|
|
189
|
+
</Provider>,
|
|
170
190
|
);
|
|
171
191
|
|
|
172
192
|
fireEvent.click(screen.getByText('suggestion 1'));
|
|
@@ -176,23 +196,27 @@ describe('HeaderSearchPopUp', () => {
|
|
|
176
196
|
it('should render without search suggestions', () => {
|
|
177
197
|
const headerSearchBoxNoSuggestions = [
|
|
178
198
|
{
|
|
199
|
+
type: 'search-input',
|
|
179
200
|
path: '/search',
|
|
180
|
-
buttonTitle: 'Advanced Search',
|
|
181
|
-
buttonUrl: '/advanced-search',
|
|
182
|
-
description: 'Sample description',
|
|
183
201
|
placeholder: 'Search',
|
|
184
|
-
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
type: 'button-link',
|
|
205
|
+
title: 'Advanced Search',
|
|
206
|
+
url: '/advanced-search',
|
|
185
207
|
},
|
|
186
208
|
];
|
|
187
209
|
|
|
188
210
|
render(
|
|
189
|
-
<
|
|
190
|
-
<
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
211
|
+
<Provider store={mockStore}>
|
|
212
|
+
<Router history={history}>
|
|
213
|
+
<HeaderSearchPopUp
|
|
214
|
+
headerSearchBox={headerSearchBoxNoSuggestions}
|
|
215
|
+
onClose={mockOnClose}
|
|
216
|
+
triggerRefs={[]}
|
|
217
|
+
/>
|
|
218
|
+
</Router>
|
|
219
|
+
</Provider>,
|
|
196
220
|
);
|
|
197
221
|
|
|
198
222
|
expect(screen.getByPlaceholderText('Search')).toBeInTheDocument();
|
|
@@ -202,28 +226,25 @@ describe('HeaderSearchPopUp', () => {
|
|
|
202
226
|
it('should render with empty suggestions array', () => {
|
|
203
227
|
const headerSearchBoxEmptySuggestions = [
|
|
204
228
|
{
|
|
229
|
+
type: 'search-input',
|
|
205
230
|
path: '/search',
|
|
206
|
-
buttonTitle: 'Advanced Search',
|
|
207
|
-
buttonUrl: '/advanced-search',
|
|
208
|
-
description: 'Sample description',
|
|
209
231
|
placeholder: 'Search',
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
maxToShow: 3,
|
|
214
|
-
},
|
|
215
|
-
isDefault: true,
|
|
232
|
+
suggestionsTitle: 'Suggestions Title',
|
|
233
|
+
suggestions: [],
|
|
234
|
+
maxToShow: 3,
|
|
216
235
|
},
|
|
217
236
|
];
|
|
218
237
|
|
|
219
238
|
render(
|
|
220
|
-
<
|
|
221
|
-
<
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
239
|
+
<Provider store={mockStore}>
|
|
240
|
+
<Router history={history}>
|
|
241
|
+
<HeaderSearchPopUp
|
|
242
|
+
headerSearchBox={headerSearchBoxEmptySuggestions}
|
|
243
|
+
onClose={mockOnClose}
|
|
244
|
+
triggerRefs={[]}
|
|
245
|
+
/>
|
|
246
|
+
</Router>
|
|
247
|
+
</Provider>,
|
|
227
248
|
);
|
|
228
249
|
|
|
229
250
|
expect(screen.getByPlaceholderText('Search')).toBeInTheDocument();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<svg fill="currentColor" height="20" viewBox="0 -960 960 960" width="20" version="1.1"
|
|
3
|
+
xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
|
|
4
|
+
<path
|
|
5
|
+
d="m 743.91749,-527.97997 q -1.19965,0 -9.59723,-7.19791 -19.19445,-73.17883 -72.57899,-126.56339 -53.38456,-53.38454 -126.56339,-72.57899 -2.39931,-1.19966 -7.19791,-9.59723 0,-2.3993 7.19791,-9.59722 73.17883,-19.19444 126.56339,-72.579 53.38454,-53.38456 72.57899,-126.56338 1.19966,-2.39931 9.59723,-7.19792 2.3993,0 9.59722,7.19792 20.39409,73.17882 73.17882,126.56338 52.78474,53.38456 125.96356,72.579 2.39931,0 7.19792,9.59722 0,1.19965 -7.19792,9.59723 -73.17882,19.19445 -126.56338,72.57899 -53.38456,53.38456 -72.579,126.56339 0,2.39931 -9.59722,7.19791 z M 796.70222,-0.13268868 494.38968,-302.44522 q -35.98958,28.79167 -82.77604,45.58681 -46.78647,16.79514 -99.5712,16.79514 -130.76217,0 -221.335957,-90.5738 -90.57379432,-90.57378 -90.57379432,-221.33595 0,-130.76217 90.57379432,-221.33597 90.573787,-90.57379 221.335957,-90.57379 34.78993,0 68.38021,7.19792 33.59029,7.19792 63.58161,21.59376 l -11.99653,104.36979 q -26.39236,-17.99479 -56.38368,-27.59202 -29.99133,-9.59722 -63.58161,-9.59722 -89.97396,0 -152.95574,62.98177 -62.981779,62.98179 -62.981779,152.95576 0,89.97396 62.981779,152.95574 62.98178,62.98178 152.95574,62.98178 79.1771,0 138.55991,-50.98526 59.38283,-50.98523 73.77866,-134.96094 l 86.375,59.98264 q -8.39756,26.39236 -20.99392,49.18577 -12.59636,22.7934 -28.19184,43.18751 l 302.31253,302.312529 z"
|
|
6
|
+
fill="rgba(255,255,255,1)" style="stroke-width:1" />
|
|
6
7
|
</svg>
|