Skip to content

Commit

Permalink
TASK: Remove plow-js from @neos-project/neos-ui-redux-store
Browse files Browse the repository at this point in the history
  • Loading branch information
grebaldi committed Dec 26, 2022
1 parent eeb6b51 commit 9a47f88
Show file tree
Hide file tree
Showing 24 changed files with 360 additions and 192 deletions.
1 change: 0 additions & 1 deletion packages/neos-ui-redux-store/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
"lodash.defaultsdeep": "^4.6.1",
"lodash.isequal": "^4.5.0",
"lodash.mapvalues": "^4.6.0",
"plow-js": "^2.2.0",
"reselect": "^3.0.1",
"typesafe-actions": "^5.1.0"
},
Expand Down
23 changes: 12 additions & 11 deletions packages/neos-ui-redux-store/src/CR/ContentDimensions/index.spec.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import {$all, $set, $get} from 'plow-js';
import produce from 'immer';

import {actionTypes, actions, reducer, selectors, defaultState} from './index';
import {actionTypes as system} from '../../System/index';

const fixtures = {};

fixtures.dimensionState = $all(
$set('active', {language: ['fr']}),
$set('allowedPresets', {
fixtures.dimensionState = {
active: {language: ['fr']},
allowedPresets: {
language: ['en_US', 'en_UK', 'de', 'fr']
}),
$set('byName', {
},
byName: {
language: {
label: 'Language',
icon: 'fa-language',
Expand Down Expand Up @@ -67,9 +67,8 @@ fixtures.dimensionState = $all(
}
}
}
}),
{}
);
}
};

