refs #13887 Merge branch 'origin/13887-extract-common-functionality-from-project...
authorDaniel Kos <daniel.kos@contractors.roche.com>
Thu, 2 Aug 2018 07:29:15 +0000 (09:29 +0200)
committerDaniel Kos <daniel.kos@contractors.roche.com>
Thu, 2 Aug 2018 09:18:35 +0000 (11:18 +0200)
# Conflicts:
# src/store/navigation/navigation-action.ts
# src/store/store.ts
# src/views-components/create-project-dialog/create-project-dialog.tsx
# src/views/project-panel/project-panel.tsx
# src/views/workbench/workbench.tsx

Arvados-DCO-1.1-Signed-off-by: Daniel Kos <daniel.kos@contractors.roche.com>

1  2 
src/components/column-selector/column-selector.test.tsx
src/store/data-explorer/data-explorer-middleware.test.ts
src/store/navigation/navigation-action.ts
src/store/store.ts
src/views-components/context-menu/action-sets/collection-action-set.ts
src/views-components/context-menu/action-sets/favorite-action-set.ts
src/views-components/context-menu/action-sets/project-action-set.ts
src/views-components/create-project-dialog/create-project-dialog.tsx
src/views/project-panel/project-panel.tsx
src/views/workbench/workbench.tsx

index 01dba85c0621e1ac50718a340cb9059f8c147a2a,01dba85c0621e1ac50718a340cb9059f8c147a2a..02265fc4fd5f2f16da793a9a5cb72081a417ca71
@@@ -17,7 -17,7 +17,8 @@@ describe("<ColumnSelector />", () => 
              {
                  name: "Column 1",
                  render: () => <span />,
--                selected: true
++                selected: true,
++                configurable: true
              },
              {
                  name: "Column 2",
              {
                  name: "Column 1",
                  render: () => <span />,
--                selected: true
++                selected: true,
++                configurable: true
              },
              {
                  name: "Column 2",
                  render: () => <span />,
--                selected: false
++                selected: false,
++                configurable: true
              },
              {
                  name: "Column 3",
                  render: () => <span />,
--                selected: true
++                selected: true,
++                configurable: true
              }
          ];
          const columnsConfigurator = mount(<ColumnSelector columns={columns} onColumnToggle={jest.fn()} />);
@@@ -67,7 -67,7 +71,8 @@@
              {
                  name: "Column 1",
                  render: () => <span />,
--                selected: true
++                selected: true,
++                configurable: true
              }
          ];
          const onColumnToggle = jest.fn();
index 0000000000000000000000000000000000000000,637a0ccea170af99fba0d9624ec31c22eb1e3363..6b8297b0c8c3bb84afc55a4fe16e5e2772db9a06
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,234 +1,210 @@@
 -import { MiddlewareAPI } from "../../../node_modules/redux";
+ // Copyright (C) The Arvados Authors. All rights reserved.
+ //
+ // SPDX-License-Identifier: AGPL-3.0
+ import { DataExplorerMiddlewareService } from "./data-explorer-middleware-service";
+ import { dataExplorerMiddleware } from "./data-explorer-middleware";
 -
 -    it("initializes columns in the store", () => {
 -        const config = {
 -            id: "Id",
 -            columns: [{
 -                name: "Column",
 -                selected: true,
 -                configurable: false,
 -                render: jest.fn()
 -            }],
 -            requestItems: jest.fn(),
 -            setApi: jest.fn()
 -        };
 -        const service = new ServiceMock(config);
 -        const api = {
 -            getState: jest.fn(),
 -            dispatch: jest.fn()
 -        };
 -        const next = jest.fn();
 -        dataExplorerMiddleware(service)(api)(next);
 -        expect(next)
 -            .toHaveBeenCalledWith(dataExplorerActions.SET_COLUMNS({ id: service.getId(), columns: service.getColumns() }));
 -    });
 -
