[arvados-workbench2] created: 2.4.0-362-gbe5e418e

git repository hosting git at public.arvados.org
Fri Dec 9 04:31:38 UTC 2022


        at  be5e418e1bb0ff54401b71c2cf6865222d182c18 (commit)


commit be5e418e1bb0ff54401b71c2cf6865222d182c18
Author: Peter Amstutz <peter.amstutz at curii.com>
Date:   Thu Dec 8 23:31:10 2022 -0500

    19783: All done
    
    Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <peter.amstutz at curii.com>

diff --git a/src/store/side-panel-tree/side-panel-tree-actions.ts b/src/store/side-panel-tree/side-panel-tree-actions.ts
index dd56b428..f6015fbf 100644
--- a/src/store/side-panel-tree/side-panel-tree-actions.ts
+++ b/src/store/side-panel-tree/side-panel-tree-actions.ts
@@ -20,7 +20,7 @@ import { CategoriesListReducer } from 'common/plugintypes';
 import { pluginConfig } from 'plugins';
 
 export enum SidePanelTreeCategory {
-    PROJECTS = 'Projects',
+    PROJECTS = 'Home Projects',
     SHARED_WITH_ME = 'Shared with me',
     PUBLIC_FAVORITES = 'Public Favorites',
     FAVORITES = 'My Favorites',
diff --git a/src/store/tree-picker/tree-picker-actions.ts b/src/store/tree-picker/tree-picker-actions.ts
index e6c4e09b..cabc9f72 100644
--- a/src/store/tree-picker/tree-picker-actions.ts
+++ b/src/store/tree-picker/tree-picker-actions.ts
@@ -233,7 +233,7 @@ export const initUserProject = (pickerId: string) =>
             dispatch(receiveTreePickerData({
                 id: '',
                 pickerId,
-                data: [{ uuid, name: 'Projects' }],
+                data: [{ uuid, name: 'Home Projects' }],
                 extractNodeData: value => ({
                     id: value.uuid,
                     status: TreeNodeStatus.INITIAL,
diff --git a/src/views-components/projects-tree-picker/projects-tree-picker.tsx b/src/views-components/projects-tree-picker/projects-tree-picker.tsx
index 13f43e81..de7362d9 100644
--- a/src/views-components/projects-tree-picker/projects-tree-picker.tsx
+++ b/src/views-components/projects-tree-picker/projects-tree-picker.tsx
@@ -42,11 +42,14 @@ interface ProjectsTreePickerActionProps {
     onCollectionFilter: (value: string) => void;
 }
 
-const mapStateToProps = (state: RootState, props: ToplevelPickerProps): ProjectsTreePickerSearchProps => ({
-    projectSearch: state.treePickerSearch.projectSearchValues[props.pickerId],
-    collectionFilter: state.treePickerSearch.collectionFilterValues[props.pickerId],
-    ...props
-});
+const mapStateToProps = (state: RootState, props: ToplevelPickerProps): ProjectsTreePickerSearchProps => {
+    const { search } = getProjectsTreePickerIds(props.pickerId);
+    return {
+        ...props,
+        projectSearch: state.treePickerSearch.projectSearchValues[search],
+        collectionFilter: state.treePickerSearch.collectionFilterValues[search],
+    };
+};
 
 const mapDispatchToProps = (dispatch: Dispatch, props: ToplevelPickerProps): (ProjectsTreePickerActionProps & DispatchProp) => {
     const { home, shared, favorites, publicFavorites, search } = getProjectsTreePickerIds(props.pickerId);
@@ -74,17 +77,21 @@ const mapDispatchToProps = (dispatch: Dispatch, props: ToplevelPickerProps): (Pr
     }
 };
 
-type CssRules = 'pickerHeight' | 'searchFlex';
+type CssRules = 'pickerHeight' | 'searchFlex' | 'scrolledBox';
 
 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     pickerHeight: {
-        height: "80vh"
+        height: "70vh"
     },
     searchFlex: {
         display: "flex",
         justifyContent: "space-around",
         paddingBottom: "1em"
     },
+    scrolledBox: {
+        height: "100%",
+        overflow: "scroll"
+    }
 });
 
 type ProjectsTreePickerCombinedProps = ToplevelPickerProps & ProjectsTreePickerSearchProps & ProjectsTreePickerActionProps & DispatchProp & WithStyles<CssRules>;
@@ -128,34 +135,39 @@ export const ProjectsTreePicker = connect(mapStateToProps, mapDispatchToProps)(
                     includeFiles: this.props.includeFiles,
                     showSelection: this.props.showSelection,
                     options: this.props.options,
+                    toggleItemActive: this.props.toggleItemActive,
+                    toggleItemSelection: this.props.toggleItemSelection,
                     relatedTreePickers,
                     disableActivation,
                 };
                 return <div className={this.props.classes.pickerHeight} >
                     <span className={this.props.classes.searchFlex}>
-                        <SearchInput value="" label="Search Projects" selfClearProp='' onSearch={onProjectSearch} debounce={200} />
-                        <SearchInput value="" label="Filter Collections inside Projects" selfClearProp='' onSearch={onCollectionFilter} debounce={200} />
+                        <SearchInput value="" label="Search all Projects" selfClearProp='' onSearch={onProjectSearch} debounce={200} />
+                        {this.props.includeCollections &&
+                            <SearchInput value="" label="Filter Collections inside Projects" selfClearProp='' onSearch={onCollectionFilter} debounce={200} />}
                     </span>
 
-                    {this.props.projectSearch !== "" ?
-                        <div data-cy="projects-tree-search-picker">
-                            <SearchProjectsPicker {...p} pickerId={search} />
-                        </div>
-                        :
-                        <>
-                            <div data-cy="projects-tree-home-tree-picker">
-                                <HomeTreePicker {...p} pickerId={home} />
-                            </div>
-                            <div data-cy="projects-tree-shared-tree-picker">
-                                <SharedTreePicker {...p} pickerId={shared} />
-                            </div>
-                            <div data-cy="projects-tree-public-favourites-tree-picker">
-                                <PublicFavoritesTreePicker {...p} pickerId={publicFavorites} />
-                            </div>
-                            <div data-cy="projects-tree-favourites-tree-picker">
-                                <FavoritesTreePicker {...p} pickerId={favorites} />
+                    <div className={this.props.classes.scrolledBox}>
+                        {this.props.projectSearch ?
+                            <div data-cy="projects-tree-search-picker">
+                                <SearchProjectsPicker {...p} pickerId={search} />
                             </div>
-                        </>}
+                            :
+                            <>
+                                <div data-cy="projects-tree-home-tree-picker">
+                                    <HomeTreePicker {...p} pickerId={home} />
+                                </div>
+                                <div data-cy="projects-tree-shared-tree-picker">
+                                    <SharedTreePicker {...p} pickerId={shared} />
+                                </div>
+                                <div data-cy="projects-tree-public-favourites-tree-picker">
+                                    <PublicFavoritesTreePicker {...p} pickerId={publicFavorites} />
+                                </div>
+                                <div data-cy="projects-tree-favourites-tree-picker">
+                                    <FavoritesTreePicker {...p} pickerId={favorites} />
+                                </div>
+                            </>}
+                    </div>
                 </div >;
             }
         }));

commit 2787be4c060410a37dc3cf4b512ccb0561d5c394
Author: Peter Amstutz <peter.amstutz at curii.com>
Date:   Thu Dec 8 22:03:23 2022 -0500

    19783: Project search almost works
    
    Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <peter.amstutz at curii.com>

