@backstage/plugin-search-react 1.8.8-next.0 → 1.8.8-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/dist/alpha/blueprints/SearchResultListItemBlueprint.esm.js +7 -6
- package/dist/alpha/blueprints/SearchResultListItemBlueprint.esm.js.map +1 -1
- package/dist/components/DefaultResultListItem/DefaultResultListItem.esm.js +42 -38
- package/dist/components/DefaultResultListItem/DefaultResultListItem.esm.js.map +1 -1
- package/dist/components/HighlightedSearchResultText/HighlightedSearchResultText.esm.js +5 -4
- package/dist/components/HighlightedSearchResultText/HighlightedSearchResultText.esm.js.map +1 -1
- package/dist/components/SearchAutocomplete/SearchAutocomplete.esm.js +7 -6
- package/dist/components/SearchAutocomplete/SearchAutocomplete.esm.js.map +1 -1
- package/dist/components/SearchAutocomplete/SearchAutocompleteDefaultOption.esm.js +14 -11
- package/dist/components/SearchAutocomplete/SearchAutocompleteDefaultOption.esm.js.map +1 -1
- package/dist/components/SearchBar/SearchBar.esm.js +23 -22
- package/dist/components/SearchBar/SearchBar.esm.js.map +1 -1
- package/dist/components/SearchFilter/SearchFilter.Autocomplete.esm.js +5 -4
- package/dist/components/SearchFilter/SearchFilter.Autocomplete.esm.js.map +1 -1
- package/dist/components/SearchFilter/SearchFilter.esm.js +45 -42
- package/dist/components/SearchFilter/SearchFilter.esm.js.map +1 -1
- package/dist/components/SearchPagination/SearchPagination.esm.js +4 -3
- package/dist/components/SearchPagination/SearchPagination.esm.js.map +1 -1
- package/dist/components/SearchResult/SearchResult.esm.js +12 -12
- package/dist/components/SearchResult/SearchResult.esm.js.map +1 -1
- package/dist/components/SearchResultGroup/SearchResultGroup.esm.js +99 -79
- package/dist/components/SearchResultGroup/SearchResultGroup.esm.js.map +1 -1
- package/dist/components/SearchResultList/SearchResultList.esm.js +22 -22
- package/dist/components/SearchResultList/SearchResultList.esm.js.map +1 -1
- package/dist/components/SearchResultPager/SearchResultPager.esm.js +24 -21
- package/dist/components/SearchResultPager/SearchResultPager.esm.js.map +1 -1
- package/dist/context/SearchContext.esm.js +7 -6
- package/dist/context/SearchContext.esm.js.map +1 -1
- package/dist/extensions.esm.js +23 -22
- package/dist/extensions.esm.js.map +1 -1
- package/dist/index.d.ts +39 -37
- package/package.json +13 -13
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SearchResultGroup.esm.js","sources":["../../../src/components/SearchResultGroup/SearchResultGroup.tsx"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, {\n ChangeEvent,\n PropsWithChildren,\n ReactNode,\n useCallback,\n useState,\n} from 'react';\nimport qs from 'qs';\n\nimport List, { ListProps } from '@material-ui/core/List';\nimport ListSubheader from '@material-ui/core/ListSubheader';\nimport Menu from '@material-ui/core/Menu';\nimport MenuItem from '@material-ui/core/MenuItem';\nimport InputBase from '@material-ui/core/InputBase';\nimport Select from '@material-ui/core/Select';\nimport Chip from '@material-ui/core/Chip';\nimport Typography, { TypographyProps } from '@material-ui/core/Typography';\nimport { makeStyles, Theme } from '@material-ui/core/styles';\nimport AddIcon from '@material-ui/icons/Add';\nimport ArrowRightIcon from '@material-ui/icons/ArrowForwardIos';\n\nimport { JsonValue } from '@backstage/types';\nimport {\n Link,\n LinkProps,\n Progress,\n EmptyState,\n ResponseErrorPanel,\n} from '@backstage/core-components';\nimport { AnalyticsContext } from '@backstage/core-plugin-api';\nimport { SearchResult } from '@backstage/plugin-search-common';\n\nimport { useSearchResultListItemExtensions } from '../../extensions';\n\nimport { DefaultResultListItem } from '../DefaultResultListItem';\nimport { SearchResultState, SearchResultStateProps } from '../SearchResult';\n\nconst useStyles = makeStyles((theme: Theme) => ({\n listSubheader: {\n display: 'flex',\n alignItems: 'center',\n },\n listSubheaderName: {\n marginLeft: theme.spacing(1),\n textTransform: 'uppercase',\n },\n listSubheaderChip: {\n color: theme.palette.text.secondary,\n margin: theme.spacing(0, 0, 0, 1.5),\n },\n listSubheaderFilter: {\n display: 'flex',\n color: theme.palette.text.secondary,\n margin: theme.spacing(0, 0, 0, 1.5),\n },\n listSubheaderLink: {\n marginLeft: 'auto',\n display: 'flex',\n alignItems: 'center',\n },\n listSubheaderLinkIcon: {\n fontSize: 'inherit',\n marginLeft: theme.spacing(0.5),\n },\n}));\n\n/**\n * Props for {@link SearchResultGroupFilterFieldLayout}\n * @public\n */\nexport type SearchResultGroupFilterFieldLayoutProps = PropsWithChildren<{\n label: string;\n value?: JsonValue;\n onDelete: () => void;\n}>;\n\n/**\n * Default layout for a search group filter field.\n * @param props - See {@link SearchResultGroupFilterFieldLayoutProps}.\n * @public\n */\nexport const SearchResultGroupFilterFieldLayout = (\n props: SearchResultGroupFilterFieldLayoutProps,\n) => {\n const classes = useStyles();\n const { label, children, ...rest } = props;\n\n return (\n <Chip\n {...rest}\n className={classes.listSubheaderFilter}\n variant=\"outlined\"\n label={\n <>\n {label}: {children}\n </>\n }\n />\n );\n};\n\nconst NullIcon = () => null;\n\n/**\n * Common props for a result group filter field.\n * @public\n */\nexport type SearchResultGroupFilterFieldPropsWith<T> = T &\n SearchResultGroupFilterFieldLayoutProps & {\n onChange: (value: JsonValue) => void;\n };\n\nconst useSearchResultGroupTextFilterStyles = makeStyles((theme: Theme) => ({\n root: {\n fontSize: 'inherit',\n '&:focus': {\n outline: 'none',\n background: theme.palette.common.white,\n },\n '&:not(:focus)': {\n cursor: 'pointer',\n color: theme.palette.primary.main,\n '&:hover': {\n textDecoration: 'underline',\n },\n },\n },\n}));\n\n/**\n * Props for {@link SearchResultGroupTextFilterField}.\n * @public\n */\nexport type SearchResultGroupTextFilterFieldProps =\n SearchResultGroupFilterFieldPropsWith<{}>;\n\n/**\n * A text field that can be used as filter on search result groups.\n * @param props - See {@link SearchResultGroupTextFilterFieldProps}.\n * @example\n * ```\n * <SearchResultGroupTextFilterField\n * id=\"lifecycle\"\n * label=\"Lifecycle\"\n * value={value}\n * onChange={handleChangeFilter}\n * onDelete={handleDeleteFilter}\n * />\n * ```\n * @public\n */\nexport const SearchResultGroupTextFilterField = (\n props: SearchResultGroupTextFilterFieldProps,\n) => {\n const classes = useSearchResultGroupTextFilterStyles();\n const { label, value = 'None', onChange, onDelete } = props;\n\n const handleChange = useCallback(\n (e: ChangeEvent<HTMLInputElement>) => {\n onChange(e.target.value);\n },\n [onChange],\n );\n\n return (\n <SearchResultGroupFilterFieldLayout label={label} onDelete={onDelete}>\n <Typography\n role=\"textbox\"\n component=\"span\"\n className={classes.root}\n onChange={handleChange}\n contentEditable\n suppressContentEditableWarning\n >\n {value?.toString()}\n </Typography>\n </SearchResultGroupFilterFieldLayout>\n );\n};\n\nconst useSearchResultGroupSelectFilterStyles = makeStyles((theme: Theme) => ({\n root: {\n fontSize: 'inherit',\n '&:not(:focus)': {\n cursor: 'pointer',\n color: theme.palette.primary.main,\n '&:hover': {\n textDecoration: 'underline',\n },\n },\n '&:focus': {\n outline: 'none',\n },\n '&>div:first-child': {\n padding: 0,\n },\n },\n}));\n\n/**\n * Props for {@link SearchResultGroupTextFilterField}.\n * @public\n */\nexport type SearchResultGroupSelectFilterFieldProps =\n SearchResultGroupFilterFieldPropsWith<{\n children: ReactNode;\n }>;\n\n/**\n * A select field that can be used as filter on search result groups.\n * @param props - See {@link SearchResultGroupSelectFilterFieldProps}.\n * @example\n * ```\n * <SearchResultGroupSelectFilterField\n * id=\"lifecycle\"\n * label=\"Lifecycle\"\n * value={filters.lifecycle}\n * onChange={handleChangeFilter}\n * onDelete={handleDeleteFilter}\n * >\n * <MenuItem value=\"experimental\">Experimental</MenuItem>\n * <MenuItem value=\"production\">Production</MenuItem>\n * </SearchResultGroupSelectFilterField>\n * ```\n * @public\n */\nexport const SearchResultGroupSelectFilterField = (\n props: SearchResultGroupSelectFilterFieldProps,\n) => {\n const classes = useSearchResultGroupSelectFilterStyles();\n const { label, value = 'none', onChange, onDelete, children } = props;\n\n const handleChange = useCallback(\n (e: ChangeEvent<{ value: unknown }>) => {\n onChange(e.target.value as JsonValue);\n },\n [onChange],\n );\n\n return (\n <SearchResultGroupFilterFieldLayout label={label} onDelete={onDelete}>\n <Select\n className={classes.root}\n value={value}\n onChange={handleChange}\n input={<InputBase />}\n IconComponent={NullIcon}\n >\n <MenuItem value=\"none\">None</MenuItem>\n {children}\n </Select>\n </SearchResultGroupFilterFieldLayout>\n );\n};\n\n/**\n * Props for {@link SearchResultGroupLayout}\n * @public\n */\nexport type SearchResultGroupLayoutProps<FilterOption> = ListProps & {\n /**\n * If defined, will render a default error panel.\n */\n error?: Error;\n /**\n * If defined, will render a default loading progress.\n */\n loading?: boolean;\n /**\n * Icon that representing a result group.\n */\n icon: JSX.Element;\n /**\n * The results group title content, it could be a text or an element.\n */\n title: ReactNode;\n /**\n * Props for the results group title.\n */\n titleProps?: Partial<TypographyProps>;\n /**\n * The results group link content, it could be a text or an element.\n */\n link?: ReactNode;\n /**\n * Props for the results group link, the \"to\" prop defaults to \"/search\".\n */\n linkProps?: Partial<LinkProps>;\n /**\n * A generic filter options that is rendered on the \"Add filter\" dropdown.\n */\n filterOptions?: FilterOption[];\n /**\n * Function to customize how filter options are rendered.\n * @remarks Defaults to a menu item where its value and label bounds to the option string.\n */\n renderFilterOption?: (\n value: FilterOption,\n index: number,\n array: FilterOption[],\n ) => JSX.Element | null;\n /**\n * A list of search filter keys, also known as filter field names.\n */\n filterFields?: string[];\n /**\n * Function to customize how filter chips are rendered.\n */\n renderFilterField?: (key: string) => JSX.Element | null;\n /**\n * Search results to be rendered as a group.\n */\n resultItems?: SearchResult[];\n /**\n * Function to customize how result items are rendered.\n */\n renderResultItem?: (\n value: SearchResult,\n index: number,\n array: SearchResult[],\n ) => JSX.Element | null;\n /**\n * Optional component to render when no results. Default to <EmptyState /> component.\n */\n noResultsComponent?: ReactNode;\n /**\n * Optional property to provide if component should not render the component when no results are found.\n */\n disableRenderingWithNoResults?: boolean;\n};\n\n/**\n * Default layout for rendering search results in a group.\n * @param props - See {@link SearchResultGroupLayoutProps}.\n * @public\n */\nexport function SearchResultGroupLayout<FilterOption>(\n props: SearchResultGroupLayoutProps<FilterOption>,\n) {\n const classes = useStyles();\n const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);\n\n const {\n error,\n loading,\n icon,\n title,\n titleProps = {},\n link = (\n <>\n See all\n <ArrowRightIcon className={classes.listSubheaderLinkIcon} />\n </>\n ),\n linkProps = {},\n filterOptions,\n renderFilterOption = filterOption => (\n <MenuItem key={String(filterOption)} value={String(filterOption)}>\n {String(filterOption)}\n </MenuItem>\n ),\n filterFields,\n renderFilterField,\n resultItems,\n renderResultItem = resultItem => (\n <DefaultResultListItem\n key={resultItem.document.location}\n result={resultItem.document}\n />\n ),\n disableRenderingWithNoResults,\n noResultsComponent = disableRenderingWithNoResults ? null : (\n <EmptyState missing=\"data\" title=\"Sorry, no results were found\" />\n ),\n ...rest\n } = props;\n\n const handleClick = useCallback((e: React.MouseEvent<HTMLButtonElement>) => {\n setAnchorEl(e.currentTarget);\n }, []);\n\n const handleClose = useCallback(() => {\n setAnchorEl(null);\n }, []);\n\n if (loading) {\n return <Progress />;\n }\n\n if (error) {\n return (\n <ResponseErrorPanel\n title=\"Error encountered while fetching search results\"\n error={error}\n />\n );\n }\n\n if (!resultItems?.length) {\n return <>{noResultsComponent}</>;\n }\n\n return (\n <List {...rest}>\n <ListSubheader className={classes.listSubheader}>\n {icon}\n <Typography\n className={classes.listSubheaderName}\n component=\"strong\"\n {...titleProps}\n >\n {title}\n </Typography>\n {filterOptions ? (\n <Chip\n className={classes.listSubheaderChip}\n component=\"button\"\n icon={<AddIcon />}\n variant=\"outlined\"\n label=\"Add filter\"\n aria-controls=\"filters-menu\"\n aria-haspopup=\"true\"\n onClick={handleClick}\n />\n ) : null}\n {filterOptions ? (\n <Menu\n id=\"filters-menu\"\n anchorEl={anchorEl}\n open={Boolean(anchorEl)}\n onClose={handleClose}\n onClick={handleClose}\n keepMounted\n >\n {filterOptions.map(renderFilterOption)}\n </Menu>\n ) : null}\n {filterFields?.map(\n filterField => renderFilterField?.(filterField) ?? null,\n )}\n <Link className={classes.listSubheaderLink} to=\"/search\" {...linkProps}>\n {link}\n </Link>\n </ListSubheader>\n {resultItems.map(renderResultItem)}\n </List>\n );\n}\n\n/**\n * Props for {@link SearchResultGroup}.\n * @public\n */\nexport type SearchResultGroupProps<FilterOption> = Pick<\n SearchResultStateProps,\n 'query'\n> &\n Omit<\n SearchResultGroupLayoutProps<FilterOption>,\n 'loading' | 'error' | 'resultItems' | 'filterFields'\n >;\n\n/**\n * Given a query, search for results and render them as a group.\n * @param props - See {@link SearchResultGroupProps}.\n * @public\n */\nexport function SearchResultGroup<FilterOption>(\n props: SearchResultGroupProps<FilterOption>,\n) {\n const { query, children, renderResultItem, linkProps = {}, ...rest } = props;\n\n const defaultRenderResultItem = useSearchResultListItemExtensions(children);\n\n return (\n <AnalyticsContext\n attributes={{\n pluginId: 'search',\n extension: 'SearchResultGroup',\n }}\n >\n <SearchResultState query={query}>\n {(\n { loading, error, value },\n { term, types, pageCursor, filters = {} },\n ) => {\n const to = `/search?${qs.stringify(\n { term, types, filters, pageCursor, query: term },\n { arrayFormat: 'brackets' },\n )}`;\n\n return (\n <SearchResultGroupLayout\n {...rest}\n error={error}\n loading={loading}\n linkProps={{ to, ...linkProps }}\n filterFields={Object.keys(filters)}\n resultItems={value?.results}\n renderResultItem={renderResultItem ?? defaultRenderResultItem}\n />\n );\n }}\n </SearchResultState>\n </AnalyticsContext>\n );\n}\n"],"names":["DefaultResultListItem"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SearchResultGroup.esm.js","sources":["../../../src/components/SearchResultGroup/SearchResultGroup.tsx"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n MouseEvent,\n ChangeEvent,\n PropsWithChildren,\n ReactNode,\n useCallback,\n useState,\n} from 'react';\nimport qs from 'qs';\n\nimport List, { ListProps } from '@material-ui/core/List';\nimport ListSubheader from '@material-ui/core/ListSubheader';\nimport Menu from '@material-ui/core/Menu';\nimport MenuItem from '@material-ui/core/MenuItem';\nimport InputBase from '@material-ui/core/InputBase';\nimport Select from '@material-ui/core/Select';\nimport Chip from '@material-ui/core/Chip';\nimport Typography, { TypographyProps } from '@material-ui/core/Typography';\nimport { makeStyles, Theme } from '@material-ui/core/styles';\nimport AddIcon from '@material-ui/icons/Add';\nimport ArrowRightIcon from '@material-ui/icons/ArrowForwardIos';\n\nimport { JsonValue } from '@backstage/types';\nimport {\n Link,\n LinkProps,\n Progress,\n EmptyState,\n ResponseErrorPanel,\n} from '@backstage/core-components';\nimport { AnalyticsContext } from '@backstage/core-plugin-api';\nimport { SearchResult } from '@backstage/plugin-search-common';\n\nimport { useSearchResultListItemExtensions } from '../../extensions';\n\nimport { DefaultResultListItem } from '../DefaultResultListItem';\nimport { SearchResultState, SearchResultStateProps } from '../SearchResult';\n\nconst useStyles = makeStyles((theme: Theme) => ({\n listSubheader: {\n display: 'flex',\n alignItems: 'center',\n },\n listSubheaderName: {\n marginLeft: theme.spacing(1),\n textTransform: 'uppercase',\n },\n listSubheaderChip: {\n color: theme.palette.text.secondary,\n margin: theme.spacing(0, 0, 0, 1.5),\n },\n listSubheaderFilter: {\n display: 'flex',\n color: theme.palette.text.secondary,\n margin: theme.spacing(0, 0, 0, 1.5),\n },\n listSubheaderLink: {\n marginLeft: 'auto',\n display: 'flex',\n alignItems: 'center',\n },\n listSubheaderLinkIcon: {\n fontSize: 'inherit',\n marginLeft: theme.spacing(0.5),\n },\n}));\n\n/**\n * Props for {@link SearchResultGroupFilterFieldLayout}\n * @public\n */\nexport type SearchResultGroupFilterFieldLayoutProps = PropsWithChildren<{\n label: string;\n value?: JsonValue;\n onDelete: () => void;\n}>;\n\n/**\n * Default layout for a search group filter field.\n * @param props - See {@link SearchResultGroupFilterFieldLayoutProps}.\n * @public\n */\nexport const SearchResultGroupFilterFieldLayout = (\n props: SearchResultGroupFilterFieldLayoutProps,\n) => {\n const classes = useStyles();\n const { label, children, ...rest } = props;\n\n return (\n <Chip\n {...rest}\n className={classes.listSubheaderFilter}\n variant=\"outlined\"\n label={\n <>\n {label}: {children}\n </>\n }\n />\n );\n};\n\nconst NullIcon = () => null;\n\n/**\n * Common props for a result group filter field.\n * @public\n */\nexport type SearchResultGroupFilterFieldPropsWith<T> = T &\n SearchResultGroupFilterFieldLayoutProps & {\n onChange: (value: JsonValue) => void;\n };\n\nconst useSearchResultGroupTextFilterStyles = makeStyles((theme: Theme) => ({\n root: {\n fontSize: 'inherit',\n '&:focus': {\n outline: 'none',\n background: theme.palette.common.white,\n },\n '&:not(:focus)': {\n cursor: 'pointer',\n color: theme.palette.primary.main,\n '&:hover': {\n textDecoration: 'underline',\n },\n },\n },\n}));\n\n/**\n * Props for {@link SearchResultGroupTextFilterField}.\n * @public\n */\nexport type SearchResultGroupTextFilterFieldProps =\n SearchResultGroupFilterFieldPropsWith<{}>;\n\n/**\n * A text field that can be used as filter on search result groups.\n * @param props - See {@link SearchResultGroupTextFilterFieldProps}.\n * @example\n * ```\n * <SearchResultGroupTextFilterField\n * id=\"lifecycle\"\n * label=\"Lifecycle\"\n * value={value}\n * onChange={handleChangeFilter}\n * onDelete={handleDeleteFilter}\n * />\n * ```\n * @public\n */\nexport const SearchResultGroupTextFilterField = (\n props: SearchResultGroupTextFilterFieldProps,\n) => {\n const classes = useSearchResultGroupTextFilterStyles();\n const { label, value = 'None', onChange, onDelete } = props;\n\n const handleChange = useCallback(\n (e: ChangeEvent<HTMLInputElement>) => {\n onChange(e.target.value);\n },\n [onChange],\n );\n\n return (\n <SearchResultGroupFilterFieldLayout label={label} onDelete={onDelete}>\n <Typography\n role=\"textbox\"\n component=\"span\"\n className={classes.root}\n onChange={handleChange}\n contentEditable\n suppressContentEditableWarning\n >\n {value?.toString()}\n </Typography>\n </SearchResultGroupFilterFieldLayout>\n );\n};\n\nconst useSearchResultGroupSelectFilterStyles = makeStyles((theme: Theme) => ({\n root: {\n fontSize: 'inherit',\n '&:not(:focus)': {\n cursor: 'pointer',\n color: theme.palette.primary.main,\n '&:hover': {\n textDecoration: 'underline',\n },\n },\n '&:focus': {\n outline: 'none',\n },\n '&>div:first-child': {\n padding: 0,\n },\n },\n}));\n\n/**\n * Props for {@link SearchResultGroupTextFilterField}.\n * @public\n */\nexport type SearchResultGroupSelectFilterFieldProps =\n SearchResultGroupFilterFieldPropsWith<{\n children: ReactNode;\n }>;\n\n/**\n * A select field that can be used as filter on search result groups.\n * @param props - See {@link SearchResultGroupSelectFilterFieldProps}.\n * @example\n * ```\n * <SearchResultGroupSelectFilterField\n * id=\"lifecycle\"\n * label=\"Lifecycle\"\n * value={filters.lifecycle}\n * onChange={handleChangeFilter}\n * onDelete={handleDeleteFilter}\n * >\n * <MenuItem value=\"experimental\">Experimental</MenuItem>\n * <MenuItem value=\"production\">Production</MenuItem>\n * </SearchResultGroupSelectFilterField>\n * ```\n * @public\n */\nexport const SearchResultGroupSelectFilterField = (\n props: SearchResultGroupSelectFilterFieldProps,\n) => {\n const classes = useSearchResultGroupSelectFilterStyles();\n const { label, value = 'none', onChange, onDelete, children } = props;\n\n const handleChange = useCallback(\n (e: ChangeEvent<{ value: unknown }>) => {\n onChange(e.target.value as JsonValue);\n },\n [onChange],\n );\n\n return (\n <SearchResultGroupFilterFieldLayout label={label} onDelete={onDelete}>\n <Select\n className={classes.root}\n value={value}\n onChange={handleChange}\n input={<InputBase />}\n IconComponent={NullIcon}\n >\n <MenuItem value=\"none\">None</MenuItem>\n {children}\n </Select>\n </SearchResultGroupFilterFieldLayout>\n );\n};\n\n/**\n * Props for {@link SearchResultGroupLayout}\n * @public\n */\nexport type SearchResultGroupLayoutProps<FilterOption> = ListProps & {\n /**\n * If defined, will render a default error panel.\n */\n error?: Error;\n /**\n * If defined, will render a default loading progress.\n */\n loading?: boolean;\n /**\n * Icon that representing a result group.\n */\n icon: JSX.Element;\n /**\n * The results group title content, it could be a text or an element.\n */\n title: ReactNode;\n /**\n * Props for the results group title.\n */\n titleProps?: Partial<TypographyProps>;\n /**\n * The results group link content, it could be a text or an element.\n */\n link?: ReactNode;\n /**\n * Props for the results group link, the \"to\" prop defaults to \"/search\".\n */\n linkProps?: Partial<LinkProps>;\n /**\n * A generic filter options that is rendered on the \"Add filter\" dropdown.\n */\n filterOptions?: FilterOption[];\n /**\n * Function to customize how filter options are rendered.\n * @remarks Defaults to a menu item where its value and label bounds to the option string.\n */\n renderFilterOption?: (\n value: FilterOption,\n index: number,\n array: FilterOption[],\n ) => JSX.Element | null;\n /**\n * A list of search filter keys, also known as filter field names.\n */\n filterFields?: string[];\n /**\n * Function to customize how filter chips are rendered.\n */\n renderFilterField?: (key: string) => JSX.Element | null;\n /**\n * Search results to be rendered as a group.\n */\n resultItems?: SearchResult[];\n /**\n * Function to customize how result items are rendered.\n */\n renderResultItem?: (\n value: SearchResult,\n index: number,\n array: SearchResult[],\n ) => JSX.Element | null;\n /**\n * Optional component to render when no results. Default to <EmptyState /> component.\n */\n noResultsComponent?: ReactNode;\n /**\n * Optional property to provide if component should not render the component when no results are found.\n */\n disableRenderingWithNoResults?: boolean;\n};\n\n/**\n * Default layout for rendering search results in a group.\n * @param props - See {@link SearchResultGroupLayoutProps}.\n * @public\n */\nexport function SearchResultGroupLayout<FilterOption>(\n props: SearchResultGroupLayoutProps<FilterOption>,\n) {\n const classes = useStyles();\n const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);\n\n const {\n error,\n loading,\n icon,\n title,\n titleProps = {},\n link = (\n <>\n See all\n <ArrowRightIcon className={classes.listSubheaderLinkIcon} />\n </>\n ),\n linkProps = {},\n filterOptions,\n renderFilterOption = filterOption => (\n <MenuItem key={String(filterOption)} value={String(filterOption)}>\n {String(filterOption)}\n </MenuItem>\n ),\n filterFields,\n renderFilterField,\n resultItems,\n renderResultItem = resultItem => (\n <DefaultResultListItem\n key={resultItem.document.location}\n result={resultItem.document}\n />\n ),\n disableRenderingWithNoResults,\n noResultsComponent = disableRenderingWithNoResults ? null : (\n <EmptyState missing=\"data\" title=\"Sorry, no results were found\" />\n ),\n ...rest\n } = props;\n\n const handleClick = useCallback((e: MouseEvent<HTMLButtonElement>) => {\n setAnchorEl(e.currentTarget);\n }, []);\n\n const handleClose = useCallback(() => {\n setAnchorEl(null);\n }, []);\n\n if (loading) {\n return <Progress />;\n }\n\n if (error) {\n return (\n <ResponseErrorPanel\n title=\"Error encountered while fetching search results\"\n error={error}\n />\n );\n }\n\n if (!resultItems?.length) {\n return <>{noResultsComponent}</>;\n }\n\n return (\n <List {...rest}>\n <ListSubheader className={classes.listSubheader}>\n {icon}\n <Typography\n className={classes.listSubheaderName}\n component=\"strong\"\n {...titleProps}\n >\n {title}\n </Typography>\n {filterOptions ? (\n <Chip\n className={classes.listSubheaderChip}\n component=\"button\"\n icon={<AddIcon />}\n variant=\"outlined\"\n label=\"Add filter\"\n aria-controls=\"filters-menu\"\n aria-haspopup=\"true\"\n onClick={handleClick}\n />\n ) : null}\n {filterOptions ? (\n <Menu\n id=\"filters-menu\"\n anchorEl={anchorEl}\n open={Boolean(anchorEl)}\n onClose={handleClose}\n onClick={handleClose}\n keepMounted\n >\n {filterOptions.map(renderFilterOption)}\n </Menu>\n ) : null}\n {filterFields?.map(\n filterField => renderFilterField?.(filterField) ?? null,\n )}\n <Link className={classes.listSubheaderLink} to=\"/search\" {...linkProps}>\n {link}\n </Link>\n </ListSubheader>\n {resultItems.map(renderResultItem)}\n </List>\n );\n}\n\n/**\n * Props for {@link SearchResultGroup}.\n * @public\n */\nexport type SearchResultGroupProps<FilterOption> = Pick<\n SearchResultStateProps,\n 'query'\n> &\n Omit<\n SearchResultGroupLayoutProps<FilterOption>,\n 'loading' | 'error' | 'resultItems' | 'filterFields'\n >;\n\n/**\n * Given a query, search for results and render them as a group.\n * @param props - See {@link SearchResultGroupProps}.\n * @public\n */\nexport function SearchResultGroup<FilterOption>(\n props: SearchResultGroupProps<FilterOption>,\n) {\n const { query, children, renderResultItem, linkProps = {}, ...rest } = props;\n\n const defaultRenderResultItem = useSearchResultListItemExtensions(children);\n\n return (\n <AnalyticsContext\n attributes={{\n pluginId: 'search',\n extension: 'SearchResultGroup',\n }}\n >\n <SearchResultState query={query}>\n {(\n { loading, error, value },\n { term, types, pageCursor, filters = {} },\n ) => {\n const to = `/search?${qs.stringify(\n { term, types, filters, pageCursor, query: term },\n { arrayFormat: 'brackets' },\n )}`;\n\n return (\n <SearchResultGroupLayout\n {...rest}\n error={error}\n loading={loading}\n linkProps={{ to, ...linkProps }}\n filterFields={Object.keys(filters)}\n resultItems={value?.results}\n renderResultItem={renderResultItem ?? defaultRenderResultItem}\n />\n );\n }}\n </SearchResultState>\n </AnalyticsContext>\n );\n}\n"],"names":["DefaultResultListItem"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import List from '@material-ui/core/List';
|
|
3
3
|
import { Progress, ResponseErrorPanel, EmptyState } from '@backstage/core-components';
|
|
4
4
|
import { AnalyticsContext } from '@backstage/core-plugin-api';
|
|
@@ -11,22 +11,22 @@ const SearchResultListLayout = (props) => {
|
|
|
11
11
|
error,
|
|
12
12
|
loading,
|
|
13
13
|
resultItems,
|
|
14
|
-
renderResultItem = (resultItem) => /* @__PURE__ */
|
|
14
|
+
renderResultItem = (resultItem) => /* @__PURE__ */ jsx(
|
|
15
15
|
HigherOrderDefaultResultListItem,
|
|
16
16
|
{
|
|
17
|
-
key: resultItem.document.location,
|
|
18
17
|
result: resultItem.document
|
|
19
|
-
}
|
|
18
|
+
},
|
|
19
|
+
resultItem.document.location
|
|
20
20
|
),
|
|
21
21
|
disableRenderingWithNoResults,
|
|
22
|
-
noResultsComponent = disableRenderingWithNoResults ? null : /* @__PURE__ */
|
|
22
|
+
noResultsComponent = disableRenderingWithNoResults ? null : /* @__PURE__ */ jsx(EmptyState, { missing: "data", title: "Sorry, no results were found" }),
|
|
23
23
|
...rest
|
|
24
24
|
} = props;
|
|
25
25
|
if (loading) {
|
|
26
|
-
return /* @__PURE__ */
|
|
26
|
+
return /* @__PURE__ */ jsx(Progress, {});
|
|
27
27
|
}
|
|
28
28
|
if (error) {
|
|
29
|
-
return /* @__PURE__ */
|
|
29
|
+
return /* @__PURE__ */ jsx(
|
|
30
30
|
ResponseErrorPanel,
|
|
31
31
|
{
|
|
32
32
|
title: "Error encountered while fetching search results",
|
|
@@ -35,31 +35,31 @@ const SearchResultListLayout = (props) => {
|
|
|
35
35
|
);
|
|
36
36
|
}
|
|
37
37
|
if (!resultItems?.length) {
|
|
38
|
-
return /* @__PURE__ */
|
|
38
|
+
return /* @__PURE__ */ jsx(Fragment, { children: noResultsComponent });
|
|
39
39
|
}
|
|
40
|
-
return /* @__PURE__ */
|
|
40
|
+
return /* @__PURE__ */ jsx(List, { ...rest, children: resultItems.map(renderResultItem) });
|
|
41
41
|
};
|
|
42
42
|
const SearchResultList = (props) => {
|
|
43
43
|
const { query, renderResultItem, children, ...rest } = props;
|
|
44
44
|
const defaultRenderResultItem = useSearchResultListItemExtensions(children);
|
|
45
|
-
return /* @__PURE__ */
|
|
45
|
+
return /* @__PURE__ */ jsx(
|
|
46
46
|
AnalyticsContext,
|
|
47
47
|
{
|
|
48
48
|
attributes: {
|
|
49
49
|
pluginId: "search",
|
|
50
50
|
extension: "SearchResultList"
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
|
|
51
|
+
},
|
|
52
|
+
children: /* @__PURE__ */ jsx(SearchResultState, { query, children: ({ loading, error, value }) => /* @__PURE__ */ jsx(
|
|
53
|
+
SearchResultListLayout,
|
|
54
|
+
{
|
|
55
|
+
...rest,
|
|
56
|
+
error,
|
|
57
|
+
loading,
|
|
58
|
+
resultItems: value?.results,
|
|
59
|
+
renderResultItem: renderResultItem ?? defaultRenderResultItem
|
|
60
|
+
}
|
|
61
|
+
) })
|
|
62
|
+
}
|
|
63
63
|
);
|
|
64
64
|
};
|
|
65
65
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SearchResultList.esm.js","sources":["../../../src/components/SearchResultList/SearchResultList.tsx"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport
|
|
1
|
+
{"version":3,"file":"SearchResultList.esm.js","sources":["../../../src/components/SearchResultList/SearchResultList.tsx"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ReactNode } from 'react';\n\nimport List, { ListProps } from '@material-ui/core/List';\n\nimport {\n Progress,\n EmptyState,\n ResponseErrorPanel,\n} from '@backstage/core-components';\nimport { AnalyticsContext } from '@backstage/core-plugin-api';\nimport { SearchResult } from '@backstage/plugin-search-common';\n\nimport { useSearchResultListItemExtensions } from '../../extensions';\n\nimport { DefaultResultListItem } from '../DefaultResultListItem';\nimport { SearchResultState, SearchResultStateProps } from '../SearchResult';\n\n/**\n * Props for {@link SearchResultListLayout}\n * @public\n */\nexport type SearchResultListLayoutProps = ListProps & {\n /**\n * If defined, will render a default error panel.\n */\n error?: Error;\n /**\n * If defined, will render a default loading progress.\n */\n loading?: boolean;\n /**\n * Search results to be rendered as a list.\n */\n resultItems?: SearchResult[];\n /**\n * Function to customize how result items are rendered.\n */\n renderResultItem?: (\n value: SearchResult,\n index: number,\n array: SearchResult[],\n ) => JSX.Element | null;\n /**\n * Optional component to render when no results. Default to <EmptyState /> component.\n */\n noResultsComponent?: ReactNode;\n /**\n * Optional property to provide if component should not render the component when no results are found.\n */\n disableRenderingWithNoResults?: boolean;\n};\n\n/**\n * Default layout for rendering search results in a list.\n * @param props - See {@link SearchResultListLayoutProps}.\n * @public\n */\nexport const SearchResultListLayout = (props: SearchResultListLayoutProps) => {\n const {\n error,\n loading,\n resultItems,\n renderResultItem = resultItem => (\n <DefaultResultListItem\n key={resultItem.document.location}\n result={resultItem.document}\n />\n ),\n disableRenderingWithNoResults,\n noResultsComponent = disableRenderingWithNoResults ? null : (\n <EmptyState missing=\"data\" title=\"Sorry, no results were found\" />\n ),\n ...rest\n } = props;\n\n if (loading) {\n return <Progress />;\n }\n\n if (error) {\n return (\n <ResponseErrorPanel\n title=\"Error encountered while fetching search results\"\n error={error}\n />\n );\n }\n\n if (!resultItems?.length) {\n return <>{noResultsComponent}</>;\n }\n\n return <List {...rest}>{resultItems.map(renderResultItem)}</List>;\n};\n\n/**\n * Props for {@link SearchResultList}.\n * @public\n */\nexport type SearchResultListProps = Pick<SearchResultStateProps, 'query'> &\n Omit<SearchResultListLayoutProps, 'loading' | 'error' | 'resultItems'>;\n\n/**\n * Given a query, search for results and render them as a list.\n * @param props - See {@link SearchResultListProps}.\n * @public\n */\nexport const SearchResultList = (props: SearchResultListProps) => {\n const { query, renderResultItem, children, ...rest } = props;\n\n const defaultRenderResultItem = useSearchResultListItemExtensions(children);\n\n return (\n <AnalyticsContext\n attributes={{\n pluginId: 'search',\n extension: 'SearchResultList',\n }}\n >\n <SearchResultState query={query}>\n {({ loading, error, value }) => (\n <SearchResultListLayout\n {...rest}\n error={error}\n loading={loading}\n resultItems={value?.results}\n renderResultItem={renderResultItem ?? defaultRenderResultItem}\n />\n )}\n </SearchResultState>\n </AnalyticsContext>\n );\n};\n"],"names":["DefaultResultListItem"],"mappings":";;;;;;;;AAyEa,MAAA,sBAAA,GAAyB,CAAC,KAAuC,KAAA;AAC5E,EAAM,MAAA;AAAA,IACJ,KAAA;AAAA,IACA,OAAA;AAAA,IACA,WAAA;AAAA,IACA,mBAAmB,CACjB,UAAA,qBAAA,GAAA;AAAA,MAACA,gCAAA;AAAA,MAAA;AAAA,QAEC,QAAQ,UAAW,CAAA;AAAA,OAAA;AAAA,MADd,WAAW,QAAS,CAAA;AAAA,KAE3B;AAAA,IAEF,6BAAA;AAAA,IACA,kBAAA,GAAqB,gCAAgC,IACnD,mBAAA,GAAA,CAAC,cAAW,OAAQ,EAAA,MAAA,EAAO,OAAM,8BAA+B,EAAA,CAAA;AAAA,IAElE,GAAG;AAAA,GACD,GAAA,KAAA;AAEJ,EAAA,IAAI,OAAS,EAAA;AACX,IAAA,2BAAQ,QAAS,EAAA,EAAA,CAAA;AAAA;AAGnB,EAAA,IAAI,KAAO,EAAA;AACT,IACE,uBAAA,GAAA;AAAA,MAAC,kBAAA;AAAA,MAAA;AAAA,QACC,KAAM,EAAA,iDAAA;AAAA,QACN;AAAA;AAAA,KACF;AAAA;AAIJ,EAAI,IAAA,CAAC,aAAa,MAAQ,EAAA;AACxB,IAAA,uCAAU,QAAmB,EAAA,kBAAA,EAAA,CAAA;AAAA;AAG/B,EAAA,2BAAQ,IAAM,EAAA,EAAA,GAAG,MAAO,QAAY,EAAA,WAAA,CAAA,GAAA,CAAI,gBAAgB,CAAE,EAAA,CAAA;AAC5D;AAca,MAAA,gBAAA,GAAmB,CAAC,KAAiC,KAAA;AAChE,EAAA,MAAM,EAAE,KAAO,EAAA,gBAAA,EAAkB,QAAU,EAAA,GAAG,MAAS,GAAA,KAAA;AAEvD,EAAM,MAAA,uBAAA,GAA0B,kCAAkC,QAAQ,CAAA;AAE1E,EACE,uBAAA,GAAA;AAAA,IAAC,gBAAA;AAAA,IAAA;AAAA,MACC,UAAY,EAAA;AAAA,QACV,QAAU,EAAA,QAAA;AAAA,QACV,SAAW,EAAA;AAAA,OACb;AAAA,MAEA,QAAA,kBAAA,GAAA,CAAC,qBAAkB,KAChB,EAAA,QAAA,EAAA,CAAC,EAAE,OAAS,EAAA,KAAA,EAAO,OAClB,qBAAA,GAAA;AAAA,QAAC,sBAAA;AAAA,QAAA;AAAA,UACE,GAAG,IAAA;AAAA,UACJ,KAAA;AAAA,UACA,OAAA;AAAA,UACA,aAAa,KAAO,EAAA,OAAA;AAAA,UACpB,kBAAkB,gBAAoB,IAAA;AAAA;AAAA,OAG5C,EAAA;AAAA;AAAA,GACF;AAEJ;;;;"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
2
2
|
import Button from '@material-ui/core/Button';
|
|
3
3
|
import { makeStyles } from '@material-ui/core/styles';
|
|
4
4
|
import ArrowBackIosIcon from '@material-ui/icons/ArrowBackIos';
|
|
@@ -17,27 +17,30 @@ const SearchResultPager = () => {
|
|
|
17
17
|
const { fetchNextPage, fetchPreviousPage } = useSearch();
|
|
18
18
|
const classes = useStyles();
|
|
19
19
|
if (!fetchNextPage && !fetchPreviousPage) {
|
|
20
|
-
return /* @__PURE__ */
|
|
20
|
+
return /* @__PURE__ */ jsx(Fragment, {});
|
|
21
21
|
}
|
|
22
|
-
return /* @__PURE__ */
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
22
|
+
return /* @__PURE__ */ jsxs("nav", { "aria-label": "pagination navigation", className: classes.root, children: [
|
|
23
|
+
/* @__PURE__ */ jsx(
|
|
24
|
+
Button,
|
|
25
|
+
{
|
|
26
|
+
"aria-label": "previous page",
|
|
27
|
+
disabled: !fetchPreviousPage,
|
|
28
|
+
onClick: fetchPreviousPage,
|
|
29
|
+
startIcon: /* @__PURE__ */ jsx(ArrowBackIosIcon, {}),
|
|
30
|
+
children: "Previous"
|
|
31
|
+
}
|
|
32
|
+
),
|
|
33
|
+
/* @__PURE__ */ jsx(
|
|
34
|
+
Button,
|
|
35
|
+
{
|
|
36
|
+
"aria-label": "next page",
|
|
37
|
+
disabled: !fetchNextPage,
|
|
38
|
+
onClick: fetchNextPage,
|
|
39
|
+
endIcon: /* @__PURE__ */ jsx(ArrowRightIcon, {}),
|
|
40
|
+
children: "Next"
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
] });
|
|
41
44
|
};
|
|
42
45
|
|
|
43
46
|
export { SearchResultPager };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SearchResultPager.esm.js","sources":["../../../src/components/SearchResultPager/SearchResultPager.tsx"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport
|
|
1
|
+
{"version":3,"file":"SearchResultPager.esm.js","sources":["../../../src/components/SearchResultPager/SearchResultPager.tsx"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport Button from '@material-ui/core/Button';\nimport { makeStyles } from '@material-ui/core/styles';\nimport ArrowBackIosIcon from '@material-ui/icons/ArrowBackIos';\nimport ArrowForwardIosIcon from '@material-ui/icons/ArrowForwardIos';\n\nimport { useSearch } from '../../context';\n\nconst useStyles = makeStyles(theme => ({\n root: {\n display: 'flex',\n justifyContent: 'space-between',\n gap: theme.spacing(2),\n margin: theme.spacing(2, 0),\n },\n}));\n\n/**\n * @public\n */\nexport const SearchResultPager = () => {\n const { fetchNextPage, fetchPreviousPage } = useSearch();\n const classes = useStyles();\n\n if (!fetchNextPage && !fetchPreviousPage) {\n return <></>;\n }\n\n return (\n <nav aria-label=\"pagination navigation\" className={classes.root}>\n <Button\n aria-label=\"previous page\"\n disabled={!fetchPreviousPage}\n onClick={fetchPreviousPage}\n startIcon={<ArrowBackIosIcon />}\n >\n Previous\n </Button>\n\n <Button\n aria-label=\"next page\"\n disabled={!fetchNextPage}\n onClick={fetchNextPage}\n endIcon={<ArrowForwardIosIcon />}\n >\n Next\n </Button>\n </nav>\n );\n};\n"],"names":["ArrowForwardIosIcon"],"mappings":";;;;;;;AAuBA,MAAM,SAAA,GAAY,WAAW,CAAU,KAAA,MAAA;AAAA,EACrC,IAAM,EAAA;AAAA,IACJ,OAAS,EAAA,MAAA;AAAA,IACT,cAAgB,EAAA,eAAA;AAAA,IAChB,GAAA,EAAK,KAAM,CAAA,OAAA,CAAQ,CAAC,CAAA;AAAA,IACpB,MAAQ,EAAA,KAAA,CAAM,OAAQ,CAAA,CAAA,EAAG,CAAC;AAAA;AAE9B,CAAE,CAAA,CAAA;AAKK,MAAM,oBAAoB,MAAM;AACrC,EAAA,MAAM,EAAE,aAAA,EAAe,iBAAkB,EAAA,GAAI,SAAU,EAAA;AACvD,EAAA,MAAM,UAAU,SAAU,EAAA;AAE1B,EAAI,IAAA,CAAC,aAAiB,IAAA,CAAC,iBAAmB,EAAA;AACxC,IAAA,uBAAS,GAAA,CAAA,QAAA,EAAA,EAAA,CAAA;AAAA;AAGX,EAAA,4BACG,KAAI,EAAA,EAAA,YAAA,EAAW,uBAAwB,EAAA,SAAA,EAAW,QAAQ,IACzD,EAAA,QAAA,EAAA;AAAA,oBAAA,GAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QACC,YAAW,EAAA,eAAA;AAAA,QACX,UAAU,CAAC,iBAAA;AAAA,QACX,OAAS,EAAA,iBAAA;AAAA,QACT,SAAA,sBAAY,gBAAiB,EAAA,EAAA,CAAA;AAAA,QAC9B,QAAA,EAAA;AAAA;AAAA,KAED;AAAA,oBAEA,GAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QACC,YAAW,EAAA,WAAA;AAAA,QACX,UAAU,CAAC,aAAA;AAAA,QACX,OAAS,EAAA,aAAA;AAAA,QACT,OAAA,sBAAUA,cAAoB,EAAA,EAAA,CAAA;AAAA,QAC/B,QAAA,EAAA;AAAA;AAAA;AAED,GACF,EAAA,CAAA;AAEJ;;;;"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
1
2
|
import { isEqual } from 'lodash';
|
|
2
|
-
import
|
|
3
|
+
import { useContext, useState, useCallback, useEffect } from 'react';
|
|
3
4
|
import useAsync from 'react-use/esm/useAsync';
|
|
4
5
|
import usePrevious from 'react-use/esm/usePrevious';
|
|
5
6
|
import { createVersionedContext, createVersionedValueMap } from '@backstage/version-bridge';
|
|
@@ -96,12 +97,12 @@ const useSearchContextValue = (initialValue = defaultInitialSearchState) => {
|
|
|
96
97
|
const LocalSearchContext = (props) => {
|
|
97
98
|
const { initialState, children } = props;
|
|
98
99
|
const value = useSearchContextValue(initialState);
|
|
99
|
-
return /* @__PURE__ */
|
|
100
|
+
return /* @__PURE__ */ jsx(
|
|
100
101
|
AnalyticsContext,
|
|
101
102
|
{
|
|
102
|
-
attributes: { searchTypes: value.types.sort().join(",") }
|
|
103
|
-
|
|
104
|
-
|
|
103
|
+
attributes: { searchTypes: value.types.sort().join(",") },
|
|
104
|
+
children: /* @__PURE__ */ jsx(SearchContext.Provider, { value: createVersionedValueMap({ 1: value }), children })
|
|
105
|
+
}
|
|
105
106
|
);
|
|
106
107
|
};
|
|
107
108
|
const SearchContextProvider = (props) => {
|
|
@@ -115,7 +116,7 @@ const SearchContextProvider = (props) => {
|
|
|
115
116
|
...propsInitialSearchState,
|
|
116
117
|
...configInitialSearchState
|
|
117
118
|
};
|
|
118
|
-
return hasParentContext && inheritParentContextIfAvailable ? /* @__PURE__ */
|
|
119
|
+
return hasParentContext && inheritParentContextIfAvailable ? /* @__PURE__ */ jsx(Fragment, { children }) : /* @__PURE__ */ jsx(LocalSearchContext, { initialState: searchContextInitialState, children });
|
|
119
120
|
};
|
|
120
121
|
|
|
121
122
|
export { SearchContextProvider, useSearch, useSearchContextCheck };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SearchContext.esm.js","sources":["../../src/context/SearchContext.tsx"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { isEqual } from 'lodash';\nimport React, {\n PropsWithChildren,\n useCallback,\n useContext,\n useEffect,\n useState,\n} from 'react';\nimport useAsync, { AsyncState } from 'react-use/esm/useAsync';\nimport usePrevious from 'react-use/esm/usePrevious';\n\nimport {\n createVersionedContext,\n createVersionedValueMap,\n} from '@backstage/version-bridge';\nimport { JsonObject } from '@backstage/types';\nimport {\n AnalyticsContext,\n useApi,\n configApiRef,\n useAnalytics,\n} from '@backstage/core-plugin-api';\nimport { SearchResultSet } from '@backstage/plugin-search-common';\n\nimport { searchApiRef } from '../api';\n\n/**\n *\n * @public\n */\nexport type SearchContextValue = {\n result: AsyncState<SearchResultSet>;\n setTerm: React.Dispatch<React.SetStateAction<string>>;\n setTypes: React.Dispatch<React.SetStateAction<string[]>>;\n setFilters: React.Dispatch<React.SetStateAction<JsonObject>>;\n setPageLimit: React.Dispatch<React.SetStateAction<number | undefined>>;\n setPageCursor: React.Dispatch<React.SetStateAction<string | undefined>>;\n fetchNextPage?: React.DispatchWithoutAction;\n fetchPreviousPage?: React.DispatchWithoutAction;\n} & SearchContextState;\n\n/**\n *\n * @public\n */\nexport type SearchContextState = {\n term: string;\n types: string[];\n filters: JsonObject;\n pageLimit?: number;\n pageCursor?: string;\n};\n\nconst SearchContext = createVersionedContext<{\n 1: SearchContextValue;\n}>('search-context');\n\n/**\n * @public\n *\n * React hook which provides the search context\n */\nexport const useSearch = () => {\n const context = useContext(SearchContext);\n if (!context) {\n throw new Error('useSearch must be used within a SearchContextProvider');\n }\n\n const value = context.atVersion(1);\n if (!value) {\n throw new Error('No SearchContext v1 found');\n }\n return value;\n};\n\n/**\n * @public\n *\n * React hook which checks for an existing search context\n */\nexport const useSearchContextCheck = () => {\n const context = useContext(SearchContext);\n return context !== undefined;\n};\n\n/**\n * The initial state of `SearchContextProvider`.\n *\n */\nconst defaultInitialSearchState: SearchContextState = {\n term: '',\n types: [],\n filters: {},\n pageLimit: undefined,\n pageCursor: undefined,\n};\n\nconst useSearchContextValue = (\n initialValue: SearchContextState = defaultInitialSearchState,\n) => {\n const searchApi = useApi(searchApiRef);\n const analytics = useAnalytics();\n\n const [term, setTerm] = useState<string>(initialValue.term);\n const [types, setTypes] = useState<string[]>(initialValue.types);\n const [filters, setFilters] = useState<JsonObject>(initialValue.filters);\n const [pageLimit, setPageLimit] = useState<number | undefined>(\n initialValue.pageLimit,\n );\n const [pageCursor, setPageCursor] = useState<string | undefined>(\n initialValue.pageCursor,\n );\n\n const prevTerm = usePrevious(term);\n const prevFilters = usePrevious(filters);\n\n const result = useAsync(async () => {\n const resultSet = await searchApi.query({\n term,\n types,\n filters,\n pageLimit,\n pageCursor,\n });\n if (term) {\n analytics.captureEvent('search', term, {\n value: resultSet.numberOfResults,\n });\n }\n return resultSet;\n }, [term, types, filters, pageLimit, pageCursor]);\n\n const hasNextPage =\n !result.loading && !result.error && result.value?.nextPageCursor;\n const hasPreviousPage =\n !result.loading && !result.error && result.value?.previousPageCursor;\n\n const fetchNextPage = useCallback(() => {\n setPageCursor(result.value?.nextPageCursor);\n }, [result.value?.nextPageCursor]);\n\n const fetchPreviousPage = useCallback(() => {\n setPageCursor(result.value?.previousPageCursor);\n }, [result.value?.previousPageCursor]);\n\n useEffect(() => {\n // Any time a term is reset, we want to start from page 0.\n // Only reset the term if it has been modified by the user at least once, the initial state must not reset the term.\n if (prevTerm !== undefined && term !== prevTerm) {\n setPageCursor(undefined);\n }\n }, [term, prevTerm, setPageCursor]);\n\n useEffect(() => {\n // Any time filters is reset, we want to start from page 0.\n // Only reset the page if it has been modified by the user at least once, the initial state must not reset the page.\n if (prevFilters !== undefined && !isEqual(filters, prevFilters)) {\n setPageCursor(undefined);\n }\n }, [filters, prevFilters, setPageCursor]);\n\n const value: SearchContextValue = {\n result,\n term,\n setTerm,\n types,\n setTypes,\n filters,\n setFilters,\n pageLimit,\n setPageLimit,\n pageCursor,\n setPageCursor,\n fetchNextPage: hasNextPage ? fetchNextPage : undefined,\n fetchPreviousPage: hasPreviousPage ? fetchPreviousPage : undefined,\n };\n\n return value;\n};\n\nexport type LocalSearchContextProps = PropsWithChildren<{\n initialState?: SearchContextState;\n}>;\n\nconst LocalSearchContext = (props: SearchContextProviderProps) => {\n const { initialState, children } = props;\n const value = useSearchContextValue(initialState);\n\n return (\n <AnalyticsContext\n attributes={{ searchTypes: value.types.sort().join(',') }}\n >\n <SearchContext.Provider value={createVersionedValueMap({ 1: value })}>\n {children}\n </SearchContext.Provider>\n </AnalyticsContext>\n );\n};\n\n/**\n * Props for {@link SearchContextProvider}\n *\n * @public\n */\nexport type SearchContextProviderProps =\n | PropsWithChildren<{\n /**\n * State initialized by a local context.\n */\n initialState?: SearchContextState;\n /**\n * Do not create an inheritance from the parent, as a new initial state must be defined in a local context.\n */\n inheritParentContextIfAvailable?: never;\n }>\n | PropsWithChildren<{\n /**\n * Does not accept initial state since it is already initialized by parent context.\n */\n initialState?: never;\n /**\n * If true, don't create a child context if there is a parent one already defined.\n * @remarks Defaults to false.\n */\n inheritParentContextIfAvailable?: boolean;\n }>;\n\n/**\n * @public\n * Search context provider which gives you access to shared state between search components\n */\nexport const SearchContextProvider = (props: SearchContextProviderProps) => {\n const { initialState, inheritParentContextIfAvailable, children } = props;\n const hasParentContext = useSearchContextCheck();\n\n const configApi = useApi(configApiRef);\n\n const propsInitialSearchState = initialState ?? {};\n\n const configInitialSearchState = configApi.has('search.query.pageLimit')\n ? { pageLimit: configApi.getNumber('search.query.pageLimit') }\n : {};\n\n const searchContextInitialState = {\n ...defaultInitialSearchState,\n ...propsInitialSearchState,\n ...configInitialSearchState,\n };\n\n return hasParentContext && inheritParentContextIfAvailable ? (\n <>{children}</>\n ) : (\n <LocalSearchContext initialState={searchContextInitialState}>\n {children}\n </LocalSearchContext>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SearchContext.esm.js","sources":["../../src/context/SearchContext.tsx"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { isEqual } from 'lodash';\nimport {\n SetStateAction,\n Dispatch,\n DispatchWithoutAction,\n PropsWithChildren,\n useCallback,\n useContext,\n useEffect,\n useState,\n} from 'react';\nimport useAsync, { AsyncState } from 'react-use/esm/useAsync';\nimport usePrevious from 'react-use/esm/usePrevious';\n\nimport {\n createVersionedContext,\n createVersionedValueMap,\n} from '@backstage/version-bridge';\nimport { JsonObject } from '@backstage/types';\nimport {\n AnalyticsContext,\n useApi,\n configApiRef,\n useAnalytics,\n} from '@backstage/core-plugin-api';\nimport { SearchResultSet } from '@backstage/plugin-search-common';\n\nimport { searchApiRef } from '../api';\n\n/**\n *\n * @public\n */\nexport type SearchContextValue = {\n result: AsyncState<SearchResultSet>;\n setTerm: Dispatch<SetStateAction<string>>;\n setTypes: Dispatch<SetStateAction<string[]>>;\n setFilters: Dispatch<SetStateAction<JsonObject>>;\n setPageLimit: Dispatch<SetStateAction<number | undefined>>;\n setPageCursor: Dispatch<SetStateAction<string | undefined>>;\n fetchNextPage?: DispatchWithoutAction;\n fetchPreviousPage?: DispatchWithoutAction;\n} & SearchContextState;\n\n/**\n *\n * @public\n */\nexport type SearchContextState = {\n term: string;\n types: string[];\n filters: JsonObject;\n pageLimit?: number;\n pageCursor?: string;\n};\n\nconst SearchContext = createVersionedContext<{\n 1: SearchContextValue;\n}>('search-context');\n\n/**\n * @public\n *\n * React hook which provides the search context\n */\nexport const useSearch = () => {\n const context = useContext(SearchContext);\n if (!context) {\n throw new Error('useSearch must be used within a SearchContextProvider');\n }\n\n const value = context.atVersion(1);\n if (!value) {\n throw new Error('No SearchContext v1 found');\n }\n return value;\n};\n\n/**\n * @public\n *\n * React hook which checks for an existing search context\n */\nexport const useSearchContextCheck = () => {\n const context = useContext(SearchContext);\n return context !== undefined;\n};\n\n/**\n * The initial state of `SearchContextProvider`.\n *\n */\nconst defaultInitialSearchState: SearchContextState = {\n term: '',\n types: [],\n filters: {},\n pageLimit: undefined,\n pageCursor: undefined,\n};\n\nconst useSearchContextValue = (\n initialValue: SearchContextState = defaultInitialSearchState,\n) => {\n const searchApi = useApi(searchApiRef);\n const analytics = useAnalytics();\n\n const [term, setTerm] = useState<string>(initialValue.term);\n const [types, setTypes] = useState<string[]>(initialValue.types);\n const [filters, setFilters] = useState<JsonObject>(initialValue.filters);\n const [pageLimit, setPageLimit] = useState<number | undefined>(\n initialValue.pageLimit,\n );\n const [pageCursor, setPageCursor] = useState<string | undefined>(\n initialValue.pageCursor,\n );\n\n const prevTerm = usePrevious(term);\n const prevFilters = usePrevious(filters);\n\n const result = useAsync(async () => {\n const resultSet = await searchApi.query({\n term,\n types,\n filters,\n pageLimit,\n pageCursor,\n });\n if (term) {\n analytics.captureEvent('search', term, {\n value: resultSet.numberOfResults,\n });\n }\n return resultSet;\n }, [term, types, filters, pageLimit, pageCursor]);\n\n const hasNextPage =\n !result.loading && !result.error && result.value?.nextPageCursor;\n const hasPreviousPage =\n !result.loading && !result.error && result.value?.previousPageCursor;\n\n const fetchNextPage = useCallback(() => {\n setPageCursor(result.value?.nextPageCursor);\n }, [result.value?.nextPageCursor]);\n\n const fetchPreviousPage = useCallback(() => {\n setPageCursor(result.value?.previousPageCursor);\n }, [result.value?.previousPageCursor]);\n\n useEffect(() => {\n // Any time a term is reset, we want to start from page 0.\n // Only reset the term if it has been modified by the user at least once, the initial state must not reset the term.\n if (prevTerm !== undefined && term !== prevTerm) {\n setPageCursor(undefined);\n }\n }, [term, prevTerm, setPageCursor]);\n\n useEffect(() => {\n // Any time filters is reset, we want to start from page 0.\n // Only reset the page if it has been modified by the user at least once, the initial state must not reset the page.\n if (prevFilters !== undefined && !isEqual(filters, prevFilters)) {\n setPageCursor(undefined);\n }\n }, [filters, prevFilters, setPageCursor]);\n\n const value: SearchContextValue = {\n result,\n term,\n setTerm,\n types,\n setTypes,\n filters,\n setFilters,\n pageLimit,\n setPageLimit,\n pageCursor,\n setPageCursor,\n fetchNextPage: hasNextPage ? fetchNextPage : undefined,\n fetchPreviousPage: hasPreviousPage ? fetchPreviousPage : undefined,\n };\n\n return value;\n};\n\nexport type LocalSearchContextProps = PropsWithChildren<{\n initialState?: SearchContextState;\n}>;\n\nconst LocalSearchContext = (props: SearchContextProviderProps) => {\n const { initialState, children } = props;\n const value = useSearchContextValue(initialState);\n\n return (\n <AnalyticsContext\n attributes={{ searchTypes: value.types.sort().join(',') }}\n >\n <SearchContext.Provider value={createVersionedValueMap({ 1: value })}>\n {children}\n </SearchContext.Provider>\n </AnalyticsContext>\n );\n};\n\n/**\n * Props for {@link SearchContextProvider}\n *\n * @public\n */\nexport type SearchContextProviderProps =\n | PropsWithChildren<{\n /**\n * State initialized by a local context.\n */\n initialState?: SearchContextState;\n /**\n * Do not create an inheritance from the parent, as a new initial state must be defined in a local context.\n */\n inheritParentContextIfAvailable?: never;\n }>\n | PropsWithChildren<{\n /**\n * Does not accept initial state since it is already initialized by parent context.\n */\n initialState?: never;\n /**\n * If true, don't create a child context if there is a parent one already defined.\n * @remarks Defaults to false.\n */\n inheritParentContextIfAvailable?: boolean;\n }>;\n\n/**\n * @public\n * Search context provider which gives you access to shared state between search components\n */\nexport const SearchContextProvider = (props: SearchContextProviderProps) => {\n const { initialState, inheritParentContextIfAvailable, children } = props;\n const hasParentContext = useSearchContextCheck();\n\n const configApi = useApi(configApiRef);\n\n const propsInitialSearchState = initialState ?? {};\n\n const configInitialSearchState = configApi.has('search.query.pageLimit')\n ? { pageLimit: configApi.getNumber('search.query.pageLimit') }\n : {};\n\n const searchContextInitialState = {\n ...defaultInitialSearchState,\n ...propsInitialSearchState,\n ...configInitialSearchState,\n };\n\n return hasParentContext && inheritParentContextIfAvailable ? (\n <>{children}</>\n ) : (\n <LocalSearchContext initialState={searchContextInitialState}>\n {children}\n </LocalSearchContext>\n );\n};\n"],"names":[],"mappings":"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"}
|
package/dist/extensions.esm.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useCallback, createElement, Fragment, isValidElement, cloneElement } from 'react';
|
|
2
3
|
import { useAnalytics, createReactExtension, useElementFilter, getComponentData } from '@backstage/core-plugin-api';
|
|
3
4
|
import List from '@material-ui/core/List';
|
|
4
5
|
import ListItem from '@material-ui/core/ListItem';
|
|
@@ -41,15 +42,15 @@ const SearchResultListItemExtension = (props) => {
|
|
|
41
42
|
value: rank
|
|
42
43
|
});
|
|
43
44
|
}, [rank, result, noTrack, analytics]);
|
|
44
|
-
return /* @__PURE__ */
|
|
45
|
+
return /* @__PURE__ */ jsx(
|
|
45
46
|
ListItem,
|
|
46
47
|
{
|
|
47
48
|
divider: true,
|
|
48
49
|
alignItems,
|
|
49
50
|
onClickCapture: handleClickCapture,
|
|
50
|
-
...rest
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
...rest,
|
|
52
|
+
children
|
|
53
|
+
}
|
|
53
54
|
);
|
|
54
55
|
};
|
|
55
56
|
const createSearchResultListItemExtension = (options) => {
|
|
@@ -58,14 +59,14 @@ const createSearchResultListItemExtension = (options) => {
|
|
|
58
59
|
name,
|
|
59
60
|
component: {
|
|
60
61
|
lazy: () => component().then(
|
|
61
|
-
(type) => (props) => /* @__PURE__ */
|
|
62
|
+
(type) => (props) => /* @__PURE__ */ jsx(
|
|
62
63
|
SearchResultListItemExtension,
|
|
63
64
|
{
|
|
64
65
|
rank: props.rank,
|
|
65
66
|
result: props.result,
|
|
66
|
-
noTrack: props.noTrack
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
noTrack: props.noTrack,
|
|
68
|
+
children: createElement(type, props)
|
|
69
|
+
}
|
|
69
70
|
)
|
|
70
71
|
)
|
|
71
72
|
},
|
|
@@ -90,21 +91,21 @@ const useSearchResultListItemExtensions = (children) => {
|
|
|
90
91
|
elements,
|
|
91
92
|
result
|
|
92
93
|
);
|
|
93
|
-
return /* @__PURE__ */
|
|
94
|
+
return /* @__PURE__ */ jsx(Fragment, { children: element ?? /* @__PURE__ */ jsx(
|
|
94
95
|
SearchResultListItemExtension,
|
|
95
96
|
{
|
|
96
97
|
rank: result.rank,
|
|
97
|
-
result: result.document
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
));
|
|
98
|
+
result: result.document,
|
|
99
|
+
children: /* @__PURE__ */ jsx(
|
|
100
|
+
HigherOrderDefaultResultListItem,
|
|
101
|
+
{
|
|
102
|
+
rank: result.rank,
|
|
103
|
+
highlight: result.highlight,
|
|
104
|
+
result: result.document
|
|
105
|
+
}
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
) }, key);
|
|
108
109
|
},
|
|
109
110
|
[elements]
|
|
110
111
|
);
|
|
@@ -112,7 +113,7 @@ const useSearchResultListItemExtensions = (children) => {
|
|
|
112
113
|
const SearchResultListItemExtensions = (props) => {
|
|
113
114
|
const { results, children, ...rest } = props;
|
|
114
115
|
const render = useSearchResultListItemExtensions(children);
|
|
115
|
-
return /* @__PURE__ */
|
|
116
|
+
return /* @__PURE__ */ jsx(List, { ...rest, children: results.map(render) });
|
|
116
117
|
};
|
|
117
118
|
|
|
118
119
|
export { SearchResultListItemExtension, SearchResultListItemExtensions, createSearchResultListItemExtension, useSearchResultListItemExtensions };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extensions.esm.js","sources":["../src/extensions.tsx"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport
|
|
1
|
+
{"version":3,"file":"extensions.esm.js","sources":["../src/extensions.tsx"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Fragment,\n ReactNode,\n PropsWithChildren,\n isValidElement,\n createElement,\n cloneElement,\n useCallback,\n} from 'react';\n\nimport {\n getComponentData,\n useElementFilter,\n Extension,\n createReactExtension,\n useAnalytics,\n} from '@backstage/core-plugin-api';\nimport { SearchDocument, SearchResult } from '@backstage/plugin-search-common';\n\nimport List, { ListProps } from '@material-ui/core/List';\nimport ListItem, { ListItemProps } from '@material-ui/core/ListItem';\n\nimport { DefaultResultListItem } from './components/DefaultResultListItem';\n\n/**\n * @internal\n * Key for result extensions.\n */\nconst SEARCH_RESULT_LIST_ITEM_EXTENSION =\n 'search.results.list.items.extensions.v1';\n\n/**\n * @internal\n * Returns the first extension element found for a given result, and null otherwise.\n * @param elements - All extension elements.\n * @param result - The search result.\n */\nconst findSearchResultListItemExtensionElement = (\n elements: ReactNode[],\n result: SearchResult,\n) => {\n for (const element of elements) {\n if (!isValidElement(element)) continue;\n const predicate = getComponentData<(result: SearchResult) => boolean>(\n element,\n SEARCH_RESULT_LIST_ITEM_EXTENSION,\n );\n if (!predicate?.(result)) continue;\n return cloneElement(element, {\n rank: result.rank,\n highlight: result.highlight,\n result: result.document,\n // Use props in situations where a consumer is manually rendering the extension\n ...element.props,\n });\n }\n return null;\n};\n\n/**\n * @public\n * Extends props for any search result list item extension\n */\nexport type SearchResultListItemExtensionProps<Props extends {} = {}> = Props &\n PropsWithChildren<\n {\n rank?: number;\n result?: SearchDocument;\n noTrack?: boolean;\n } & Omit<ListItemProps, 'button'>\n >;\n\n/**\n * @internal\n * Extends children with extension capabilities.\n * @param props - see {@link SearchResultListItemExtensionProps}.\n */\nexport const SearchResultListItemExtension = (\n props: SearchResultListItemExtensionProps,\n) => {\n const {\n rank,\n result,\n noTrack,\n children,\n alignItems = 'flex-start',\n ...rest\n } = props;\n const analytics = useAnalytics();\n\n const handleClickCapture = useCallback(() => {\n if (noTrack) return;\n if (!result) return;\n analytics.captureEvent('discover', result.title, {\n attributes: { to: result.location },\n value: rank,\n });\n }, [rank, result, noTrack, analytics]);\n\n return (\n <ListItem\n divider\n alignItems={alignItems}\n onClickCapture={handleClickCapture}\n {...rest}\n >\n {children}\n </ListItem>\n );\n};\n\n/**\n * @public\n * Options for {@link createSearchResultListItemExtension}.\n */\nexport type SearchResultListItemExtensionOptions<\n Component extends (props: any) => JSX.Element | null,\n> = {\n /**\n * The extension name.\n */\n name: string;\n /**\n * The extension component.\n */\n component: () => Promise<Component>;\n /**\n * When an extension defines a predicate, it returns true if the result should be rendered by that extension.\n * Defaults to a predicate that returns true, which means it renders all sorts of results.\n */\n predicate?: (result: SearchResult) => boolean;\n};\n\n/**\n * @public\n * Creates a search result item extension.\n * @param options - The extension options, see {@link SearchResultListItemExtensionOptions} for more details.\n */\nexport const createSearchResultListItemExtension = <\n Component extends (props: any) => JSX.Element | null,\n>(\n options: SearchResultListItemExtensionOptions<Component>,\n): Extension<Component> => {\n const { name, component, predicate = () => true } = options;\n\n return createReactExtension<Component>({\n name,\n component: {\n lazy: () =>\n component().then(\n type =>\n (props => (\n <SearchResultListItemExtension\n rank={props.rank}\n result={props.result}\n noTrack={props.noTrack}\n >\n {createElement(type, props)}\n </SearchResultListItemExtension>\n )) as Component,\n ),\n },\n data: {\n [SEARCH_RESULT_LIST_ITEM_EXTENSION]: predicate,\n },\n });\n};\n\n/**\n * @public\n * Returns a function that renders a result using extensions.\n */\nexport const useSearchResultListItemExtensions = (children: ReactNode) => {\n const elements = useElementFilter(\n children,\n collection => {\n return collection\n .selectByComponentData({\n key: SEARCH_RESULT_LIST_ITEM_EXTENSION,\n })\n .getElements();\n },\n [children],\n );\n\n return useCallback(\n (result: SearchResult, key?: number) => {\n const element = findSearchResultListItemExtensionElement(\n elements,\n result,\n );\n\n return (\n <Fragment key={key}>\n {element ?? (\n <SearchResultListItemExtension\n rank={result.rank}\n result={result.document}\n >\n <DefaultResultListItem\n rank={result.rank}\n highlight={result.highlight}\n result={result.document}\n />\n </SearchResultListItemExtension>\n )}\n </Fragment>\n );\n },\n [elements],\n );\n};\n\n/**\n * @public\n * Props for {@link SearchResultListItemExtensions}\n */\nexport type SearchResultListItemExtensionsProps = Omit<ListProps, 'results'> & {\n /**\n * Search result list.\n */\n results: SearchResult[];\n};\n\n/**\n * @public\n * Render results using search extensions.\n * @param props - see {@link SearchResultListItemExtensionsProps}\n */\nexport const SearchResultListItemExtensions = (\n props: SearchResultListItemExtensionsProps,\n) => {\n const { results, children, ...rest } = props;\n const render = useSearchResultListItemExtensions(children);\n return <List {...rest}>{results.map(render)}</List>;\n};\n"],"names":["DefaultResultListItem"],"mappings":"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"}
|