@plone/volto 19.0.0-alpha.36 → 19.0.0-alpha.38

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 (181) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/README.md +1 -1
  3. package/locales/af/LC_MESSAGES/volto.po +29 -3
  4. package/locales/af.json +1 -1
  5. package/locales/ar/LC_MESSAGES/volto.po +29 -3
  6. package/locales/ar.json +1 -1
  7. package/locales/bg/LC_MESSAGES/volto.po +29 -3
  8. package/locales/bg.json +1 -1
  9. package/locales/bn/LC_MESSAGES/volto.po +29 -3
  10. package/locales/bn.json +1 -1
  11. package/locales/ca/LC_MESSAGES/volto.po +32 -6
  12. package/locales/ca.json +1 -1
  13. package/locales/cs/LC_MESSAGES/volto.po +30 -4
  14. package/locales/cs.json +1 -1
  15. package/locales/cy/LC_MESSAGES/volto.po +29 -3
  16. package/locales/cy.json +1 -1
  17. package/locales/da/LC_MESSAGES/volto.po +29 -3
  18. package/locales/da.json +1 -1
  19. package/locales/de/LC_MESSAGES/volto.po +32 -6
  20. package/locales/de.json +1 -1
  21. package/locales/el/LC_MESSAGES/volto.po +29 -3
  22. package/locales/el.json +1 -1
  23. package/locales/en/LC_MESSAGES/volto.po +25 -0
  24. package/locales/en.json +1 -1
  25. package/locales/en_AU/LC_MESSAGES/volto.po +29 -3
  26. package/locales/en_AU.json +1 -1
  27. package/locales/en_GB/LC_MESSAGES/volto.po +29 -3
  28. package/locales/en_GB.json +1 -1
  29. package/locales/eo/LC_MESSAGES/volto.po +29 -3
  30. package/locales/eo.json +1 -1
  31. package/locales/es/LC_MESSAGES/volto.po +30 -5
  32. package/locales/es.json +1 -1
  33. package/locales/et/LC_MESSAGES/volto.po +29 -3
  34. package/locales/et.json +1 -1
  35. package/locales/eu/LC_MESSAGES/volto.po +30 -5
  36. package/locales/eu.json +1 -1
  37. package/locales/fa/LC_MESSAGES/volto.po +29 -3
  38. package/locales/fa.json +1 -1
  39. package/locales/fi/LC_MESSAGES/volto.po +30 -4
  40. package/locales/fi.json +1 -1
  41. package/locales/fr/LC_MESSAGES/volto.po +208 -183
  42. package/locales/fr.json +1 -1
  43. package/locales/fu/LC_MESSAGES/volto.po +29 -3
  44. package/locales/fu.json +1 -1
  45. package/locales/gl/LC_MESSAGES/volto.po +27 -2
  46. package/locales/gl.json +1 -1
  47. package/locales/he/LC_MESSAGES/volto.po +29 -3
  48. package/locales/he.json +1 -1
  49. package/locales/hi/LC_MESSAGES/volto.po +34 -8
  50. package/locales/hi.json +1 -1
  51. package/locales/hr/LC_MESSAGES/volto.po +30 -4
  52. package/locales/hr.json +1 -1
  53. package/locales/hu/LC_MESSAGES/volto.po +29 -3
  54. package/locales/hu.json +1 -1
  55. package/locales/hy/LC_MESSAGES/volto.po +29 -3
  56. package/locales/hy.json +1 -1
  57. package/locales/id/LC_MESSAGES/volto.po +29 -3
  58. package/locales/id.json +1 -1
  59. package/locales/it/LC_MESSAGES/volto.po +29 -4
  60. package/locales/it.json +1 -1
  61. package/locales/ja/LC_MESSAGES/volto.po +29 -3
  62. package/locales/ja.json +1 -1
  63. package/locales/ka/LC_MESSAGES/volto.po +29 -3
  64. package/locales/ka.json +1 -1
  65. package/locales/kn/LC_MESSAGES/volto.po +29 -3
  66. package/locales/kn.json +1 -1
  67. package/locales/ko/LC_MESSAGES/volto.po +29 -3
  68. package/locales/ko.json +1 -1
  69. package/locales/lt/LC_MESSAGES/volto.po +30 -4
  70. package/locales/lt.json +1 -1
  71. package/locales/lv/LC_MESSAGES/volto.po +29 -3
  72. package/locales/lv.json +1 -1
  73. package/locales/mi/LC_MESSAGES/volto.po +29 -3
  74. package/locales/mi.json +1 -1
  75. package/locales/mk/LC_MESSAGES/volto.po +29 -3
  76. package/locales/mk.json +1 -1
  77. package/locales/my/LC_MESSAGES/volto.po +29 -3
  78. package/locales/my.json +1 -1
  79. package/locales/nb_NO/LC_MESSAGES/volto.po +29 -3
  80. package/locales/nb_NO.json +1 -1
  81. package/locales/nl/LC_MESSAGES/volto.po +69 -43
  82. package/locales/nl.json +1 -1
  83. package/locales/nn/LC_MESSAGES/volto.po +29 -3
  84. package/locales/nn.json +1 -1
  85. package/locales/pl/LC_MESSAGES/volto.po +30 -4
  86. package/locales/pl.json +1 -1
  87. package/locales/pt/LC_MESSAGES/volto.po +30 -4
  88. package/locales/pt.json +1 -1
  89. package/locales/pt_BR/LC_MESSAGES/volto.po +54 -29
  90. package/locales/pt_BR.json +1 -1
  91. package/locales/rm/LC_MESSAGES/volto.po +29 -3
  92. package/locales/rm.json +1 -1
  93. package/locales/ro/LC_MESSAGES/volto.po +30 -5
  94. package/locales/ro.json +1 -1
  95. package/locales/ru/LC_MESSAGES/volto.po +30 -4
  96. package/locales/ru.json +1 -1
  97. package/locales/sk/LC_MESSAGES/volto.po +30 -4
  98. package/locales/sk.json +1 -1
  99. package/locales/sl/LC_MESSAGES/volto.po +29 -3
  100. package/locales/sl.json +1 -1
  101. package/locales/sm/LC_MESSAGES/volto.po +29 -3
  102. package/locales/sm.json +1 -1
  103. package/locales/sq/LC_MESSAGES/volto.po +29 -3
  104. package/locales/sq.json +1 -1
  105. package/locales/sr/LC_MESSAGES/volto.po +30 -4
  106. package/locales/sr.json +1 -1
  107. package/locales/sr@cyrl/LC_MESSAGES/volto.po +29 -3
  108. package/locales/sr@cyrl.json +1 -1
  109. package/locales/sr@latn/LC_MESSAGES/volto.po +29 -3
  110. package/locales/sr@latn.json +1 -1
  111. package/locales/sv/LC_MESSAGES/volto.po +31 -5
  112. package/locales/sv.json +1 -1
  113. package/locales/ta/LC_MESSAGES/volto.po +30 -5
  114. package/locales/ta.json +1 -1
  115. package/locales/te/LC_MESSAGES/volto.po +29 -3
  116. package/locales/te.json +1 -1
  117. package/locales/th/LC_MESSAGES/volto.po +29 -3
  118. package/locales/th.json +1 -1
  119. package/locales/to/LC_MESSAGES/volto.po +29 -3
  120. package/locales/to.json +1 -1
  121. package/locales/tr/LC_MESSAGES/volto.po +29 -4
  122. package/locales/tr.json +1 -1
  123. package/locales/uk/LC_MESSAGES/volto.po +30 -4
  124. package/locales/uk.json +1 -1
  125. package/locales/vi/LC_MESSAGES/volto.po +29 -3
  126. package/locales/vi.json +1 -1
  127. package/locales/volto.pot +26 -1
  128. package/locales/zh_CN/LC_MESSAGES/volto.po +29 -4
  129. package/locales/zh_CN.json +1 -1
  130. package/locales/zh_Hant/LC_MESSAGES/volto.po +29 -3
  131. package/locales/zh_Hant.json +1 -1
  132. package/locales/zh_Hant_HK/LC_MESSAGES/volto.po +29 -3
  133. package/locales/zh_Hant_HK.json +1 -1
  134. package/package.json +9 -9
  135. package/src/components/manage/BlockChooser/BlockChooser.jsx +7 -10
  136. package/src/components/manage/Blocks/Block/Edit.jsx +19 -10
  137. package/src/components/manage/Blocks/Block/Order/Item.jsx +9 -4
  138. package/src/components/manage/Contents/DropZoneContent.jsx +1 -0
  139. package/src/components/manage/Controlpanels/BlockType.tsx +2 -3
  140. package/src/components/manage/Controlpanels/ContentTypes.jsx +9 -2
  141. package/src/components/manage/Controlpanels/Users/UsersControlpanel.jsx +58 -5
  142. package/src/components/manage/Controlpanels/Users/UsersControlpanel.ssr.test.jsx +624 -0
  143. package/src/components/manage/Controlpanels/Users/UsersControlpanel.test.jsx +8 -0
  144. package/src/components/manage/Form/Form.jsx +6 -1
  145. package/src/components/manage/Form/ModalForm.jsx +164 -88
  146. package/src/components/manage/Sidebar/ObjectBrowser.jsx +7 -0
  147. package/src/components/manage/Sidebar/ObjectBrowserBody.jsx +7 -3
  148. package/src/components/manage/Sidebar/ObjectBrowserBody.test.jsx +52 -0
  149. package/src/components/manage/Sidebar/Sidebar.jsx +2 -0
  150. package/src/components/manage/Toolbar/Toolbar.jsx +89 -7
  151. package/src/components/manage/Widgets/ObjectBrowserWidget.jsx +1 -0
  152. package/src/components/manage/Widgets/TokenWidget.jsx +142 -186
  153. package/src/components/theme/Search/Search.jsx +230 -327
  154. package/src/components/theme/Search/Search.test.jsx +5 -10
  155. package/src/components/theme/Sitemap/Sitemap.jsx +22 -30
  156. package/src/components/theme/Sitemap/Sitemap.test.jsx +18 -0
  157. package/src/components/theme/Widgets/DateWidget.jsx +4 -5
  158. package/src/components/theme/Widgets/DatetimeWidget.jsx +4 -5
  159. package/src/components/theme/Widgets/RichTextWidget.jsx +1 -1
  160. package/src/config/index.js +1 -0
  161. package/src/helpers/I18n/I18n.test.ts +44 -0
  162. package/src/helpers/I18n/I18n.ts +31 -0
  163. package/src/helpers/Utils/Date.js +26 -1
  164. package/src/helpers/Utils/Date.test.js +237 -0
  165. package/src/helpers/index.js +1 -0
  166. package/theme/themes/pastanaga/collections/form.overrides +21 -0
  167. package/theme/themes/pastanaga/elements/button.overrides +30 -3
  168. package/types/components/manage/Controlpanels/Relations/RelationsMatrix.d.ts +1 -1
  169. package/types/components/manage/Controlpanels/Users/UsersControlpanel.d.ts +2 -6
  170. package/types/components/manage/Controlpanels/Users/UsersControlpanel.ssr.test.d.ts +1 -0
  171. package/types/components/manage/Controlpanels/index.d.ts +1 -1
  172. package/types/components/manage/Multilingual/ManageTranslations.d.ts +1 -1
  173. package/types/components/manage/Sidebar/ObjectBrowser.d.ts +1 -1
  174. package/types/components/manage/Sidebar/ObjectBrowserBody.test.d.ts +1 -0
  175. package/types/components/manage/Widgets/ImageWidget.d.ts +1 -1
  176. package/types/components/manage/Widgets/InternalUrlWidget.d.ts +1 -1
  177. package/types/components/manage/Widgets/UrlWidget.d.ts +1 -1
  178. package/types/components/manage/Widgets/index.d.ts +2 -2
  179. package/types/components/theme/Search/Search.d.ts +1 -1
  180. package/types/helpers/I18n/I18n.d.ts +20 -0
  181. package/types/helpers/index.d.ts +1 -0