diff --git a/src/store/auth/auth-action.ts b/src/store/auth/auth-action.ts
index 7fc9df77..6eb8356f 100644
--- a/src/store/auth/auth-action.ts
+++ b/src/store/auth/auth-action.ts
@@ -92,6 +92,9 @@ export const saveApiToken = (token: string) => async (dispatch: Dispatch, getSta
 
     // If the token is from a LoginCluster federation, get user & token data
     // from the token issuing cluster.
+    if (!config) {
+        return;
+    }
     const lc = (config as Config).loginCluster
     const tokenCluster = tokenParts.length === 3
         ? tokenParts[1].substring(0, 5)
@@ -127,7 +130,7 @@ export const getNewExtraToken = (reuseStored: boolean = false) =>
                 const client = await svc.apiClientAuthorizationService.get('current');
                 dispatch(authActions.SET_EXTRA_TOKEN({
                     extraApiToken: extraToken,
-                    extraApiTokenExpiration: client.expiresAt ? new Date(client.expiresAt): undefined,
+                    extraApiTokenExpiration: client.expiresAt ? new Date(client.expiresAt) : undefined,
                 }));
                 return extraToken;
             } catch (e) {
@@ -145,7 +148,7 @@ export const getNewExtraToken = (reuseStored: boolean = false) =>
             const newExtraToken = getTokenV2(client);
             dispatch(authActions.SET_EXTRA_TOKEN({
                 extraApiToken: newExtraToken,
-                extraApiTokenExpiration: client.expiresAt ? new Date(client.expiresAt): undefined,
+                extraApiTokenExpiration: client.expiresAt ? new Date(client.expiresAt) : undefined,
             }));
             return newExtraToken;
         } catch {
diff --git a/src/store/tree-picker/tree-picker-actions.ts b/src/store/tree-picker/tree-picker-actions.ts
index b7710494..e6c4e09b 100644
--- a/src/store/tree-picker/tree-picker-actions.ts
+++ b/src/store/tree-picker/tree-picker-actions.ts
@@ -44,7 +44,6 @@ export interface LoadProjectParams {
     includeCollections?: boolean;
     includeFiles?: boolean;
     includeFilterGroups?: boolean;
-    loadShared?: boolean;
     options?: { showOnlyOwned: boolean; showOnlyWritable: boolean; };
 }
 
@@ -60,7 +59,8 @@ export const getProjectsTreePickerIds = (pickerId: string) => ({
     home: `${pickerId}_home`,
     shared: `${pickerId}_shared`,
     favorites: `${pickerId}_favorites`,
-    publicFavorites: `${pickerId}_publicFavorites`
+    publicFavorites: `${pickerId}_publicFavorites`,
+    search: `${pickerId}_search`,
 });
 
 export const getAllNodes = <Value>(pickerId: string, filter = (node: TreeNode<Value>) => true) => (state: TreePicker) =>
@@ -88,11 +88,12 @@ export const getSelectedNodes = <Value>(pickerId: string) => (state: TreePicker)
 
 export const initProjectsTreePicker = (pickerId: string) =>
     async (dispatch: Dispatch, _: () => RootState, services: ServiceRepository) => {
-        const { home, shared, favorites, publicFavorites } = getProjectsTreePickerIds(pickerId);
+        const { home, shared, favorites, publicFavorites, search } = getProjectsTreePickerIds(pickerId);
         dispatch<any>(initUserProject(home));
         dispatch<any>(initSharedProject(shared));
         dispatch<any>(initFavoritesProject(favorites));
         dispatch<any>(initPublicFavoritesProject(publicFavorites));
+        dispatch<any>(initSearchProject(search));
     };
 
 interface ReceiveTreePickerDataParams<T> {
@@ -116,35 +117,37 @@ export const receiveTreePickerData = <T>(params: ReceiveTreePickerDataParams<T>)
 interface LoadProjectParamsWithId extends LoadProjectParams {
     id: string;
     pickerId: string;
-    includeCollections?: boolean;
-    includeFiles?: boolean;
-    includeFilterGroups?: boolean;
     loadShared?: boolean;
-    options?: { showOnlyOwned: boolean; showOnlyWritable: boolean; };
+    searchProjects?: boolean;
 }
 
 export const loadProject = (params: LoadProjectParamsWithId) =>
     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
-        const { id, pickerId, includeCollections = false, includeFiles = false, includeFilterGroups = false, loadShared = false, options } = params;
+        const { id, pickerId, includeCollections = false, includeFiles = false, includeFilterGroups = false, loadShared = false, options, searchProjects = false } = params;
 
         dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id, pickerId }));
 
-        let filterB = pipe(
-            (fb: FilterBuilder) => includeCollections
-                ? fb.addIsA('uuid', [ResourceKind.PROJECT, ResourceKind.COLLECTION])
-                : fb.addIsA('uuid', [ResourceKind.PROJECT]),
-            fb => fb.addNotIn("collections.properties.type", ["intermediate", "log"]),
-        )(new FilterBuilder());
+        let filterB = new FilterBuilder();
+
+        filterB = (includeCollections && !searchProjects)
+            ? filterB.addIsA('uuid', [ResourceKind.PROJECT, ResourceKind.COLLECTION])
+            : filterB.addIsA('uuid', [ResourceKind.PROJECT]);
 
         const state = getState();
 
         if (state.treePickerSearch.collectionFilterValues[pickerId]) {
             filterB = filterB.addILike('collections.name', state.treePickerSearch.collectionFilterValues[pickerId]);
+        } else {
+            filterB = filterB.addNotIn("collections.properties.type", ["intermediate", "log"]);
+        }
+
+        if (searchProjects && state.treePickerSearch.projectSearchValues[pickerId]) {
+            filterB = filterB.addILike('groups.name', state.treePickerSearch.projectSearchValues[pickerId]);
         }
 
         const filters = filterB.getFilters();
 
-        const { items, itemsAvailable } = await services.groupsService.contents(loadShared ? '' : id, { filters, excludeHomeProject: loadShared || undefined, limit: 1000 });
+        const { items, itemsAvailable } = await services.groupsService.contents((loadShared || searchProjects) ? '' : id, { filters, excludeHomeProject: loadShared || undefined, limit: 1000 });
 
         if (itemsAvailable > 1000) {
             items.push({
@@ -292,6 +295,22 @@ export const initPublicFavoritesProject = (pickerId: string) =>
         }));
     };
 
+export const SEARCH_PROJECT_ID = 'Search all Projects';
+export const initSearchProject = (pickerId: string) =>
+    async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
+        dispatch(receiveTreePickerData({
+            id: '',
+            pickerId,
+            data: [{ uuid: SEARCH_PROJECT_ID, name: SEARCH_PROJECT_ID }],
+            extractNodeData: value => ({
+                id: value.uuid,
+                status: TreeNodeStatus.INITIAL,
+                value,
+            }),
+        }));
+    };
+
+
 interface LoadFavoritesProjectParams {
     pickerId: string;
     includeCollections?: boolean;
diff --git a/src/views-components/projects-tree-picker/home-tree-picker.tsx b/src/views-components/projects-tree-picker/home-tree-picker.tsx
index 4e8eeda9..3133c5db 100644
--- a/src/views-components/projects-tree-picker/home-tree-picker.tsx
+++ b/src/views-components/projects-tree-picker/home-tree-picker.tsx
@@ -6,12 +6,12 @@ import { connect } from 'react-redux';
 import { ProjectsTreePicker, ProjectsTreePickerProps } from 'views-components/projects-tree-picker/generic-projects-tree-picker';
 import { Dispatch } from 'redux';
 import { loadUserProject } from 'store/tree-picker/tree-picker-actions';
-import { ProjectIcon } from 'components/icon/icon';
+import { ProjectsIcon } from 'components/icon/icon';
 
 export const HomeTreePicker = connect(() => ({
-    rootItemIcon: ProjectIcon,
+    rootItemIcon: ProjectsIcon,
 }), (dispatch: Dispatch): Pick<ProjectsTreePickerProps, 'loadRootItem'> => ({
     loadRootItem: (_, pickerId, includeCollections, includeFiles, options) => {
         dispatch<any>(loadUserProject(pickerId, includeCollections, includeFiles, options));
     },
-}))(ProjectsTreePicker);
\ No newline at end of file
+}))(ProjectsTreePicker);
diff --git a/src/views-components/projects-tree-picker/projects-tree-picker.tsx b/src/views-components/projects-tree-picker/projects-tree-picker.tsx
index 8f21784b..13f43e81 100644
--- a/src/views-components/projects-tree-picker/projects-tree-picker.tsx
+++ b/src/views-components/projects-tree-picker/projects-tree-picker.tsx
@@ -10,6 +10,7 @@ import { values, pipe } from 'lodash/fp';
 import { HomeTreePicker } from 'views-components/projects-tree-picker/home-tree-picker';
 import { SharedTreePicker } from 'views-components/projects-tree-picker/shared-tree-picker';
 import { FavoritesTreePicker } from 'views-components/projects-tree-picker/favorites-tree-picker';
