diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 132368388f..9761c1120e 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -18,7 +18,7 @@ jobs: node-version-file: '.nvmrc' cache: 'yarn' - name: Install - run: make install + run: make install-and-verify - name: Lint run: make lint @@ -32,7 +32,7 @@ jobs: node-version-file: '.nvmrc' cache: 'yarn' - name: Install - run: make install + run: make install-and-verify - name: Build run: make build-production - name: Test diff --git a/Build/Jenkins/release-neos-ui.sh b/Build/Jenkins/release-neos-ui.sh index 0b624ee20a..786d0e4fb3 100755 --- a/Build/Jenkins/release-neos-ui.sh +++ b/Build/Jenkins/release-neos-ui.sh @@ -54,10 +54,8 @@ make test VERSION=$VERSION make bump-version VERSION=$VERSION NPM_TOKEN=$NPM_TOKEN make publish-npm -# add changes to git and push -git add . -git commit -m "Updating composer dependency and npm versions for release of $VERSION" +# we do not commit our working dir (the changes to the `version` field, as they will cause conflicts) +# see https://github.com/neos/neos-ui/issues/3778 -git push origin HEAD:$BRANCH git tag -a -m "$VERSION" $VERSION git push origin $VERSION diff --git a/Classes/Controller/BackendServiceController.php b/Classes/Controller/BackendServiceController.php index 81f6285642..0932b11248 100644 --- a/Classes/Controller/BackendServiceController.php +++ b/Classes/Controller/BackendServiceController.php @@ -465,6 +465,7 @@ public function changeBaseWorkspaceAction(string $targetWorkspaceName, string $d // If current document node doesn't exist in the base workspace, // traverse its parents to find the one that exists + // todo ensure that https://github.com/neos/neos-ui/pull/3734 doesnt need to be refixed in Neos 9.0 $redirectNode = $documentNode; while (true) { $redirectNodeInBaseWorkspace = $subgraph->findNodeById($redirectNode->nodeAggregateId); diff --git a/Makefile b/Makefile index 68452e83e0..34a0e5e342 100644 --- a/Makefile +++ b/Makefile @@ -60,6 +60,10 @@ check-requirements: install: ## Install dependencies yarn install +install-and-verify: + # Validate this project because were using Zero-Installs (slightly safer as we accept external PRs) + yarn install --immutable --immutable-cache --check-cache + setup: check-requirements install build ## Run a clean setup @echo Please remember to set frontendDevelopmentMode \ to true in your Settings.yaml. diff --git a/Resources/Private/Translations/de/Main.xlf b/Resources/Private/Translations/de/Main.xlf index 9554fce299..c0e0bffb67 100644 --- a/Resources/Private/Translations/de/Main.xlf +++ b/Resources/Private/Translations/de/Main.xlf @@ -638,6 +638,10 @@ Add EinfΓΌgen + + Collapse All + Alle Ordner zuklappen + diff --git a/Resources/Private/Translations/en/Main.xlf b/Resources/Private/Translations/en/Main.xlf index 810ab1cb43..43fb072c93 100644 --- a/Resources/Private/Translations/en/Main.xlf +++ b/Resources/Private/Translations/en/Main.xlf @@ -377,6 +377,9 @@ For more information about the error please refer to the JavaScript console. + + Collapse All + Copy node type to clipboard diff --git a/Resources/Private/Translations/es/Main.xlf b/Resources/Private/Translations/es/Main.xlf index 82ddbeadaf..d7fb87efc3 100644 --- a/Resources/Private/Translations/es/Main.xlf +++ b/Resources/Private/Translations/es/Main.xlf @@ -538,6 +538,9 @@ Add AΓ±adir + + Copy node type to clipboard + Copiar tipo de nodo al portapapeles Synchronize personal workspace Sincronizar el espacio de trabajo personal diff --git a/Tests/IntegrationTests/Fixtures/1Dimension/issue-3184.e2e.js b/Tests/IntegrationTests/Fixtures/1Dimension/issue-3184.e2e.js new file mode 100644 index 0000000000..8e8d20e7b2 --- /dev/null +++ b/Tests/IntegrationTests/Fixtures/1Dimension/issue-3184.e2e.js @@ -0,0 +1,150 @@ +import {beforeEach, checkPropTypes} from './../../utils.js'; +import {Page, PublishDropDown} from './../../pageModel'; +import { Selector } from 'testcafe'; + +/* global fixture:true */ + +// +// Original Issue: https://github.com/neos/neos-ui/issues/3184 +// +fixture`FIX #3184: Discarded node move changes are reflected correctly in the document tree` + .beforeEach(beforeEach) + .afterEach(() => checkPropTypes()); + +// +// This is an excerpt of the document tree in our E2E test distribution, +// stripped down to only show the relevant document nodes for this test +// scenario: +// +// πŸ—‹ Home +// β”œβ”€ πŸ—‹ Discarding +// └─ πŸ—‹ Tree multiselect +// β”œβ”€ πŸ—‹ MultiA +// β”œβ”€ πŸ—‹ MultiB +// └─ πŸ—‹ MultiC +// +// In reference to that hierarchy, we're putting some selectors into variables +// for later use in the concrete test cases, so we don't have to repeat the +// long form over and over: +// +const Discarding = Page.getTreeNodeButton('Discarding'); +const MultiA = Page.getTreeNodeButton('MultiA'); +const MultiB = Page.getTreeNodeButton('MultiB'); +const MultiC = Page.getTreeNodeButton('MultiC'); +const withCmdClick = { + modifiers: { + ctrl: true + } +}; + +test( + 'Scenario #1: Moving nodes and then discarding that change does not lead to an error', + async t => { + // + // Select πŸ—‹ MultiA and πŸ—‹ MultiB, then drag both documents INTO πŸ—‹ MultiC. + // + await t + .click(MultiA) + .click(MultiB, withCmdClick) + .dragToElement(MultiA, MultiC); + + // + // Discard that change. + // + await PublishDropDown.discardAll(); + + // + // Assert that no error flash message shows up. + // + await t + .expect(Selector('[role="alert"][class*="error"]').exists) + .notOk(); + } +); + +test( + 'Scenario #2: Moved nodes do not just disappear after discarding the move change', + async t => { + // + // Select πŸ—‹ MultiA and πŸ—‹ MultiB, then drag both documents INTO πŸ—‹ Discarding. + // + await t + .click(MultiA) + .click(MultiB, withCmdClick) + .dragToElement(MultiA, Discarding); + + // + // Go to πŸ—‹ Tree multiselect, so we can check for stale metadata + // coming from the guest frame. + // We also need to reload to avoid Scenario #1. + // + await Page.goToPage('Tree multiselect'); + await t.eval(() => location.reload(true)); + await Page.waitForIframeLoading(); + + // + // Discard the move change and wait for the guest frame to reload plus + // some extra timeout, so there's a chance for stale metadata to + // overwrite the tree state. + // + await PublishDropDown.discardAll(); + await Page.waitForIframeLoading(); + await t.wait(500); + + // + // Assert that πŸ—‹ MultiA and πŸ—‹ MultiB can still be found. + // + await t + .expect(MultiA.exists) + .ok('πŸ—‹ MultiA has disappeared'); + await t + .expect(MultiB.exists) + .ok('πŸ—‹ MultiB has disappeared'); + } +) + +test( + 'Scenario #3: Discarding a move change while being on a moved node does not' + + ' lead to an error in the guest frame', + async t => { + // + // Select πŸ—‹ MultiA and πŸ—‹ MultiB, then drag both documents INTO πŸ—‹ MultiC. + // + await t + .click(MultiA) + .click(MultiB, withCmdClick) + .dragToElement(MultiA, MultiC); + + // + // Go to πŸ—‹ Home and reload the backend, so we avoid Scenario #1's + // underlying issue. + // + await Page.goToPage('Home'); + await t.eval(() => location.reload(true)); + + // + // Go to πŸ—‹ MultiA, so we are on a moved node in the guest frame. + // + await Page.goToPage('MultiA'); + + // + // Discard the move change. + // + await PublishDropDown.discardAll(); + + // + // Assert that there's no error showing up in the guest frame and + // that we're instead seeing the next-higher document node. + // + await Page.waitForIframeLoading(); + await t.switchToIframe('[name="neos-content-main"]'); + await t + .expect(Selector('.neos-error-screen').exists) + .notOk('There\'s an unexpected error screen in the guest frame.'); + + await t.switchToMainWindow(); + await t + .expect(Selector('[role="treeitem"] [role="button"][class*="isFocused"]').textContent) + .eql('MultiC'); + } +); diff --git a/Tests/IntegrationTests/TestDistribution/composer.json b/Tests/IntegrationTests/TestDistribution/composer.json index 97773a76be..d65a2abe02 100644 --- a/Tests/IntegrationTests/TestDistribution/composer.json +++ b/Tests/IntegrationTests/TestDistribution/composer.json @@ -5,8 +5,7 @@ "vendor-dir": "Packages/Libraries", "bin-dir": "bin", "allow-plugins": { - "neos/composer-plugin": true, - "cweagans/composer-patches": true + "neos/composer-plugin": true } }, "minimum-stability": "dev", @@ -25,15 +24,17 @@ "cweagans/composer-patches": "^1.7.3" }, + "extra": { + "patches": { + "neos/neos-development-collection": { + } + } + }, "require-dev": { "neos/buildessentials": "@dev", "phpunit/phpunit": "^9.0", "phpstan/phpstan": "^1.10" }, - "extra": { - "patches": { - } - }, "repositories": { "distribution": { "type": "path", diff --git a/package.json b/package.json index 982b544610..18f96d449a 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,6 @@ "bugs": "https://github.com/neos/neos-ui/issues", "homepage": "https://github.com/neos/neos-ui/blob/master/README.md", "license": "GNU GPLv3", - "version": "9.0.0-beta8", "private": true, "resolutions": { "moment": "^2.20.1", @@ -44,6 +43,5 @@ "jest": { "preset": "@neos-project/jest-preset-neos-ui" }, - "packageManager": "yarn@3.2.0", - "stableVersion": "8.3.7" + "packageManager": "yarn@3.2.0" } diff --git a/packages/debug-reason-for-rendering/package.json b/packages/debug-reason-for-rendering/package.json index b145840b96..559162b2f4 100644 --- a/packages/debug-reason-for-rendering/package.json +++ b/packages/debug-reason-for-rendering/package.json @@ -1,6 +1,5 @@ { "name": "@neos-project/debug-reason-for-rendering", - "version": "9.0.0-beta8", "description": "React Performance Optimization Utility - Why does a component re-render?", "repository": "neos/neos-ui", "bugs": "https://github.com/neos/neos-ui/issues", @@ -9,6 +8,5 @@ "license": "MIT", "peerDependencies": { "react": "^16.12.0" - }, - "stableVersion": "8.3.7" + } } diff --git a/packages/jest-preset-neos-ui/package.json b/packages/jest-preset-neos-ui/package.json index da83632911..d959df2db4 100644 --- a/packages/jest-preset-neos-ui/package.json +++ b/packages/jest-preset-neos-ui/package.json @@ -1,6 +1,5 @@ { "name": "@neos-project/jest-preset-neos-ui", - "version": "9.0.0-beta8", "description": "The jest preset for all packages of the neos-ui mono-repo.", "main": "jest-preset.json", "private": true, @@ -13,6 +12,5 @@ "peerDependencies": { "enzyme": "^3.8.0" }, - "license": "GNU GPLv3", - "stableVersion": "8.3.7" + "license": "GNU GPLv3" } diff --git a/packages/neos-ts-interfaces/package.json b/packages/neos-ts-interfaces/package.json index d736816a67..0e7eb7538b 100644 --- a/packages/neos-ts-interfaces/package.json +++ b/packages/neos-ts-interfaces/package.json @@ -1,6 +1,5 @@ { "name": "@neos-project/neos-ts-interfaces", - "version": "9.0.0-beta8", "description": "Neos domain-related TypeScript interfaces", "private": true, "main": "src/index.ts", @@ -9,6 +8,5 @@ "@neos-project/neos-ui-build": "workspace:*", "typescript": "^4.6.4" }, - "license": "GNU GPLv3", - "stableVersion": "8.3.7" + "license": "GNU GPLv3" } diff --git a/packages/neos-ui-backend-connector/package.json b/packages/neos-ui-backend-connector/package.json index 156d4e783f..bfc50f65ab 100644 --- a/packages/neos-ui-backend-connector/package.json +++ b/packages/neos-ui-backend-connector/package.json @@ -1,6 +1,5 @@ { "name": "@neos-project/neos-ui-backend-connector", - "version": "9.0.0-beta8", "description": "Endoints and fetch cals to the Neos CMS backend", "private": true, "main": "./src/index.ts", @@ -12,6 +11,5 @@ "@neos-project/neos-ts-interfaces": "workspace:*", "@neos-project/utils-helpers": "workspace:*" }, - "license": "GNU GPLv3", - "stableVersion": "8.3.7" + "license": "GNU GPLv3" } diff --git a/packages/neos-ui-build/package.json b/packages/neos-ui-build/package.json index 1a7990f450..c9222a197a 100644 --- a/packages/neos-ui-build/package.json +++ b/packages/neos-ui-build/package.json @@ -1,7 +1,5 @@ { "name": "@neos-project/neos-ui-build", - "version": "9.0.0-beta8", "description": "Bob der Baumeister", - "private": true, - "stableVersion": "8.3.7" + "private": true } diff --git a/packages/neos-ui-ckeditor5-bindings/package.json b/packages/neos-ui-ckeditor5-bindings/package.json index a0c985136c..d88a202ce6 100644 --- a/packages/neos-ui-ckeditor5-bindings/package.json +++ b/packages/neos-ui-ckeditor5-bindings/package.json @@ -1,6 +1,5 @@ { "name": "@neos-project/neos-ui-ckeditor5-bindings", - "version": "9.0.0-beta8", "description": "Prepare CKEditor5 for the Neos CMS UI", "private": true, "main": "./src/manifest.js", @@ -34,6 +33,5 @@ "react": "^16.12.0", "react-redux": "^7.1.3" }, - "license": "GNU GPLv3", - "stableVersion": "8.3.7" + "license": "GNU GPLv3" } diff --git a/packages/neos-ui-constants/package.json b/packages/neos-ui-constants/package.json index 0f3b93e974..9f926cfb63 100644 --- a/packages/neos-ui-constants/package.json +++ b/packages/neos-ui-constants/package.json @@ -1,6 +1,5 @@ { "name": "@neos-project/neos-ui-constants", - "version": "9.0.0-beta8", "description": "Container package to store Neos CMS UI constants", "private": true, "main": "./src/index.js", @@ -8,6 +7,5 @@ "@neos-project/jest-preset-neos-ui": "workspace:*", "typescript": "^4.6.4" }, - "license": "GNU GPLv3", - "stableVersion": "8.3.7" + "license": "GNU GPLv3" } diff --git a/packages/neos-ui-containers/package.json b/packages/neos-ui-containers/package.json index 3874068311..a245dd5eb0 100644 --- a/packages/neos-ui-containers/package.json +++ b/packages/neos-ui-containers/package.json @@ -1,6 +1,5 @@ { "name": "@neos-project/neos-ui-containers", - "version": "9.0.0-beta8", "description": "Smart components for Neos CMS UI.", "private": true, "main": "./src/index.js", @@ -18,6 +17,5 @@ "prop-types": "^15.5.10", "react": "^16.12.0" }, - "license": "GNU GPLv3", - "stableVersion": "8.3.7" + "license": "GNU GPLv3" } diff --git a/packages/neos-ui-contentrepository/package.json b/packages/neos-ui-contentrepository/package.json index e144f96fd1..2652ed8ccd 100644 --- a/packages/neos-ui-contentrepository/package.json +++ b/packages/neos-ui-contentrepository/package.json @@ -1,6 +1,5 @@ { "name": "@neos-project/neos-ui-contentrepository", - "version": "9.0.0-beta8", "description": "Bindings for the Neos ContentRepository", "private": true, "main": "./src/manifest.js", @@ -21,6 +20,5 @@ "setupFiles": [ "../jest-preset-neos-ui/src/setupNeosUiHostEnv.js" ] - }, - "stableVersion": "8.3.7" + } } diff --git a/packages/neos-ui-decorators/package.json b/packages/neos-ui-decorators/package.json index 445be0733a..beb73a7822 100644 --- a/packages/neos-ui-decorators/package.json +++ b/packages/neos-ui-decorators/package.json @@ -1,6 +1,5 @@ { "name": "@neos-project/neos-ui-decorators", - "version": "9.0.0-beta8", "description": "Decorators for Neos CMS UI.", "private": true, "main": "./src/index.ts", @@ -16,6 +15,5 @@ "@neos-project/neos-ts-interfaces": "workspace:*", "reselect": "^3.0.1" }, - "license": "GNU GPLv3", - "stableVersion": "8.3.7" + "license": "GNU GPLv3" } diff --git a/packages/neos-ui-editors/package.json b/packages/neos-ui-editors/package.json index b68f244d92..c6881b0e97 100644 --- a/packages/neos-ui-editors/package.json +++ b/packages/neos-ui-editors/package.json @@ -1,6 +1,5 @@ { "name": "@neos-project/neos-ui-editors", - "version": "9.0.0-beta8", "description": "Neos CMS UI Editors for use in the inspector.", "main": "src/manifest.js", "private": true, @@ -42,6 +41,5 @@ "react-redux": "^7.1.3", "redux": "^4.0.5" }, - "license": "GNU GPLv3", - "stableVersion": "8.3.7" + "license": "GNU GPLv3" } diff --git a/packages/neos-ui-extensibility-webpack-adapter/package.json b/packages/neos-ui-extensibility-webpack-adapter/package.json index 59f0d1c256..b3477845ab 100644 --- a/packages/neos-ui-extensibility-webpack-adapter/package.json +++ b/packages/neos-ui-extensibility-webpack-adapter/package.json @@ -1,6 +1,5 @@ { "name": "@neos-project/neos-ui-extensibility-webpack-adapter", - "version": "9.0.0-beta8", "description": "Minimal configuration, highly opinionated Webpack 4 + Babel plugin build stack for the Neos CMS UI", "repository": "neos/neos-ui", "bugs": "https://github.com/neos/neos-ui/issues", @@ -36,6 +35,5 @@ "babel-core": "^6.26.3", "typescript": "^4.6.4" } - }, - "stableVersion": "8.3.7" + } } diff --git a/packages/neos-ui-extensibility/package.json b/packages/neos-ui-extensibility/package.json index 6c2b10d47f..d7a550dce3 100644 --- a/packages/neos-ui-extensibility/package.json +++ b/packages/neos-ui-extensibility/package.json @@ -1,6 +1,5 @@ { "name": "@neos-project/neos-ui-extensibility", - "version": "9.0.0-beta8", "description": "Core of the extensibility mechanisms for the Neos UI", "repository": "neos/neos-ui", "bugs": "https://github.com/neos/neos-ui/issues", @@ -22,6 +21,5 @@ }, "dependencies": { "@neos-project/positional-array-sorter": "workspace:*" - }, - "stableVersion": "8.3.7" + } } diff --git a/packages/neos-ui-guest-frame/package.json b/packages/neos-ui-guest-frame/package.json index 4178bd746a..95c384135a 100644 --- a/packages/neos-ui-guest-frame/package.json +++ b/packages/neos-ui-guest-frame/package.json @@ -1,6 +1,5 @@ { "name": "@neos-project/neos-ui-guest-frame", - "version": "9.0.0-beta8", "description": "Guest frame initialization bindings", "private": true, "main": "./src/manifest.js", @@ -28,6 +27,5 @@ "react": "^16.12.0", "react-redux": "^7.1.3" }, - "license": "GNU GPLv3", - "stableVersion": "8.3.7" + "license": "GNU GPLv3" } diff --git a/packages/neos-ui-i18n/package.json b/packages/neos-ui-i18n/package.json index 81b82ce079..d8dd910dbe 100644 --- a/packages/neos-ui-i18n/package.json +++ b/packages/neos-ui-i18n/package.json @@ -1,6 +1,5 @@ { "name": "@neos-project/neos-ui-i18n", - "version": "9.0.0-beta8", "description": "I18n utilities and components for Neos CMS UI.", "private": true, "main": "./src/index.tsx", @@ -18,6 +17,5 @@ "peerDependencies": { "react": "^16.12.0" }, - "license": "GNU GPLv3", - "stableVersion": "8.3.7" + "license": "GNU GPLv3" } diff --git a/packages/neos-ui-inspector/package.json b/packages/neos-ui-inspector/package.json index 5e0d20edbe..af059e8cbb 100644 --- a/packages/neos-ui-inspector/package.json +++ b/packages/neos-ui-inspector/package.json @@ -1,6 +1,5 @@ { "name": "@neos-project/neos-ui-inspector", - "version": "9.0.0-beta8", "description": "Components for integrating views and editors into the Neos CMS UI inspector.", "private": true, "main": "./src/index.js", @@ -19,6 +18,5 @@ "react": "^16.12.0", "react-redux": "^7.1.3" }, - "license": "GNU GPLv3", - "stableVersion": "8.3.7" + "license": "GNU GPLv3" } diff --git a/packages/neos-ui-redux-store/package.json b/packages/neos-ui-redux-store/package.json index 5fe648c6ef..e6b2b3d861 100644 --- a/packages/neos-ui-redux-store/package.json +++ b/packages/neos-ui-redux-store/package.json @@ -1,6 +1,5 @@ { "name": "@neos-project/neos-ui-redux-store", - "version": "9.0.0-beta8", "description": "Redux store implementation for the Neos CMS UI", "private": true, "main": "./src/index.ts", @@ -20,6 +19,5 @@ "reselect": "^3.0.1", "typesafe-actions": "^5.1.0" }, - "license": "GNU GPLv3", - "stableVersion": "8.3.7" + "license": "GNU GPLv3" } diff --git a/packages/neos-ui-redux-store/src/CR/Nodes/selectors.ts b/packages/neos-ui-redux-store/src/CR/Nodes/selectors.ts index ad1c01834f..f7b8e3cc0b 100644 --- a/packages/neos-ui-redux-store/src/CR/Nodes/selectors.ts +++ b/packages/neos-ui-redux-store/src/CR/Nodes/selectors.ts @@ -65,6 +65,67 @@ export const makeGetDocumentNodes = (nodeTypesRegistry: NodeTypesRegistry) => cr } ); +export const makeGetCollapsibleDocumentNodes = (nodeTypesRegistry: NodeTypesRegistry) => createSelector( + [ + nodesByContextPathSelector + ], + nodesMap => { + const documentRole = nodeTypesRegistry.getRole('document'); + if (!documentRole) { + throw new Error('Document role is not loaded!'); + } + const documentSubNodeTypes = nodeTypesRegistry.getSubTypesOf(documentRole); + + const result: NodeMap = {}; + Object.keys(nodesMap).forEach(contextPath => { + const node = nodesMap[contextPath]; + if (!node) { + throw new Error('This error should never be thrown, it\'s a way to fool TypeScript'); + } + const isCollapsible = node.children.some( + child => child ? documentSubNodeTypes.includes(child.nodeType) : false + ) + if (documentSubNodeTypes.includes(node.nodeType) && isCollapsible) { + result[contextPath] = node; + } + }); + return result; + } +); + +export const makeGetCollapsibleContentNodes = (nodeTypesRegistry: NodeTypesRegistry) => createSelector( + [ + nodesByContextPathSelector + ], + nodesMap => { + const contentRole = nodeTypesRegistry.getRole('content'); + const collectionRole = nodeTypesRegistry.getRole('contentCollection'); + if (!contentRole) { + throw new Error('Content role is not loaded!'); + } + if (!collectionRole) { + throw new Error('ContentCollection role is not loaded!'); + } + const contentSubNodeTypes = nodeTypesRegistry.getSubTypesOf(contentRole); + contentSubNodeTypes.push(...nodeTypesRegistry.getSubTypesOf(collectionRole)) + + const result: NodeMap = {}; + Object.keys(nodesMap).forEach(contextPath => { + const node = nodesMap[contextPath]; + if (!node) { + throw new Error('This error should never be thrown, it\'s a way to fool TypeScript'); + } + const isCollapsible = node.children.some( + child => child ? contentSubNodeTypes.includes(child.nodeType) : false + ) + if (contentSubNodeTypes.includes(node.nodeType) && isCollapsible) { + result[contextPath] = node; + } + }); + return result; + } +); + export const makeGetNodeByContextPathSelector = (contextPath: NodeContextPath) => createSelector( [ (state: GlobalState) => state?.cr?.nodes?.byContextPath?.[contextPath] diff --git a/packages/neos-ui-redux-store/src/UI/ContentTree/index.ts b/packages/neos-ui-redux-store/src/UI/ContentTree/index.ts index aa7c933115..f541403315 100644 --- a/packages/neos-ui-redux-store/src/UI/ContentTree/index.ts +++ b/packages/neos-ui-redux-store/src/UI/ContentTree/index.ts @@ -31,6 +31,7 @@ export enum actionTypes { REQUEST_CHILDREN = '@neos/neos-ui/UI/ContentTree/REQUEST_CHILDREN', SET_AS_LOADING = '@neos/neos-ui/UI/ContentTree/SET_AS_LOADING', SET_AS_LOADED = '@neos/neos-ui/UI/ContentTree/SET_AS_LOADED', + COLLAPSE_ALL = '@neos/neos-ui/UI/ContentTree/COLLAPSE_ALL' } const toggle = (contextPath: NodeContextPath) => createAction(actionTypes.TOGGLE, contextPath); @@ -40,6 +41,10 @@ const reloadTree = () => createAction(actionTypes.RELOAD_TREE); const requestChildren = (contextPath: NodeContextPath, {unCollapse = true, activate = false} = {}) => createAction(actionTypes.REQUEST_CHILDREN, {contextPath, opts: {unCollapse, activate}}); const setAsLoading = (contextPath: NodeContextPath) => createAction(actionTypes.SET_AS_LOADING, {contextPath}); const setAsLoaded = (contextPath: NodeContextPath) => createAction(actionTypes.SET_AS_LOADED, {contextPath}); +const collapseAll = ( + nodeContextPaths: NodeContextPath[], + collapsedByDefaultNodeContextPaths: NodeContextPath[] +) => createAction(actionTypes.COLLAPSE_ALL, {nodeContextPaths, collapsedByDefaultNodeContextPaths}); // // Export the actions @@ -51,7 +56,8 @@ export const actions = { reloadTree, requestChildren, setAsLoading, - setAsLoaded + setAsLoaded, + collapseAll }; export type Action = ActionType; @@ -89,6 +95,22 @@ export const reducer = (state: State = defaultState, action: InitAction | Action draft.loading = draft.loading.filter(i => i !== contextPath); break; } + case actionTypes.COLLAPSE_ALL: { + const {nodeContextPaths, collapsedByDefaultNodeContextPaths} = action.payload; + + nodeContextPaths.forEach(path => { + if (!draft.toggled.includes(path)) { + draft.toggled.push(path); + } + }); + + collapsedByDefaultNodeContextPaths.forEach(path => { + if (draft.toggled.includes(path)) { + draft.toggled = draft.toggled.filter(i => i !== path); + } + }); + break; + } } }); diff --git a/packages/neos-ui-redux-store/src/UI/PageTree/index.ts b/packages/neos-ui-redux-store/src/UI/PageTree/index.ts index 8b55561600..a321f7a5e3 100644 --- a/packages/neos-ui-redux-store/src/UI/PageTree/index.ts +++ b/packages/neos-ui-redux-store/src/UI/PageTree/index.ts @@ -40,7 +40,8 @@ export enum actionTypes { SET_AS_LOADED = '@neos/neos-ui/UI/PageTree/SET_AS_LOADED', REQUEST_CHILDREN = '@neos/neos-ui/UI/PageTree/REQUEST_CHILDREN', COMMENCE_SEARCH = '@neos/neos-ui/UI/PageTree/COMMENCE_SEARCH', - SET_SEARCH_RESULT = '@neos/neos-ui/UI/PageTree/SET_SEARCH_RESULT' + SET_SEARCH_RESULT = '@neos/neos-ui/UI/PageTree/SET_SEARCH_RESULT', + COLLAPSE_ALL = '@neos/neos-ui/UI/PageTree/COLLAPSE_ALL' } const focus = (contextPath: NodeContextPath, _: undefined, selectionMode: SelectionModeTypes = SelectionModeTypes.SINGLE_SELECT) => createAction(actionTypes.FOCUS, {contextPath, selectionMode}); @@ -49,6 +50,11 @@ const invalidate = (contextPath: NodeContextPath) => createAction(actionTypes.IN const requestChildren = (contextPath: NodeContextPath, {unCollapse = true, activate = false} = {}) => createAction(actionTypes.REQUEST_CHILDREN, {contextPath, opts: {unCollapse, activate}}); const setAsLoading = (contextPath: NodeContextPath) => createAction(actionTypes.SET_AS_LOADING, {contextPath}); const setAsLoaded = (contextPath: NodeContextPath) => createAction(actionTypes.SET_AS_LOADED, {contextPath}); +const collapseAll = ( + nodeContextPaths: NodeContextPath[], + collapsedByDefaultNodeContextPaths: NodeContextPath[] +) => createAction(actionTypes.COLLAPSE_ALL, {nodeContextPaths, collapsedByDefaultNodeContextPaths}); + interface CommenceSearchOptions extends Readonly<{ query: string; filterNodeType: string; @@ -72,7 +78,8 @@ export const actions = { setAsLoaded, requestChildren, commenceSearch, - setSearchResult + setSearchResult, + collapseAll }; export type Action = ActionType; @@ -134,6 +141,22 @@ export const reducer = (state: State = defaultState, action: InitAction | Action draft.filterNodeType = action.payload.filterNodeType; break; } + case actionTypes.COLLAPSE_ALL: { + const {nodeContextPaths, collapsedByDefaultNodeContextPaths} = action.payload; + + nodeContextPaths.forEach(path => { + if (!draft.toggled.includes(path)) { + draft.toggled.push(path); + } + }); + + collapsedByDefaultNodeContextPaths.forEach(path => { + if (draft.toggled.includes(path)) { + draft.toggled = draft.toggled.filter(i => i !== path); + } + }); + break; + } } }); diff --git a/packages/neos-ui-sagas/package.json b/packages/neos-ui-sagas/package.json index 5c4ba1a515..d8cbc7f3d6 100644 --- a/packages/neos-ui-sagas/package.json +++ b/packages/neos-ui-sagas/package.json @@ -1,6 +1,5 @@ { "name": "@neos-project/neos-ui-sagas", - "version": "9.0.0-beta8", "description": "Redux Sagas implementation for the Neos CMS UI", "private": true, "main": "src/index.js", @@ -18,6 +17,5 @@ "mousetrap": "^1.6.3", "redux-saga": "^0.15.0" }, - "license": "GNU GPLv3", - "stableVersion": "8.3.7" + "license": "GNU GPLv3" } diff --git a/packages/neos-ui-validators/package.json b/packages/neos-ui-validators/package.json index 5c3122c777..def2f8e9fb 100644 --- a/packages/neos-ui-validators/package.json +++ b/packages/neos-ui-validators/package.json @@ -1,6 +1,5 @@ { "name": "@neos-project/neos-ui-validators", - "version": "9.0.0-beta8", "description": "Validators for Neos CMS UI.", "private": true, "main": "./src/index.ts", @@ -18,6 +17,5 @@ "peerDependencies": { "react": "^16.12.0" }, - "license": "GNU GPLv3", - "stableVersion": "8.3.7" + "license": "GNU GPLv3" } diff --git a/packages/neos-ui-views/package.json b/packages/neos-ui-views/package.json index 0bfefe8851..321a0b2cdd 100644 --- a/packages/neos-ui-views/package.json +++ b/packages/neos-ui-views/package.json @@ -1,6 +1,5 @@ { "name": "@neos-project/neos-ui-views", - "version": "9.0.0-beta8", "description": "Neos CMS UI Views for use in the inspector.", "main": "./src/index.js", "private": true, @@ -27,6 +26,5 @@ "react": "^16.12.0", "react-redux": "^7.1.3" }, - "license": "GNU GPLv3", - "stableVersion": "8.3.7" + "license": "GNU GPLv3" } diff --git a/packages/neos-ui/package.json b/packages/neos-ui/package.json index bb4614fe8b..28cb341efe 100644 --- a/packages/neos-ui/package.json +++ b/packages/neos-ui/package.json @@ -1,6 +1,5 @@ { "name": "@neos-project/neos-ui", - "version": "9.0.0-beta8", "description": "Neos CMS UI written in ReactJS and a ton of other fun technology.", "private": true, "devDependencies": { @@ -65,6 +64,5 @@ "reselect": "^3.0.1", "uuid": "^3.3.2" }, - "license": "GNU GPLv3", - "stableVersion": "8.3.7" + "license": "GNU GPLv3" } diff --git a/packages/neos-ui/src/Containers/LeftSideBar/NodeTree/index.js b/packages/neos-ui/src/Containers/LeftSideBar/NodeTree/index.js index 8d0764bd78..2c91381a12 100644 --- a/packages/neos-ui/src/Containers/LeftSideBar/NodeTree/index.js +++ b/packages/neos-ui/src/Containers/LeftSideBar/NodeTree/index.js @@ -12,6 +12,7 @@ import {dndTypes} from '@neos-project/neos-ui-constants'; import {PageTreeNode, ContentTreeNode} from './Node/index'; import style from './style.module.css'; +import {neos} from '@neos-project/neos-ui-decorators'; const ConnectedDragLayer = connect((state, {currentlyDraggedNodes}) => { const getNodeByContextPath = selectors.CR.Nodes.nodeByContextPath(state); @@ -27,11 +28,15 @@ export default class NodeTree extends PureComponent { allowOpeningNodesInNewWindow: PropTypes.bool, nodeTypeRole: PropTypes.string, toggle: PropTypes.func, + collapseAll: PropTypes.func, focus: PropTypes.func, requestScrollIntoView: PropTypes.func, setActiveContentCanvasSrc: PropTypes.func, setActiveContentCanvasContextPath: PropTypes.func, - moveNodes: PropTypes.func + moveNodes: PropTypes.func, + allCollapsibleNodes: PropTypes.object, + loadingDepth: PropTypes.number, + i18nRegistry: PropTypes.object.isRequired }; state = { @@ -44,6 +49,25 @@ export default class NodeTree extends PureComponent { toggle(contextPath); } + handleCollapseAll = () => { + const {collapseAll, allCollapsibleNodes, rootNode, loadingDepth} = this.props + let nodeContextPaths = [] + const collapsedByDefaultNodesContextPaths = [] + + Object.values(allCollapsibleNodes).forEach(node => { + const collapsedByDefault = loadingDepth === 0 ? false : node.depth - rootNode.depth >= loadingDepth + if (collapsedByDefault) { + collapsedByDefaultNodesContextPaths.push(node.contextPath) + } else { + nodeContextPaths.push(node.contextPath) + } + }); + + // Do not Collapse RootNode + nodeContextPaths = nodeContextPaths.filter(i => i !== rootNode.contextPath); + collapseAll(nodeContextPaths, collapsedByDefaultNodesContextPaths); + } + handleFocus = (contextPath, metaKeyPressed, altKeyPressed, shiftKeyPressed) => { const {focus} = this.props; @@ -107,7 +131,7 @@ export default class NodeTree extends PureComponent { } render() { - const {rootNode, ChildRenderer} = this.props; + const {rootNode, ChildRenderer, i18nRegistry} = this.props; if (!rootNode) { return (
@@ -122,6 +146,13 @@ export default class NodeTree extends PureComponent { return ( + ({ - rootNode: selectors.CR.Nodes.siteNodeSelector(state), - focusedNodesContextPaths: selectors.UI.PageTree.getAllFocused(state), - ChildRenderer: PageTreeNode, - allowOpeningNodesInNewWindow: true -}), { - toggle: actions.UI.PageTree.toggle, - focus: actions.UI.PageTree.focus, - setActiveContentCanvasSrc: actions.UI.ContentCanvas.setSrc, - setActiveContentCanvasContextPath: actions.CR.Nodes.setDocumentNode, - moveNodes: actions.CR.Nodes.moveMultiple, - requestScrollIntoView: null -}, (stateProps, dispatchProps, ownProps) => { - return Object.assign({}, stateProps, dispatchProps, ownProps); -})(NodeTree); - -export const ContentTree = connect(state => ({ - rootNode: selectors.CR.Nodes.documentNodeSelector(state), - focusedNodesContextPaths: selectors.CR.Nodes.focusedNodePathsSelector(state), - ChildRenderer: ContentTreeNode, - allowOpeningNodesInNewWindow: false -}), { - toggle: actions.UI.ContentTree.toggle, - focus: actions.CR.Nodes.focus, - moveNodes: actions.CR.Nodes.moveMultiple, - requestScrollIntoView: actions.UI.ContentCanvas.requestScrollIntoView -}, (stateProps, dispatchProps, ownProps) => { - return Object.assign({}, stateProps, dispatchProps, ownProps); -})(NodeTree); +const withNodeTypeRegistryAndI18nRegistry = neos(globalRegistry => ({ + nodeTypesRegistry: globalRegistry.get('@neos-project/neos-ui-contentrepository'), + i18nRegistry: globalRegistry.get('i18n') +})); + +export const PageTree = withNodeTypeRegistryAndI18nRegistry(connect( + (state, {neos, nodeTypesRegistry}) => { + const documentNodesSelector = selectors.CR.Nodes.makeGetCollapsibleDocumentNodes(nodeTypesRegistry); + return ({ + rootNode: selectors.CR.Nodes.siteNodeSelector(state), + focusedNodesContextPaths: selectors.UI.PageTree.getAllFocused(state), + ChildRenderer: PageTreeNode, + allowOpeningNodesInNewWindow: true, + loadingDepth: neos.configuration.structureTree.loadingDepth, + allCollapsibleNodes: documentNodesSelector(state) + }) + }, { + toggle: actions.UI.PageTree.toggle, + collapseAll: actions.UI.PageTree.collapseAll, + focus: actions.UI.PageTree.focus, + setActiveContentCanvasSrc: actions.UI.ContentCanvas.setSrc, + setActiveContentCanvasContextPath: actions.CR.Nodes.setDocumentNode, + moveNodes: actions.CR.Nodes.moveMultiple, + requestScrollIntoView: null, + isContentTree: false + }, (stateProps, dispatchProps, ownProps) => { + return Object.assign({}, stateProps, dispatchProps, ownProps); + } +)(NodeTree)); + +export const ContentTree = withNodeTypeRegistryAndI18nRegistry(connect( + (state, {neos, nodeTypesRegistry}) => { + const contentNodesSelector = selectors.CR.Nodes.makeGetCollapsibleContentNodes(nodeTypesRegistry); + return ({ + rootNode: selectors.CR.Nodes.documentNodeSelector(state), + focusedNodesContextPaths: selectors.CR.Nodes.focusedNodePathsSelector(state), + ChildRenderer: ContentTreeNode, + allowOpeningNodesInNewWindow: false, + loadingDepth: neos.configuration.structureTree.loadingDepth, + allCollapsibleNodes: contentNodesSelector(state) + }) + }, { + toggle: actions.UI.ContentTree.toggle, + collapseAll: actions.UI.ContentTree.collapseAll, + focus: actions.CR.Nodes.focus, + moveNodes: actions.CR.Nodes.moveMultiple, + requestScrollIntoView: actions.UI.ContentCanvas.requestScrollIntoView, + isContentTree: true + }, (stateProps, dispatchProps, ownProps) => { + return Object.assign({}, stateProps, dispatchProps, ownProps); + } +)(NodeTree)); diff --git a/packages/neos-ui/src/Containers/LeftSideBar/NodeTree/style.module.css b/packages/neos-ui/src/Containers/LeftSideBar/NodeTree/style.module.css index 96f33a7a7f..89daf292db 100644 --- a/packages/neos-ui/src/Containers/LeftSideBar/NodeTree/style.module.css +++ b/packages/neos-ui/src/Containers/LeftSideBar/NodeTree/style.module.css @@ -6,7 +6,31 @@ background: var(--colors-ContrastDarker); border-bottom: 1px solid var(--colors-ContrastDark); border-right: 1px solid var(--colors-ContrastDark); + position: relative; } .loader { margin: var(--spacing-Quarter); } +.collapseAll { + position: absolute; + right: 0; + top: 0; + opacity: .5; + cursor: pointer; + z-index: 5; + padding: var(--spacing-Half); + background-color: transparent; + border: 0; + + .collapseAllIcon { + font-size: 1.2em; + } + + &:hover > .collapseAllIcon { + color: var(--colors-PrimaryBlueHover); + } + + &:hover { + opacity: 1; + } +} diff --git a/packages/positional-array-sorter/package.json b/packages/positional-array-sorter/package.json index a3ad85b7a0..853b0e4cad 100644 --- a/packages/positional-array-sorter/package.json +++ b/packages/positional-array-sorter/package.json @@ -1,6 +1,5 @@ { "name": "@neos-project/positional-array-sorter", - "version": "9.0.0-beta8", "description": "Flexible array sorter that sorts an array according to a 'position' meta data.", "repository": "neos/neos-ui", "bugs": "https://github.com/neos/neos-ui/issues", @@ -19,6 +18,5 @@ "devDependencies": { "@neos-project/jest-preset-neos-ui": "workspace:*", "typescript": "^4.6.4" - }, - "stableVersion": "8.3.7" + } } diff --git a/packages/react-proptypes/package.json b/packages/react-proptypes/package.json index c61e22b943..109bbbe1b5 100644 --- a/packages/react-proptypes/package.json +++ b/packages/react-proptypes/package.json @@ -1,6 +1,5 @@ { "name": "@neos-project/react-proptypes", - "version": "9.0.0-beta8", "description": "Neos CMS specific proptypes for react", "private": true, "main": "./src/index.js", @@ -11,6 +10,5 @@ "peerDependencies": { "prop-types": "^15.5.10" }, - "license": "GNU GPLv3", - "stableVersion": "8.3.7" + "license": "GNU GPLv3" } diff --git a/packages/react-ui-components/package.json b/packages/react-ui-components/package.json index 1010b0be0f..aad105e179 100644 --- a/packages/react-ui-components/package.json +++ b/packages/react-ui-components/package.json @@ -1,6 +1,5 @@ { "name": "@neos-project/react-ui-components", - "version": "9.0.0-beta8", "description": "The UI components which power the Neos backend application.", "repository": "neos/neos-ui", "bugs": "https://github.com/neos/neos-ui/issues", @@ -95,6 +94,5 @@ "stylelint": "^13.7.2", "typescript": "^4.6.0" }, - "license": "GNU GPLv3", - "stableVersion": "8.3.7" + "license": "GNU GPLv3" } diff --git a/packages/utils-helpers/package.json b/packages/utils-helpers/package.json index 9cb34891ab..e7f30a340a 100644 --- a/packages/utils-helpers/package.json +++ b/packages/utils-helpers/package.json @@ -1,6 +1,5 @@ { "name": "@neos-project/utils-helpers", - "version": "9.0.0-beta8", "description": "Helper functions for Neos CMS UI.", "private": true, "main": "./src/index.ts", @@ -11,6 +10,5 @@ "@neos-project/jest-preset-neos-ui": "workspace:*", "typescript": "^4.6.4" }, - "license": "GNU GPLv3", - "stableVersion": "8.3.7" + "license": "GNU GPLv3" } diff --git a/packages/utils-logger/package.json b/packages/utils-logger/package.json index fff849102c..1366a6cfea 100644 --- a/packages/utils-logger/package.json +++ b/packages/utils-logger/package.json @@ -1,6 +1,5 @@ { "name": "@neos-project/utils-logger", - "version": "9.0.0-beta8", "description": "Logger implementation for Neos CMS UI.", "private": true, "main": "./src/index.ts", @@ -11,6 +10,5 @@ "@neos-project/jest-preset-neos-ui": "workspace:*", "typescript": "^4.6.4" }, - "license": "GNU GPLv3", - "stableVersion": "8.3.7" + "license": "GNU GPLv3" } diff --git a/packages/utils-redux/package.json b/packages/utils-redux/package.json index cce8e9e8e9..3d2e893102 100644 --- a/packages/utils-redux/package.json +++ b/packages/utils-redux/package.json @@ -1,6 +1,5 @@ { "name": "@neos-project/utils-redux", - "version": "9.0.0-beta8", "description": "Neos CMS UI Redux Helper utilitites", "private": true, "main": "./src/index.ts", @@ -8,6 +7,5 @@ "@neos-project/jest-preset-neos-ui": "workspace:*", "typescript": "^4.6.4" }, - "license": "GNU GPLv3", - "stableVersion": "8.3.7" + "license": "GNU GPLv3" }