fixtures.globalState = {
cr: {
Expand Down Expand Up @@ -175,8 +174,10 @@ test(`activePresets selector should return the active dimensions configuration`,
});

test(`activePresets selector should fall back to default preset, if none is set`, () => {
const state = $set('cr.contentDimensions.active.language', null, fixtures.globalState);
const state = produce(fixtures.globalState, state => {
state.cr.contentDimensions.active.language = null;
});
const activePresets = selectors.activePresets(state);

expect($get('language.name', activePresets)).toBe('en_US');
expect(activePresets?.language?.name).toBe('en_US');
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import produce from 'immer';
import {$get} from 'plow-js';
import mapValues from 'lodash.mapvalues';
import {createSelector} from 'reselect';
import {action as createAction, ActionType} from 'typesafe-actions';
Expand Down Expand Up @@ -107,7 +106,7 @@ export const reducer = (state: State = defaultState, action: InitAction | Action
* country: ["fr"]
* }
*/
const activeSelector = (state: GlobalState) => $get(['cr', 'contentDimensions', 'active'], state);
const activeSelector = (state: GlobalState) => state?.cr?.contentDimensions?.active;

/**
* Get the allowed presets for the currently active dimension values by dimension name
Expand All @@ -118,7 +117,7 @@ const activeSelector = (state: GlobalState) => $get(['cr', 'contentDimensions',
* language: ["en_US", "en_UK", "de", "fr", ...]
* }
*/
const allowedPresetsSelector = (state: GlobalState) => $get(['cr', 'contentDimensions', 'allowedPresets'], state);
const allowedPresetsSelector = (state: GlobalState) => state?.cr?.contentDimensions?.allowedPresets;

/**
* Get dimension configurations by dimension name
Expand All @@ -142,7 +141,7 @@ const allowedPresetsSelector = (state: GlobalState) => $get(['cr', 'contentDimen
* }
* }
*/
const byNameSelector = (state: GlobalState) => $get(['cr', 'contentDimensions', 'byName'], state);
const byNameSelector = (state: GlobalState) => state?.cr?.contentDimensions?.byName;

/**
* Get the currently active dimension presets by dimension name
Expand Down
95 changes: 73 additions & 22 deletions packages/neos-ui-redux-store/src/CR/Nodes/selectors.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import {$get} from 'plow-js';
import {createSelector, defaultMemoize} from 'reselect';
import {GlobalState} from '../../System';
import {NodeContextPath, NodeMap, Node, NodeTypeName, ClipboardMode, NodeTypesRegistry} from '@neos-project/neos-ts-interfaces';

export const inlineValidationErrorsSelector = (state: GlobalState) => $get(['cr', 'nodes', 'inlineValidationErrors'], state);
export const nodesByContextPathSelector = (state: GlobalState) => $get(['cr', 'nodes', 'byContextPath'], state);
export const siteNodeContextPathSelector = (state: GlobalState) => $get(['cr', 'nodes', 'siteNode'], state);
export const documentNodeContextPathSelector = (state: GlobalState) => $get(['cr', 'nodes', 'documentNode'], state);
export const focusedNodePathsSelector = (state: GlobalState) => $get(['cr', 'nodes', 'focused', 'contextPaths'], state);
export const inlineValidationErrorsSelector = (state: GlobalState) => state?.cr?.nodes?.inlineValidationErrors;
export const nodesByContextPathSelector = (state: GlobalState) => state?.cr?.nodes?.byContextPath;
export const siteNodeContextPathSelector = (state: GlobalState) => state?.cr?.nodes?.siteNode;
export const documentNodeContextPathSelector = (state: GlobalState) => state?.cr?.nodes?.documentNode;
export const focusedNodePathsSelector = (state: GlobalState) => state?.cr?.nodes?.focused?.contextPaths;

// This is internal, as in most cases you want `focusedNodePathSelector`, which is able to fallback to documentNode, when no node is focused
export const _focusedNodeContextPathSelector = createSelector(
Expand Down Expand Up @@ -39,7 +38,7 @@ export const hasFocusedContentNode = createSelector(
);

export const nodeByContextPath = (state: GlobalState) => (contextPath: NodeContextPath) =>
$get(['cr', 'nodes', 'byContextPath', contextPath], state);
state?.cr?.nodes?.byContextPath?.[contextPath];

export const makeGetDocumentNodes = (nodeTypesRegistry: NodeTypesRegistry) => createSelector(
[
Expand Down Expand Up @@ -68,14 +67,14 @@ export const makeGetDocumentNodes = (nodeTypesRegistry: NodeTypesRegistry) => cr

export const makeGetNodeByContextPathSelector = (contextPath: NodeContextPath) => createSelector(
[
(state: GlobalState) => $get(['cr', 'nodes', 'byContextPath', contextPath], state)
(state: GlobalState) => state?.cr?.nodes?.byContextPath?.[contextPath]
],
node => node
);

export const makeHasChildrenSelector = (allowedNodeTypes: NodeTypeName[]) => createSelector(
[
(state: GlobalState, contextPath: NodeContextPath) => $get(['cr', 'nodes', 'byContextPath', contextPath], state)
(state: GlobalState, contextPath: NodeContextPath) => state?.cr?.nodes?.byContextPath?.[contextPath]
],
node => (node && node.children || []).some(
childNodeEnvelope => childNodeEnvelope ? allowedNodeTypes.includes(childNodeEnvelope.nodeType) : false
Expand All @@ -84,7 +83,7 @@ export const makeHasChildrenSelector = (allowedNodeTypes: NodeTypeName[]) => cre

export const makeChildrenOfSelector = (allowedNodeTypes: NodeTypeName[]) => createSelector(
[
(state: GlobalState, contextPath: NodeContextPath) => $get(['cr', 'nodes', 'byContextPath', contextPath], state),
(state: GlobalState, contextPath: NodeContextPath) => state?.cr?.nodes?.byContextPath?.[contextPath],
nodesByContextPathSelector
],
(node, nodesByContextPath: NodeMap) => (node && node.children || [])
Expand Down Expand Up @@ -222,7 +221,7 @@ export const focusedGrandParentSelector = createSelector(
}
);

export const clipboardNodesContextPathsSelector = (state: GlobalState) => $get(['cr', 'nodes', 'clipboard'], state);
export const clipboardNodesContextPathsSelector = (state: GlobalState) => state?.cr?.nodes?.clipboard;

export const clipboardNodeContextPathSelector = createSelector(
[
Expand All @@ -238,11 +237,61 @@ export const clipboardIsEmptySelector = createSelector(
clipboardNodePath => Boolean(clipboardNodePath)
);

// TODO: deprecate
/**
* @TODO: Remove getPathInNode in version 10
* @deprecated getPathInNode will be removed in version 10. In the meantime
* please consider retrieving the node at `contextPath` via
* `selectors.CR.Nodes.byContextPathSelector` and accessing the
* property at `propertyPath` manually.
*/
export const getPathInNode = (state: GlobalState, contextPath: NodeContextPath, propertyPath: any) => {
const node = $get(['cr', 'nodes', 'byContextPath', contextPath], state);
console.warn(
'Neos UI Deprecation Warning: `selectors.CR.Nodes.getPathInNode` is' +
' deprecated and will be removed in version 10. In the meantime' +
' please consider retrieving the node at `contextPath` via' +
' `selectors.CR.Nodes.byContextPathSelector` and accessing the' +
' property at `propertyPath` manually.',
{contextPath, propertyPath}
);

const node = state?.cr?.nodes?.byContextPath?.[contextPath];

let resolvedPropertyPath: Array<string | number>;
switch (true) {
case Array.isArray(propertyPath):
resolvedPropertyPath = propertyPath as Array<string | number>;
break;
case typeof propertyPath === 'number':
resolvedPropertyPath = [propertyPath as number];
break;
case typeof propertyPath === 'string':
resolvedPropertyPath = (propertyPath as string)
.split('.')
.map(part => {
const partAsInteger = parseInt(part, 10);

if (
!isNaN(partAsInteger) &&
String(partAsInteger) === part
) {
return partAsInteger;
}

return part;
});
break;
default:
// As per plow-js:
// > This function returns the path, if it is neither
// > an array nor a string nor a number
//
// @see: https://codeberg.org/wbehncke/plow-js/src/commit/d82eb78234f2c47158d6541f98282e3464a240ac/src/projections/atoms/get/index.js#L9-L15
return propertyPath;
}

return $get(propertyPath, node);
return resolvedPropertyPath.reduce((subject, part) => {
return subject && (subject as any)[part];
}, node);
};

export const makeGetAllowedChildNodeTypesSelector = (nodeTypesRegistry: NodeTypesRegistry, elevator: (id: string, state: GlobalState) => string | null = id => id) => createSelector(
Expand All @@ -253,19 +302,20 @@ export const makeGetAllowedChildNodeTypesSelector = (nodeTypesRegistry: NodeType
}
const elevatedReference = elevator(reference, state);
if (elevatedReference) {
return $get(['cr', 'nodes', 'byContextPath', elevatedReference], state) || null;
return state?.cr?.nodes?.byContextPath[elevatedReference] || null;
}
return null;
},
(state: GlobalState, {reference}: {reference: NodeContextPath | null, role: string}) => {
if (reference === null) {
return null;
}
const parentReference = getPathInNode(state, reference, 'parent');
const parentReference =
state?.cr?.nodes?.byContextPath[reference]?.parent ?? null;
if (parentReference !== null) {
const elevatedReferenceParent = elevator(parentReference, state);
if (elevatedReferenceParent !== null) {
return $get(['cr', 'nodes', 'byContextPath', elevatedReferenceParent], state) || null;
return state?.cr?.nodes?.byContextPath?.[elevatedReferenceParent] || null;
}
}
return null;
Expand All @@ -287,7 +337,7 @@ export const makeGetAllowedChildNodeTypesSelector = (nodeTypesRegistry: NodeType
);

export const makeGetAllowedSiblingNodeTypesSelector = (nodeTypesRegistry: NodeTypesRegistry) =>
makeGetAllowedChildNodeTypesSelector(nodeTypesRegistry, (nodeContextPath, state) => getPathInNode(state, nodeContextPath, 'parent'));
makeGetAllowedChildNodeTypesSelector(nodeTypesRegistry, (nodeContextPath, state) => state?.cr?.nodes?.byContextPath[nodeContextPath]?.parent ?? null);

export const makeIsAllowedToAddChildOrSiblingNodes = (nodeTypesRegistry: NodeTypesRegistry) => createSelector(
[
Expand All @@ -300,15 +350,15 @@ export const makeIsAllowedToAddChildOrSiblingNodes = (nodeTypesRegistry: NodeTyp

export const makeCanBeCopiedAlongsideSelector = (nodeTypesRegistry: NodeTypesRegistry) => createSelector(
[
(state: GlobalState, {subject}: {subject: NodeContextPath | null}) => subject ? $get(['cr', 'nodes', 'byContextPath', subject], state) : false,
(state: GlobalState, {subject}: {subject: NodeContextPath | null}) => subject ? state?.cr?.nodes?.byContextPath?.[subject] : false,
makeGetAllowedSiblingNodeTypesSelector(nodeTypesRegistry)
],
(subjectNode, allowedNodeTypes) => subjectNode ? allowedNodeTypes.includes(subjectNode.nodeType) : false
);

export const makeCanBeCopiedIntoSelector = (nodeTypesRegistry: NodeTypesRegistry) => createSelector(
[
(state: GlobalState, {subject}: {subject: NodeContextPath | null}) => subject ? $get(['cr', 'nodes', 'byContextPath', subject], state) : false,
(state: GlobalState, {subject}: {subject: NodeContextPath | null}) => subject ? state?.cr?.nodes?.byContextPath?.[subject] : false,
makeGetAllowedChildNodeTypesSelector(nodeTypesRegistry)
],
(subjectNode, allowedNodeTypes) => subjectNode ? allowedNodeTypes.includes(subjectNode.nodeType) : false
Expand All @@ -333,7 +383,8 @@ export const makeCanBeMovedAlongsideSelector = (nodeTypesRegistry: NodeTypesRegi
return false;
}
const subjectPath = subject && subject.split('@')[0];
const referenceParent = getPathInNode(state, reference, 'parent') as string;
const referenceParent =
state?.cr?.nodes?.byContextPath[reference]?.parent ?? null;
if (!referenceParent) {
return false;
}
Expand Down Expand Up @@ -363,7 +414,7 @@ export const makeCanBePastedSelector = (nodeTypesRegistry: NodeTypesRegistry) =>
[
makeCanBeMovedSelector(nodeTypesRegistry),
makeCanBeCopiedSelector(nodeTypesRegistry),
(state: GlobalState) => $get(['cr', 'nodes', 'clipboardMode'], state)
(state: GlobalState) => state?.cr?.nodes?.clipboardMode
],
(canBeMoved, canBeCopied, mode) => mode === ClipboardMode.COPY ? canBeCopied : canBeMoved
);
Expand Down
22 changes: 18 additions & 4 deletions packages/neos-ui-redux-store/src/CR/Workspaces/index.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import {$set} from 'plow-js';

import {actionTypes, actions, reducer, selectors} from './index';
import {actionTypes as system} from '../../System/index';

Expand Down Expand Up @@ -36,7 +34,15 @@ test(`The reducer should create a valid initial state`, () => {
const state = {};
const nextState = reducer(state, {
type: system.INIT,
payload: $set('cr.workspaces.personalWorkspace', {initial: 'workspace-data'}, {})
payload: {
cr: {
workspaces: {
personalWorkspace: {
initial: 'workspace-data'
}
}
}
}
});

expect(nextState).toMatchSnapshot();
Expand All @@ -45,7 +51,15 @@ test(`The reducer should create a valid initial state`, () => {
test(`UPDATE should set personal workspace data to given data`, () => {
const state = reducer({}, {
type: system.INIT,
payload: $set('cr.workspaces.personalWorkspace', {initial: 'workspace-data'}, {})
payload: {
cr: {
workspaces: {
personalWorkspace: {
initial: 'workspace-data'
}
}
}
}
});
const action = actions.update({totally: 'different-workspace-data'});
const nextState = reducer(state, action);
Expand Down
9 changes: 4 additions & 5 deletions packages/neos-ui-redux-store/src/CR/Workspaces/selectors.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import {$get} from 'plow-js';
import {createSelector} from 'reselect';
import {documentNodeContextPathSelector} from '../Nodes/selectors';
import {GlobalState} from '../../System';
import {NodeContextPath} from '@neos-project/neos-ts-interfaces';

export const personalWorkspaceNameSelector = (state: GlobalState) => $get(['cr', 'workspaces', 'personalWorkspace', 'name'], state);
export const personalWorkspaceNameSelector = (state: GlobalState) => state?.cr?.workspaces?.personalWorkspace?.name;

export const baseWorkspaceSelector = (state: GlobalState) => $get(['cr', 'workspaces', 'personalWorkspace', 'baseWorkspace'], state);
export const baseWorkspaceSelector = (state: GlobalState) => state?.cr?.workspaces?.personalWorkspace?.baseWorkspace;

export const isWorkspaceReadOnlySelector = (state: GlobalState) => $get(['cr', 'workspaces', 'personalWorkspace', 'readOnly'], state) || false;
export const isWorkspaceReadOnlySelector = (state: GlobalState) => state?.cr?.workspaces?.personalWorkspace?.readOnly || false;

export const publishableNodesSelector = (state: GlobalState) => $get(['cr', 'workspaces', 'personalWorkspace', 'publishableNodes'], state);
export const publishableNodesSelector = (state: GlobalState) => state?.cr?.workspaces?.personalWorkspace?.publishableNodes;

export const publishableNodesInDocumentSelector = createSelector(
[
Expand Down
3 changes: 1 addition & 2 deletions packages/neos-ui-redux-store/src/System/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import produce from 'immer';
import {$get} from 'plow-js';
import {action as createAction, ActionType} from 'typesafe-actions';
import {State as UIState} from './../UI/index';
import {State as UserState} from './../User/index';
Expand Down Expand Up @@ -71,5 +70,5 @@ export const reducer = (state: State = defaultState, action: Action) => produce(
// Export the selectors
//
export const selectors = {
authenticationTimeout: (state: any) => $get(['system'], state).authenticationTimeout
authenticationTimeout: (state: any) => state?.system?.authenticationTimeout
};
3 changes: 1 addition & 2 deletions packages/neos-ui-redux-store/src/UI/AddNodeModal/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import produce from 'immer';
import {action as createAction, ActionType} from 'typesafe-actions';
import {$get} from 'plow-js';

import {actionTypes as system, InitAction} from '../../System';
import {NodeContextPath, FusionPath} from '@neos-project/neos-ts-interfaces';
Expand Down Expand Up @@ -70,7 +69,7 @@ export enum errorMessages {
export const reducer = (state: State = defaultState, action: InitAction | Action) => produce(state, draft => {
switch (action.type) {
case system.INIT: {
draft.toggledGroups = $get(['payload', 'ui', 'addNodeModal', 'toggledGroups'], action) || [];
draft.toggledGroups = action?.payload?.ui?.addNodeModal?.toggledGroups || [];
break;
}
case actionTypes.OPEN: {
Expand Down
Loading

0 comments on commit 9a47f88

Please sign in to comment.