+import { SearchProjectsPicker } from 'views-components/projects-tree-picker/search-projects-picker';
 import {
     getProjectsTreePickerIds, treePickerActions, treePickerSearchActions, initProjectsTreePicker,
     SHARED_PROJECT_ID, FAVORITES_PROJECT_ID
@@ -42,13 +43,13 @@ interface ProjectsTreePickerActionProps {
 }
 
 const mapStateToProps = (state: RootState, props: ToplevelPickerProps): ProjectsTreePickerSearchProps => ({
-    projectSearch: "",
-    collectionFilter: "",
+    projectSearch: state.treePickerSearch.projectSearchValues[props.pickerId],
+    collectionFilter: state.treePickerSearch.collectionFilterValues[props.pickerId],
     ...props
 });
 
 const mapDispatchToProps = (dispatch: Dispatch, props: ToplevelPickerProps): (ProjectsTreePickerActionProps & DispatchProp) => {
-    const { home, shared, favorites, publicFavorites } = getProjectsTreePickerIds(props.pickerId);
+    const { home, shared, favorites, publicFavorites, search } = getProjectsTreePickerIds(props.pickerId);
     const params = {
         includeCollections: props.includeCollections,
         includeFiles: props.includeFiles,
@@ -58,20 +59,22 @@ const mapDispatchToProps = (dispatch: Dispatch, props: ToplevelPickerProps): (Pr
     dispatch(treePickerSearchActions.SET_TREE_PICKER_LOAD_PARAMS({ pickerId: shared, params }));
     dispatch(treePickerSearchActions.SET_TREE_PICKER_LOAD_PARAMS({ pickerId: favorites, params }));
     dispatch(treePickerSearchActions.SET_TREE_PICKER_LOAD_PARAMS({ pickerId: publicFavorites, params }));
+    dispatch(treePickerSearchActions.SET_TREE_PICKER_LOAD_PARAMS({ pickerId: search, params }));
 
     return {
-        onProjectSearch: (projectSearchValue: string) => dispatch(treePickerSearchActions.SET_TREE_PICKER_PROJECT_SEARCH({ pickerId: props.pickerId, projectSearchValue })),
+        onProjectSearch: (projectSearchValue: string) => dispatch(treePickerSearchActions.SET_TREE_PICKER_PROJECT_SEARCH({ pickerId: search, projectSearchValue })),
         onCollectionFilter: (collectionFilterValue: string) => {
             dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: home, collectionFilterValue }));
             dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: shared, collectionFilterValue }));
             dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: favorites, collectionFilterValue }));
             dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: publicFavorites, collectionFilterValue }));
+            dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: search, collectionFilterValue }));
         },
         dispatch
     }
 };
 
-type CssRules = 'pickerHeight' | 'searchFlex' | 'searchPadding';
+type CssRules = 'pickerHeight' | 'searchFlex';
 
 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     pickerHeight: {
@@ -91,11 +94,12 @@ export const ProjectsTreePicker = connect(mapStateToProps, mapDispatchToProps)(
         class FileInputComponent extends React.Component<ProjectsTreePickerCombinedProps> {
 
             componentDidMount() {
-                const { home, shared, favorites, publicFavorites } = getProjectsTreePickerIds(this.props.pickerId);
+                const { home, shared, favorites, publicFavorites, search } = getProjectsTreePickerIds(this.props.pickerId);
 
                 this.props.dispatch<any>(initProjectsTreePicker(this.props.pickerId));
 
-                this.props.dispatch(treePickerSearchActions.SET_TREE_PICKER_PROJECT_SEARCH({ pickerId: this.props.pickerId, projectSearchValue: "" }));
+                this.props.dispatch(treePickerSearchActions.SET_TREE_PICKER_PROJECT_SEARCH({ pickerId: search, projectSearchValue: "" }));
+                this.props.dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: search, collectionFilterValue: "" }));
                 this.props.dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: home, collectionFilterValue: "" }));
                 this.props.dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: shared, collectionFilterValue: "" }));
                 this.props.dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: favorites, collectionFilterValue: "" }));
@@ -103,9 +107,9 @@ export const ProjectsTreePicker = connect(mapStateToProps, mapDispatchToProps)(
             }
 
             componentWillUnmount() {
-                const { home, shared, favorites, publicFavorites } = getProjectsTreePickerIds(this.props.pickerId);
+                const { home, shared, favorites, publicFavorites, search } = getProjectsTreePickerIds(this.props.pickerId);
                 // Release all the state, we don't need it to hang around forever.
-                this.props.dispatch(treePickerActions.RESET_TREE_PICKER({ pickerId: this.props.pickerId }));
+                this.props.dispatch(treePickerActions.RESET_TREE_PICKER({ pickerId: search }));
                 this.props.dispatch(treePickerActions.RESET_TREE_PICKER({ pickerId: home }));
                 this.props.dispatch(treePickerActions.RESET_TREE_PICKER({ pickerId: shared }));
                 this.props.dispatch(treePickerActions.RESET_TREE_PICKER({ pickerId: favorites }));
@@ -117,7 +121,7 @@ export const ProjectsTreePicker = connect(mapStateToProps, mapDispatchToProps)(
                 const onProjectSearch = this.props.onProjectSearch;
                 const onCollectionFilter = this.props.onCollectionFilter;
 
-                const { home, shared, favorites, publicFavorites } = getProjectsTreePickerIds(pickerId);
+                const { home, shared, favorites, publicFavorites, search } = getProjectsTreePickerIds(pickerId);
                 const relatedTreePickers = getRelatedTreePickers(pickerId);
                 const p = {
                     includeCollections: this.props.includeCollections,
@@ -132,18 +136,26 @@ export const ProjectsTreePicker = connect(mapStateToProps, mapDispatchToProps)(
                         <SearchInput value="" label="Search Projects" selfClearProp='' onSearch={onProjectSearch} debounce={200} />
                         <SearchInput value="" label="Filter Collections inside Projects" selfClearProp='' onSearch={onCollectionFilter} debounce={200} />
                     </span>
-                    <div data-cy="projects-tree-home-tree-picker">
-                        <HomeTreePicker {...p} pickerId={home} />
-                    </div>
-                    <div data-cy="projects-tree-shared-tree-picker">
-                        <SharedTreePicker {...p} pickerId={shared} />
-                    </div>
-                    <div data-cy="projects-tree-public-favourites-tree-picker">
-                        <PublicFavoritesTreePicker {...p} pickerId={publicFavorites} />
-                    </div>
-                    <div data-cy="projects-tree-favourites-tree-picker">
-                        <FavoritesTreePicker {...p} pickerId={favorites} />
-                    </div>
+
+                    {this.props.projectSearch !== "" ?
+                        <div data-cy="projects-tree-search-picker">
+                            <SearchProjectsPicker {...p} pickerId={search} />
+                        </div>
+                        :
+                        <>
+                            <div data-cy="projects-tree-home-tree-picker">
+                                <HomeTreePicker {...p} pickerId={home} />
+                            </div>
+                            <div data-cy="projects-tree-shared-tree-picker">
+                                <SharedTreePicker {...p} pickerId={shared} />
+                            </div>
+                            <div data-cy="projects-tree-public-favourites-tree-picker">
+                                <PublicFavoritesTreePicker {...p} pickerId={publicFavorites} />
+                            </div>
+                            <div data-cy="projects-tree-favourites-tree-picker">
+                                <FavoritesTreePicker {...p} pickerId={favorites} />
+                            </div>
+                        </>}
                 </div >;
             }
         }));
diff --git a/src/views-components/projects-tree-picker/shared-tree-picker.tsx b/src/views-components/projects-tree-picker/shared-tree-picker.tsx
index 201bd118..c15df6ba 100644
--- a/src/views-components/projects-tree-picker/shared-tree-picker.tsx
+++ b/src/views-components/projects-tree-picker/shared-tree-picker.tsx
@@ -7,11 +7,12 @@ import { ProjectsTreePicker, ProjectsTreePickerProps } from 'views-components/pr
 import { Dispatch } from 'redux';
 import { ShareMeIcon } from 'components/icon/icon';
 import { loadProject } from 'store/tree-picker/tree-picker-actions';
+import { SHARED_PROJECT_ID } from 'store/tree-picker/tree-picker-actions';
 
 export const SharedTreePicker = connect(() => ({
     rootItemIcon: ShareMeIcon,
 }), (dispatch: Dispatch): Pick<ProjectsTreePickerProps, 'loadRootItem'> => ({
     loadRootItem: (_, pickerId, includeCollections, includeFiles, options) => {
-        dispatch<any>(loadProject({ id: 'Shared with me', pickerId, includeCollections, includeFiles, loadShared: true, options }));
+        dispatch<any>(loadProject({ id: SHARED_PROJECT_ID, pickerId, includeCollections, includeFiles, loadShared: true, options }));
     },
-}))(ProjectsTreePicker);
\ No newline at end of file
+}))(ProjectsTreePicker);

