studiokit-scaffolding-js 5.0.0 → 5.1.0-next.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/lib/components/HOC/CollectionComponent.d.ts +4 -3
  2. package/lib/components/HOC/CollectionComponent.js +3 -1
  3. package/lib/components/HOC/CollectionFirstItemComponent.d.ts +6 -2
  4. package/lib/components/HOC/CollectionFirstItemComponent.js +9 -2
  5. package/lib/components/HOC/CollectionItemComponent.d.ts +3 -2
  6. package/lib/components/HOC/CollectionItemComponent.js +3 -1
  7. package/lib/components/HOC/ConnectedModalComponent.d.ts +41 -3
  8. package/lib/components/HOC/ConnectedModalComponent.js +30 -8
  9. package/lib/components/HOC/FullscreenModalComponent.js +5 -3
  10. package/lib/components/HOC/SearchPersistorComponent.d.ts +9 -5
  11. package/lib/components/HOC/SearchPersistorComponent.js +53 -8
  12. package/lib/components/ManageTable.d.ts +19 -0
  13. package/lib/components/ManageTable.js +112 -0
  14. package/lib/components/SearchControls.d.ts +1 -1
  15. package/lib/components/SearchControls.js +7 -7
  16. package/lib/components/UserRoles/index.d.ts +1 -1
  17. package/lib/constants/index.d.ts +1 -0
  18. package/lib/constants/index.js +1 -0
  19. package/lib/constants/table.d.ts +15 -0
  20. package/lib/constants/table.js +24 -0
  21. package/lib/hooks/useCollection.d.ts +2 -2
  22. package/lib/hooks/useCollectionConfiguration.js +10 -2
  23. package/lib/hooks/useCollectionItem.d.ts +2 -2
  24. package/lib/redux/reducers/fetchReducer.js +12 -3
  25. package/lib/redux/reducers/modalsReducer.d.ts +6 -2
  26. package/lib/redux/reducers/modalsReducer.js +5 -1
  27. package/lib/redux/sagas/fetchSaga.js +2 -0
  28. package/lib/types/Collection.d.ts +9 -2
  29. package/lib/types/Search.d.ts +8 -1
  30. package/lib/types/net/FetchAction.d.ts +2 -0
  31. package/lib/utils/collection.js +24 -18
  32. package/lib/utils/manageTable.d.ts +2 -0
  33. package/lib/utils/manageTable.js +28 -0
  34. package/lib/utils/search.d.ts +28 -0
  35. package/lib/utils/search.js +96 -0
  36. package/package.json +2 -1
@@ -1,14 +1,14 @@
1
1
  import React, { ComponentClass, ComponentType } from 'react';
2
2
  import { RouteComponentProps } from 'react-router';
3
3
  import { MODEL_STATUS } from '../../constants/modelStatus';
4
- import { BaseReduxState, Model } from '../../types';
5
- import { CollectionCommonProps, CollectionCommonState, CollectionCreateParams, CollectionDeleteParams, CollectionLoadParams, CollectionMethodConfiguration, CollectionMethods, CollectionReduxResponse, CollectionUpdateParams } from '../../types/Collection';
4
+ import { BaseReduxState, Model, ModelCollection } from '../../types';
5
+ import { CollectionCommonProps, CollectionCommonState, CollectionCreateParams, CollectionDeleteParams, CollectionDerivedProps, CollectionLoadParams, CollectionMethodConfiguration, CollectionMethods, CollectionReduxResponse, CollectionUpdateParams } from '../../types/Collection';
6
6
  import { GuidComponentWrappedProps } from './GuidComponent';
7
7
  /** The props passed into `CollectionComponent` from the user and other HOCs. */
8
8
  export interface CollectionComponentProps<TModel extends Model> extends CollectionCommonProps, GuidComponentWrappedProps, RouteComponentProps, CollectionReduxResponse<TModel> {
9
9
  }
10
10
  /** The props passed down to the `WrappedComponent`. */