++import { MiddlewareAPI } from "redux";
+ import { DataColumns } from "../../components/data-table/data-table";
+ import { dataExplorerActions } from "./data-explorer-action";
+ describe("DataExplorerMiddleware", () => {
 -        expect(next).toHaveBeenCalledTimes(4);
++    
+     it("handles only actions that are identified by service id", () => {
+         const config = {
+             id: "ServiceId",
+             columns: [{
+                 name: "Column",
+                 selected: true,
+                 configurable: false,
+                 render: jest.fn()
+             }],
+             requestItems: jest.fn(),
+             setApi: jest.fn()
+         };
+         const service = new ServiceMock(config);
+         const api = {
+             getState: jest.fn(),
+             dispatch: jest.fn()
+         };
+         const next = jest.fn();
+         const middleware = dataExplorerMiddleware(service)(api)(next);
+         middleware(dataExplorerActions.SET_PAGE({ id: "OtherId", page: 0 }));
+         middleware(dataExplorerActions.SET_PAGE({ id: "ServiceId", page: 0 }));
+         middleware(dataExplorerActions.SET_PAGE({ id: "OtherId", page: 0 }));
+         expect(api.dispatch).toHaveBeenCalledWith(dataExplorerActions.REQUEST_ITEMS({ id: "ServiceId" }));
+         expect(api.dispatch).toHaveBeenCalledTimes(1);
+     });
+     it("handles REQUEST_ITEMS action", () => {
+         const config = {
+             id: "ServiceId",
+             columns: [{
+                 name: "Column",
+                 selected: true,
+                 configurable: false,
+                 render: jest.fn()
+             }],
+             requestItems: jest.fn(),
+             setApi: jest.fn()
+         };
+         const service = new ServiceMock(config);
+         const api = {
+             getState: jest.fn(),
+             dispatch: jest.fn()
+         };
+         const next = jest.fn();
+         const middleware = dataExplorerMiddleware(service)(api)(next);
+         middleware(dataExplorerActions.REQUEST_ITEMS({ id: "ServiceId" }));
+         expect(config.requestItems).toHaveBeenCalled();
+     });
+     it("handles SET_PAGE action", () => {
+         const config = {
+             id: "ServiceId",
+             columns: [],
+             requestItems: jest.fn(),
+             setApi: jest.fn()
+         };
+         const service = new ServiceMock(config);
+         const api = {
+             getState: jest.fn(),
+             dispatch: jest.fn()
+         };
+         const next = jest.fn();
+         const middleware = dataExplorerMiddleware(service)(api)(next);
+         middleware(dataExplorerActions.SET_PAGE({ id: service.getId(), page: 0 }));
+         expect(api.dispatch).toHaveBeenCalledTimes(1);
+     });
+     it("handles SET_ROWS_PER_PAGE action", () => {
+         const config = {
+             id: "ServiceId",
+             columns: [],
+             requestItems: jest.fn(),
+             setApi: jest.fn()
+         };
+         const service = new ServiceMock(config);
+         const api = {
+             getState: jest.fn(),
+             dispatch: jest.fn()
+         };
+         const next = jest.fn();
+         const middleware = dataExplorerMiddleware(service)(api)(next);
+         middleware(dataExplorerActions.SET_ROWS_PER_PAGE({ id: service.getId(), rowsPerPage: 0 }));
+         expect(api.dispatch).toHaveBeenCalledTimes(1);
+     });
+     it("handles SET_FILTERS action", () => {
+         const config = {
+             id: "ServiceId",
+             columns: [],
+             requestItems: jest.fn(),
+             setApi: jest.fn()
+         };
+         const service = new ServiceMock(config);
+         const api = {
+             getState: jest.fn(),
+             dispatch: jest.fn()
+         };
+         const next = jest.fn();
+         const middleware = dataExplorerMiddleware(service)(api)(next);
+         middleware(dataExplorerActions.SET_FILTERS({ id: service.getId(), columnName: "", filters: [] }));
+         expect(api.dispatch).toHaveBeenCalledTimes(2);
+     });
+     it("handles SET_ROWS_PER_PAGE action", () => {
+         const config = {
+             id: "ServiceId",
+             columns: [],
+             requestItems: jest.fn(),
+             setApi: jest.fn()
+         };
+         const service = new ServiceMock(config);
+         const api = {
+             getState: jest.fn(),
+             dispatch: jest.fn()
+         };
+         const next = jest.fn();
+         const middleware = dataExplorerMiddleware(service)(api)(next);
+         middleware(dataExplorerActions.SET_ROWS_PER_PAGE({ id: service.getId(), rowsPerPage: 0 }));
+         expect(api.dispatch).toHaveBeenCalledTimes(1);
+     });
+     it("handles TOGGLE_SORT action", () => {
+         const config = {
+             id: "ServiceId",
+             columns: [],
+             requestItems: jest.fn(),
+             setApi: jest.fn()
+         };
+         const service = new ServiceMock(config);
+         const api = {
+             getState: jest.fn(),
+             dispatch: jest.fn()
+         };
+         const next = jest.fn();
+         const middleware = dataExplorerMiddleware(service)(api)(next);
+         middleware(dataExplorerActions.TOGGLE_SORT({ id: service.getId(), columnName: "" }));
+         expect(api.dispatch).toHaveBeenCalledTimes(1);
+     });
+     it("handles SET_SEARCH_VALUE action", () => {
+         const config = {
+             id: "ServiceId",
+             columns: [],
+             requestItems: jest.fn(),
+             setApi: jest.fn()
+         };
+         const service = new ServiceMock(config);
+         const api = {
+             getState: jest.fn(),
+             dispatch: jest.fn()
+         };
+         const next = jest.fn();
+         const middleware = dataExplorerMiddleware(service)(api)(next);
+         middleware(dataExplorerActions.SET_SEARCH_VALUE({ id: service.getId(), searchValue: "" }));
+         expect(api.dispatch).toHaveBeenCalledTimes(2);
+     });
+     it("forwards other actions", () => {
+         const config = {
+             id: "ServiceId",
+             columns: [],
+             requestItems: jest.fn(),
+             setApi: jest.fn()
+         };
+         const service = new ServiceMock(config);
+         const api = {
+             getState: jest.fn(),
+             dispatch: jest.fn()
+         };
+         const next = jest.fn();
+         const middleware = dataExplorerMiddleware(service)(api)(next);
+         middleware(dataExplorerActions.SET_COLUMNS({ id: service.getId(), columns: [] }));
+         middleware(dataExplorerActions.SET_ITEMS({ id: service.getId(), items: [], rowsPerPage: 0, itemsAvailable: 0, page: 0 }));
+         middleware(dataExplorerActions.TOGGLE_COLUMN({ id: service.getId(), columnName: "" }));
+         expect(api.dispatch).toHaveBeenCalledTimes(0);
 -        requestItems: (api: MiddlewareAPI) => void;
 -        setApi: () => void;
++        expect(next).toHaveBeenCalledTimes(3);
+     });
+ });
+ class ServiceMock extends DataExplorerMiddlewareService {
+     constructor(private config: {
+         id: string,
+         columns: DataColumns<any>,
++        requestItems: (api: MiddlewareAPI) => void
+     }) {
+         super(config.id);
+     }
+     getColumns() {
+         return this.config.columns;
+     }
+     requestItems(api: MiddlewareAPI) {
+         this.config.requestItems(api);
+     }
+ }
index f8687ed754ad51f4604b4541361c5e3aff318fb5,5e9d3041eb03f4499b21877683ac463aea4e193b..ffb0f7acf6f5fc3e69b1c0295936f6ee8c4eacc5
@@@ -7,12 -7,9 +7,11 @@@ import { projectActions, getProjectLis
  import { push } from "react-router-redux";
  import { TreeItemStatus } from "../../components/tree/tree";
  import { findTreeItem } from "../project/project-reducer";
- import { dataExplorerActions } from "../data-explorer/data-explorer-action";
- import { PROJECT_PANEL_ID } from "../../views/project-panel/project-panel";
  import { RootState } from "../store";
  import { Resource, ResourceKind } from "../../models/resource";
+ import { projectPanelActions } from "../project-panel/project-panel-action";
 +import { getCollectionUrl } from "../../models/collection";
 +import { getProjectUrl } from "../../models/project";
  
  export const getResourceUrl = <T extends Resource>(resource: T): string => {
      switch (resource.kind) {
index 5c928fcad7b8c1952e1301ff19fd76ba75fa0582,e84c17fb59c1e492d63feb087d5fdbaa8601b5bb..53a01e2a3a335bdad525a3dc56adefdd062f50f1
@@@ -17,9 -16,11 +16,13 @@@ import { contextMenuReducer, ContextMen
  import { reducer as formReducer } from 'redux-form';
  import { FavoritesState, favoritesReducer } from './favorites/favorites-reducer';
  import { snackbarReducer, SnackbarState } from './snackbar/snackbar-reducer';
+ import { dataExplorerMiddleware } from "./data-explorer/data-explorer-middleware";
+ import { FAVORITE_PANEL_ID } from "./favorite-panel/favorite-panel-action";
+ import { PROJECT_PANEL_ID } from "./project-panel/project-panel-action";
+ import { ProjectPanelMiddlewareService } from "./project-panel/project-panel-middleware-service";
+ import { FavoritePanelMiddlewareService } from "./favorite-panel/favorite-panel-middleware-service";
 +import { CollectionCreatorState, collectionCreationReducer } from './collections/creator/collection-creator-reducer';
 +import { CollectionPanelState, collectionPanelReducer } from './collection-panel/collection-panel-reducer';
  
  const composeEnhancers =
      (process.env.NODE_ENV === 'development' &&
index 0822b781e4e3dd1ad39177d6c17c77820316b3a7,0000000000000000000000000000000000000000..fbb5c864b8a2a608a5b5904229054b50728a9f66
mode 100644,000000..100644
--- /dev/null
@@@ -1,77 -1,0 +1,77 @@@
- import { FAVORITE_PANEL_ID } from "../../../views/favorite-panel/favorite-panel";
 +// Copyright (C) The Arvados Authors. All rights reserved.
 +//
 +// SPDX-License-Identifier: AGPL-3.0
 +
 +import { ContextMenuActionSet } from "../context-menu-action-set";
 +import { ToggleFavoriteAction } from "../actions/favorite-action";
 +import { toggleFavorite } from "../../../store/favorites/favorites-actions";
 +import { dataExplorerActions } from "../../../store/data-explorer/data-explorer-action";
-                 dispatch<any>(dataExplorerActions.REQUEST_ITEMS({ id: FAVORITE_PANEL_ID }));
 +import { RenameIcon, ShareIcon, MoveToIcon, CopyIcon, DetailsIcon, ProvenanceGraphIcon, AdvancedIcon, RemoveIcon } from "../../../components/icon/icon";
++import { favoritePanelActions } from "../../../store/favorite-panel/favorite-panel-action";
 +
 +export const collectionActionSet: ContextMenuActionSet = [[
 +    {
 +        icon: RenameIcon,
 +        name: "Edit collection",
 +        execute: (dispatch, resource) => {
 +            // add code
 +        }
 +    },
 +    {
 +        icon: ShareIcon,
 +        name: "Share",
 +        execute: (dispatch, resource) => {
 +            // add code
 +        }
 +    },
 +    {
 +        icon: MoveToIcon,
 +        name: "Move to",
 +        execute: (dispatch, resource) => {
 +            // add code
 +        }
 +    },
 +    {
 +        component: ToggleFavoriteAction,
 +        execute: (dispatch, resource) => {
 +            dispatch<any>(toggleFavorite(resource)).then(() => {
++                dispatch<any>(favoritePanelActions.REQUEST_ITEMS());
 +            });
 +        }
 +    },
 +    {
 +        icon: CopyIcon,
 +        name: "Copy to project",
 +        execute: (dispatch, resource) => {
 +            // add code
 +        }
 +    },
 +    {
 +        icon: DetailsIcon,
 +        name: "View details",
 +        execute: (dispatch, resource) => {
 +            // add code
 +        }
 +    },
 +    {
 +        icon: ProvenanceGraphIcon,
 +        name: "Provenance graph",
 +        execute: (dispatch, resource) => {
 +            // add code
 +        }
 +    },
 +    {
 +        icon: AdvancedIcon,
 +        name: "Advanced",
 +        execute: (dispatch, resource) => {
 +            // add code
 +        }
 +    },
 +    {
 +        icon: RemoveIcon,
 +        name: "Remove",
 +        execute: (dispatch, resource) => {
 +            // add code
 +        }
 +    }
 +]];
index 9682d4bf9a57778f8969239d320334aff9c616d5,a6117b3902bf7ec8952217c1abee3654ffb592bf..72c72fa9cb0c6f44eecc548399877eedccfb06ce
@@@ -3,10 -3,9 +3,9 @@@
  // SPDX-License-Identifier: AGPL-3.0
  
  import { ContextMenuActionSet } from "../context-menu-action-set";
 -import { ToggleFavoriteAction } from "./favorite-action";
 +import { ToggleFavoriteAction } from "../actions/favorite-action";
  import { toggleFavorite } from "../../../store/favorites/favorites-actions";
- import { dataExplorerActions } from "../../../store/data-explorer/data-explorer-action";
- import { FAVORITE_PANEL_ID } from "../../../views/favorite-panel/favorite-panel";
+ import { favoritePanelActions } from "../../../store/favorite-panel/favorite-panel-action";
  
  export const favoriteActionSet: ContextMenuActionSet = [[{
      component: ToggleFavoriteAction,
index 25e0ba37315cbc9124471167b496a8e615e7f7ce,d0034e1faaf117f7608085063faf42a87d92da54..df298e4b1f8c1c472bae5fdc7c2b8d5ba8f464ad
@@@ -5,10 -5,9 +5,9 @@@
  import { ContextMenuActionSet } from "../context-menu-action-set";
  import { projectActions } from "../../../store/project/project-action";
  import { NewProjectIcon } from "../../../components/icon/icon";
 -import { ToggleFavoriteAction } from "./favorite-action";
 +import { ToggleFavoriteAction } from "../actions/favorite-action";
  import { toggleFavorite } from "../../../store/favorites/favorites-actions";
- import { dataExplorerActions } from "../../../store/data-explorer/data-explorer-action";
- import { FAVORITE_PANEL_ID } from "../../../views/favorite-panel/favorite-panel";
+ import { favoritePanelActions } from "../../../store/favorite-panel/favorite-panel-action";
  
  export const projectActionSet: ContextMenuActionSet = [[{
      icon: NewProjectIcon,
index eacb1eb5c09a3d58823d1230c3eb269fe2f22c7a,e06b5691e58af9888441d21589fed724d768509d..1a521890d71be9ac684cb57335dee9d935e29526
@@@ -9,9 -9,7 +9,8 @@@ import { SubmissionError } from "redux-
  import { RootState } from "../../store/store";
  import { DialogProjectCreate } from "../dialog-create/dialog-project-create";
  import { projectActions, createProject, getProjectList } from "../../store/project/project-action";
- import { dataExplorerActions } from "../../store/data-explorer/data-explorer-action";
- import { PROJECT_PANEL_ID } from "../../views/project-panel/project-panel";
+ import { projectPanelActions } from "../../store/project-panel/project-panel-action";
 +import { snackbarActions } from "../../store/snackbar/snackbar-actions";
  
  const mapStateToProps = (state: RootState) => ({
      open: state.projects.creator.opened
@@@ -21,11 -19,7 +20,11 @@@ const addProject = (data: { name: strin
      (dispatch: Dispatch, getState: () => RootState) => {
          const { ownerUuid } = getState().projects.creator;
          return dispatch<any>(createProject(data)).then(() => {
-             dispatch(dataExplorerActions.REQUEST_ITEMS({ id: PROJECT_PANEL_ID }));
 +            dispatch(snackbarActions.OPEN_SNACKBAR({
 +                message: "Created a new project",
 +                hideDuration: 2000
 +            }));
+             dispatch(projectPanelActions.REQUEST_ITEMS());
              dispatch<any>(getProjectList(ownerUuid));
          });
      };
index de040effb81dcc975445c6bb74bfb2a79e7013d4,b6985a935e85b332b65d3df01175abaac9857a84..5c3fb2b0421837c43cf2ae721de6306c50358bd3
@@@ -16,9 -15,9 +15,8 @@@ import { ContainerRequestState } from '
  import { SortDirection } from '../../components/data-table/data-column';
  import { ResourceKind } from '../../models/resource';
  import { resourceLabel } from '../../common/labels';
- import { ProjectIcon, CollectionIcon, ProcessIcon, DefaultIcon, FavoriteIcon } from '../../components/icon/icon';
  import { ArvadosTheme } from '../../common/custom-theme';
- import { FavoriteStar } from '../../views-components/favorite-star/favorite-star';
+ import { renderName, renderStatus, renderType, renderOwner, renderFileSize, renderDate } from '../../views-components/data-explorer/renderers';
 -import { PROJECT_PANEL_ID } from "../../store/project-panel/project-panel-action";
  
  type CssRules = "toolbar" | "button";
  
index 160e12f84017e5a99e19c6e0084d3546dbdd59c9,667a0193c6fc61f757c3e326bed602848de105ef..96398c736c8d4206854cbe57ba5aa4d7def0f1e2
@@@ -33,14 -32,10 +33,15 @@@ import { SidePanelIdentifiers } from '.
  import { ProjectResource } from '../../models/project';
  import { ResourceKind } from '../../models/resource';
  import { ContextMenu, ContextMenuKind } from "../../views-components/context-menu/context-menu";
- import { FavoritePanel, FAVORITE_PANEL_ID } from "../favorite-panel/favorite-panel";
+ import { FavoritePanel } from "../favorite-panel/favorite-panel";
  import { CurrentTokenDialog } from '../../views-components/current-token-dialog/current-token-dialog';
 +import { dataExplorerActions } from '../../store/data-explorer/data-explorer-action';
  import { Snackbar } from '../../views-components/snackbar/snackbar';
+ import { favoritePanelActions } from '../../store/favorite-panel/favorite-panel-action';
 +import { CreateCollectionDialog } from '../../views-components/create-collection-dialog/create-collection-dialog';
 +import { CollectionPanel } from '../collection-panel/collection-panel';
 +import { loadCollection } from '../../store/collection-panel/collection-panel-action';
 +import { getCollectionUrl } from '../../models/collection';
  
  const drawerWidth = 240;
  const appBarHeight = 100;