commit 8a1dfbf52d1407b2773874c03db85a56143d20e6
Author: Peter Amstutz <peter.amstutz at curii.com>
Date:   Thu Dec 8 20:59:09 2022 -0500

    19783: Filtering collections works now
    
    Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <peter.amstutz at curii.com>

diff --git a/src/store/tree-picker/tree-picker-reducer.ts b/src/store/tree-picker/tree-picker-reducer.ts
index d4b710c9..df0ee0ad 100644
--- a/src/store/tree-picker/tree-picker-reducer.ts
+++ b/src/store/tree-picker/tree-picker-reducer.ts
@@ -72,6 +72,10 @@ const receiveNodes = <V>(nodes: Array<TreeNode<V>>) => (parent: string) => (stat
         newState = setNode({ ...parentNode, children: [] })(state);
     }
     return nodes.reduce((tree, node) => {
+        const preexistingNode = getNode(node.id)(state);
+        if (preexistingNode) {
+            node = { ...preexistingNode, value: node.value };
+        }
         return setNode({ ...node, parent })(tree);
     }, newState);
 };
diff --git a/src/views-components/projects-tree-picker/projects-tree-picker.tsx b/src/views-components/projects-tree-picker/projects-tree-picker.tsx
index bddde892..8f21784b 100644
--- a/src/views-components/projects-tree-picker/projects-tree-picker.tsx
+++ b/src/views-components/projects-tree-picker/projects-tree-picker.tsx
@@ -4,19 +4,24 @@
 
 import React from 'react';
 import { Dispatch } from 'redux';
-import { connect } from 'react-redux';
+import { connect, DispatchProp } from 'react-redux';
 import { RootState } from 'store/store';
 import { values, pipe } from 'lodash/fp';
 import { HomeTreePicker } from 'views-components/projects-tree-picker/home-tree-picker';
 import { SharedTreePicker } from 'views-components/projects-tree-picker/shared-tree-picker';
 import { FavoritesTreePicker } from 'views-components/projects-tree-picker/favorites-tree-picker';
-import { getProjectsTreePickerIds, treePickerSearchActions, SHARED_PROJECT_ID, FAVORITES_PROJECT_ID } from 'store/tree-picker/tree-picker-actions';
+import {
+    getProjectsTreePickerIds, treePickerActions, treePickerSearchActions, initProjectsTreePicker,
+    SHARED_PROJECT_ID, FAVORITES_PROJECT_ID
+} from 'store/tree-picker/tree-picker-actions';
 import { TreeItem } from 'components/tree/tree';
 import { ProjectsTreePickerItem } from 'store/tree-picker/tree-picker-middleware';
 import { PublicFavoritesTreePicker } from './public-favorites-tree-picker';
 import { SearchInput } from 'components/search-input/search-input';
+import { withStyles, StyleRulesCallback, WithStyles } from '@material-ui/core';
+import { ArvadosTheme } from 'common/custom-theme';
 
-export interface ProjectsTreePickerProps {
+export interface ToplevelPickerProps {
     pickerId: string;
     includeCollections?: boolean;
     includeFiles?: boolean;
@@ -36,15 +41,13 @@ interface ProjectsTreePickerActionProps {
     onCollectionFilter: (value: string) => void;
 }
 
-type ProjectsTreePickerCombinedProps = ProjectsTreePickerProps & ProjectsTreePickerSearchProps & ProjectsTreePickerActionProps;
-
-const mapStateToProps = (state: RootState, props: ProjectsTreePickerProps): ProjectsTreePickerSearchProps => ({
+const mapStateToProps = (state: RootState, props: ToplevelPickerProps): ProjectsTreePickerSearchProps => ({
     projectSearch: "",
     collectionFilter: "",
     ...props
 });
 
-const mapDispatchToProps = (dispatch: Dispatch, props: ProjectsTreePickerProps): ProjectsTreePickerActionProps => {
+const mapDispatchToProps = (dispatch: Dispatch, props: ToplevelPickerProps): (ProjectsTreePickerActionProps & DispatchProp) => {
     const { home, shared, favorites, publicFavorites } = getProjectsTreePickerIds(props.pickerId);
     const params = {
         includeCollections: props.includeCollections,
@@ -63,37 +66,87 @@ const mapDispatchToProps = (dispatch: Dispatch, props: ProjectsTreePickerProps):
             dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: shared, collectionFilterValue }));
             dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: favorites, collectionFilterValue }));
             dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: publicFavorites, collectionFilterValue }));
-        }
+        },
+        dispatch
     }
 };
 
-export const ProjectsTreePicker = connect(mapStateToProps, mapDispatchToProps)(({ pickerId, onProjectSearch, onCollectionFilter, ...props }: ProjectsTreePickerCombinedProps) => {
-    const { home, shared, favorites, publicFavorites } = getProjectsTreePickerIds(pickerId);
-    const relatedTreePickers = getRelatedTreePickers(pickerId);
-    const p = {
-        ...props,
-        relatedTreePickers,
-        disableActivation,
-    };
-    return <div>
-        <span>
-            <SearchInput value="" label="Search Projects" selfClearProp='' onSearch={onProjectSearch} debounce={200} />
-            <SearchInput value="" label="Filter Collections" selfClearProp='' onSearch={onCollectionFilter} debounce={200} />
-        </span>
-        <div data-cy="projects-tree-home-tree-picker">
-            <HomeTreePicker pickerId={home} {...p} />
-        </div>
-        <div data-cy="projects-tree-shared-tree-picker">
-            <SharedTreePicker pickerId={shared} {...p} />
-        </div>
-        <div data-cy="projects-tree-public-favourites-tree-picker">
-            <PublicFavoritesTreePicker pickerId={publicFavorites} {...p} />
-        </div>
-        <div data-cy="projects-tree-favourites-tree-picker">
-            <FavoritesTreePicker pickerId={favorites} {...p} />
-        </div>
-    </div>;
+type CssRules = 'pickerHeight' | 'searchFlex' | 'searchPadding';
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+    pickerHeight: {
+        height: "80vh"
+    },
+    searchFlex: {
+        display: "flex",
+        justifyContent: "space-around",
+        paddingBottom: "1em"
+    },
 });
 
+type ProjectsTreePickerCombinedProps = ToplevelPickerProps & ProjectsTreePickerSearchProps & ProjectsTreePickerActionProps & DispatchProp & WithStyles<CssRules>;
+
+export const ProjectsTreePicker = connect(mapStateToProps, mapDispatchToProps)(
+    withStyles(styles)(
+        class FileInputComponent extends React.Component<ProjectsTreePickerCombinedProps> {
+
+            componentDidMount() {
+                const { home, shared, favorites, publicFavorites } = getProjectsTreePickerIds(this.props.pickerId);
+
+                this.props.dispatch<any>(initProjectsTreePicker(this.props.pickerId));
+
+                this.props.dispatch(treePickerSearchActions.SET_TREE_PICKER_PROJECT_SEARCH({ pickerId: this.props.pickerId, projectSearchValue: "" }));
+                this.props.dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: home, collectionFilterValue: "" }));
+                this.props.dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: shared, collectionFilterValue: "" }));
+                this.props.dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: favorites, collectionFilterValue: "" }));
+                this.props.dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: publicFavorites, collectionFilterValue: "" }));
+            }
+
+            componentWillUnmount() {
+                const { home, shared, favorites, publicFavorites } = getProjectsTreePickerIds(this.props.pickerId);
+                // Release all the state, we don't need it to hang around forever.
+                this.props.dispatch(treePickerActions.RESET_TREE_PICKER({ pickerId: this.props.pickerId }));
+                this.props.dispatch(treePickerActions.RESET_TREE_PICKER({ pickerId: home }));
+                this.props.dispatch(treePickerActions.RESET_TREE_PICKER({ pickerId: shared }));
+                this.props.dispatch(treePickerActions.RESET_TREE_PICKER({ pickerId: favorites }));
+                this.props.dispatch(treePickerActions.RESET_TREE_PICKER({ pickerId: publicFavorites }));
+            }
+
+            render() {
+                const pickerId = this.props.pickerId;
+                const onProjectSearch = this.props.onProjectSearch;
+                const onCollectionFilter = this.props.onCollectionFilter;
+
+                const { home, shared, favorites, publicFavorites } = getProjectsTreePickerIds(pickerId);
+                const relatedTreePickers = getRelatedTreePickers(pickerId);
+                const p = {
+                    includeCollections: this.props.includeCollections,
+                    includeFiles: this.props.includeFiles,
+                    showSelection: this.props.showSelection,
+                    options: this.props.options,
+                    relatedTreePickers,
+                    disableActivation,
+                };
+                return <div className={this.props.classes.pickerHeight} >
+                    <span className={this.props.classes.searchFlex}>
+                        <SearchInput value="" label="Search Projects" selfClearProp='' onSearch={onProjectSearch} debounce={200} />
+                        <SearchInput value="" label="Filter Collections inside Projects" selfClearProp='' onSearch={onCollectionFilter} debounce={200} />
+                    </span>
+                    <div data-cy="projects-tree-home-tree-picker">
+                        <HomeTreePicker {...p} pickerId={home} />
+                    </div>
+                    <div data-cy="projects-tree-shared-tree-picker">
+                        <SharedTreePicker {...p} pickerId={shared} />
+                    </div>
+                    <div data-cy="projects-tree-public-favourites-tree-picker">
+                        <PublicFavoritesTreePicker {...p} pickerId={publicFavorites} />
+                    </div>
+                    <div data-cy="projects-tree-favourites-tree-picker">
+                        <FavoritesTreePicker {...p} pickerId={favorites} />
+                    </div>
+                </div >;
+            }
+        }));
+
 const getRelatedTreePickers = pipe(getProjectsTreePickerIds, values);
 const disableActivation = [SHARED_PROJECT_ID, FAVORITES_PROJECT_ID];
