Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PLANNER-1752: Move frontend to Immutable.js #530

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions optaweb-employee-rostering-frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions optaweb-employee-rostering-frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"i18next": "^19.1.0",
"i18next-browser-languagedetector": "^4.0.1",
"i18next-xhr-backend": "^3.2.2",
"immutable": "^4.0.0-rc.12",
"moment": "^2.24.0",
"node-sass": "^4.14.1",
"react": "^16.13.1",
Expand Down
3 changes: 2 additions & 1 deletion optaweb-employee-rostering-frontend/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { SpinnerIcon } from '@patternfly/react-icons';
import './index.css';
import { I18nextProvider } from 'react-i18next';
import { configureStore } from 'store';
import { List } from 'immutable';
import App from './ui/App';

// import i18n (needs to be bundled)
Expand All @@ -39,7 +40,7 @@ const store = configureStore({
}, {
tenantData: {
currentTenantId: windowTenantId,
tenantList: [],
tenantList: List(),
timezoneList: [],
},
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import * as tenantOperations from 'store/tenant/operations';
import { onPost } from 'store/rest/RestTestUtils';
import { alert } from 'store/alert';
import { doNothing } from 'types';
import { List } from 'immutable';
import { mockStore } from '../mockStore';
import { AppState } from '../types';
import * as adminOperations from './operations';
Expand Down Expand Up @@ -51,7 +52,7 @@ describe('Contract operations', () => {
const state: Partial<AppState> = {
tenantData: {
currentTenantId: 0,
tenantList: [],
tenantList: List(),
timezoneList: ['America/Toronto'],
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { withElement, withoutElementWithId } from 'util/ImmutableCollectionOperations';
import { ServerSideExceptionInfo, BasicObject } from 'types';
import { List } from 'immutable';
import { mockStore } from '../mockStore';
import { AppState } from '../types';
import * as actions from './actions';
Expand All @@ -23,15 +23,15 @@ import { AlertInfo, AlertComponent } from './types';

const state: Partial<AppState> = {
alerts: {
alertList: [{
alertList: List([{
id: 0,
createdAt: new Date(),
i18nKey: 'alert1',
variant: 'info',
variant: 'info' as 'info',
params: {},
components: [],
componentProps: [],
}],
}]),
idGeneratorIndex: 1,
},
};
Expand Down Expand Up @@ -162,12 +162,14 @@ describe('Alert reducers', () => {
it('add an alert', () => {
expect(
reducer(state.alerts, actions.addAlert(addedAlert)),
).toEqual({ idGeneratorIndex: 2, alertList: withElement(storeState.alerts.alertList, { ...addedAlert, id: 1 }) });
).toEqual({ idGeneratorIndex: 2, alertList: storeState.alerts.alertList.push({ ...addedAlert, id: 1 }) });
});

it('remove an alert', () => {
expect(
reducer(state.alerts, actions.removeAlert(removedAlertId)),
).toEqual({ ...state.alerts, alertList: withoutElementWithId(storeState.alerts.alertList, removedAlertId) });
).toEqual({ ...state.alerts,
alertList: storeState.alerts.alertList
.filterNot(a => a.id === removedAlertId) });
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
* limitations under the License.
*/

import { withElement, withoutElementWithId } from 'util/ImmutableCollectionOperations';
import { List } from 'immutable';
import { ActionType, AlertList, AlertAction } from './types';

export const initialState: AlertList = {
alertList: [],
alertList: List(),
idGeneratorIndex: 0,
};

Expand All @@ -27,10 +27,10 @@ const alertReducer = (state = initialState, action: AlertAction): AlertList => {
case ActionType.ADD_ALERT: {
const alertWithId = { ...action.alertInfo, id: state.idGeneratorIndex };
const nextIndex = state.idGeneratorIndex + 1;
return { ...state, idGeneratorIndex: nextIndex, alertList: withElement(state.alertList, alertWithId) };
return { ...state, idGeneratorIndex: nextIndex, alertList: state.alertList.push(alertWithId) };
}
case ActionType.REMOVE_ALERT: {
return { ...state, alertList: withoutElementWithId(state.alertList, action.id) };
return { ...state, alertList: state.alertList.filterNot(alert => alert.id === action.id) };
}
default:
return state;
Expand Down
3 changes: 2 additions & 1 deletion optaweb-employee-rostering-frontend/src/store/alert/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import { Action } from 'redux';
import { BasicObject } from 'types';
import { List } from 'immutable';

export enum ActionType {
ADD_ALERT = 'ADD_ALERT',
Expand Down Expand Up @@ -47,6 +48,6 @@ export interface AlertInfo {
}

export interface AlertList {
readonly alertList: AlertInfo[];
readonly alertList: List<AlertInfo>;
readonly idGeneratorIndex: number;
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@
*/

import { alert } from 'store/alert';
import {
createIdMapFromList, mapWithElement, mapWithoutElement,
mapWithUpdatedElement,
} from 'util/ImmutableCollectionOperations';
import { createIdMapFromList } from 'util/ImmutableCollectionOperations';
import { onGet, onPost, onDelete } from 'store/rest/RestTestUtils';
import { Contract } from 'domain/Contract';
import { mockStore } from '../mockStore';
Expand All @@ -29,8 +26,8 @@ import reducer, { contractSelectors, contractOperations } from './index';
const state: Partial<AppState> = {
contractList: {
isLoading: false,
contractMapById: new Map([
[0, {
contractMapById: createIdMapFromList([
{
tenantId: 0,
id: 0,
version: 0,
Expand All @@ -39,8 +36,8 @@ const state: Partial<AppState> = {
maximumMinutesPerWeek: null,
maximumMinutesPerMonth: null,
maximumMinutesPerYear: null,
}],
[1, {
},
{
tenantId: 0,
id: 1,
version: 0,
Expand All @@ -49,8 +46,8 @@ const state: Partial<AppState> = {
maximumMinutesPerWeek: 100,
maximumMinutesPerMonth: null,
maximumMinutesPerYear: null,
}],
[2, {
},
{
tenantId: 0,
id: 2,
version: 0,
Expand All @@ -59,7 +56,7 @@ const state: Partial<AppState> = {
maximumMinutesPerWeek: null,
maximumMinutesPerMonth: null,
maximumMinutesPerYear: 100,
}],
},
]),
},
};
Expand Down Expand Up @@ -220,19 +217,19 @@ describe('Contract reducers', () => {
expect(
reducer(state.contractList, actions.addContract(addedContract)),
).toEqual({ ...state.contractList,
contractMapById: mapWithElement(storeState.contractList.contractMapById, addedContract) });
contractMapById: storeState.contractList.contractMapById.set(addedContract.id as number, addedContract) });
});
it('remove contract', () => {
expect(
reducer(state.contractList, actions.removeContract(deletedContract)),
).toEqual({ ...state.contractList,
contractMapById: mapWithoutElement(storeState.contractList.contractMapById, deletedContract) });
contractMapById: storeState.contractList.contractMapById.delete(deletedContract.id as number) });
});
it('update contract', () => {
expect(
reducer(state.contractList, actions.updateContract(updatedContract)),
).toEqual({ ...state.contractList,
contractMapById: mapWithUpdatedElement(storeState.contractList.contractMapById, updatedContract) });
contractMapById: storeState.contractList.contractMapById.set(updatedContract.id as number, updatedContract) });
});
it('refresh contract list', () => {
expect(
Expand Down
18 changes: 7 additions & 11 deletions optaweb-employee-rostering-frontend/src/store/contract/reducers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,28 @@
* limitations under the License.
*/

import {
createIdMapFromList, mapWithElement, mapWithoutElement,
mapWithUpdatedElement,
} from 'util/ImmutableCollectionOperations';
import { createIdMapFromList } from 'util/ImmutableCollectionOperations';
import DomainObjectView from 'domain/DomainObjectView';
import { Contract } from 'domain/Contract';
import { Map } from 'immutable';
import { ActionType, ContractList, ContractAction } from './types';

export const initialState: ContractList = {
isLoading: true,
contractMapById: new Map<number, DomainObjectView<Contract>>(),
contractMapById: Map<number, DomainObjectView<Contract>>(),
};

const contractReducer = (state = initialState, action: ContractAction): ContractList => {
switch (action.type) {
case ActionType.SET_CONTRACT_LIST_LOADING: {
return { ...state, isLoading: action.isLoading };
}
case ActionType.ADD_CONTRACT: {
return { ...state, contractMapById: mapWithElement(state.contractMapById, action.contract) };
case ActionType.ADD_CONTRACT:
case ActionType.UPDATE_CONTRACT: {
return { ...state, contractMapById: state.contractMapById.set(action.contract.id as number, action.contract) };
}
case ActionType.REMOVE_CONTRACT: {
return { ...state, contractMapById: mapWithoutElement(state.contractMapById, action.contract) };
}
case ActionType.UPDATE_CONTRACT: {
return { ...state, contractMapById: mapWithUpdatedElement(state.contractMapById, action.contract) };
return { ...state, contractMapById: state.contractMapById.remove(action.contract.id as number) };
}
case ActionType.REFRESH_CONTRACT_LIST: {
return { ...state, contractMapById: createIdMapFromList(action.contractList) };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
* limitations under the License.
*/
import { Contract } from 'domain/Contract';
import { Map } from 'immutable';
import DomainObjectView from 'domain/DomainObjectView';
import { AppState } from '../types';

export const getContractById = (state: AppState, id: number): Contract => {
Expand All @@ -23,7 +25,7 @@ export const getContractById = (state: AppState, id: number): Contract => {
return state.contractList.contractMapById.get(id) as Contract;
};

let oldContractMapById: Map<number, Contract> | null = null;
let oldContractMapById: Map<number, DomainObjectView<Contract>> | null = null;
let contractListForOldContractMapById: Contract[] | null = null;

export const getContractList = (state: AppState): Contract[] => {
Expand All @@ -33,11 +35,11 @@ export const getContractList = (state: AppState): Contract[] => {
if (oldContractMapById === state.contractList.contractMapById && contractListForOldContractMapById !== null) {
return contractListForOldContractMapById;
}

const out: Contract[] = [];
state.contractList.contractMapById.forEach((value, key) => out.push(getContractById(state, key)));
const out = state.contractList.contractMapById.keySeq().map(id => getContractById(state, id))
.sortBy(contract => contract.name).toList();

oldContractMapById = state.contractList.contractMapById;
yurloc marked this conversation as resolved.
Show resolved Hide resolved
contractListForOldContractMapById = out;
return out;
contractListForOldContractMapById = out.toArray();

return contractListForOldContractMapById;
};
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import { Action } from 'redux';
import { Contract } from 'domain/Contract';
import DomainObjectView from 'domain/DomainObjectView';
import { Map } from 'immutable';

export enum ActionType {
ADD_CONTRACT = 'ADD_CONTRACT',
Expand Down
Loading