Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/8.4' into 9.0
Browse files Browse the repository at this point in the history
  • Loading branch information
mhsdesign committed Aug 23, 2024
2 parents 7638e49 + 22cd1c6 commit 9e47fdf
Show file tree
Hide file tree
Showing 35 changed files with 943 additions and 64 deletions.
3 changes: 2 additions & 1 deletion Classes/Controller/BackendServiceController.php
Original file line number Diff line number Diff line change
Expand Up @@ -710,7 +710,8 @@ public function flowQueryAction(array $chain): string
),
'getForTreeWithParents' => $nodeInfoHelper->renderNodesWithParents(
array_filter($flowQuery->get()),
$this->request
$this->request,
$finisher['payload']['nodeTypeFilter'] ?? null
),
default => []
};
Expand Down
4 changes: 2 additions & 2 deletions Classes/Fusion/Helper/NodeInfoHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ public function renderNodes(
* @param array<int,?array<string,mixed>> $nodes
* @return array<int,?array<string,mixed>>
*/
public function renderNodesWithParents(array $nodes, ActionRequest $actionRequest): array
public function renderNodesWithParents(array $nodes, ActionRequest $actionRequest, ?string $nodeTypeFilter = null): array
{
// For search operation we want to include all nodes, not respecting the "baseNodeType" setting
$baseNodeTypeOverride = $this->documentNodeTypeRole;
Expand All @@ -285,7 +285,7 @@ public function renderNodesWithParents(array $nodes, ActionRequest $actionReques
} elseif ($renderedNode = $this->renderNodeWithMinimalPropertiesAndChildrenInformation(
$node,
$actionRequest,
$baseNodeTypeOverride
$nodeTypeFilter ?? $baseNodeTypeOverride
)) {
$renderedNode['matched'] = true;
$renderedNodes[$node->aggregateId->value] = $renderedNode;
Expand Down
259 changes: 259 additions & 0 deletions Tests/IntegrationTests/Fixtures/1Dimension/nodeTreePresets.e2e.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
import {beforeEach, checkPropTypes} from './../../utils.js';
import {Page} from './../../pageModel';
import {Selector} from 'testcafe';

/* global fixture:true */

fixture`Node Tree Presets`
.beforeEach(beforeEach)
.afterEach(() => checkPropTypes())
.before(async () => {
const response = await fetch('http://127.0.0.1:8081/test/write-additional-settings', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({settings: SETTINGS_WITH_NODE_TREE_PRESETS})
});
const json = await response.json();
if (!('success' in json) || !json.success) {
throw new Error('Additional settings could not be written.');
}
})
.after(async () => {
const response = await fetch('http://127.0.0.1:8081/test/remove-additional-settings', {
method: 'POST'
});
const json = await response.json();
if (!('success' in json) || !json.success) {
throw new Error('Additional settings could not be removed.');
}
});

const SETTINGS_WITH_NODE_TREE_PRESETS = {
Neos: {
Neos: {
userInterface: {
navigateComponent: {
nodeTree: {
loadingDepth: 2,
presets: {
'default': {
baseNodeType: 'Neos.Neos:Document,!Neos.TestNodeTypes:Document.Blog,!Neos.TestNodeTypes:Document.BlogArticle'
},
'blog': {
ui: {
icon: 'newspaper-o',
label: 'Show Blog only'
},
baseNodeType: 'Neos.TestNodeTypes:Document.Blog'
},
'blog-articles': {
ui: {
icon: 'file-text-o',
label: 'Show Blog Articles only'
},
baseNodeType: 'Neos.TestNodeTypes:Document.BlogArticle'
}
}
}
}
}
}
}
};

fixture.skip`TODO Reimplement / Fix Node Tree Presets for 9.0 https://github.com/neos/neos-ui/issues/3702`;

test('Node tree preset "default" removes all blog related nodes and only loads nodes with depth <= 2', async (t) => {
//
// Assert that all documents with a depth > 2 are no longer visible in
// "default" preset
//
await t.expect(Page.treeNode.withExactText('Nested Page #2').exists)
.notOk('[🗋 Nested Page #2] can still be found in the document tree with preset "default".');

//
// Assert that all the blog-related documents are not visible in "default"
// preset
//
await t.expect(Page.treeNode.withExactText('Blog').exists)
.notOk('[🗋 Blog] can still be found in the document tree with preset "default".');
await t.expect(Page.treeNode.withExactText('Hello World!').exists)
.notOk('[🗋 Hello World!] can still be found in the document tree with preset "default".');
await t.expect(Page.treeNode.withExactText('Fix all the bugs with this weird little trick!').exists)
.notOk('[🗋 Fix all the bugs with this weird little trick!] can still be found in the document tree with preset "default".');
await t.expect(Page.treeNode.withExactText('Writing Blog Articles considered harmful').exists)
.notOk('[🗋 Writing Blog Articles considered harmful] can still be found in the document tree with preset "default".');
});

test('Node tree preset "blog" shows nothing but page [🗋 Blog]', async (t) => {
await t.click('#btn-ToggleDocumentTreeFilter');
await t.click('#neos-NodeTreeFilter');
await t.click(Selector('[role="button"]').withText('Show Blog only'));

await t.expect(Page.treeNode.withExactText('Blog').exists)
.ok('[🗋 Blog] did not show up after switching to node tree preset "blog".');
});

test('In node tree preset "blog", page [🗋 Blog] has no toggle handle', async (t) => {
await t.click('#btn-ToggleDocumentTreeFilter');
await t.click('#neos-NodeTreeFilter');
await t.click(Selector('[role="button"]').withText('Show Blog only'));

await t.expect(Page.getToggleChildrenButtonOf('Blog').exists)
.notOk('[🗋 Blog] has a toggle handle, even though its children do not match the currently set filter in node tree preset "blog".');
});

test('Reloading the node tree while in preset "blog" results in nothing but page [🗋 Blog]', async (t) => {
await t.click('#btn-ToggleDocumentTreeFilter');
await t.click('#neos-NodeTreeFilter');
await t.click(Selector('[role="button"]').withText('Show Blog only'));
await t.click('#neos-PageTree-RefreshPageTree');

await t.expect(Page.treeNode.withExactText('Blog').exists)
.ok('[🗋 Blog] did not show up after switching to node tree preset "blog".');
});

test('Node tree preset "blog-articles" shows page [🗋 Blog] and all articles beneath it', async (t) => {
await t.click('#btn-ToggleDocumentTreeFilter');
await t.click('#neos-NodeTreeFilter');
await t.click(Selector('[role="button"]').withText('Show Blog Articles only'));

await t.expect(Page.treeNode.withExactText('Blog').exists)
.ok('[🗋 Blog] did not show up after switching to node tree preset "blog-articles".');
await t.expect(Page.treeNode.withExactText('Hello World!').exists)
.ok('[🗋 Hello World!] did not show up after switching to node tree preset "blog-articles".');
await t.expect(Page.treeNode.withExactText('Fix all the bugs with this weird little trick!').exists)
.ok('[🗋 Fix all the bugs with this weird little trick!] did not show up after switching to node tree preset "blog-articles".');
await t.expect(Page.treeNode.withExactText('Writing Blog Articles considered harmful').exists)
.ok('[🗋 Writing Blog Articles considered harmful] did not show up after switching to node tree preset "blog-articles".');
});

//
// Original issue: https://github.com/neos/neos-ui/issues/3816
//
test('BUG #3816: Switching back from node tree preset "blog" does not affect loading children in node tree preset "default"', async (t) => {
await t.click(Page.getToggleChildrenButtonOf('Nested Page #1'));
await t.expect(Page.treeNode.withExactText('Nested Page #2').exists)
.ok('[🗋 Nested Page #2] did not show up after toggling children of [🗋 Nested Page #1] the first time.');
await t.click(Page.getToggleChildrenButtonOf('Nested Page #1'));
await t.expect(Page.treeNode.withExactText('Nested Page #2').exists)
.notOk('[🗋 Nested Page #2] did not disappear after toggling children of [🗋 Nested Page #1] the second time.');

await t.click('#btn-ToggleDocumentTreeFilter');
await t.click('#neos-NodeTreeFilter');
await t.click(Selector('[role="button"]').withText('Show Blog only'));
await t.expect(Page.treeNode.withExactText('Blog').exists)
.ok('[🗋 Blog] did not show up after switching to node tree preset "blog".');

await t.click('#neos-NodeTreeFilter-SelectBox-btn-reset');
await t.expect(Page.treeNode.withExactText('Blog').exists)
.notOk('[🗋 Blog] did not disappear after switching back to node tree preset "default".');

await t.click(Page.getToggleChildrenButtonOf('Nested Page #1'));
await t.expect(Page.treeNode.withExactText('Nested Page #2').exists)
.ok('[🗋 Nested Page #2] did not show up after toggling children of [🗋 Nested Page #1] the third time.');
await t.click(Page.getToggleChildrenButtonOf('Nested Page #1'));
await t.expect(Page.treeNode.withExactText('Nested Page #2').exists)
.notOk('[🗋 Nested Page #2] did not disappear after toggling children of [🗋 Nested Page #1] the fourth time.');
});

//
// Original issue: https://github.com/neos/neos-ui/issues/2583
//
test('BUG #2583: Searching the document tree does not break expansion in node tree preset "default"', async (t) => {
await t.click(Page.getToggleChildrenButtonOf('Nested Page #1'));
await t.expect(Page.treeNode.withExactText('Nested Page #2').exists)
.ok('[🗋 Nested Page #2] did not show up after toggling children of [🗋 Nested Page #1] the first time.');
await t.click(Page.getToggleChildrenButtonOf('Nested Page #1'));
await t.expect(Page.treeNode.withExactText('Nested Page #2').exists)
.notOk('[🗋 Nested Page #2] did not disappear after toggling children of [🗋 Nested Page #1] the second time.');

await t.click('#btn-ToggleDocumentTreeFilter');

await t.typeText(Selector('#neos-NodeTreeSearchInput input[type="search"]'), 'Nested Page #10');
await t.expect(Page.treeNode.withExactText('Nested Page #10').exists)
.ok('[🗋 Nested Page #10] did not show up after searching for "Nested Page #10".');

await t.click('#neos-NodeTreeSearchInput-btn-reset');
await t.expect(Page.treeNode.withExactText('Nested Page #10').exists)
.notOk('[🗋 Nested Page #10] did not disappear after clearing search.');

await t.click(Page.getToggleChildrenButtonOf('Nested Page #1'));
await t.expect(Page.treeNode.withExactText('Nested Page #2').exists)
.ok('[🗋 Nested Page #2] did not show up after toggling children of [🗋 Nested Page #1] the third time.');
await t.click(Page.getToggleChildrenButtonOf('Nested Page #1'));
await t.expect(Page.treeNode.withExactText('Nested Page #2').exists)
.notOk('[🗋 Nested Page #2] did not disappear after toggling children of [🗋 Nested Page #1] the fourth time.');
});

//
// Original issue: https://github.com/neos/neos-ui/issues/2800
//
test('BUG #2800 1/2: Moving pages before/after in a filtered view does not lead to the disappearance of nodes', async (t) => {
await t.click('#btn-ToggleDocumentTreeFilter');
await t.click('#neos-NodeTreeFilter');
await t.click(Selector('[role="button"]').withText('Show Blog Articles only'));

//
// Move Blog Article [🗋 Hello World!] before [🗋 Writing Blog Articles considered harmful]
//
await t.dragToElement(
Page.getTreeNodeButton('Hello World!'),
Page.getTreeNodeButton('Writing Blog Articles considered harmful').prevSibling()
);

await t.expect(Page.treeNode.withExactText('Blog').exists)
.ok('[🗋 Blog] disappeared after moving nodes in node tree preset "blog-articles".');
await t.expect(Page.treeNode.withExactText('Hello World!').exists)
.ok('[🗋 Hello World!] disappeared after moving nodes in node tree preset "blog-articles".');
await t.expect(Page.treeNode.withExactText('Fix all the bugs with this weird little trick!').exists)
.ok('[🗋 Fix all the bugs with this weird little trick!] disappeared after moving nodes in node tree preset "blog-articles".');
await t.expect(Page.treeNode.withExactText('Writing Blog Articles considered harmful').exists)
.ok('[🗋 Writing Blog Articles considered harmful] disappeared after moving nodes in node tree preset "blog-articles".');

//
// Move Blog Article [🗋 Hello World!] after [🗋 Writing Blog Articles considered harmful]
//
await t.dragToElement(
Page.getTreeNodeButton('Hello World!'),
Page.getTreeNodeButton('Writing Blog Articles considered harmful').nextSibling()
);

await t.expect(Page.treeNode.withExactText('Blog').exists)
.ok('[🗋 Blog] disappeared after moving nodes in node tree preset "blog-articles".');
await t.expect(Page.treeNode.withExactText('Hello World!').exists)
.ok('[🗋 Hello World!] disappeared after moving nodes in node tree preset "blog-articles".');
await t.expect(Page.treeNode.withExactText('Fix all the bugs with this weird little trick!').exists)
.ok('[🗋 Fix all the bugs with this weird little trick!] disappeared after moving nodes in node tree preset "blog-articles".');
await t.expect(Page.treeNode.withExactText('Writing Blog Articles considered harmful').exists)
.ok('[🗋 Writing Blog Articles considered harmful] disappeared after moving nodes in node tree preset "blog-articles".');
});

test('BUG #2800 2/2: Moving pages into each other in a filtered view does not break expansion', async (t) => {
await t.click('#btn-ToggleDocumentTreeFilter');
await t.click('#neos-NodeTreeFilter');
await t.click(Selector('[role="button"]').withText('Show Blog Articles only'));

//
// Move Blog Article [🗋 Hello World!] into [🗋 Writing Blog Articles considered harmful]
//
await t.dragToElement(
Page.getTreeNodeButton('Hello World!'),
Page.getTreeNodeButton('Writing Blog Articles considered harmful')
);

await t.expect(Page.treeNode.withExactText('Blog').exists)
.ok('[🗋 Blog] disappeared after moving nodes in node tree preset "blog-articles".');
await t.expect(Page.treeNode.withExactText('Fix all the bugs with this weird little trick!').exists)
.ok('[🗋 Fix all the bugs with this weird little trick!] disappeared after moving nodes in node tree preset "blog-articles".');
await t.expect(Page.treeNode.withExactText('Writing Blog Articles considered harmful').exists)
.ok('[🗋 Writing Blog Articles considered harmful] disappeared after moving nodes in node tree preset "blog-articles".');

await t.expect(Page.treeNode.withExactText('Hello World!').exists)
.notOk('[🗋 Hello World!] unexpectedly appeared before uncollapsing [🗋 Writing Blog Articles considered harmful].');
await t.click(Page.getToggleChildrenButtonOf('Writing Blog Articles considered harmful'));
await t.expect(Page.treeNode.withExactText('Hello World!').exists)
.ok('[🗋 Writing Blog Articles considered harmful] cannot be uncollapsed after [🗋 Hello World!] was moved into it in node tree preset "blog-articles".');
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

/*
* This file is part of the Neos.Neos.Ui package.
*
* (c) Contributors of the Neos Project - www.neos.io
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* source code.
*/

declare(strict_types=1);

namespace Neos\TestNodeTypes\Application\RemoveAdditionalSettings\Controller;

use Neos\Flow\Annotations as Flow;
use Neos\Flow\Mvc\ActionRequest;
use Neos\Flow\Mvc\ActionResponse;
use Neos\Flow\Mvc\Controller\ControllerInterface;
use Neos\TestNodeTypes\Application\RemoveAdditionalSettings\RemoveAdditionalSettingsCommand;
use Neos\TestNodeTypes\Application\RemoveAdditionalSettings\RemoveAdditionalSettingsCommandHandler;

#[Flow\Scope("singleton")]
final class RemoveAdditionalSettingsController implements ControllerInterface
{
#[Flow\Inject]
protected RemoveAdditionalSettingsCommandHandler $commandHandler;

public function processRequest(ActionRequest $request, ActionResponse $response)
{
$request->setDispatched(true);
$response->setContentType('application/json');

try {
$command = RemoveAdditionalSettingsCommand::fromArray($request->getArguments());
$this->commandHandler->handle($command);

$response->setStatusCode(200);
$response->setContent(
json_encode(
['success' => true],
JSON_THROW_ON_ERROR
)
);
} catch (\InvalidArgumentException $e) {
$response->setStatusCode(400);
$response->setContent(
json_encode(
['error' => [
'type' => $e::class,
'code' => $e->getCode(),
'message' => $e->getMessage(),
]],
JSON_THROW_ON_ERROR
)
);
} catch (\Exception $e) {
$response->setStatusCode(500);
$response->setContent(
json_encode(
['error' => [
'type' => $e::class,
'code' => $e->getCode(),
'message' => $e->getMessage(),
]],
JSON_THROW_ON_ERROR
)
);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

/*
* This file is part of the Neos.Neos.Ui package.
*
* (c) Contributors of the Neos Project - www.neos.io
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* source code.
*/

declare(strict_types=1);

namespace Neos\TestNodeTypes\Application\RemoveAdditionalSettings;

use Neos\Flow\Annotations as Flow;

#[Flow\Proxy(false)]
final class RemoveAdditionalSettingsCommand
{
public function __construct(
) {
}

public static function fromArray(array $array): self
{
return new self();
}
}
Loading

0 comments on commit 9e47fdf

Please sign in to comment.