diff --git a/src/views/run-process-panel/inputs/file-input.tsx b/src/views/run-process-panel/inputs/file-input.tsx
index 099a8128..24ea0b79 100644
--- a/src/views/run-process-panel/inputs/file-input.tsx
+++ b/src/views/run-process-panel/inputs/file-input.tsx
@@ -137,7 +137,7 @@ const FileInputComponent = connect()(
                         color='primary'
                         onClick={this.submit}>Ok</Button>
                 </DialogActions>
-            </Dialog>;
+            </Dialog >;
         }
 
     });

commit db1c206c8403ed2b874625f1d1afc7af85dde25c
Author: Peter Amstutz <peter.amstutz at curii.com>
Date:   Thu Dec 8 17:41:33 2022 -0500

    19783: Work in progress on filtering collections
    
    Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <peter.amstutz at curii.com>

diff --git a/src/store/store.ts b/src/store/store.ts
index 94f110a0..1b9e05a9 100644
--- a/src/store/store.ts
+++ b/src/store/store.ts
@@ -26,7 +26,8 @@ import { AllProcessesPanelMiddlewareService } from "./all-processes-panel/all-pr
 import { collectionPanelReducer } from './collection-panel/collection-panel-reducer';
 import { dialogReducer } from './dialog/dialog-reducer';
 import { ServiceRepository } from "services/services";
-import { treePickerReducer } from './tree-picker/tree-picker-reducer';
+import { treePickerReducer, treePickerSearchReducer } from './tree-picker/tree-picker-reducer';
+import { treePickerSearchMiddleware } from './tree-picker/tree-picker-middleware';
 import { resourcesReducer } from 'store/resources/resources-reducer';
 import { propertiesReducer } from './properties/properties-reducer';
 import { fileUploaderReducer } from './file-uploader/file-uploader-reducer';
@@ -76,7 +77,7 @@ import { MiddlewareListReducer } from 'common/plugintypes';
 
 declare global {
     interface Window {
-      __REDUX_DEVTOOLS_EXTENSION_COMPOSE__?: typeof compose;
+        __REDUX_DEVTOOLS_EXTENSION_COMPOSE__?: typeof compose;
     }
 }
 