@@ -1,26 +1,20 @@
1
- /**
2
- * Search component.
3
- * @module components/theme/Search/Search
4
- */
5
-
6
- import React, { Component } from 'react';
7
- import PropTypes from 'prop-types';
8
- import { connect } from 'react-redux';
9
- import { compose } from 'redux';
1
+ import { useEffect, useState, useCallback, useRef } from 'react';
2
+ import { useSelector, useDispatch } from 'react-redux';
3
+ import { useLocation, useHistory } from 'react-router-dom';
4
+ import { useIntl, defineMessages, FormattedMessage } from 'react-intl';
10
5
  import UniversalLink from '@plone/volto/components/manage/UniversalLink/UniversalLink';
11
6
  import { asyncConnect } from '@plone/volto/helpers/AsyncConnect';
12
- import { FormattedMessage } from 'react-intl';
13
7
  import { createPortal } from 'react-dom';
14
8
  import { Container, Pagination, Button, Header } from 'semantic-ui-react';
15
9
  import qs from 'query-string';
16
10
  import classNames from 'classnames';
17
- import { defineMessages, injectIntl } from 'react-intl';
18
11
  import config from '@plone/volto/registry';
19
12
  import Helmet from '@plone/volto/helpers/Helmet/Helmet';
20
13
  import { searchContent } from '@plone/volto/actions/search/search';
