@backstage/plugin-search 0.9.1-next.3 → 1.0.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.
@@ -1,1024 +0,0 @@
1
- import React, { useCallback, useContext, useState, useEffect, Fragment, cloneElement } from 'react';
2
- import FilterListIcon from '@material-ui/icons/FilterList';
3
- import { makeStyles, IconButton, Typography, Card, CardHeader, Button, Divider, CardContent, Select, MenuItem, List, ListItem, Checkbox, ListItemText, Dialog, useTheme, DialogTitle, Paper, DialogContent, Grid, DialogActions, Accordion, AccordionSummary, AccordionDetails, ListItemIcon, Tabs, Tab, FormControl, InputLabel, Chip } from '@material-ui/core';
4
- import { SearchBarBase as SearchBarBase$1, useSearch, CheckboxFilter, SelectFilter, AutocompleteFilter, searchApiRef, SearchResult as SearchResult$2, DefaultResultListItem as DefaultResultListItem$1, SearchBar as SearchBar$2, SearchResultPager as SearchResultPager$1, useSearchContextCheck, SearchContextProvider } from '@backstage/plugin-search-react';
5
- import LaunchIcon from '@material-ui/icons/Launch';
6
- import { makeStyles as makeStyles$1 } from '@material-ui/core/styles';
7
- import { createRouteRef, createPlugin, createApiFactory, discoveryApiRef, identityApiRef, createRoutableExtension, createComponentExtension, useRouteRef, useApi } from '@backstage/core-plugin-api';
8
- import { useContent, Link, Progress, EmptyState, Table, useQueryParamState, Page, Header, Content, SidebarSearchField } from '@backstage/core-components';
9
- import { ResponseError } from '@backstage/errors';
10
- import qs from 'qs';
11
- import { createVersionedContext, createVersionedValueMap } from '@backstage/version-bridge';
12
- import usePrevious from 'react-use/lib/usePrevious';
13
- import { useOutlet, useLocation } from 'react-router';
14
- import useDebounce from 'react-use/lib/useDebounce';
15
- import InputBase from '@material-ui/core/InputBase';
16
- import IconButton$1 from '@material-ui/core/IconButton';
17
- import SearchIcon from '@material-ui/icons/Search';
18
- import ClearButton from '@material-ui/icons/Clear';
19
- import { Alert } from '@material-ui/lab';
20
- import useAsync from 'react-use/lib/useAsync';
21
- import { catalogApiRef } from '@backstage/plugin-catalog-react';
22
- import { DEFAULT_NAMESPACE } from '@backstage/catalog-model';
23
- import ArrowBackIosIcon from '@material-ui/icons/ArrowBackIos';
24
- import ArrowForwardIosIcon from '@material-ui/icons/ArrowForwardIos';
25
- import useEffectOnce from 'react-use/lib/useEffectOnce';
26
- import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
27
- import AllIcon from '@material-ui/icons/FontDownload';
28
- import { useNavigate } from 'react-router-dom';
29
-
30
- const useStyles$a = makeStyles((theme) => ({
31
- filters: {
32
- width: "250px",
33
- display: "flex"
34
- },
35
- icon: {
36
- margin: theme.spacing(-1, 0, 0, 0)
37
- }
38
- }));
39
- const FiltersButton$1 = ({
40
- numberOfSelectedFilters,
41
- handleToggleFilters
42
- }) => {
43
- const classes = useStyles$a();
44
- return /* @__PURE__ */ React.createElement("div", {
45
- className: classes.filters
46
- }, /* @__PURE__ */ React.createElement(IconButton, {
47
- className: classes.icon,
48
- "aria-label": "settings",
49
- onClick: handleToggleFilters
50
- }, /* @__PURE__ */ React.createElement(FilterListIcon, null)), /* @__PURE__ */ React.createElement(Typography, {
51
- variant: "h6"
52
- }, "Filters (", numberOfSelectedFilters ? numberOfSelectedFilters : 0, ")"));
53
- };
54
-
55
- const useStyles$9 = makeStyles((theme) => ({
56
- filters: {
57
- background: "transparent",
58
- boxShadow: "0px 0px 0px 0px"
59
- },
60
- checkbox: {
61
- padding: theme.spacing(0, 1, 0, 1)
62
- },
63
- dropdown: {
64
- width: "100%"
65
- }
66
- }));
67
- const Filters$1 = ({
68
- filters,
69
- filterOptions,
70
- resetFilters,
71
- updateSelected,
72
- updateChecked
73
- }) => {
74
- const classes = useStyles$9();
75
- return /* @__PURE__ */ React.createElement(Card, {
76
- className: classes.filters
77
- }, /* @__PURE__ */ React.createElement(CardHeader, {
78
- title: /* @__PURE__ */ React.createElement(Typography, {
79
- variant: "h6"
80
- }, "Filters"),
81
- action: /* @__PURE__ */ React.createElement(Button, {
82
- color: "primary",
83
- onClick: () => resetFilters()
84
- }, "CLEAR ALL")
85
- }), /* @__PURE__ */ React.createElement(Divider, null), filterOptions.kind.length === 0 && filterOptions.lifecycle.length === 0 && /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(Typography, {
86
- variant: "subtitle2"
87
- }, "Filters cannot be applied to available results")), filterOptions.kind.length > 0 && /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(Typography, {
88
- variant: "subtitle2"
89
- }, "Kind"), /* @__PURE__ */ React.createElement(Select, {
90
- id: "outlined-select",
91
- onChange: (e) => {
92
- var _a;
93
- return updateSelected((_a = e == null ? void 0 : e.target) == null ? void 0 : _a.value);
94
- },
95
- variant: "outlined",
96
- className: classes.dropdown,
97
- value: filters.selected
98
- }, filterOptions.kind.map((filter) => /* @__PURE__ */ React.createElement(MenuItem, {
99
- selected: filter === "",
100
- dense: true,
101
- key: filter,
102
- value: filter
103
- }, filter)))), filterOptions.lifecycle.length > 0 && /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(Typography, {
104
- variant: "subtitle2"
105
- }, "Lifecycle"), /* @__PURE__ */ React.createElement(List, {
106
- disablePadding: true,
107
- dense: true
108
- }, filterOptions.lifecycle.map((filter) => /* @__PURE__ */ React.createElement(ListItem, {
109
- key: filter,
110
- dense: true,
111
- button: true,
112
- onClick: () => updateChecked(filter)
113
- }, /* @__PURE__ */ React.createElement(Checkbox, {
114
- edge: "start",
115
- disableRipple: true,
116
- className: classes.checkbox,
117
- color: "primary",
118
- checked: filters.checked.includes(filter),
119
- tabIndex: -1,
120
- value: filter,
121
- name: filter
122
- }), /* @__PURE__ */ React.createElement(ListItemText, {
123
- id: filter,
124
- primary: filter
125
- }))))));
126
- };
127
-
128
- const SearchBarBase = SearchBarBase$1;
129
- const SearchBar$1 = ({ onChange, ...props }) => {
130
- const { term, setTerm } = useSearch();
131
- const handleChange = useCallback((newValue) => {
132
- if (onChange) {
133
- onChange(newValue);
134
- } else {
135
- setTerm(newValue);
136
- }
137
- }, [onChange, setTerm]);
138
- return /* @__PURE__ */ React.createElement(SearchBarBase, {
139
- value: term,
140
- onChange: handleChange,
141
- ...props
142
- });
143
- };
144
-
145
- const SearchFilter = ({
146
- component: Element,
147
- ...props
148
- }) => /* @__PURE__ */ React.createElement(Element, {
149
- ...props
150
- });
151
- SearchFilter.Checkbox = (props) => /* @__PURE__ */ React.createElement(SearchFilter, {
152
- ...props,
153
- component: CheckboxFilter
154
- });
155
- SearchFilter.Select = (props) => /* @__PURE__ */ React.createElement(SearchFilter, {
156
- ...props,
157
- component: SelectFilter
158
- });
159
- SearchFilter.Autocomplete = (props) => /* @__PURE__ */ React.createElement(SearchFilter, {
160
- ...props,
161
- component: AutocompleteFilter
162
- });
163
-
164
- class SearchClient {
165
- constructor(options) {
166
- this.discoveryApi = options.discoveryApi;
167
- this.identityApi = options.identityApi;
168
- }
169
- async query(query) {
170
- const { token } = await this.identityApi.getCredentials();
171
- const queryString = qs.stringify(query);
172
- const url = `${await this.discoveryApi.getBaseUrl("search/query")}?${queryString}`;
173
- const response = await fetch(url, {
174
- headers: token ? { Authorization: `Bearer ${token}` } : {}
175
- });
176
- if (!response.ok) {
177
- throw await ResponseError.fromResponse(response);
178
- }
179
- return response.json();
180
- }
181
- }
182
-
183
- const rootRouteRef = createRouteRef({
184
- id: "search"
185
- });
186
- const searchPlugin = createPlugin({
187
- id: "search",
188
- apis: [
189
- createApiFactory({
190
- api: searchApiRef,
191
- deps: { discoveryApi: discoveryApiRef, identityApi: identityApiRef },
192
- factory: ({ discoveryApi, identityApi }) => {
193
- return new SearchClient({ discoveryApi, identityApi });
194
- }
195
- })
196
- ],
197
- routes: {
198
- root: rootRouteRef
199
- }
200
- });
201
- const SearchPage$1 = searchPlugin.provide(createRoutableExtension({
202
- name: "SearchPage",
203
- component: () => import('./index-2e41707a.esm.js').then((m) => m.SearchPage),
204
- mountPoint: rootRouteRef
205
- }));
206
- const SearchResult$1 = SearchResult$2;
207
- const SidebarSearchModal = searchPlugin.provide(createComponentExtension({
208
- name: "SidebarSearchModal",
209
- component: {
210
- lazy: () => import('./index-30f1f9c5.esm.js').then((m) => m.SidebarSearchModal)
211
- }
212
- }));
213
- const DefaultResultListItem = DefaultResultListItem$1;
214
- const HomePageSearchBar = searchPlugin.provide(createComponentExtension({
215
- name: "HomePageSearchBar",
216
- component: {
217
- lazy: () => import('./index-50da964c.esm.js').then((m) => m.HomePageSearchBar)
218
- }
219
- }));
220
-
221
- const useStyles$8 = makeStyles$1((theme) => ({
222
- container: {
223
- borderRadius: 30,
224
- display: "flex",
225
- height: "2.4em"
226
- },
227
- input: {
228
- flex: 1
229
- },
230
- paperFullWidth: { height: "calc(100% - 128px)" },
231
- dialogActionsContainer: { padding: theme.spacing(1, 3) },
232
- viewResultsLink: { verticalAlign: "0.5em" }
233
- }));
234
- const Modal = ({ toggleModal }) => {
235
- const getSearchLink = useRouteRef(rootRouteRef);
236
- const classes = useStyles$8();
237
- const { term } = useSearch();
238
- const { focusContent } = useContent();
239
- const { transitions } = useTheme();
240
- const handleResultClick = () => {
241
- toggleModal();
242
- setTimeout(focusContent, transitions.duration.leavingScreen);
243
- };
244
- const handleKeyPress = () => {
245
- handleResultClick();
246
- };
247
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(DialogTitle, null, /* @__PURE__ */ React.createElement(Paper, {
248
- className: classes.container
249
- }, /* @__PURE__ */ React.createElement(SearchBar$2, {
250
- className: classes.input
251
- }))), /* @__PURE__ */ React.createElement(DialogContent, null, /* @__PURE__ */ React.createElement(Grid, {
252
- container: true,
253
- direction: "row-reverse",
254
- justifyContent: "flex-start",
255
- alignItems: "center"
256
- }, /* @__PURE__ */ React.createElement(Grid, {
257
- item: true
258
- }, /* @__PURE__ */ React.createElement(Link, {
259
- onClick: () => {
260
- toggleModal();
261
- setTimeout(focusContent, transitions.duration.leavingScreen);
262
- },
263
- to: `${getSearchLink()}?query=${term}`
264
- }, /* @__PURE__ */ React.createElement("span", {
265
- className: classes.viewResultsLink
266
- }, "View Full Results"), /* @__PURE__ */ React.createElement(LaunchIcon, {
267
- color: "primary"
268
- })))), /* @__PURE__ */ React.createElement(Divider, null), /* @__PURE__ */ React.createElement(SearchResult$2, null, ({ results }) => /* @__PURE__ */ React.createElement(List, null, results.map(({ document, highlight }) => /* @__PURE__ */ React.createElement("div", {
269
- role: "button",
270
- tabIndex: 0,
271
- key: `${document.location}-btn`,
272
- onClick: handleResultClick,
273
- onKeyPress: handleKeyPress
274
- }, /* @__PURE__ */ React.createElement(DefaultResultListItem$1, {
275
- key: document.location,
276
- result: document,
277
- highlight
278
- })))))), /* @__PURE__ */ React.createElement(DialogActions, {
279
- className: classes.dialogActionsContainer
280
- }, /* @__PURE__ */ React.createElement(Grid, {
281
- container: true,
282
- direction: "row"
283
- }, /* @__PURE__ */ React.createElement(Grid, {
284
- item: true,
285
- xs: 12
286
- }, /* @__PURE__ */ React.createElement(SearchResultPager$1, null)))));
287
- };
288
- const Context = ({ children }) => {
289
- const hasParentContext = useSearchContextCheck();
290
- if (hasParentContext) {
291
- return /* @__PURE__ */ React.createElement(React.Fragment, null, children);
292
- }
293
- return /* @__PURE__ */ React.createElement(SearchContextProvider, null, children);
294
- };
295
- const SearchModal = ({
296
- open = true,
297
- hidden,
298
- toggleModal,
299
- children
300
- }) => {
301
- var _a;
302
- const classes = useStyles$8();
303
- return /* @__PURE__ */ React.createElement(Dialog, {
304
- classes: {
305
- paperFullWidth: classes.paperFullWidth
306
- },
307
- onClose: toggleModal,
308
- "aria-labelledby": "search-modal-title",
309
- fullWidth: true,
310
- maxWidth: "lg",
311
- open,
312
- hidden
313
- }, open && /* @__PURE__ */ React.createElement(Context, null, (_a = children && children({ toggleModal })) != null ? _a : /* @__PURE__ */ React.createElement(Modal, {
314
- toggleModal
315
- })));
316
- };
317
-
318
- const SearchModalContext = createVersionedContext("search-modal-context");
319
- const SearchModalProvider = ({
320
- children,
321
- showInitially
322
- }) => {
323
- const value = useSearchModal(showInitially);
324
- const versionedValue = createVersionedValueMap({ 1: value });
325
- return /* @__PURE__ */ React.createElement(SearchModalContext.Provider, {
326
- value: versionedValue
327
- }, children);
328
- };
329
- function useSearchModal(initialState = false) {
330
- const parentContext = useContext(SearchModalContext);
331
- const parentContextValue = parentContext == null ? void 0 : parentContext.atVersion(1);
332
- const [state, setState] = useState({
333
- hidden: !initialState,
334
- open: initialState
335
- });
336
- const toggleModal = useCallback(() => setState((prevState) => ({
337
- open: true,
338
- hidden: !prevState.hidden
339
- })), []);
340
- const setOpen = useCallback((open) => setState((prevState) => ({
341
- open: prevState.open || open,
342
- hidden: !open
343
- })), []);
344
- return (parentContextValue == null ? void 0 : parentContextValue.state) ? parentContextValue : { state, toggleModal, setOpen };
345
- }
346
-
347
- const useStyles$7 = makeStyles$1(() => ({
348
- root: {
349
- display: "flex",
350
- alignItems: "center"
351
- },
352
- input: {
353
- flex: 1
354
- }
355
- }));
356
- const SearchBar = ({
357
- searchQuery,
358
- handleSearch,
359
- handleClearSearchBar
360
- }) => {
361
- const classes = useStyles$7();
362
- return /* @__PURE__ */ React.createElement(Paper, {
363
- component: "form",
364
- onSubmit: (e) => handleSearch(e),
365
- className: classes.root
366
- }, /* @__PURE__ */ React.createElement(IconButton$1, {
367
- disabled: true,
368
- type: "submit",
369
- "aria-label": "search"
370
- }, /* @__PURE__ */ React.createElement(SearchIcon, null)), /* @__PURE__ */ React.createElement(InputBase, {
371
- className: classes.input,
372
- placeholder: "Search in Backstage",
373
- value: searchQuery,
374
- onChange: (e) => handleSearch(e),
375
- inputProps: { "aria-label": "search backstage" }
376
- }), /* @__PURE__ */ React.createElement(IconButton$1, {
377
- "aria-label": "search",
378
- onClick: () => handleClearSearchBar()
379
- }, /* @__PURE__ */ React.createElement(ClearButton, null)));
380
- };
381
-
382
- const useStyles$6 = makeStyles((theme) => ({
383
- filters: {
384
- width: "250px",
385
- display: "flex"
386
- },
387
- icon: {
388
- margin: theme.spacing(-1, 0, 0, 0)
389
- }
390
- }));
391
- const FiltersButton = ({
392
- numberOfSelectedFilters,
393
- handleToggleFilters
394
- }) => {
395
- const classes = useStyles$6();
396
- return /* @__PURE__ */ React.createElement("div", {
397
- className: classes.filters
398
- }, /* @__PURE__ */ React.createElement(IconButton, {
399
- className: classes.icon,
400
- "aria-label": "settings",
401
- onClick: handleToggleFilters
402
- }, /* @__PURE__ */ React.createElement(FilterListIcon, null)), /* @__PURE__ */ React.createElement(Typography, {
403
- variant: "h6"
404
- }, "Filters (", numberOfSelectedFilters ? numberOfSelectedFilters : 0, ")"));
405
- };
406
-
407
- const useStyles$5 = makeStyles((theme) => ({
408
- filters: {
409
- background: "transparent",
410
- boxShadow: "0px 0px 0px 0px"
411
- },
412
- checkbox: {
413
- padding: theme.spacing(0, 1, 0, 1)
414
- },
415
- dropdown: {
416
- width: "100%"
417
- }
418
- }));
419
- const Filters = ({
420
- filters,
421
- filterOptions,
422
- resetFilters,
423
- updateSelected,
424
- updateChecked
425
- }) => {
426
- const classes = useStyles$5();
427
- return /* @__PURE__ */ React.createElement(Card, {
428
- className: classes.filters
429
- }, /* @__PURE__ */ React.createElement(CardHeader, {
430
- title: /* @__PURE__ */ React.createElement(Typography, {
431
- variant: "h6"
432
- }, "Filters"),
433
- action: /* @__PURE__ */ React.createElement(Button, {
434
- color: "primary",
435
- onClick: () => resetFilters()
436
- }, "CLEAR ALL")
437
- }), /* @__PURE__ */ React.createElement(Divider, null), filterOptions.kind.length === 0 && filterOptions.lifecycle.length === 0 && /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(Typography, {
438
- variant: "subtitle2"
439
- }, "Filters cannot be applied to available results")), filterOptions.kind.length > 0 && /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(Typography, {
440
- variant: "subtitle2"
441
- }, "Kind"), /* @__PURE__ */ React.createElement(Select, {
442
- id: "outlined-select",
443
- onChange: (e) => {
444
- var _a;
445
- return updateSelected((_a = e == null ? void 0 : e.target) == null ? void 0 : _a.value);
446
- },
447
- variant: "outlined",
448
- className: classes.dropdown,
449
- value: filters.selected
450
- }, filterOptions.kind.map((filter) => /* @__PURE__ */ React.createElement(MenuItem, {
451
- selected: filter === "",
452
- dense: true,
453
- key: filter,
454
- value: filter
455
- }, filter)))), filterOptions.lifecycle.length > 0 && /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(Typography, {
456
- variant: "subtitle2"
457
- }, "Lifecycle"), /* @__PURE__ */ React.createElement(List, {
458
- disablePadding: true,
459
- dense: true
460
- }, filterOptions.lifecycle.map((filter) => /* @__PURE__ */ React.createElement(ListItem, {
461
- key: filter,
462
- dense: true,
463
- button: true,
464
- onClick: () => updateChecked(filter)
465
- }, /* @__PURE__ */ React.createElement(Checkbox, {
466
- edge: "start",
467
- disableRipple: true,
468
- className: classes.checkbox,
469
- color: "primary",
470
- checked: filters.checked.includes(filter),
471
- tabIndex: -1,
472
- value: filter,
473
- name: filter
474
- }), /* @__PURE__ */ React.createElement(ListItemText, {
475
- id: filter,
476
- primary: filter
477
- }))))));
478
- };
479
-
480
- const useStyles$4 = makeStyles((theme) => ({
481
- searchQuery: {
482
- color: theme.palette.text.primary,
483
- background: theme.palette.background.default,
484
- borderRadius: "10%"
485
- },
486
- tableHeader: {
487
- margin: theme.spacing(1, 0, 0, 0),
488
- display: "flex"
489
- },
490
- divider: {
491
- width: "1px",
492
- margin: theme.spacing(0, 2),
493
- padding: theme.spacing(2, 0)
494
- }
495
- }));
496
- const columns = [
497
- {
498
- title: "Name",
499
- field: "name",
500
- highlight: true,
501
- render: (result) => /* @__PURE__ */ React.createElement(Link, {
502
- to: result.url || ""
503
- }, result.name)
504
- },
505
- {
506
- title: "Description",
507
- field: "description"
508
- },
509
- {
510
- title: "Owner",
511
- field: "owner"
512
- },
513
- {
514
- title: "Kind",
515
- field: "kind"
516
- },
517
- {
518
- title: "LifeCycle",
519
- field: "lifecycle"
520
- }
521
- ];
522
- const TableHeader = ({
523
- searchQuery,
524
- numberOfSelectedFilters,
525
- numberOfResults,
526
- handleToggleFilters
527
- }) => {
528
- const classes = useStyles$4();
529
- return /* @__PURE__ */ React.createElement("div", {
530
- className: classes.tableHeader
531
- }, /* @__PURE__ */ React.createElement(FiltersButton, {
532
- numberOfSelectedFilters,
533
- handleToggleFilters
534
- }), /* @__PURE__ */ React.createElement(Divider, {
535
- className: classes.divider,
536
- orientation: "vertical"
537
- }), /* @__PURE__ */ React.createElement(Grid, {
538
- item: true,
539
- xs: 12
540
- }, searchQuery ? /* @__PURE__ */ React.createElement(Typography, {
541
- variant: "h6"
542
- }, `${numberOfResults} `, numberOfResults > 1 ? `results for ` : `result for `, /* @__PURE__ */ React.createElement("span", {
543
- className: classes.searchQuery
544
- }, '"', searchQuery, '"'), " ") : /* @__PURE__ */ React.createElement(Typography, {
545
- variant: "h6"
546
- }, `${numberOfResults} results`)));
547
- };
548
- const SearchResult = ({ searchQuery }) => {
549
- const catalogApi = useApi(catalogApiRef);
550
- const [showFilters, toggleFilters] = useState(false);
551
- const [selectedFilters, setSelectedFilters] = useState({
552
- selected: "",
553
- checked: []
554
- });
555
- const [filteredResults, setFilteredResults] = useState([]);
556
- const {
557
- loading,
558
- error,
559
- value: results
560
- } = useAsync(async () => {
561
- const entities = await catalogApi.getEntities();
562
- return entities.items.map((entity) => {
563
- var _a, _b, _c, _d, _e;
564
- return {
565
- name: entity.metadata.name,
566
- description: entity.metadata.description,
567
- owner: typeof ((_a = entity.spec) == null ? void 0 : _a.owner) === "string" ? (_b = entity.spec) == null ? void 0 : _b.owner : void 0,
568
- kind: entity.kind,
569
- lifecycle: typeof ((_c = entity.spec) == null ? void 0 : _c.lifecycle) === "string" ? (_d = entity.spec) == null ? void 0 : _d.lifecycle : void 0,
570
- url: `/catalog/${((_e = entity.metadata.namespace) == null ? void 0 : _e.toLocaleLowerCase("en-US")) || DEFAULT_NAMESPACE}/${entity.kind.toLocaleLowerCase("en-US")}/${entity.metadata.name}`
571
- };
572
- });
573
- }, []);
574
- useEffect(() => {
575
- if (results) {
576
- let withFilters = results;
577
- if (selectedFilters.selected !== "") {
578
- withFilters = results.filter((result) => selectedFilters.selected.includes(result.kind));
579
- }
580
- if (selectedFilters.checked.length > 0) {
581
- withFilters = withFilters.filter((result) => result.lifecycle && selectedFilters.checked.includes(result.lifecycle));
582
- }
583
- if (searchQuery) {
584
- withFilters = withFilters.filter((result) => {
585
- var _a, _b, _c;
586
- return ((_a = result.name) == null ? void 0 : _a.toLocaleLowerCase("en-US").includes(searchQuery)) || ((_b = result.name) == null ? void 0 : _b.toLocaleLowerCase("en-US").includes(searchQuery.split(" ").join("-"))) || ((_c = result.description) == null ? void 0 : _c.toLocaleLowerCase("en-US").includes(searchQuery));
587
- });
588
- }
589
- setFilteredResults(withFilters);
590
- }
591
- }, [selectedFilters, searchQuery, results]);
592
- if (loading) {
593
- return /* @__PURE__ */ React.createElement(Progress, null);
594
- }
595
- if (error) {
596
- return /* @__PURE__ */ React.createElement(Alert, {
597
- severity: "error"
598
- }, "Error encountered while fetching search results. ", error.toString());
599
- }
600
- if (!results || results.length === 0) {
601
- return /* @__PURE__ */ React.createElement(EmptyState, {
602
- missing: "data",
603
- title: "Sorry, no results were found"
604
- });
605
- }
606
- const resetFilters = () => {
607
- setSelectedFilters({
608
- selected: "",
609
- checked: []
610
- });
611
- };
612
- const updateSelected = (filter) => {
613
- setSelectedFilters((prevState) => ({
614
- ...prevState,
615
- selected: filter
616
- }));
617
- };
618
- const updateChecked = (filter) => {
619
- if (selectedFilters.checked.includes(filter)) {
620
- setSelectedFilters((prevState) => ({
621
- ...prevState,
622
- checked: prevState.checked.filter((item) => item !== filter)
623
- }));
624
- return;
625
- }
626
- setSelectedFilters((prevState) => ({
627
- ...prevState,
628
- checked: [...prevState.checked, filter]
629
- }));
630
- };
631
- const filterOptions = results.reduce((acc, curr) => {
632
- if (curr.kind && acc.kind.indexOf(curr.kind) < 0) {
633
- acc.kind.push(curr.kind);
634
- }
635
- if (curr.lifecycle && acc.lifecycle.indexOf(curr.lifecycle) < 0) {
636
- acc.lifecycle.push(curr.lifecycle);
637
- }
638
- return acc;
639
- }, {
640
- kind: [],
641
- lifecycle: []
642
- });
643
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Grid, {
644
- container: true
645
- }, showFilters && /* @__PURE__ */ React.createElement(Grid, {
646
- item: true,
647
- xs: 3
648
- }, /* @__PURE__ */ React.createElement(Filters, {
649
- filters: selectedFilters,
650
- filterOptions,
651
- resetFilters,
652
- updateSelected,
653
- updateChecked
654
- })), /* @__PURE__ */ React.createElement(Grid, {
655
- item: true,
656
- xs: showFilters ? 9 : 12
657
- }, /* @__PURE__ */ React.createElement(Table, {
658
- options: { paging: true, pageSize: 20, search: false },
659
- data: filteredResults,
660
- columns,
661
- title: /* @__PURE__ */ React.createElement(TableHeader, {
662
- searchQuery,
663
- numberOfResults: filteredResults.length,
664
- numberOfSelectedFilters: (selectedFilters.selected !== "" ? 1 : 0) + selectedFilters.checked.length,
665
- handleToggleFilters: () => toggleFilters(!showFilters)
666
- })
667
- }))));
668
- };
669
-
670
- const LegacySearchPage = () => {
671
- const [queryString, setQueryString] = useQueryParamState("query");
672
- const [searchQuery, setSearchQuery] = useState(queryString != null ? queryString : "");
673
- const handleSearch = (event) => {
674
- event.preventDefault();
675
- setSearchQuery(event.target.value);
676
- };
677
- useEffect(() => setSearchQuery(queryString != null ? queryString : ""), [queryString]);
678
- useDebounce(() => {
679
- setQueryString(searchQuery);
680
- }, 200, [searchQuery]);
681
- const handleClearSearchBar = () => {
682
- setSearchQuery("");
683
- };
684
- return /* @__PURE__ */ React.createElement(Page, {
685
- themeId: "home"
686
- }, /* @__PURE__ */ React.createElement(Header, {
687
- title: "Search"
688
- }), /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(Grid, {
689
- container: true,
690
- direction: "row"
691
- }, /* @__PURE__ */ React.createElement(Grid, {
692
- item: true,
693
- xs: 12
694
- }, /* @__PURE__ */ React.createElement(SearchBar, {
695
- handleSearch,
696
- handleClearSearchBar,
697
- searchQuery
698
- })), /* @__PURE__ */ React.createElement(Grid, {
699
- item: true,
700
- xs: 12
701
- }, /* @__PURE__ */ React.createElement(SearchResult, {
702
- searchQuery: (queryString != null ? queryString : "").toLocaleLowerCase("en-US")
703
- })))));
704
- };
705
-
706
- const UrlUpdater = () => {
707
- const location = useLocation();
708
- const {
709
- term,
710
- setTerm,
711
- types,
712
- setTypes,
713
- pageCursor,
714
- setPageCursor,
715
- filters,
716
- setFilters
717
- } = useSearch();
718
- const prevQueryParams = usePrevious(location.search);
719
- useEffect(() => {
720
- if (location.search === prevQueryParams) {
721
- return;
722
- }
723
- const query = qs.parse(location.search.substring(1), { arrayLimit: 0 }) || {};
724
- if (query.filters) {
725
- setFilters(query.filters);
726
- }
727
- if (query.query) {
728
- setTerm(query.query);
729
- }
730
- if (query.pageCursor) {
731
- setPageCursor(query.pageCursor);
732
- }
733
- if (query.types) {
734
- setTypes(query.types);
735
- }
736
- }, [prevQueryParams, location, setTerm, setTypes, setPageCursor, setFilters]);
737
- useEffect(() => {
738
- const newParams = qs.stringify({
739
- query: term,
740
- types,
741
- pageCursor,
742
- filters
743
- }, { arrayFormat: "brackets" });
744
- const newUrl = `${window.location.pathname}?${newParams}`;
745
- window.history.replaceState(null, document.title, newUrl);
746
- }, [term, types, pageCursor, filters]);
747
- return null;
748
- };
749
- const SearchPage = () => {
750
- const outlet = useOutlet();
751
- return /* @__PURE__ */ React.createElement(SearchContextProvider, null, /* @__PURE__ */ React.createElement(UrlUpdater, null), outlet || /* @__PURE__ */ React.createElement(LegacySearchPage, null));
752
- };
753
-
754
- const useStyles$3 = makeStyles((theme) => ({
755
- root: {
756
- display: "flex",
757
- justifyContent: "space-between",
758
- gap: theme.spacing(2),
759
- margin: theme.spacing(2, 0)
760
- }
761
- }));
762
- const SearchResultPager = () => {
763
- const { fetchNextPage, fetchPreviousPage } = useSearch();
764
- const classes = useStyles$3();
765
- if (!fetchNextPage && !fetchPreviousPage) {
766
- return /* @__PURE__ */ React.createElement(React.Fragment, null);
767
- }
768
- return /* @__PURE__ */ React.createElement("nav", {
769
- "arial-label": "pagination navigation",
770
- className: classes.root
771
- }, /* @__PURE__ */ React.createElement(Button, {
772
- "aria-label": "previous page",
773
- disabled: !fetchPreviousPage,
774
- onClick: fetchPreviousPage,
775
- startIcon: /* @__PURE__ */ React.createElement(ArrowBackIosIcon, null)
776
- }, "Previous"), /* @__PURE__ */ React.createElement(Button, {
777
- "aria-label": "next page",
778
- disabled: !fetchNextPage,
779
- onClick: fetchNextPage,
780
- endIcon: /* @__PURE__ */ React.createElement(ArrowForwardIosIcon, null)
781
- }, "Next"));
782
- };
783
-
784
- const useStyles$2 = makeStyles((theme) => ({
785
- card: {
786
- backgroundColor: "rgba(0, 0, 0, .11)"
787
- },
788
- cardContent: {
789
- paddingTop: theme.spacing(1)
790
- },
791
- icon: {
792
- color: theme.palette.common.black
793
- },
794
- list: {
795
- width: "100%"
796
- },
797
- listItemIcon: {
798
- width: "24px",
799
- height: "24px"
800
- },
801
- accordion: {
802
- backgroundColor: theme.palette.background.paper
803
- },
804
- accordionSummary: {
805
- minHeight: "auto",
806
- "&.Mui-expanded": {
807
- minHeight: "auto"
808
- }
809
- },
810
- accordionSummaryContent: {
811
- margin: theme.spacing(2, 0),
812
- "&.Mui-expanded": {
813
- margin: theme.spacing(2, 0)
814
- }
815
- },
816
- accordionDetails: {
817
- padding: theme.spacing(0, 0, 1)
818
- }
819
- }));
820
- const SearchTypeAccordion = (props) => {
821
- const classes = useStyles$2();
822
- const { setPageCursor, setTypes, types } = useSearch();
823
- const [expanded, setExpanded] = useState(true);
824
- const { defaultValue, name, types: givenTypes } = props;
825
- const toggleExpanded = () => setExpanded((prevState) => !prevState);
826
- const handleClick = (type) => {
827
- return () => {
828
- setTypes(type !== "" ? [type] : []);
829
- setPageCursor(void 0);
830
- setExpanded(false);
831
- };
832
- };
833
- useEffect(() => {
834
- if (defaultValue) {
835
- setTypes([defaultValue]);
836
- }
837
- }, []);
838
- const definedTypes = [
839
- {
840
- value: "",
841
- name: "All",
842
- icon: /* @__PURE__ */ React.createElement(AllIcon, null)
843
- },
844
- ...givenTypes
845
- ];
846
- const selected = types[0] || "";
847
- return /* @__PURE__ */ React.createElement(Card, {
848
- className: classes.card
849
- }, /* @__PURE__ */ React.createElement(CardHeader, {
850
- title: name,
851
- titleTypographyProps: { variant: "overline" }
852
- }), /* @__PURE__ */ React.createElement(CardContent, {
853
- className: classes.cardContent
854
- }, /* @__PURE__ */ React.createElement(Accordion, {
855
- className: classes.accordion,
856
- expanded,
857
- onChange: toggleExpanded
858
- }, /* @__PURE__ */ React.createElement(AccordionSummary, {
859
- classes: {
860
- root: classes.accordionSummary,
861
- content: classes.accordionSummaryContent
862
- },
863
- expandIcon: /* @__PURE__ */ React.createElement(ExpandMoreIcon, {
864
- className: classes.icon
865
- }),
866
- IconButtonProps: { size: "small" }
867
- }, expanded ? "Collapse" : definedTypes.filter((t) => t.value === selected)[0].name), /* @__PURE__ */ React.createElement(AccordionDetails, {
868
- classes: { root: classes.accordionDetails }
869
- }, /* @__PURE__ */ React.createElement(List, {
870
- className: classes.list,
871
- component: "nav",
872
- "aria-label": "filter by type",
873
- disablePadding: true,
874
- dense: true
875
- }, definedTypes.map((type) => /* @__PURE__ */ React.createElement(Fragment, {
876
- key: type.value
877
- }, /* @__PURE__ */ React.createElement(Divider, null), /* @__PURE__ */ React.createElement(ListItem, {
878
- selected: types[0] === type.value || types.length === 0 && type.value === "",
879
- onClick: handleClick(type.value),
880
- button: true
881
- }, /* @__PURE__ */ React.createElement(ListItemIcon, null, cloneElement(type.icon, {
882
- className: classes.listItemIcon
883
- })), /* @__PURE__ */ React.createElement(ListItemText, {
884
- primary: type.name
885
- })))))))));
886
- };
887
-
888
- const useStyles$1 = makeStyles((theme) => ({
889
- tabs: {
890
- borderBottom: `1px solid ${theme.palette.textVerySubtle}`,
891
- padding: theme.spacing(0, 4)
892
- },
893
- tab: {
894
- height: "50px",
895
- fontWeight: theme.typography.fontWeightBold,
896
- fontSize: theme.typography.pxToRem(13),
897
- color: theme.palette.textSubtle,
898
- minWidth: "130px"
899
- }
900
- }));
901
- const SearchTypeTabs = (props) => {
902
- const classes = useStyles$1();
903
- const { setPageCursor, setTypes, types } = useSearch();
904
- const { defaultValue, types: givenTypes } = props;
905
- const changeTab = (_, newType) => {
906
- setTypes(newType !== "" ? [newType] : []);
907
- setPageCursor(void 0);
908
- };
909
- useEffect(() => {
910
- if (defaultValue) {
911
- setTypes([defaultValue]);
912
- }
913
- }, []);
914
- const definedTypes = [
915
- {
916
- value: "",
917
- name: "All"
918
- },
919
- ...givenTypes
920
- ];
921
- return /* @__PURE__ */ React.createElement(Tabs, {
922
- className: classes.tabs,
923
- indicatorColor: "primary",
924
- value: types.length === 0 ? "" : types[0],
925
- onChange: changeTab
926
- }, definedTypes.map((type, idx) => /* @__PURE__ */ React.createElement(Tab, {
927
- key: idx,
928
- className: classes.tab,
929
- disableRipple: true,
930
- label: type.name,
931
- value: type.value
932
- })));
933
- };
934
-
935
- const useStyles = makeStyles((theme) => ({
936
- label: {
937
- textTransform: "capitalize"
938
- },
939
- chips: {
940
- display: "flex",
941
- flexWrap: "wrap",
942
- marginTop: theme.spacing(1)
943
- },
944
- chip: {
945
- margin: 2
946
- }
947
- }));
948
- const SearchType = (props) => {
949
- const { className, defaultValue, name, values = [] } = props;
950
- const classes = useStyles();
951
- const { types, setTypes } = useSearch();
952
- useEffectOnce(() => {
953
- if (!types.length) {
954
- if (defaultValue && Array.isArray(defaultValue)) {
955
- setTypes(defaultValue);
956
- } else if (defaultValue) {
957
- setTypes([defaultValue]);
958
- }
959
- }
960
- });
961
- const handleChange = (e) => {
962
- const value = e.target.value;
963
- setTypes(value);
964
- };
965
- return /* @__PURE__ */ React.createElement(FormControl, {
966
- className,
967
- variant: "filled",
968
- fullWidth: true,
969
- "data-testid": "search-typefilter-next"
970
- }, /* @__PURE__ */ React.createElement(InputLabel, {
971
- className: classes.label,
972
- margin: "dense"
973
- }, name), /* @__PURE__ */ React.createElement(Select, {
974
- multiple: true,
975
- variant: "outlined",
976
- value: types,
977
- onChange: handleChange,
978
- placeholder: "All Results",
979
- renderValue: (selected) => /* @__PURE__ */ React.createElement("div", {
980
- className: classes.chips
981
- }, selected.map((value) => /* @__PURE__ */ React.createElement(Chip, {
982
- key: value,
983
- label: value,
984
- className: classes.chip,
985
- size: "small"
986
- })))
987
- }, values.map((value) => /* @__PURE__ */ React.createElement(MenuItem, {
988
- key: value,
989
- value
990
- }, /* @__PURE__ */ React.createElement(Checkbox, {
991
- checked: types.indexOf(value) > -1
992
- }), /* @__PURE__ */ React.createElement(ListItemText, {
993
- primary: value
994
- })))));
995
- };
996
- SearchType.Accordion = (props) => {
997
- return /* @__PURE__ */ React.createElement(SearchTypeAccordion, {
998
- ...props
999
- });
1000
- };
1001
- SearchType.Tabs = (props) => {
1002
- return /* @__PURE__ */ React.createElement(SearchTypeTabs, {
1003
- ...props
1004
- });
1005
- };
1006
-
1007
- const SidebarSearch = (props) => {
1008
- const searchRoute = useRouteRef(rootRouteRef);
1009
- const { focusContent } = useContent();
1010
- const navigate = useNavigate();
1011
- const handleSearch = useCallback((query) => {
1012
- const queryString = qs.stringify({ query }, { addQueryPrefix: true });
1013
- focusContent();
1014
- navigate(`${searchRoute()}${queryString}`);
1015
- }, [focusContent, navigate, searchRoute]);
1016
- return /* @__PURE__ */ React.createElement(SidebarSearchField, {
1017
- icon: props.icon,
1018
- onSearch: handleSearch,
1019
- to: "/search"
1020
- });
1021
- };
1022
-
1023
- export { DefaultResultListItem as D, Filters$1 as F, HomePageSearchBar as H, SearchPage as S, SearchModalProvider as a, SearchModal as b, FiltersButton$1 as c, SearchBar$1 as d, SearchBarBase as e, SearchFilter as f, SearchResultPager as g, SearchType as h, SidebarSearch as i, SearchPage$1 as j, SearchResult$1 as k, SidebarSearchModal as l, rootRouteRef as r, searchPlugin as s, useSearchModal as u };
1024
- //# sourceMappingURL=index-5187be91.esm.js.map