123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- import {Component} from 'react';
- import {RouteComponentProps} from 'react-router';
- import flattenDepth from 'lodash/flattenDepth';
- import {Organization, Project} from 'sentry/types';
- import {createFuzzySearch, Fuse} from 'sentry/utils/fuzzySearch';
- import replaceRouterParams from 'sentry/utils/replaceRouterParams';
- import withLatestContext from 'sentry/utils/withLatestContext';
- import accountSettingsNavigation from 'sentry/views/settings/account/navigationConfiguration';
- import organizationSettingsNavigation from 'sentry/views/settings/organization/navigationConfiguration';
- import projectSettingsNavigation from 'sentry/views/settings/project/navigationConfiguration';
- import {NavigationItem} from 'sentry/views/settings/types';
- import {ChildProps, ResultItem} from './types';
- import {strGetFn} from './utils';
- type Config =
- | typeof accountSettingsNavigation
- | typeof organizationSettingsNavigation
- | typeof projectSettingsNavigation;
- // XXX(epurkhiser): We use the context in mapFunc to handle both producing the
- // NavigationSection list AND filtering out items in the sections that should
- // not be shown using the `show` attribute of the NavigationItem
- type Context = Parameters<Extract<Config, Function>>[0] &
- Parameters<Extract<NavigationItem['show'], Function>>[0];
- /**
- * navigation configuration can currently be either:
- *
- * - an array of {name: string, items: Array<{NavItem}>} OR
- * - a function that returns the above
- * (some navigation items require additional context, e.g. a badge based on
- * a `project` property)
- *
- * We need to go through all navigation configurations and get a flattened list
- * of all navigation item objects
- */
- const mapFunc = (config: Config, context: Context | null = null) =>
- (Array.isArray(config) ? config : context !== null ? config(context) : []).map(
- ({items}) =>
- items.filter(({show}) =>
- typeof show === 'function' && context !== null ? show(context) : true
- )
- );
- type DefaultProps = {
- /**
- * Fuse configuration for searching NavigationItem's
- */
- searchOptions: Fuse.IFuseOptions<NavigationItem>;
- };
- type Props = RouteComponentProps<{}, {}> &
- DefaultProps & {
- /**
- * Render function that renders the route matches
- */
- children: (props: ChildProps) => React.ReactNode;
- /**
- * The string to search the navigation routes for
- */
- query: string;
- organization?: Organization;
- project?: Project;
- };
- type State = {
- /**
- * A Fuse instance configured to search NavigationItem's
- */
- fuzzy: undefined | null | Fuse<NavigationItem>;
- };
- class RouteSource extends Component<Props, State> {
- static defaultProps: DefaultProps = {
- searchOptions: {},
- };
- state: State = {
- fuzzy: undefined,
- };
- componentDidMount() {
- this.createSearch();
- }
- componentDidUpdate(prevProps: Props) {
- if (
- prevProps.project === this.props.project &&
- prevProps.organization === this.props.organization
- ) {
- return;
- }
- this.createSearch();
- }
- async createSearch() {
- const {project, organization} = this.props;
- const context = {
- project,
- organization,
- access: new Set(organization?.access ?? []),
- features: new Set(project?.features ?? []),
- } as Context;
- const searchMap = flattenDepth<NavigationItem>(
- [
- mapFunc(accountSettingsNavigation, context),
- mapFunc(projectSettingsNavigation, context),
- mapFunc(organizationSettingsNavigation, context),
- ],
- 2
- );
- const options = {
- ...this.props.searchOptions,
- keys: ['title', 'description'],
- getFn: strGetFn,
- };
- const fuzzy = await createFuzzySearch(searchMap ?? [], options);
- this.setState({fuzzy});
- }
- render() {
- const {query, params, children} = this.props;
- const {fuzzy} = this.state;
- const results =
- fuzzy?.search(query).map(({item, ...rest}) => ({
- item: {
- ...item,
- sourceType: 'route',
- resultType: 'route',
- to: replaceRouterParams(item.path, params),
- } as ResultItem,
- ...rest,
- })) ?? [];
- return children({
- isLoading: this.state.fuzzy === undefined,
- results,
- });
- }
- }
- export default withLatestContext(RouteSource);
- export {RouteSource};
|