11
- export interface CollectionComponentWrappedProps<TModel extends Model> extends Omit<CollectionComponentProps<TModel>, keyof RouteComponentProps>, CollectionMethods, CollectionCommonState {
11
+ export interface CollectionComponentWrappedProps<TModel extends Model> extends Omit<CollectionComponentProps<TModel>, keyof RouteComponentProps>, CollectionDerivedProps<TModel>, CollectionMethods, CollectionCommonState {
12
12
  }
13
13
  export declare function configureCollectionComponent<TModel extends Model, TOwnProps extends {}>(WrappedComponent: ComponentType<TOwnProps & CollectionComponentWrappedProps<TModel>>, LoaderComponent?: ComponentType): {
14
14
  new (props: TOwnProps & CollectionComponentProps<TModel>): {
@@ -22,6 +22,7 @@ export declare function configureCollectionComponent<TModel extends Model, TOwnP
22
22
  create: (params: CollectionCreateParams) => void;
23
23
  update: (params: CollectionUpdateParams) => void;
24
24
  delete: (params: CollectionDeleteParams) => void;
25
+ getModelArray: import("memoize-one").MemoizedFn<(model: ModelCollection<TModel>, guid?: string | undefined) => TModel[]>;
25
26
  render(): JSX.Element;
26
27
  context: any;
27
28
  setState<K extends "modelStatus" | "previousModelStatus" | "fetchingId">(state: CollectionCommonState | ((prevState: Readonly<CollectionCommonState>, props: Readonly<TOwnProps & CollectionComponentProps<TModel>>) => CollectionCommonState | Pick<CollectionCommonState, K> | null) | Pick<CollectionCommonState, K> | null, callback?: (() => void) | undefined): void;
@@ -58,6 +58,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
58
58
  };
59
59
  Object.defineProperty(exports, "__esModule", { value: true });
60
60
  exports.configureMapStateToProps = exports.configureCollectionComponent = void 0;
61
+ var memoize_one_1 = __importDefault(require("memoize-one"));
61
62
  var react_1 = __importStar(require("react"));
62
63
  var react_redux_1 = require("react-redux");
63
64
  var react_router_1 = require("react-router");
@@ -110,6 +111,7 @@ function configureCollectionComponent(WrappedComponent, LoaderComponent) {
110
111
  _this.delete = function (params) {
111
112
  collection_1.deleteItemFromCollection(_this.getCollectionMethodConfig(), params);
112
113
  };
114
+ _this.getModelArray = memoize_one_1.default(function (model, guid) { return model_1.getModelArray(model, guid); });
113
115
  _this.state = {
114
116
  // initializing until model is loaded
115
117
  modelStatus: modelStatus_1.MODEL_STATUS.UNINITIALIZED,
@@ -139,7 +141,7 @@ function configureCollectionComponent(WrappedComponent, LoaderComponent) {
139
141
  if (modelStatus === modelStatus_1.MODEL_STATUS.UNINITIALIZED) {
140
142
  return react_1.default.createElement(LoaderComponent, null);
141
143
  }
142
- return (react_1.default.createElement(WrappedComponent, __assign({}, otherProps, { modelStatus: modelStatus, previousModelStatus: previousModelStatus, fetchingId: fetchingId, load: this.load, stopPeriodicLoad: this.stopPeriodicLoad, create: this.create, update: this.update, delete: this.delete })));
144
+ return (react_1.default.createElement(WrappedComponent, __assign({}, otherProps, { modelArray: this.getModelArray(this.props.model, this.props.guid), modelStatus: modelStatus, previousModelStatus: previousModelStatus, fetchingId: fetchingId, load: this.load, stopPeriodicLoad: this.stopPeriodicLoad, create: this.create, update: this.update, delete: this.delete })));
143
145
  };
144
146
  return CollectionComponent;
145
147
  }(react_1.Component));
@@ -1,8 +1,8 @@
1
1
  import React, { ComponentClass, ComponentType } from 'react';
2
- import { CollectionItemReduxResponse, Model } from '../../types';
2
+ import { CollectionItemDerivedProps, CollectionItemReduxResponse, Model, ModelCollection } from '../../types';
3
3
  import { CollectionComponentWrappedProps } from './CollectionComponent';
4
4
  /** The props passed into `WrappedComponent`. */
5
- export interface CollectionFirstItemComponentWrappedProps<TModel extends Model> extends Omit<CollectionComponentWrappedProps<TModel>, 'model'>, CollectionItemReduxResponse<TModel> {
5
+ export interface CollectionFirstItemComponentWrappedProps<TModel extends Model> extends Omit<CollectionComponentWrappedProps<TModel>, 'model'>, CollectionItemReduxResponse<TModel>, CollectionItemDerivedProps<TModel> {
6
6
  }
7
7
  /**
8
8
  * HOC meant to pass the first collection item to the wrapped component as its model.
@@ -10,6 +10,8 @@ export interface CollectionFirstItemComponentWrappedProps<TModel extends Model>
10
10
  */
11
11
  export declare function configureCollectionFirstItemComponent<TModel extends Model, TOwnProps extends {}>(WrappedComponent: ComponentType<TOwnProps & CollectionFirstItemComponentWrappedProps<TModel>>): {
12
12
  new (props: (TOwnProps & CollectionComponentWrappedProps<TModel>) | Readonly<TOwnProps & CollectionComponentWrappedProps<TModel>>): {
13
+ getModelArray: import("memoize-one").MemoizedFn<(model: ModelCollection<TModel>, guid?: string | undefined) => TModel[]>;
14
+ getModelMinusRelations: import("memoize-one").MemoizedFn<(model: TModel) => Partial<TModel>>;
13
15
  getFirstItem: () => TModel;
14
16
  render(): JSX.Element;
15
17
  context: any;
@@ -36,6 +38,8 @@ export declare function configureCollectionFirstItemComponent<TModel extends Mod
36
38
  UNSAFE_componentWillUpdate?(nextProps: Readonly<TOwnProps & CollectionComponentWrappedProps<TModel>>, nextState: Readonly<{}>, nextContext: any): void;
37
39
  };
38
40
  new (props: TOwnProps & CollectionComponentWrappedProps<TModel>, context: any): {
41
+ getModelArray: import("memoize-one").MemoizedFn<(model: ModelCollection<TModel>, guid?: string | undefined) => TModel[]>;
42
+ getModelMinusRelations: import("memoize-one").MemoizedFn<(model: TModel) => Partial<TModel>>;
39
43
  getFirstItem: () => TModel;
40
44
  render(): JSX.Element;
41
45
  context: any;
@@ -49,8 +49,12 @@ var __spreadArrays = (this && this.__spreadArrays) || function () {
49
49
  r[k] = a[j];
50
50
  return r;
51
51
  };
52
+ var __importDefault = (this && this.__importDefault) || function (mod) {
53
+ return (mod && mod.__esModule) ? mod : { "default": mod };
54
+ };
52
55
  Object.defineProperty(exports, "__esModule", { value: true });
53
56
  exports.configureCollectionFirstItemComponent = void 0;
57
+ var memoize_one_1 = __importDefault(require("memoize-one"));
54
58
  var react_1 = __importStar(require("react"));
55
59
  var model_1 = require("../../utils/model");
56
60
  /**
@@ -62,8 +66,11 @@ function configureCollectionFirstItemComponent(WrappedComponent) {
62
66
  __extends(CollectionFirstItemComponent, _super);
63
67
  function CollectionFirstItemComponent() {
64
68
  var _this = _super !== null && _super.apply(this, arguments) || this;
69
+ _this.getModelArray = memoize_one_1.default(function (model, guid) { return model_1.getModelArray(model, guid); });
70
+ _this.getModelMinusRelations = memoize_one_1.default(function (model) { return model_1.getModelMinusRelations(model); });
65
71
  _this.getFirstItem = function () {
66
- var _a = _this.props, model = _a.model, modelArray = _a.modelArray, guid = _a.guid;
72
+ var _a = _this.props, model = _a.model, guid = _a.guid;
73
+ var modelArray = _this.getModelArray(model, guid);
67
74
  var singleItem = modelArray.length > 0
68
75
  ? modelArray[0]
69
76
  : model[guid]
@@ -76,7 +83,7 @@ function configureCollectionFirstItemComponent(WrappedComponent) {
76
83
  CollectionFirstItemComponent.prototype.render = function () {
77
84
  var pathParams = this.props.pathParams;
78
85
  var firstItem = this.getFirstItem();
79
- var modelMinusRelations = model_1.getModelMinusRelations(firstItem);
86
+ var modelMinusRelations = this.getModelMinusRelations(firstItem);
80
87
  var p = __spreadArrays(pathParams);
81
88
  if (firstItem.id) {
82
89
  p.push(firstItem.id.toString());
@@ -1,13 +1,13 @@
1
1
  import React, { ComponentClass, ComponentType } from 'react';
2
2
  import { RouteComponentProps } from 'react-router';
3
3
  import { MODEL_STATUS } from '../../constants/modelStatus';
4
- import { BaseReduxState, CollectionCommonProps, CollectionCommonState, CollectionCreateParams, CollectionItemDeleteParams, CollectionItemLoadParams, CollectionItemMethods, CollectionItemReduxResponse, CollectionItemUpdateParams, CollectionMethodConfiguration, Model } from '../../types';
4
+ import { BaseReduxState, CollectionCommonProps, CollectionCommonState, CollectionCreateParams, CollectionItemDeleteParams, CollectionItemDerivedProps, CollectionItemLoadParams, CollectionItemMethods, CollectionItemReduxResponse, CollectionItemUpdateParams, CollectionMethodConfiguration, Model } from '../../types';
5
5
  import { GuidComponentWrappedProps } from './GuidComponent';
6
6
  /** The props passed into `CollectionItemComponent` from the user and other HOCs. */
7
7
  export interface CollectionItemComponentProps<TModel extends Model> extends CollectionCommonProps, GuidComponentWrappedProps, RouteComponentProps, CollectionItemReduxResponse<TModel> {
8
8
  }
9
9
  /** The props passed down to the `WrappedComponent`. */
10
- export interface CollectionItemComponentWrappedProps<TModel extends Model> extends Omit<CollectionItemComponentProps<TModel>, keyof RouteComponentProps>, CollectionItemMethods, CollectionCommonState {
10
+ export interface CollectionItemComponentWrappedProps<TModel extends Model> extends Omit<CollectionItemComponentProps<TModel>, keyof RouteComponentProps>, CollectionItemDerivedProps<TModel>, CollectionItemMethods, CollectionCommonState {
11
11
  }
12
12
  export declare function configureCollectionItemComponent<TModel extends Model, TOwnProps extends {}>(WrappedComponent: ComponentType<TOwnProps & CollectionItemComponentWrappedProps<TModel>>, LoaderComponent?: ComponentType): {
13
13
  new (props: TOwnProps & CollectionItemComponentProps<TModel>): {
@@ -20,6 +20,7 @@ export declare function configureCollectionItemComponent<TModel extends Model, T
20
20
  create: (params: CollectionCreateParams) => void;
21
21
  update: (params: CollectionItemUpdateParams) => void;
22
22
  delete: (params?: CollectionItemDeleteParams) => void;
23
+ getModelMinusRelations: import("memoize-one").MemoizedFn<(model: TModel) => Partial<TModel>>;
23
24
  render(): JSX.Element;
24
25
  context: any;
25
26
  setState<K extends "modelStatus" | "previousModelStatus" | "fetchingId">(state: CollectionCommonState | ((prevState: Readonly<CollectionCommonState>, props: Readonly<TOwnProps & CollectionItemComponentProps<TModel>>) => CollectionCommonState | Pick<CollectionCommonState, K> | null) | Pick<CollectionCommonState, K> | null, callback?: (() => void) | undefined): void;
@@ -58,6 +58,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
58
58
  };
59
59
  Object.defineProperty(exports, "__esModule", { value: true });
60
60
  exports.configureMapStateToProps = exports.configureCollectionItemComponent = void 0;
61
+ var memoize_one_1 = __importDefault(require("memoize-one"));
61
62
  var react_1 = __importStar(require("react"));
62
63
  var react_redux_1 = require("react-redux");
63
64
  var react_router_1 = require("react-router");
@@ -113,6 +114,7 @@ function configureCollectionItemComponent(WrappedComponent, LoaderComponent) {
113
114
  var model = _this.props.model;
114
115
  collection_1.deleteCollectionItem(model, _this.getCollectionMethodConfig(), params);
115
116
  };
117
+ _this.getModelMinusRelations = memoize_one_1.default(function (model) { return model_1.getModelMinusRelations(model); });
116
118
  _this.state = {
117
119
  // initializing until model is loaded, or if no model
118
120
  modelStatus: modelStatus_1.MODEL_STATUS.UNINITIALIZED,
@@ -136,7 +138,7 @@ function configureCollectionItemComponent(WrappedComponent, LoaderComponent) {
136
138
  if (modelStatus === modelStatus_1.MODEL_STATUS.UNINITIALIZED) {
137
139
  return react_1.default.createElement(LoaderComponent, null);
138
140
  }
139
- return (react_1.default.createElement(WrappedComponent, __assign({}, otherProps, { model: this.props.model, modelMinusRelations: this.props.modelMinusRelations, modelStatus: modelStatus, previousModelStatus: previousModelStatus, load: this.load, stopPeriodicLoad: this.stopPeriodicLoad, create: this.create, update: this.update, delete: this.delete })));
141
+ return (react_1.default.createElement(WrappedComponent, __assign({}, otherProps, { model: this.props.model, modelMinusRelations: this.getModelMinusRelations(this.props.model), modelStatus: modelStatus, previousModelStatus: previousModelStatus, load: this.load, stopPeriodicLoad: this.stopPeriodicLoad, create: this.create, update: this.update, delete: this.delete })));
140
142
  };
141
143
  return CollectionItemComponent;
142
144
  }(react_1.Component));
@@ -1,26 +1,64 @@
1
- import { ComponentClass, ComponentType } from 'react';
1
+ import React, { ComponentClass, ComponentType } from 'react';
2
+ import { ModalsState } from '../../redux/reducers/modalsReducer';
3
+ import { BaseReduxState } from '../../types';
2
4
  import { GuidComponentWrappedProps } from '../HOC/GuidComponent';
3
5
  export interface ConnectedModalWrappedProps {
4
6
  onEntering: () => void;
5
7
  onExited: () => void;
8
+ isTopOpenFullscreenModal?: boolean;
6
9
  }
7
10
  export interface ConnectedModalState {
8
11
  isModalOpen: boolean;
9
12
  }
13
+ export declare type ConnectedModalReduxProps = {
14
+ modals: ModalsState;
15
+ };
16
+ export declare const configureMapStateToProps: () => (state: BaseReduxState) => ConnectedModalReduxProps;
10
17
  /**
11
18
  * HOC that provides modal lifecycle event methods to coordinate modal state in redux.
12
19
  *
13
20
  * NOTE: Meant for testing. Should be wrapped in `guidComponent` to provide the `guid` prop normally.
14
21
  *
15
22
  * @param {*} WrappedComponent The component to wrap.
23
+ * @param {*} isFullscreen Are we rendering a fullscreen modal?
16
24
  */
17
- export declare function configureConnectedModalComponent<TOwnProps extends {}>(WrappedComponent: ComponentType<TOwnProps & ConnectedModalWrappedProps & GuidComponentWrappedProps>): ComponentClass<TOwnProps & GuidComponentWrappedProps>;
25
+ export declare function configureConnectedModalComponent<TOwnProps extends {}>(WrappedComponent: ComponentType<TOwnProps & ConnectedModalWrappedProps & GuidComponentWrappedProps>, isFullscreen?: boolean): {
26
+ new (props: TOwnProps & ConnectedModalWrappedProps & GuidComponentWrappedProps & ConnectedModalReduxProps): {
27
+ componentWillUnmount(): void;
28
+ onEntering: () => void;
29
+ onExited: () => void;
30
+ render(): JSX.Element;
31
+ context: any;
32
+ setState<K extends "isModalOpen">(state: ConnectedModalState | ((prevState: Readonly<ConnectedModalState>, props: Readonly<TOwnProps & ConnectedModalWrappedProps & GuidComponentWrappedProps & ConnectedModalReduxProps>) => ConnectedModalState | Pick<ConnectedModalState, K> | null) | Pick<ConnectedModalState, K> | null, callback?: (() => void) | undefined): void;
33
+ forceUpdate(callback?: (() => void) | undefined): void;
34
+ readonly props: Readonly<TOwnProps & ConnectedModalWrappedProps & GuidComponentWrappedProps & ConnectedModalReduxProps> & Readonly<{
35
+ children?: React.ReactNode;
36
+ }>;
37
+ state: Readonly<ConnectedModalState>;
38
+ refs: {
39
+ [key: string]: React.ReactInstance;
40
+ };
41
+ componentDidMount?(): void;
42
+ shouldComponentUpdate?(nextProps: Readonly<TOwnProps & ConnectedModalWrappedProps & GuidComponentWrappedProps & ConnectedModalReduxProps>, nextState: Readonly<ConnectedModalState>, nextContext: any): boolean;
43
+ componentDidCatch?(error: Error, errorInfo: React.ErrorInfo): void;
44
+ getSnapshotBeforeUpdate?(prevProps: Readonly<TOwnProps & ConnectedModalWrappedProps & GuidComponentWrappedProps & ConnectedModalReduxProps>, prevState: Readonly<ConnectedModalState>): any;
45
+ componentDidUpdate?(prevProps: Readonly<TOwnProps & ConnectedModalWrappedProps & GuidComponentWrappedProps & ConnectedModalReduxProps>, prevState: Readonly<ConnectedModalState>, snapshot?: any): void;
46
+ componentWillMount?(): void;
47
+ UNSAFE_componentWillMount?(): void;
48
+ componentWillReceiveProps?(nextProps: Readonly<TOwnProps & ConnectedModalWrappedProps & GuidComponentWrappedProps & ConnectedModalReduxProps>, nextContext: any): void;
49
+ UNSAFE_componentWillReceiveProps?(nextProps: Readonly<TOwnProps & ConnectedModalWrappedProps & GuidComponentWrappedProps & ConnectedModalReduxProps>, nextContext: any): void;
50
+ componentWillUpdate?(nextProps: Readonly<TOwnProps & ConnectedModalWrappedProps & GuidComponentWrappedProps & ConnectedModalReduxProps>, nextState: Readonly<ConnectedModalState>, nextContext: any): void;
51
+ UNSAFE_componentWillUpdate?(nextProps: Readonly<TOwnProps & ConnectedModalWrappedProps & GuidComponentWrappedProps & ConnectedModalReduxProps>, nextState: Readonly<ConnectedModalState>, nextContext: any): void;
52
+ };
53
+ contextType?: React.Context<any> | undefined;
54
+ };
18
55
  /**
19
56
  * HOC that provides modal lifecycle event methods to coordinate modal state in redux.
20
57
  *
21
58
  * Uses `guidComponent`.
22
59
  *
23
60
  * @param {*} WrappedComponent The component to wrap.
61
+ * @param {*} isFullscreen Are we rendering a fullscreen modal?
24
62
  */
25
- export declare function connectedModalComponent<TOwnProps extends {}>(WrappedComponent: ComponentType<TOwnProps & ConnectedModalWrappedProps & GuidComponentWrappedProps>): ComponentClass<TOwnProps>;
63
+ export declare function connectedModalComponent<TOwnProps extends {}>(WrappedComponent: ComponentType<TOwnProps & ConnectedModalWrappedProps & GuidComponentWrappedProps>, isFullscreen?: boolean): ComponentClass<TOwnProps>;
26
64
  export default connectedModalComponent;
@@ -46,26 +46,39 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
46
46
  return (mod && mod.__esModule) ? mod : { "default": mod };
47
47
  };
48
48
  Object.defineProperty(exports, "__esModule", { value: true });
49
- exports.connectedModalComponent = exports.configureConnectedModalComponent = void 0;
49
+ exports.connectedModalComponent = exports.configureConnectedModalComponent = exports.configureMapStateToProps = void 0;
50
+ var lodash_1 = require("lodash");
50
51
  var react_1 = __importStar(require("react"));
52
+ var react_redux_1 = require("react-redux");
51
53
  var actionCreator_1 = require("../../redux/actionCreator");
52
54
  var actions_1 = require("../../redux/actions");
53
55
  var GuidComponent_1 = __importDefault(require("../HOC/GuidComponent"));
56
+ var configureMapStateToProps = function () { return function (state) {
57
+ return {
58
+ modals: state.modals
59
+ };
60
+ }; };
61
+ exports.configureMapStateToProps = configureMapStateToProps;
54
62
  /**
55
63
  * HOC that provides modal lifecycle event methods to coordinate modal state in redux.
56
64
  *
57
65
  * NOTE: Meant for testing. Should be wrapped in `guidComponent` to provide the `guid` prop normally.
58
66
  *
59
67
  * @param {*} WrappedComponent The component to wrap.
68
+ * @param {*} isFullscreen Are we rendering a fullscreen modal?
60
69
  */
61
- function configureConnectedModalComponent(WrappedComponent) {
70
+ function configureConnectedModalComponent(WrappedComponent, isFullscreen) {
71
+ if (isFullscreen === void 0) { isFullscreen = false; }
62
72
  return /** @class */ (function (_super) {
63
73
  __extends(ConnectedModalComponent, _super);
64
74
  function ConnectedModalComponent(props) {
65
75
  var _this = _super.call(this, props) || this;
66
76
  _this.onEntering = function () {
67
- var guid = _this.props.guid;
68
- actionCreator_1.dispatchAction(actions_1.ACTION.MODAL_ENTERING, { guid: guid });
77
+ var _a;
78
+ var _b = _this.props, guid = _b.guid, modals = _b.modals;
79
+ // Store each modal's GUID in redux with a zero-based index to determine the order in which they were opened
80
+ var maxModal = lodash_1.maxBy(Object.values(modals), function (modal) { return modal.index; });
81
+ actionCreator_1.dispatchAction(actions_1.ACTION.MODAL_ENTERING, { guid: guid, index: ((_a = maxModal === null || maxModal === void 0 ? void 0 : maxModal.index) !== null && _a !== void 0 ? _a : -1) + 1, isFullscreen: isFullscreen });
69
82
  _this.setState({
70
83
  isModalOpen: true
71
84
  });
@@ -89,7 +102,12 @@ function configureConnectedModalComponent(WrappedComponent) {
89
102
  this.onExited();
90
103
  };
91
104
  ConnectedModalComponent.prototype.render = function () {
92
- return react_1.default.createElement(WrappedComponent, __assign({}, this.props, { onEntering: this.onEntering, onExited: this.onExited }));
105
+ var _a, _b;
106
+ var _c = this.props, modals = _c.modals, guid = _c.guid;
107
+ var topOpenFullscreenModal = lodash_1.maxBy(Object.values(modals).filter(function (m) { return m.isFullscreen; }), function (modal) { return modal.index; });
108
+ var thisModalIndex = (_b = (_a = modals[guid]) === null || _a === void 0 ? void 0 : _a.index) !== null && _b !== void 0 ? _b : null;
109
+ var isTopOpenFullscreenModal = (topOpenFullscreenModal === null || topOpenFullscreenModal === void 0 ? void 0 : topOpenFullscreenModal.index) === thisModalIndex;
110
+ return (react_1.default.createElement(WrappedComponent, __assign({}, this.props, { onEntering: this.onEntering, onExited: this.onExited, isTopOpenFullscreenModal: isTopOpenFullscreenModal })));
93
111
  };
94
112
  return ConnectedModalComponent;
95
113
  }(react_1.Component));
@@ -101,10 +119,14 @@ exports.configureConnectedModalComponent = configureConnectedModalComponent;
101
119
  * Uses `guidComponent`.
102
120
  *
103
121
  * @param {*} WrappedComponent The component to wrap.
122
+ * @param {*} isFullscreen Are we rendering a fullscreen modal?
104
123
  */
105
- function connectedModalComponent(WrappedComponent) {
106
- var component = configureConnectedModalComponent(WrappedComponent);
107
- return GuidComponent_1.default(component);
124
+ function connectedModalComponent(WrappedComponent, isFullscreen) {
125
+ if (isFullscreen === void 0) { isFullscreen = false; }
126
+ var component = configureConnectedModalComponent(WrappedComponent, isFullscreen);
127
+ var mapStateToProps = exports.configureMapStateToProps();
128
+ // @ts-ignore: could not match inferred type from the `connect` HOC
129
+ return react_redux_1.connect(mapStateToProps)(GuidComponent_1.default(component));
108
130
  }
109
131
  exports.connectedModalComponent = connectedModalComponent;
110
132
  exports.default = connectedModalComponent;
@@ -122,7 +122,7 @@ function configureFullscreenModalComponent(WrappedComponent) {
122
122
  });
123
123
  };
124
124
  FullscreenModalComponent.prototype.render = function () {
125
- var contentLabel = this.props.contentLabel;
125
+ var _a = this.props, contentLabel = _a.contentLabel, isTopOpenFullscreenModal = _a.isTopOpenFullscreenModal;
126
126
  var isOpen = this.state.isOpen;
127
127
  return (react_1.default.createElement(react_modal_1.default, { isOpen: isOpen, contentLabel: contentLabel, style: {
128
128
  content: {
@@ -144,7 +144,9 @@ function configureFullscreenModalComponent(WrappedComponent) {
144
144
  bottom: 0,
145
145
  zIndex: 2000
146
146
  }
147
- }, shouldCloseOnOverlayClick: false },
147
+ },
148
+ // hide the rendered portal if this is not the top open fullscreen modal
149
+ portalClassName: "ReactModalPortal" + (!isTopOpenFullscreenModal ? ' dn' : ''), shouldCloseOnOverlayClick: false },
148
150
  react_1.default.createElement(WrappedComponent, __assign({}, this.props, { closeModal: this.closeModal }))));
149
151
  };
150
152
  return FullscreenModalComponent;
@@ -167,7 +169,7 @@ exports.configureFullscreenModalComponent = configureFullscreenModalComponent;
167
169
  */
168
170
  function fullscreenModalComponent(WrappedComponent) {
169
171
  var component = configureFullscreenModalComponent(WrappedComponent);
170
- return ConnectedModalComponent_1.connectedModalComponent(component);
172
+ return ConnectedModalComponent_1.connectedModalComponent(component, true);
171
173
  }
172
174
  exports.fullscreenModalComponent = fullscreenModalComponent;
173
175
  exports.default = fullscreenModalComponent;
@@ -1,4 +1,5 @@
1
1
  import { ComponentClass, ComponentType } from 'react';
2
+ import { SortingRule } from 'react-table';
2
3
  import { Dispatch } from 'redux';
3
4
  import { BaseReduxState, Search } from '../../types';
4
5
  export interface SearchPersistorProps<T extends Search = Search> {
@@ -7,13 +8,16 @@ export interface SearchPersistorProps<T extends Search = Search> {
7
8
  }
8
9
  export interface SearchPersistorMethods<T extends Search = Search> {
9
10
  setSearchDefaults: (doSearch: () => void, search: T) => void;
10
- updateAndPersistSearch: (search?: Search | T, callback?: () => void) => void;
11
+ updateAndPersistSearch: (search?: T, callback?: () => void) => void;
12
+ resetSearch: () => void;
11
13
  handleSearchClick: () => void;
12
- handleChangeTab: (selectedTab: number) => void;
13
- handleKeywordsChange: (event: any) => void;
14
+ setKeywords: (event: any) => void;
14
15
  handleKeywordsKeyDown: (event: any) => void;
15
- handleQueryAllChange: (event: any) => void;
16
- resetSearch: () => void;
16
+ setQueryAll: (event: any) => void;
17
+ setSelectedTab: (selectedTab: number) => void;
18
+ setSortingRules: (newSortingRules: SortingRule[]) => void;
19
+ setPageSize: (newPageSize: number, newPage: number) => void;
20
+ setPage: (newPage: number) => void;
17
21
  }
18
22
  export interface SearchPersistorWrappedProps<T extends Search = Search> extends SearchPersistorProps<T>, SearchPersistorMethods<T> {
19
23
  search?: T;
@@ -44,6 +44,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
44
44
  };
45
45
  Object.defineProperty(exports, "__esModule", { value: true });
46
46
  exports.configureMapDispatchToProps = exports.configureMapStateToProps = exports.configureSearchPersistorComponent = void 0;
47
+ var lodash_1 = require("lodash");
47
48
  var react_1 = __importStar(require("react"));
48
49
  var react_redux_1 = require("react-redux");
49
50
  var actions_1 = require("../../redux/actions");
@@ -93,28 +94,72 @@ function configureSearchPersistorComponent(WrappedComponent) {
93
94
  invalidKeywords: false
94
95
  }, _this.doSearch);
95
96
  };
96
- _this.handleChangeTab = function (selectedTab) {
97
- _this.updateAndPersistSearch({ selectedTab: selectedTab });
97
+ _this.resetSearch = function () {
98
+ var _a;
99
+ var search = (_a = _this.state.search) !== null && _a !== void 0 ? _a : {};
100
+ _this.updateAndPersistSearch(
101
+ // preserve items related to filtering results
102
+ lodash_1.merge({}, _this.state.defaultSearch, {
103
+ selectedTab: search.selectedTab,
104
+ sortingRules: search.sortingRules,
105
+ pageSize: search.pageSize,
106
+ pageByTab: search.pageByTab
107
+ }),
108
+ // do not trigger a re-load unless queryAll is true
109
+ search.queryAll ? _this.doSearch : undefined);
110
+ };
111
+ _this.setSelectedTab = function (selectedTab) {
112
+ var _a, _b;
113
+ var pageByTab = __assign({}, ((_b = (_a = _this.state.search) === null || _a === void 0 ? void 0 : _a.pageByTab) !== null && _b !== void 0 ? _b : {}));
114
+ // if not set, default page to zero when switching tabs
115
+ if (!pageByTab[selectedTab])
116
+ pageByTab[selectedTab] = 0;
117
+ _this.updateAndPersistSearch({
118
+ selectedTab: selectedTab,
119
+ pageByTab: pageByTab
120
+ });
98
121
  };
99
- _this.handleKeywordsChange = function (event) {
122
+ _this.setKeywords = function (event) {
100
123
  _this.updateAndPersistSearch({
101
124
  keywords: event.target.value
102
125
  });
103
126
  };
104
127
  _this.handleKeywordsKeyDown = function (event) {
105
- if (event.key === 'Enter') {
128
+ var _a;
129
+ if (event.key === 'Enter' && ((_a = _this.state.search) === null || _a === void 0 ? void 0 : _a.queryAll)) {
106
130
  _this.handleSearchClick();
107
131
  }
108
132
  };
109
- _this.handleQueryAllChange = function (event) {
133
+ _this.setQueryAll = function (event) {
110
134
  var queryAll = event.target.checked;
111
135
  _this.updateAndPersistSearch({
112
136
  queryAll: queryAll,
113
137
  requiredMessage: !queryAll ? null : _this.state.search ? _this.state.search.requiredMessage : null
138
+ }, _this.doSearch);
139
+ };
140
+ _this.setSortingRules = function (newSortingRules) {
141
+ _this.updateAndPersistSearch({
142
+ sortingRules: newSortingRules
114
143
  });
115
144
  };
116
- _this.resetSearch = function () {
117
- _this.updateAndPersistSearch(_this.state.defaultSearch, _this.doSearch);
145
+ _this.setPageSize = function (newPageSize, newPage) {
146
+ var _a, _b, _c, _d;
147
+ var pageByTab = __assign({}, ((_b = (_a = _this.state.search) === null || _a === void 0 ? void 0 : _a.pageByTab) !== null && _b !== void 0 ? _b : {}));
148
+ var selectedTab = (_d = (_c = _this.state.search) === null || _c === void 0 ? void 0 : _c.selectedTab) !== null && _d !== void 0 ? _d : 1;
149
+ pageByTab[selectedTab] = newPage;
150
+ _this.updateAndPersistSearch({
151
+ pageSize: newPageSize,
152
+ pageByTab: pageByTab
153
+ });
154
+ };
155
+ _this.setPage = function (newPage) {
156
+ var _a, _b, _c, _d;
157
+ var pageByTab = __assign({}, ((_b = (_a = _this.state.search) === null || _a === void 0 ? void 0 : _a.pageByTab) !== null && _b !== void 0 ? _b : {}));
158
+ var selectedTab = (_d = (_c = _this.state.search) === null || _c === void 0 ? void 0 : _c.selectedTab) !== null && _d !== void 0 ? _d : 1;
159
+ pageByTab[selectedTab] = newPage;
160
+ _this.updateAndPersistSearch({
161
+ pageByTab: pageByTab
162
+ });
118
163
  };
119
164
  _this.state = {
120
165
  search: undefined,
@@ -130,7 +175,7 @@ function configureSearchPersistorComponent(WrappedComponent) {
130
175
  };
131
176
  //#endregion handlers
132
177
  SearchPersistorComponent.prototype.render = function () {
133
- return (react_1.default.createElement(WrappedComponent, __assign({ updateAndPersistSearch: this.updateAndPersistSearch, setSearchDefaults: this.setSearchDefaults, search: this.state.search, handleSearchClick: this.handleSearchClick, handleChangeTab: this.handleChangeTab, handleKeywordsChange: this.handleKeywordsChange, handleKeywordsKeyDown: this.handleKeywordsKeyDown, handleQueryAllChange: this.handleQueryAllChange, resetSearch: this.resetSearch }, this.props)));
178
+ return (react_1.default.createElement(WrappedComponent, __assign({ search: this.state.search, setSearchDefaults: this.setSearchDefaults, updateAndPersistSearch: this.updateAndPersistSearch, resetSearch: this.resetSearch, handleSearchClick: this.handleSearchClick, setKeywords: this.setKeywords, handleKeywordsKeyDown: this.handleKeywordsKeyDown, setQueryAll: this.setQueryAll, setSelectedTab: this.setSelectedTab, setSortingRules: this.setSortingRules, setPageSize: this.setPageSize, setPage: this.setPage }, this.props)));
134
179
  };
135
180
  return SearchPersistorComponent;
136
181
  }(react_1.Component));
@@ -0,0 +1,19 @@
1
+ /// <reference types="react" />
2
+ import { Column, SortingRule } from 'react-table';
3
+ import { Model, Search } from '../types';
4
+ export interface ManageTableProps<TModel, TSearchDataParams> {
5
+ search: Search;
6
+ isFiltered: boolean;
7
+ setSelectedTab: (selectedTab: number) => void;
8
+ setSortingRules: (newSortingRules: SortingRule[]) => void;
9
+ setPageSize: (newPageSize: number, newPage: number) => void;
10
+ setPage: (newPage: number) => void;
11
+ tabs: number[];
12
+ getTabName: (tab?: number) => string;
13
+ searchData: (params: TSearchDataParams) => Record<number, TModel[]>;
14
+ searchDataParams: TSearchDataParams;
15
+ columns: Column<TModel>[];
16
+ entityName: string;
17
+ canQueryAll?: boolean;
18
+ }
19
+ export declare const ManageTable: <TModel extends Model, TSearchDataParams extends {}>({ search: { hasSearched, queryAll, selectedTab, sortingRules, pageSize, pageByTab }, isFiltered, setSelectedTab, setSortingRules, setPageSize, setPage, tabs, getTabName, searchData, searchDataParams, columns, entityName, canQueryAll }: ManageTableProps<TModel, TSearchDataParams>) => JSX.Element;
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ManageTable = void 0;
7
+ var lodash_1 = require("lodash");
8
+ var react_1 = require("react");
9
+ var react_2 = __importDefault(require("react"));
10
+ var react_bootstrap_1 = require("react-bootstrap");
11
+ var react_table_1 = __importDefault(require("react-table"));
12
+ var table_1 = require("../constants/table");
13
+ var RefreshIndicator_1 = require("./RefreshIndicator");
14
+ var ManageTable = function (_a) {
15
+ //#region Data
16
+ var _b = _a.search, hasSearched = _b.hasSearched, queryAll = _b.queryAll, selectedTab = _b.selectedTab, sortingRules = _b.sortingRules, pageSize = _b.pageSize, pageByTab = _b.pageByTab, isFiltered = _a.isFiltered, setSelectedTab = _a.setSelectedTab, setSortingRules = _a.setSortingRules, setPageSize = _a.setPageSize, setPage = _a.setPage, tabs = _a.tabs, getTabName = _a.getTabName, searchData = _a.searchData, searchDataParams = _a.searchDataParams, columns = _a.columns, entityName = _a.entityName, canQueryAll = _a.canQueryAll;
17
+ var _c = react_1.useState(), dataByTab = _c[0], setDataByTab = _c[1];
18
+ var isInitialized = dataByTab !== undefined;
19
+ // search and set state, which can be debounced as a single unit of work
20
+ var searchAndSetData = react_1.useCallback(function (params, setDataByTab) {
21
+ setDataByTab(searchData(params));
22
+ }, [searchData]);
23
+ // search is debounced to prevent it being rapidly called
24
+ // useMemo with no dependencies ensures we keep the same debounced function between renders
25
+ var debouncedSearchAndSetData = react_1.useMemo(function () { return lodash_1.debounce(searchAndSetData); }, [searchAndSetData]);
26
+ // search once without debounce to initialize dataByTab state
27
+ react_1.useEffect(function () {
28
+ if (!isInitialized) {
29
+ setDataByTab(searchData(searchDataParams));
30
+ }
31
+ }, [isInitialized, searchData, searchDataParams]);
32
+ // search using debounced method when searchDataParams changes, after initialization
33
+ // use `isMounted` to not call the callback unless the component is mounted
34
+ react_1.useEffect(function () {
35
+ var isMounted = true;
36
+ if (isInitialized) {
37
+ debouncedSearchAndSetData(searchDataParams, function (dataByTab) {
38
+ if (isMounted)
39
+ setDataByTab(dataByTab);
40
+ });
41
+ }
42
+ return function () {
43
+ isMounted = false;
44
+ };
45
+ }, [debouncedSearchAndSetData, isInitialized, searchDataParams]);
46
+ //#endregion Data
47
+ var noResultsFound = isFiltered && !!dataByTab && Object.values(dataByTab).every(function (d) { return d.length === 0; });
48
+ var currentTab = selectedTab !== null && selectedTab !== void 0 ? selectedTab : 1;
49
+ var data = dataByTab === null || dataByTab === void 0 ? void 0 : dataByTab[currentTab];
50
+ //#region Tabs
51
+ var onTabSelect = react_1.useCallback(function (eventKey) {
52
+ setSelectedTab(Number(eventKey));
53
+ }, [setSelectedTab]);
54
+ var dataLengthByTab = react_1.useMemo(function () {
55
+ var value = {};
56
+ tabs.forEach(function (tab) {
57
+ var _a;
58
+ value[tab] = (_a = dataByTab === null || dataByTab === void 0 ? void 0 : dataByTab[tab].length) !== null && _a !== void 0 ? _a : 0;
59
+ });
60
+ return value;
61
+ }, [dataByTab, tabs]);
62
+ var SearchTabs = react_1.useMemo(function () { return (react_2.default.createElement(react_bootstrap_1.Tabs, { activeKey: selectedTab, id: "search-tabs", onSelect: onTabSelect }, tabs.map(function (tab) { return (react_2.default.createElement(react_bootstrap_1.Tab, { key: tab, eventKey: tab.toString(), title: getTabName(tab) + " (" + dataLengthByTab[tab] + ")", disabled: noResultsFound })); }))); }, [dataLengthByTab, getTabName, noResultsFound, onTabSelect, selectedTab, tabs]);
63
+ //#endregion Tabs
64
+ //#region Table
65
+ var NoDataComponent = react_1.useMemo(function () {
66
+ return hasSearched ? (react_2.default.createElement("p", { className: "pt4 tc" },
67
+ "No ",
68
+ entityName.toLocaleLowerCase(),
69
+ " matched your search.",
70
+ canQueryAll && !queryAll && (react_2.default.createElement(react_2.default.Fragment, null,
71
+ " You might want to try again with \"Search All ",
72
+ entityName,
73
+ "\" checked.")))) : (react_2.default.createElement("p", { className: "pt4 tc" },
74
+ "No ",
75
+ getTabName(selectedTab).toLowerCase(),
76
+ " ",
77
+ entityName.toLocaleLowerCase(),
78
+ ' ',
79
+ isFiltered ? 'matched your search' : 'found',
80
+ "."));
81
+ }, [canQueryAll, entityName, getTabName, hasSearched, isFiltered, queryAll, selectedTab]);
82
+ var NoDataComponentCallback = react_1.useCallback(function () { return NoDataComponent; }, [NoDataComponent]);
83
+ var Table = react_1.useMemo(function () {
84
+ var _a;
85
+ var showPagination = !!data && data.length > table_1.TABLE_DEFAULT_PAGE_SIZE;
86
+ // if pagination is not enabled, or page is not set, ensure page is 0, the first page
87
+ var pageForTab = showPagination && pageByTab && selectedTab ? pageByTab[selectedTab] : 0;
88
+ // if there are less items to display than the page size, shrink the page size so there are not empty rows
89
+ var currentPageSize = Math.min((_a = data === null || data === void 0 ? void 0 : data.length) !== null && _a !== void 0 ? _a : table_1.TABLE_DEFAULT_PAGE_SIZE, pageSize !== null && pageSize !== void 0 ? pageSize : table_1.TABLE_DEFAULT_PAGE_SIZE);
90
+ var pageSizeOptions = table_1.TABLE_PAGE_SIZE_OPTIONS.filter(function (s) { return !(data === null || data === void 0 ? void 0 : data.length) || s <= (data === null || data === void 0 ? void 0 : data.length); });
91
+ return (react_2.default.createElement(react_table_1.default, { "aria-describedby": "search-results-label", className: "manage-table -striped cb" + ((data === null || data === void 0 ? void 0 : data.length) === 1 ? ' single-row-action-button' : ''), data: data, showPagination: showPagination, showPaginationTop: true, sorted: sortingRules, pageSize: currentPageSize, page: pageForTab, pageSizeOptions: pageSizeOptions, columns: columns, onSortedChange: setSortingRules, onPageSizeChange: setPageSize, onPageChange: setPage, NoDataComponent: NoDataComponentCallback }));
92
+ }, [
93
+ NoDataComponentCallback,
94
+ columns,
95
+ data,
96
+ pageByTab,
97
+ pageSize,
98
+ selectedTab,
99
+ setPage,
100
+ setPageSize,
101
+ setSortingRules,
102
+ sortingRules
103
+ ]);
104
+ //#endregion Table
105
+ if (!dataByTab)
106
+ return react_2.default.createElement(RefreshIndicator_1.RefreshIndicator, { label: "Loading...", labelClassName: "color-white" });
107
+ return (react_2.default.createElement(react_2.default.Fragment, null,
108
+ react_2.default.createElement("h3", { id: "search-results-label", className: "visually-hidden" }, "Search Results"),
109
+ SearchTabs,
110
+ Table));
111
+ };
112
+ exports.ManageTable = ManageTable;
@@ -1,7 +1,7 @@
1
1
  import { FunctionComponent } from 'react';
2
2
  import { Search } from '../types';
3
3
  import { SearchPersistorMethods } from './HOC/SearchPersistorComponent';
4
- interface SearchControlsMethods extends Pick<SearchPersistorMethods, 'handleSearchClick' | 'handleKeywordsChange' | 'handleKeywordsKeyDown' | 'resetSearch'>, Partial<Pick<SearchPersistorMethods, 'handleQueryAllChange'>> {
4
+ interface SearchControlsMethods extends Pick<SearchPersistorMethods, 'handleSearchClick' | 'setKeywords' | 'handleKeywordsKeyDown' | 'resetSearch'>, Partial<Pick<SearchPersistorMethods, 'setQueryAll'>> {
5
5
  }
6
6
  export interface SearchControlsProps extends Search, SearchControlsMethods {
7
7
  keywordPlaceholder: string;