21
14
  import SearchTags from '@plone/volto/components/theme/Search/SearchTags';
22
15
  import Toolbar from '@plone/volto/components/manage/Toolbar/Toolbar';
23
16
  import Icon from '@plone/volto/components/theme/Icon/Icon';
17
+ import { useClient } from '@plone/volto/hooks/client/useClient';
24
18
 
25
19
  import paginationLeftSVG from '@plone/volto/icons/left-key.svg';
26
20
  import paginationRightSVG from '@plone/volto/icons/right-key.svg';
@@ -32,339 +26,248 @@ const messages = defineMessages({
32
26
  },
33
27
  });
34
28
 
35
- /**
36
- * Search class.
37
- * @class SearchComponent
38
- * @extends Component
39
- */
40
- class Search extends Component {
41
- /**
42
- * Property types.
43
- * @property {Object} propTypes Property types.
44
- * @static
45
- */
46
- static propTypes = {
47
- searchContent: PropTypes.func.isRequired,
48
- searchableText: PropTypes.string,
49
- subject: PropTypes.string,
50
- path: PropTypes.string,
51
- items: PropTypes.arrayOf(
52
- PropTypes.shape({
53
- '@id': PropTypes.string,
54
- '@type': PropTypes.string,
55
- title: PropTypes.string,
56
- description: PropTypes.string,
57
- }),
58
- ),
59
- pathname: PropTypes.string.isRequired,
60
- };
61
-
62
- /**
63
- * Default properties.
64
- * @property {Object} defaultProps Default properties.
65
- * @static
66
- */
67
- static defaultProps = {
68
- items: [],
69
- searchableText: null,
70
- subject: null,
71
- path: null,
72
- };
73
-
74
- constructor(props) {
75
- super(props);
76
- this.defaultPageSize = config.settings.defaultPageSize;
77
- this.state = { currentPage: 1, isClient: false, active: 'relevance' };
78
- }
79
-
80
- /**
81
- * Component did mount
82
- * @method componentDidMount
83
- * @returns {undefined}
84
- */
85
- componentDidMount() {
86
- this.doSearch();
87
- this.setState({ isClient: true });
88
- }
29
+ const Search = (props) => {
30
+ const intl = useIntl();
31
+ const dispatch = useDispatch();
32
+ const location = useLocation();
33
+ const history = useHistory();
34
+ const isClient = useClient();
35
+ const { search } = props;
89
36
 
90
- /**
91
- * Component will receive props
92
- * @method componentWillReceiveProps
93
- * @param {Object} nextProps Next properties
94
- * @returns {undefined}
95
- */
96
- UNSAFE_componentWillReceiveProps = (nextProps) => {
97
- if (this.props.location.search !== nextProps.location.search) {
98
- this.doSearch();
99
- }
100
- };
37
+ const defaultPageSize = config.settings.defaultPageSize;
101
38
 
102
- /**
103
- * Search based on the given searchableText, subject and path.
104
- * @method doSearch
105
- * @param {string} searchableText The searchable text string
106
- * @param {string} subject The subject (tag)
107
- * @param {string} path The path to restrict the search to
108
- * @returns {undefined}
109
- */
39
+ const items = useSelector((state) => state.search.items);
40
+ const searchableText = qs.parse(location.search).SearchableText;
41
+ const pathname = location.pathname;
110
42
 
111
- doSearch = () => {
112
- const options = qs.parse(this.props.history.location.search);
113
- this.setState({ currentPage: 1 });
114
- options['use_site_search_settings'] = 1;
115
- this.props.searchContent('', {
116
- b_size: this.defaultPageSize,
117
- ...options,
118
- });
119
- };
43
+ const [currentPage, setCurrentPage] = useState(1);
120
44
 
121
- handleQueryPaginationChange = (e, { activePage }) => {
122
- window.scrollTo(0, 0);
123
- let options = qs.parse(this.props.history.location.search);
124
- options['use_site_search_settings'] = 1;
45
+ const handleQueryPaginationChange = useCallback(
46
+ (e, { activePage }) => {
47
+ window.scrollTo(0, 0);
48
+ const options = qs.parse(location.search);
49
+ options['use_site_search_settings'] = 1;
125
50
 
126
- this.setState({ currentPage: activePage }, () => {
127
- this.props.searchContent('', {
128
- b_size: this.defaultPageSize,
129
- ...options,
130
- b_start:
131
- (this.state.currentPage - 1) *
132
- (options.b_size || this.defaultPageSize),
133
- });
134
- });
135
- };
51
+ setCurrentPage(activePage);
52
+ dispatch(
53
+ searchContent('', {
54
+ b_size: defaultPageSize,
55
+ ...options,
56
+ b_start: (activePage - 1) * (options.b_size || defaultPageSize),
57
+ }),
58
+ );
59
+ },
60
+ [location.search, defaultPageSize, dispatch],
61
+ );
136
62
 
137
- onSortChange = (event, sort_order) => {
138
- let options = qs.parse(this.props.history.location.search);
139
- options.sort_on = event.target.name;
140
- options.sort_order = sort_order || 'ascending';
141
- if (event.target.name === 'relevance') {
142
- delete options.sort_on;
143
- delete options.sort_order;
144
- }
145
- let searchParams = qs.stringify(options);
146
- this.setState({ currentPage: 1, active: event.target.name }, () => {
147
- // eslint-disable-next-line no-restricted-globals
148
- this.props.history.replace({
63
+ const onSortChange = useCallback(
64
+ (event, sort_order) => {
65
+ const options = qs.parse(location.search);
66
+ options.sort_on = event.target.name;
67
+ options.sort_order = sort_order || 'ascending';
68
+ if (event.target.name === 'relevance') {
69
+ delete options.sort_on;
70
+ delete options.sort_order;
71
+ }
72
+ const searchParams = qs.stringify(options);
73
+ history.replace({
149
74
  search: searchParams,
150
75
  });
151
- });
152
- };
153
-
154
- /**
155
- * Render method.
156
- * @method render
157
- * @returns {string} Markup for the component.
158
- */
159
- render() {
160
- const options = qs.parse(this.props.history.location.search);
76
+ },
77
+ [location.search, history],
78
+ );
161
79
 
162
- return (
163
- <Container id="page-search">
164
- <Helmet title={this.props.intl.formatMessage(messages.Search)} />
165
- <div className="container">
166
- <article id="content">
167
- <header>
168
- <h1 className="documentFirstHeading">
169
- {this.props.searchableText ? (
170
- <FormattedMessage
171
- id="Search results for {term}"
172
- defaultMessage="Search results for {term}"
173
- values={{
174
- term: <q>{this.props.searchableText}</q>,
175
- }}
176
- />
177
- ) : (
178
- <FormattedMessage
179
- id="Search results"
180
- defaultMessage="Search results"
181
- />
182
- )}
183
- </h1>
80
+ // Focus search results after they render
81
+ const resultsRef = useRef();
82
+ useEffect(() => {
83
+ resultsRef.current?.focus();
84
+ }, [items]);
184
85
 
185
- <SearchTags />
86
+ const options = qs.parse(location.search);
186
87
 
187
- {this.props.search?.items_total > 0 ? (
188
- <div className="items_total">
189
- {this.props.search.items_total}{' '}
190
- <FormattedMessage
191
- id="results found"
192
- defaultMessage="results"
193
- />
194
- <Header>
195
- <Header.Content className="header-content">
196
- <div className="sort-by">
197
- <FormattedMessage
198
- id="Sort By:"
199
- defaultMessage="Sort by:"
200
- />
201
- </div>
202
- <Button
203
- onClick={(event) => {
204
- this.onSortChange(event);
205
- }}
206
- name="relevance"
207
- size="tiny"
208
- className={classNames('button-sort', {
209
- 'button-active': this.state.active === 'relevance',
210
- })}
211
- >
212
- <FormattedMessage
213
- id="Relevance"
214
- defaultMessage="Relevance"
215
- />
216
- </Button>
217
- <Button
218
- onClick={(event) => {
219
- this.onSortChange(event);
220
- }}
221
- name="sortable_title"
222
- size="tiny"
223
- className={classNames('button-sort', {
224
- 'button-active':
225
- this.state.active === 'sortable_title',
226
- })}
227
- >
228
- <FormattedMessage
229
- id="Alphabetically"
230
- defaultMessage="Alphabetically"
231
- />
232
- </Button>
233
- <Button
234
- onClick={(event) => {
235
- this.onSortChange(event, 'reverse');
236
- }}
237
- name="effective"
238
- size="tiny"
239
- className={classNames('button-sort', {
240
- 'button-active': this.state.active === 'effective',
241
- })}
242
- >
243
- <FormattedMessage
244
- id="Date (newest first)"
245
- defaultMessage="Date (newest first)"
246
- />
247
- </Button>
248
- </Header.Content>
249
- </Header>
250
- </div>
88
+ return (
89
+ <Container id="page-search">
90
+ <Helmet title={intl.formatMessage(messages.Search)} />
91
+ <div
92
+ className="container"
93
+ role="region"
94
+ aria-live="polite"
95
+ id="search-results"
96
+ tabIndex={-1}
97
+ ref={resultsRef}
98
+ >
99
+ <article id="content">
100
+ <header>
101
+ <h1 className="documentFirstHeading">
102
+ {searchableText ? (
103
+ <FormattedMessage
104
+ id="Search results for {term}"
105
+ defaultMessage="Search results for {term}"
106
+ values={{
107
+ term: <q>{searchableText}</q>,
108
+ }}
109
+ />
251
110
  ) : (
252
- <div>
253
- <FormattedMessage
254
- id="No results found"
255
- defaultMessage="No results found"
256
- />
257
- </div>
111
+ <FormattedMessage
112
+ id="Search results"
113
+ defaultMessage="Search results"
114
+ />
258
115
  )}
259
- </header>
260
- <section id="content-core">
261
- {this.props.items.map((item) => (
262
- <article className="tileItem" key={item['@id']}>
263
- <h2 className="tileHeadline">
264
- <UniversalLink
265
- item={item}
266
- className="summary url"
267
- title={item['@type']}
268
- >
269
- {item.title}
270
- </UniversalLink>
271
- </h2>
272
- {item.description && (
273
- <div className="tileBody">
274
- <span className="description">{item.description}</span>
116
+ </h1>
117
+
118
+ <SearchTags />
119
+
120
+ {search?.items_total > 0 ? (
121
+ <div className="items_total">
122
+ {search.items_total}{' '}
123
+ <FormattedMessage id="results found" defaultMessage="results" />
124
+ <Header>
125
+ <Header.Content className="header-content">
126
+ <div className="sort-by">
127
+ <FormattedMessage
128
+ id="Sort By:"
129
+ defaultMessage="Sort by:"
130
+ />
275
131
  </div>
276
- )}
277
- <div className="tileFooter">
278
- <UniversalLink item={item}>
132
+ <Button
133
+ onClick={(event) => {
134
+ onSortChange(event);
135
+ }}
136
+ name="relevance"
137
+ size="tiny"
138
+ className={classNames('button-sort', {
139
+ 'button-active': options.sort_on === 'relevance',
140
+ })}
141
+ >
142
+ <FormattedMessage
143
+ id="Relevance"
144
+ defaultMessage="Relevance"
145
+ />
146
+ </Button>
147
+ <Button
148
+ onClick={(event) => {
149
+ onSortChange(event);
150
+ }}
151
+ name="sortable_title"
152
+ size="tiny"
153
+ className={classNames('button-sort', {
154
+ 'button-active': options.sort_on === 'sortable_title',
155
+ })}
156
+ >
157
+ <FormattedMessage
158
+ id="Alphabetically"
159
+ defaultMessage="Alphabetically"
160
+ />
161
+ </Button>
162
+ <Button
163
+ onClick={(event) => {
164
+ onSortChange(event, 'reverse');
165
+ }}
166
+ name="effective"
167
+ size="tiny"
168
+ className={classNames('button-sort', {
169
+ 'button-active': options.sort_on === 'effective',
170
+ })}
171
+ >
279
172
  <FormattedMessage
280
- id="Read More…"
281
- defaultMessage="Read More…"
173
+ id="Date (newest first)"
174
+ defaultMessage="Date (newest first)"
282
175
  />
283
- </UniversalLink>
176
+ </Button>
177
+ </Header.Content>
178
+ </Header>
179
+ </div>
180
+ ) : (
181
+ <div>
182
+ <FormattedMessage
183
+ id="No results found"
184
+ defaultMessage="No results found"
185
+ />
186
+ </div>
187
+ )}
188
+ </header>
189
+ <section id="content-core">
190
+ {items.map((item) => (
191
+ <article className="tileItem" key={item['@id']}>
192
+ <h2 className="tileHeadline">
193
+ <UniversalLink
194
+ item={item}
195
+ className="summary url"
196
+ title={item['@type']}
197
+ >
198
+ {item.title}
199
+ </UniversalLink>
200
+ </h2>
201
+ {item.description && (
202
+ <div className="tileBody">
203
+ <span className="description">{item.description}</span>
284
204
  </div>
285
- <div className="visualClear" />
286
- </article>
287
- ))}
288
-
289
- {this.props.search?.batching && (
290
- <div className="search-footer">
291
- <Pagination
292
- activePage={this.state.currentPage}
293
- totalPages={Math.ceil(
294
- this.props.search.items_total /
295
- (options.b_size || this.defaultPageSize),
296
- )}
297
- onPageChange={this.handleQueryPaginationChange}
298
- firstItem={null}
299
- lastItem={null}
300
- prevItem={{
301
- content: <Icon name={paginationLeftSVG} size="18px" />,
302
- icon: true,
303
- 'aria-disabled': !this.props.search.batching.prev,
304
- className: !this.props.search.batching.prev
305
- ? 'disabled'
306
- : null,
307
- }}
308
- nextItem={{
309
- content: <Icon name={paginationRightSVG} size="18px" />,
310
- icon: true,
311
- 'aria-disabled': !this.props.search.batching.next,
312
- className: !this.props.search.batching.next
313
- ? 'disabled'
314
- : null,
315
- }}
316
- />
205
+ )}
206
+ <div className="tileFooter">
207
+ <UniversalLink item={item}>
208
+ <FormattedMessage
209
+ id="Read More…"
210
+ defaultMessage="Read More…"
211
+ />
212
+ </UniversalLink>
317
213
  </div>
318
- )}
319
- </section>
320
- </article>
321
- </div>
322
- {this.state.isClient &&
323
- createPortal(
324
- <Toolbar
325
- pathname={this.props.pathname}
326
- hideDefaultViewButtons
327
- inner={<span />}
328
- />,
329
- document.getElementById('toolbar'),
330
- )}
331
- </Container>
332
- );
333
- }
334
- }
214
+ <div className="visualClear" />
215
+ </article>
216
+ ))}
335
217
 
336
- export const __test__ = compose(
337
- injectIntl,
338
- connect(
339
- (state, props) => ({
340
- items: state.search.items,
341
- searchableText: qs.parse(props.history.location.search).SearchableText,
342
- pathname: props.history.location.pathname,
343
- }),
344
- { searchContent },
345
- ),
346
- )(Search);
218
+ {search?.batching && (
219
+ <div className="search-footer">
220
+ <Pagination
221
+ activePage={currentPage}
222
+ totalPages={Math.ceil(
223
+ search.items_total / (options.b_size || defaultPageSize),
224
+ )}
225
+ onPageChange={handleQueryPaginationChange}
226
+ firstItem={null}
227
+ lastItem={null}
228
+ prevItem={{
229
+ content: <Icon name={paginationLeftSVG} size="18px" />,
230
+ icon: true,
231
+ 'aria-disabled': !search.batching.prev,
232
+ className: !search.batching.prev ? 'disabled' : null,
233
+ }}
234
+ nextItem={{
235
+ content: <Icon name={paginationRightSVG} size="18px" />,
236
+ icon: true,
237
+ 'aria-disabled': !search.batching.next,
238
+ className: !search.batching.next ? 'disabled' : null,
239
+ }}
240
+ />
241
+ </div>
242
+ )}
243
+ </section>
244
+ </article>
245
+ </div>
246
+ {isClient &&
247
+ createPortal(
248
+ <Toolbar
249
+ pathname={pathname}
250
+ hideDefaultViewButtons
251
+ inner={<span />}
252
+ />,
253
+ document.getElementById('toolbar'),
254
+ )}
255
+ </Container>
256
+ );
257
+ };
347
258
 
348
- export default compose(
349
- injectIntl,
350
- connect(
351
- (state, props) => ({
352
- items: state.search.items,
353
- searchableText: qs.parse(props.history.location.search).SearchableText,
354
- pathname: props.location.pathname,
355
- }),
356
- { searchContent },
357
- ),
358
- asyncConnect([
359
- {
360
- key: 'search',
361
- promise: ({ location, store: { dispatch } }) =>
362
- dispatch(
363
- searchContent('', {
364
- ...qs.parse(location.search),
365
- use_site_search_settings: 1,
366
- }),
367
- ),
368
- },
369
- ]),
370
- )(Search);
259
+ export const __test__ = Search;
260
+
261
+ export default asyncConnect([
262
+ {
263
+ key: 'search',
264
+ promise: ({ location, store: { dispatch } }) =>
265
+ dispatch(
266
+ searchContent('', {
267
+ b_size: config.settings.defaultPageSize,
268
+ ...qs.parse(location.search),
269
+ use_site_search_settings: 1,
270
+ }),
271
+ ),
272
+ },
273
+ ])(Search);
@@ -22,20 +22,18 @@ describe('Search', () => {
22
22
  const store = mockStore({
23
23
  search: {
24
24
  loaded: false,
25
+ items: [],
25
26
  },
26
27
  intl: {
27
28
  locale: 'en',
28
29
  messages: {},
29
30
  },
30
31
  });
31
- const history = {
32
- location: { pathname: '/blog', search: '?SearchableText=blog' },
33
- };
34
32
  const { container } = render(
35
33
  <Provider store={store}>
36
34
  <CookiesProvider>
37
- <MemoryRouter>
38
- <Search history={history} />
35
+ <MemoryRouter initialEntries={['/search?SearchableText=blog']}>
36
+ <Search />
39
37
  <div id="toolbar"></div>
40
38
  </MemoryRouter>
41
39
  </CookiesProvider>
@@ -63,14 +61,11 @@ describe('Search', () => {
63
61
  messages: {},
64
62
  },
65
63
  });
66
- const history = {
67
- location: { pathname: '/blog', search: '?SearchableText=blog' },
68
- };
69
64
  const { container } = render(
70
65
  <Provider store={store}>
71
66
  <CookiesProvider>
72
- <MemoryRouter>
73
- <Search history={history} />
67
+ <MemoryRouter initialEntries={['/search?SearchableText=blog']}>
68
+ <Search />
74
69
  <div id="toolbar"></div>
75
70
  </MemoryRouter>
76
71
  </CookiesProvider>