@@ -174,6 +175,7 @@ export function configureStore(history: History, services: ServiceRepository, co
         publicFavoritesMiddleware,
         collectionsContentAddress,
         subprocessMiddleware,
+        treePickerSearchMiddleware
     ];
 
     const reduceMiddlewaresFn: (a: Middleware[],
@@ -203,6 +205,7 @@ const createRootReducer = (services: ServiceRepository) => combineReducers({
     router: routerReducer,
     snackbar: snackbarReducer,
     treePicker: treePickerReducer,
+    treePickerSearch: treePickerSearchReducer,
     fileUploader: fileUploaderReducer,
     processPanel: processPanelReducer,
     progressIndicator: progressIndicatorReducer,
diff --git a/src/store/tree-picker/tree-picker-actions.ts b/src/store/tree-picker/tree-picker-actions.ts
index 1c0b2dea..b7710494 100644
--- a/src/store/tree-picker/tree-picker-actions.ts
+++ b/src/store/tree-picker/tree-picker-actions.ts
@@ -14,7 +14,7 @@ import { pipe, values } from 'lodash/fp';
 import { ResourceKind } from 'models/resource';
 import { GroupContentsResource } from 'services/groups-service/groups-service';
 import { getTreePicker, TreePicker } from './tree-picker';
-import { ProjectsTreePickerItem } from 'views-components/projects-tree-picker/generic-projects-tree-picker';
+import { ProjectsTreePickerItem } from './tree-picker-middleware';
 import { OrderBuilder } from 'services/api/order-builder';
 import { ProjectResource } from 'models/project';
 import { mapTree } from '../../models/tree';
@@ -28,6 +28,7 @@ export const treePickerActions = unionize({
     LOAD_TREE_PICKER_NODE_SUCCESS: ofType<{ id: string, nodes: Array<TreeNode<any>>, pickerId: string }>(),
     APPEND_TREE_PICKER_NODE_SUBTREE: ofType<{ id: string, subtree: Tree<any>, pickerId: string }>(),
     TOGGLE_TREE_PICKER_NODE_COLLAPSE: ofType<{ id: string, pickerId: string }>(),
+    EXPAND_TREE_PICKER_NODE: ofType<{ id: string, pickerId: string }>(),
     ACTIVATE_TREE_PICKER_NODE: ofType<{ id: string, pickerId: string, relatedTreePickers?: string[] }>(),
     DEACTIVATE_TREE_PICKER_NODE: ofType<{ pickerId: string }>(),
     TOGGLE_TREE_PICKER_NODE_SELECTION: ofType<{ id: string, pickerId: string }>(),
@@ -39,6 +40,22 @@ export const treePickerActions = unionize({
 
 export type TreePickerAction = UnionOf<typeof treePickerActions>;
 
+export interface LoadProjectParams {
+    includeCollections?: boolean;
+    includeFiles?: boolean;
+    includeFilterGroups?: boolean;
+    loadShared?: boolean;
+    options?: { showOnlyOwned: boolean; showOnlyWritable: boolean; };
+}
+
+export const treePickerSearchActions = unionize({
+    SET_TREE_PICKER_PROJECT_SEARCH: ofType<{ pickerId: string, projectSearchValue: string }>(),
+    SET_TREE_PICKER_COLLECTION_FILTER: ofType<{ pickerId: string, collectionFilterValue: string }>(),
+    SET_TREE_PICKER_LOAD_PARAMS: ofType<{ pickerId: string, params: LoadProjectParams }>(),
+});
+
+export type TreePickerSearchAction = UnionOf<typeof treePickerSearchActions>;
+
 export const getProjectsTreePickerIds = (pickerId: string) => ({
     home: `${pickerId}_home`,
     shared: `${pickerId}_shared`,
@@ -93,10 +110,10 @@ export const receiveTreePickerData = <T>(params: ReceiveTreePickerDataParams<T>)
             nodes: data.map(item => initTreeNode(extractNodeData(item))),
             pickerId,
         }));
-        dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id, pickerId }));
+        dispatch(treePickerActions.EXPAND_TREE_PICKER_NODE({ id, pickerId }));
     };
 
-interface LoadProjectParams {
+interface LoadProjectParamsWithId extends LoadProjectParams {
     id: string;
     pickerId: string;
     includeCollections?: boolean;
@@ -105,20 +122,28 @@ interface LoadProjectParams {
     loadShared?: boolean;
     options?: { showOnlyOwned: boolean; showOnlyWritable: boolean; };
 }
-export const loadProject = (params: LoadProjectParams) =>
-    async (dispatch: Dispatch, _: () => RootState, services: ServiceRepository) => {
+
+export const loadProject = (params: LoadProjectParamsWithId) =>
+    async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
         const { id, pickerId, includeCollections = false, includeFiles = false, includeFilterGroups = false, loadShared = false, options } = params;
 
         dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id, pickerId }));
 
-        const filters = pipe(
+        let filterB = pipe(
             (fb: FilterBuilder) => includeCollections
                 ? fb.addIsA('uuid', [ResourceKind.PROJECT, ResourceKind.COLLECTION])
                 : fb.addIsA('uuid', [ResourceKind.PROJECT]),
             fb => fb.addNotIn("collections.properties.type", ["intermediate", "log"]),
-            fb => fb.getFilters(),
         )(new FilterBuilder());
 
+        const state = getState();
+
+        if (state.treePickerSearch.collectionFilterValues[pickerId]) {
+            filterB = filterB.addILike('collections.name', state.treePickerSearch.collectionFilterValues[pickerId]);
+        }
+
+        const filters = filterB.getFilters();
+
         const { items, itemsAvailable } = await services.groupsService.contents(loadShared ? '' : id, { filters, excludeHomeProject: loadShared || undefined, limit: 1000 });
 
         if (itemsAvailable > 1000) {
diff --git a/src/store/tree-picker/tree-picker-reducer.ts b/src/store/tree-picker/tree-picker-reducer.ts
index 349240ea..d4b710c9 100644
--- a/src/store/tree-picker/tree-picker-reducer.ts
+++ b/src/store/tree-picker/tree-picker-reducer.ts
@@ -2,13 +2,15 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-import { createTree, TreeNode, setNode, Tree, TreeNodeStatus, setNodeStatus, expandNode, deactivateNode, selectNodes, deselectNodes } from 'models/tree';
+import {
+    createTree, TreeNode, setNode, Tree, TreeNodeStatus, setNodeStatus,
+    expandNode, deactivateNode, selectNodes, deselectNodes,
+    activateNode, getNode, toggleNodeCollapse, toggleNodeSelection, appendSubtree
+} from 'models/tree';
 import { TreePicker } from "./tree-picker";
-import { treePickerActions, TreePickerAction } from "./tree-picker-actions";
+import { treePickerActions, treePickerSearchActions, TreePickerAction, TreePickerSearchAction, LoadProjectParams } from "./tree-picker-actions";
 import { compose } from "redux";
-import { activateNode, getNode, toggleNodeCollapse, toggleNodeSelection } from 'models/tree';
 import { pipe } from 'lodash/fp';
-import { appendSubtree } from 'models/tree';
 
 export const treePickerReducer = (state: TreePicker = {}, action: TreePickerAction) =>
     treePickerActions.match(action, {
@@ -18,12 +20,15 @@ export const treePickerReducer = (state: TreePicker = {}, action: TreePickerActi
         LOAD_TREE_PICKER_NODE_SUCCESS: ({ id, nodes, pickerId }) =>
             updateOrCreatePicker(state, pickerId, compose(receiveNodes(nodes)(id), setNodeStatus(id)(TreeNodeStatus.LOADED))),
 
-        APPEND_TREE_PICKER_NODE_SUBTREE: ({ id, subtree, pickerId}) =>
+        APPEND_TREE_PICKER_NODE_SUBTREE: ({ id, subtree, pickerId }) =>
             updateOrCreatePicker(state, pickerId, compose(appendSubtree(id, subtree), setNodeStatus(id)(TreeNodeStatus.LOADED))),
 
         TOGGLE_TREE_PICKER_NODE_COLLAPSE: ({ id, pickerId }) =>
             updateOrCreatePicker(state, pickerId, toggleNodeCollapse(id)),
 
+        EXPAND_TREE_PICKER_NODE: ({ id, pickerId }) =>
+            updateOrCreatePicker(state, pickerId, expandNode(id)),
+
         ACTIVATE_TREE_PICKER_NODE: ({ id, pickerId, relatedTreePickers = [] }) =>
             pipe(
                 () => relatedTreePickers.reduce(
@@ -70,3 +75,26 @@ const receiveNodes = <V>(nodes: Array<TreeNode<V>>) => (parent: string) => (stat
         return setNode({ ...node, parent })(tree);
     }, newState);
 };
+
+interface TreePickerSearch {
+    projectSearchValues: { [pickerId: string]: string };
+    collectionFilterValues: { [pickerId: string]: string };
+    loadProjectParams: { [pickerId: string]: LoadProjectParams };
+}
+
+export const treePickerSearchReducer = (state: TreePickerSearch = { projectSearchValues: {}, collectionFilterValues: {}, loadProjectParams: {} }, action: TreePickerSearchAction) =>
+    treePickerSearchActions.match(action, {
+        SET_TREE_PICKER_PROJECT_SEARCH: ({ pickerId, projectSearchValue }) => ({
+            ...state, projectSearchValues: { ...state.projectSearchValues, [pickerId]: projectSearchValue }
+        }),
+
+        SET_TREE_PICKER_COLLECTION_FILTER: ({ pickerId, collectionFilterValue }) => ({
+            ...state, collectionFilterValues: { ...state.collectionFilterValues, [pickerId]: collectionFilterValue }
+        }),
+
+        SET_TREE_PICKER_LOAD_PARAMS: ({ pickerId, params }) => ({
+            ...state, loadProjectParams: { ...state.loadProjectParams, [pickerId]: params }
+        }),
+
+        default: () => state
+    });
diff --git a/src/views-components/form-fields/search-bar-form-fields.tsx b/src/views-components/form-fields/search-bar-form-fields.tsx
index 777fa824..0a6b8795 100644
--- a/src/views-components/form-fields/search-bar-form-fields.tsx
+++ b/src/views-components/form-fields/search-bar-form-fields.tsx
@@ -12,7 +12,7 @@ import { HomeTreePicker } from 'views-components/projects-tree-picker/home-tree-
 import { SEARCH_BAR_ADVANCED_FORM_PICKER_ID } from 'store/search-bar/search-bar-actions';
 import { SearchBarAdvancedPropertiesView } from 'views-components/search-bar/search-bar-advanced-properties-view';
 import { TreeItem } from "components/tree/tree";
-import { ProjectsTreePickerItem } from "views-components/projects-tree-picker/generic-projects-tree-picker";
+import { ProjectsTreePickerItem } from "store/tree-picker/tree-picker-middleware";
 import { PropertyKeyField, } from 'views-components/resource-properties-form/property-key-field';
 import { PropertyValueField } from 'views-components/resource-properties-form/property-value-field';
 import { connect } from "react-redux";
@@ -36,7 +36,7 @@ interface SearchBarClusterFieldProps {
 
 export const SearchBarClusterField = connect(
     (state: RootState) => ({
-        clusters: [{key: '', value: 'Any'}].concat(
+        clusters: [{ key: '', value: 'Any' }].concat(
             state.auth.sessions
                 .filter(s => s.loggedIn)
                 .map(s => ({
@@ -46,7 +46,7 @@ export const SearchBarClusterField = connect(
     }))((props: SearchBarClusterFieldProps) => <Field
         name='cluster'
         component={NativeSelectField as any}
-        items={props.clusters}/>
+        items={props.clusters} />
     );
 
 export const SearchBarProjectField = () =>
diff --git a/src/views-components/projects-tree-picker/generic-projects-tree-picker.tsx b/src/views-components/projects-tree-picker/generic-projects-tree-picker.tsx
index dd6e63bf..11b51caa 100644
--- a/src/views-components/projects-tree-picker/generic-projects-tree-picker.tsx
+++ b/src/views-components/projects-tree-picker/generic-projects-tree-picker.tsx
@@ -12,18 +12,12 @@ import { treePickerActions } from "store/tree-picker/tree-picker-actions";
 import { ListItemTextIcon } from "components/list-item-text-icon/list-item-text-icon";
 import { ProjectIcon, FileInputIcon, IconType, CollectionIcon } from 'components/icon/icon';
 import { loadProject, loadCollection } from 'store/tree-picker/tree-picker-actions';
-import { GroupContentsResource } from 'services/groups-service/groups-service';
-import { CollectionDirectory, CollectionFile, CollectionFileType } from 'models/collection-file';
+import { ProjectsTreePickerItem, ProjectsTreePickerRootItem } from 'store/tree-picker/tree-picker-middleware';
 import { ResourceKind } from 'models/resource';
 import { TreePickerProps, TreePicker } from "views-components/tree-picker/tree-picker";
-import { LinkResource } from "models/link";
+import { CollectionFileType } from 'models/collection-file';
 
-export interface ProjectsTreePickerRootItem {
-    id: string;
-    name: string;
-}
 
-export type ProjectsTreePickerItem = ProjectsTreePickerRootItem | GroupContentsResource | CollectionDirectory | CollectionFile | LinkResource;
 type PickedTreePickerProps = Pick<TreePickerProps<ProjectsTreePickerItem>, 'onContextMenu' | 'toggleItemActive' | 'toggleItemOpen' | 'toggleItemSelection'>;
 
 export interface ProjectsTreePickerDataProps {
@@ -35,7 +29,7 @@ export interface ProjectsTreePickerDataProps {
     disableActivation?: string[];
     options?: { showOnlyOwned: boolean, showOnlyWritable: boolean };
     loadRootItem: (item: TreeItem<ProjectsTreePickerRootItem>, pickerId: string,
-         includeCollections?: boolean, includeFiles?: boolean, options?: { showOnlyOwned: boolean, showOnlyWritable: boolean }) => void;
+        includeCollections?: boolean, includeFiles?: boolean, options?: { showOnlyOwned: boolean, showOnlyWritable: boolean }) => void;
 }
 
 export type ProjectsTreePickerProps = ProjectsTreePickerDataProps & Partial<PickedTreePickerProps>;
diff --git a/src/views-components/projects-tree-picker/projects-tree-picker.tsx b/src/views-components/projects-tree-picker/projects-tree-picker.tsx
index c4a6e3ab..bddde892 100644
--- a/src/views-components/projects-tree-picker/projects-tree-picker.tsx
+++ b/src/views-components/projects-tree-picker/projects-tree-picker.tsx
@@ -3,14 +3,18 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import React from 'react';
+import { Dispatch } from 'redux';
+import { connect } from 'react-redux';
+import { RootState } from 'store/store';
 import { values, pipe } from 'lodash/fp';
 import { HomeTreePicker } from 'views-components/projects-tree-picker/home-tree-picker';
 import { SharedTreePicker } from 'views-components/projects-tree-picker/shared-tree-picker';
 import { FavoritesTreePicker } from 'views-components/projects-tree-picker/favorites-tree-picker';
-import { getProjectsTreePickerIds, SHARED_PROJECT_ID, FAVORITES_PROJECT_ID } from 'store/tree-picker/tree-picker-actions';
+import { getProjectsTreePickerIds, treePickerSearchActions, SHARED_PROJECT_ID, FAVORITES_PROJECT_ID } from 'store/tree-picker/tree-picker-actions';
 import { TreeItem } from 'components/tree/tree';
-import { ProjectsTreePickerItem } from './generic-projects-tree-picker';
+import { ProjectsTreePickerItem } from 'store/tree-picker/tree-picker-middleware';
 import { PublicFavoritesTreePicker } from './public-favorites-tree-picker';
+import { SearchInput } from 'components/search-input/search-input';
 
 export interface ProjectsTreePickerProps {
     pickerId: string;
@@ -22,15 +26,60 @@ export interface ProjectsTreePickerProps {
     toggleItemSelection?: (event: React.MouseEvent<HTMLElement>, item: TreeItem<ProjectsTreePickerItem>, pickerId: string) => void;
 }
 
-export const ProjectsTreePicker = ({ pickerId, ...props }: ProjectsTreePickerProps) => {
+interface ProjectsTreePickerSearchProps {
+    projectSearch: string;
+    collectionFilter: string;
+}
+
+interface ProjectsTreePickerActionProps {
+    onProjectSearch: (value: string) => void;
+    onCollectionFilter: (value: string) => void;
+}
+
+type ProjectsTreePickerCombinedProps = ProjectsTreePickerProps & ProjectsTreePickerSearchProps & ProjectsTreePickerActionProps;
+
+const mapStateToProps = (state: RootState, props: ProjectsTreePickerProps): ProjectsTreePickerSearchProps => ({
+    projectSearch: "",
+    collectionFilter: "",
+    ...props
+});
+
+const mapDispatchToProps = (dispatch: Dispatch, props: ProjectsTreePickerProps): ProjectsTreePickerActionProps => {
+    const { home, shared, favorites, publicFavorites } = getProjectsTreePickerIds(props.pickerId);
+    const params = {
+        includeCollections: props.includeCollections,
+        includeFiles: props.includeFiles,
+        options: props.options
+    };
+    dispatch(treePickerSearchActions.SET_TREE_PICKER_LOAD_PARAMS({ pickerId: home, params }));
+    dispatch(treePickerSearchActions.SET_TREE_PICKER_LOAD_PARAMS({ pickerId: shared, params }));
+    dispatch(treePickerSearchActions.SET_TREE_PICKER_LOAD_PARAMS({ pickerId: favorites, params }));
+    dispatch(treePickerSearchActions.SET_TREE_PICKER_LOAD_PARAMS({ pickerId: publicFavorites, params }));
+
+    return {
+        onProjectSearch: (projectSearchValue: string) => dispatch(treePickerSearchActions.SET_TREE_PICKER_PROJECT_SEARCH({ pickerId: props.pickerId, projectSearchValue })),
+        onCollectionFilter: (collectionFilterValue: string) => {
+            dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: home, collectionFilterValue }));
+            dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: shared, collectionFilterValue }));
+            dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: favorites, collectionFilterValue }));
+            dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: publicFavorites, collectionFilterValue }));
+        }
+    }
+};
+
+export const ProjectsTreePicker = connect(mapStateToProps, mapDispatchToProps)(({ pickerId, onProjectSearch, onCollectionFilter, ...props }: ProjectsTreePickerCombinedProps) => {
     const { home, shared, favorites, publicFavorites } = getProjectsTreePickerIds(pickerId);
     const relatedTreePickers = getRelatedTreePickers(pickerId);
     const p = {
         ...props,
         relatedTreePickers,
-        disableActivation
+        disableActivation,
     };
     return <div>
+        <span>
+            <SearchInput value="" label="Search Projects" selfClearProp='' onSearch={onProjectSearch} debounce={200} />
+            <SearchInput value="" label="Filter Collections" selfClearProp='' onSearch={onCollectionFilter} debounce={200} />
+        </span>
         <div data-cy="projects-tree-home-tree-picker">
             <HomeTreePicker pickerId={home} {...p} />
         </div>
@@ -44,7 +93,7 @@ export const ProjectsTreePicker = ({ pickerId, ...props }: ProjectsTreePickerPro
             <FavoritesTreePicker pickerId={favorites} {...p} />
         </div>
     </div>;
-};
+});
 
 const getRelatedTreePickers = pipe(getProjectsTreePickerIds, values);
 const disableActivation = [SHARED_PROJECT_ID, FAVORITES_PROJECT_ID];
diff --git a/src/views-components/projects-tree-picker/tree-picker-field.tsx b/src/views-components/projects-tree-picker/tree-picker-field.tsx
index e5fecf97..d6ebb8ec 100644
--- a/src/views-components/projects-tree-picker/tree-picker-field.tsx
+++ b/src/views-components/projects-tree-picker/tree-picker-field.tsx
@@ -7,7 +7,7 @@ import { Typography } from "@material-ui/core";
 import { TreeItem } from "components/tree/tree";
 import { WrappedFieldProps } from 'redux-form';
 import { ProjectsTreePicker } from 'views-components/projects-tree-picker/projects-tree-picker';
-import { ProjectsTreePickerItem } from 'views-components/projects-tree-picker/generic-projects-tree-picker';
+import { ProjectsTreePickerItem } from 'store/tree-picker/tree-picker-middleware';
 import { PickerIdProp } from 'store/tree-picker/picker-id';
 
 export const ProjectTreePickerField = (props: WrappedFieldProps & PickerIdProp) =>
@@ -37,4 +37,4 @@ export const CollectionTreePickerField = (props: WrappedFieldProps & PickerIdPro
             <Typography variant='caption' color='error'>
                 {props.meta.error}
             </Typography>}
-    </div>;
\ No newline at end of file
+    </div>;
diff --git a/src/views/run-process-panel/inputs/directory-array-input.tsx b/src/views/run-process-panel/inputs/directory-array-input.tsx
index 1b04718d..72dba752 100644
--- a/src/views/run-process-panel/inputs/directory-array-input.tsx
+++ b/src/views/run-process-panel/inputs/directory-array-input.tsx
@@ -16,7 +16,7 @@ import { GenericInputProps, GenericInput } from './generic-input';
 import { ProjectsTreePicker } from 'views-components/projects-tree-picker/projects-tree-picker';
 import { connect, DispatchProp } from 'react-redux';
 import { initProjectsTreePicker, getSelectedNodes, treePickerActions, getProjectsTreePickerIds, getAllNodes } from 'store/tree-picker/tree-picker-actions';
-import { ProjectsTreePickerItem } from 'views-components/projects-tree-picker/generic-projects-tree-picker';
+import { ProjectsTreePickerItem } from 'store/tree-picker/tree-picker-middleware';
 import { createSelector, createStructuredSelector } from 'reselect';
 import { ChipsInput } from 'components/chips-input/chips-input';
 import { identity, values, noop } from 'lodash';
diff --git a/src/views/run-process-panel/inputs/directory-input.tsx b/src/views/run-process-panel/inputs/directory-input.tsx
index ab1cf9d1..2120de99 100644
--- a/src/views/run-process-panel/inputs/directory-input.tsx
+++ b/src/views/run-process-panel/inputs/directory-input.tsx
@@ -17,7 +17,7 @@ import { GenericInputProps, GenericInput } from './generic-input';
 import { ProjectsTreePicker } from 'views-components/projects-tree-picker/projects-tree-picker';
 import { initProjectsTreePicker } from 'store/tree-picker/tree-picker-actions';
 import { TreeItem } from 'components/tree/tree';
-import { ProjectsTreePickerItem } from 'views-components/projects-tree-picker/generic-projects-tree-picker';
+import { ProjectsTreePickerItem } from 'store/tree-picker/tree-picker-middleware';
 import { CollectionResource } from 'models/collection';
 import { ResourceKind } from 'models/resource';
 import { ERROR_MESSAGE } from 'validators/require';
@@ -141,5 +141,3 @@ const DirectoryInputComponent = connect()(
         }
 
     });
-
-
diff --git a/src/views/run-process-panel/inputs/file-array-input.tsx b/src/views/run-process-panel/inputs/file-array-input.tsx
index ddb558b9..712f6418 100644
--- a/src/views/run-process-panel/inputs/file-array-input.tsx
+++ b/src/views/run-process-panel/inputs/file-array-input.tsx
@@ -16,7 +16,7 @@ import { GenericInputProps, GenericInput } from './generic-input';
 import { ProjectsTreePicker } from 'views-components/projects-tree-picker/projects-tree-picker';
 import { connect, DispatchProp } from 'react-redux';
 import { initProjectsTreePicker, getSelectedNodes, treePickerActions, getProjectsTreePickerIds } from 'store/tree-picker/tree-picker-actions';
-import { ProjectsTreePickerItem } from 'views-components/projects-tree-picker/generic-projects-tree-picker';
+import { ProjectsTreePickerItem } from 'store/tree-picker/tree-picker-middleware';
 import { CollectionFile, CollectionFileType } from 'models/collection-file';
 import { createSelector, createStructuredSelector } from 'reselect';
 import { ChipsInput } from 'components/chips-input/chips-input';
diff --git a/src/views/run-process-panel/inputs/file-input.tsx b/src/views/run-process-panel/inputs/file-input.tsx
index a56d45f8..099a8128 100644
--- a/src/views/run-process-panel/inputs/file-input.tsx
+++ b/src/views/run-process-panel/inputs/file-input.tsx
@@ -18,7 +18,7 @@ import { ProjectsTreePicker } from 'views-components/projects-tree-picker/projec
 import { connect, DispatchProp } from 'react-redux';
 import { initProjectsTreePicker } from 'store/tree-picker/tree-picker-actions';
 import { TreeItem } from 'components/tree/tree';
-import { ProjectsTreePickerItem } from 'views-components/projects-tree-picker/generic-projects-tree-picker';
+import { ProjectsTreePickerItem } from 'store/tree-picker/tree-picker-middleware';
 import { CollectionFile, CollectionFileType } from 'models/collection-file';
 
 export interface FileInputProps {
@@ -141,5 +141,3 @@ const FileInputComponent = connect()(
         }
 
     });
-
-
diff --git a/src/views/run-process-panel/inputs/project-input.tsx b/src/views/run-process-panel/inputs/project-input.tsx
index 0c962ed8..9cf16c31 100644
--- a/src/views/run-process-panel/inputs/project-input.tsx
+++ b/src/views/run-process-panel/inputs/project-input.tsx
@@ -13,7 +13,7 @@ import { GenericInput, GenericInputProps } from './generic-input';
 import { ProjectsTreePicker } from 'views-components/projects-tree-picker/projects-tree-picker';
 import { initProjectsTreePicker } from 'store/tree-picker/tree-picker-actions';
 import { TreeItem } from 'components/tree/tree';
-import { ProjectsTreePickerItem } from 'views-components/projects-tree-picker/generic-projects-tree-picker';
+import { ProjectsTreePickerItem } from 'store/tree-picker/tree-picker-middleware';
 import { ProjectResource } from 'models/project';
 import { ResourceKind } from 'models/resource';
 import { RootState } from 'store/store';

commit 952c200514d8740b66377ab7d6860e3a0ef0497d
Author: Peter Amstutz <peter.amstutz at curii.com>
Date:   Wed Dec 7 14:02:41 2022 -0500

    19783: Add basic filters
    
    Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <peter.amstutz at curii.com>

diff --git a/src/store/tree-picker/tree-picker-actions.ts b/src/store/tree-picker/tree-picker-actions.ts
index 23f548cd..1c0b2dea 100644
--- a/src/store/tree-picker/tree-picker-actions.ts
+++ b/src/store/tree-picker/tree-picker-actions.ts
@@ -115,33 +115,59 @@ export const loadProject = (params: LoadProjectParams) =>
             (fb: FilterBuilder) => includeCollections
                 ? fb.addIsA('uuid', [ResourceKind.PROJECT, ResourceKind.COLLECTION])
                 : fb.addIsA('uuid', [ResourceKind.PROJECT]),
+            fb => fb.addNotIn("collections.properties.type", ["intermediate", "log"]),
             fb => fb.getFilters(),
         )(new FilterBuilder());
 
-        const { items } = await services.groupsService.contents(loadShared ? '' : id, { filters, excludeHomeProject: loadShared || undefined });
+        const { items, itemsAvailable } = await services.groupsService.contents(loadShared ? '' : id, { filters, excludeHomeProject: loadShared || undefined, limit: 1000 });
+
+        if (itemsAvailable > 1000) {
+            items.push({
+                uuid: "more-items-available",
+                kind: ResourceKind.WORKFLOW,
+                name: "*** Not all items were loaded (limit 1000 items) ***",
+                description: "",
+                definition: "",
+                ownerUuid: "",
+                createdAt: "",
+                modifiedByClientUuid: "",
+                modifiedByUserUuid: "",
+                modifiedAt: "",
+                href: "",
+                etag: ""
+            });
+        }
+
         dispatch<any>(receiveTreePickerData<GroupContentsResource>({
             id,
             pickerId,
             data: items.filter((item) => {
-                    if (!includeFilterGroups && (item as GroupResource).groupClass && (item as GroupResource).groupClass === GroupClass.FILTER) {
-                        return false;
-                    }
+                if (!includeFilterGroups && (item as GroupResource).groupClass && (item as GroupResource).groupClass === GroupClass.FILTER) {
+                    return false;
+                }
 
-                    if (options && options.showOnlyWritable && item.hasOwnProperty('frozenByUuid') && (item as ProjectResource).frozenByUuid) {
-                        return false;
-                    }
+                if (options && options.showOnlyWritable && item.hasOwnProperty('frozenByUuid') && (item as ProjectResource).frozenByUuid) {
+                    return false;
+                }
 
-                    return true;
-                }),
-            extractNodeData: item => ({
-                id: item.uuid,
-                value: item,
-                status: item.kind === ResourceKind.PROJECT
-                    ? TreeNodeStatus.INITIAL
-                    : includeFiles
-                        ? TreeNodeStatus.INITIAL
-                        : TreeNodeStatus.LOADED
+                return true;
             }),
+            extractNodeData: item => (
+                item.uuid === "more-items-available" ?
+                    {
+                        id: item.uuid,
+                        value: item,
+                        status: TreeNodeStatus.LOADED
+                    }
+                    : {
+                        id: item.uuid,
+                        value: item,
+                        status: item.kind === ResourceKind.PROJECT
+                            ? TreeNodeStatus.INITIAL
+                            : includeFiles
+                                ? TreeNodeStatus.INITIAL
+                                : TreeNodeStatus.LOADED
+                    }),
         }));
     };
 
@@ -188,7 +214,7 @@ export const initUserProject = (pickerId: string) =>
             }));
         }
     };
-export const loadUserProject = (pickerId: string, includeCollections = false, includeFiles = false, options?: { showOnlyOwned: boolean, showOnlyWritable: boolean } ) =>
+export const loadUserProject = (pickerId: string, includeCollections = false, includeFiles = false, options?: { showOnlyOwned: boolean, showOnlyWritable: boolean }) =>
     async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
         const uuid = getUserUuid(getState());
         if (uuid) {

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list