diff --git a/.circleci/config.yml b/.circleci/config.yml index 6c5370233d..41f32f1b68 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,4 +1,4 @@ -version: 2.0 +version: 2.1 aliases: - &workspace_root ~/neos-ui-workspace @@ -40,6 +40,9 @@ aliases: paths: - . +orbs: + gh: circleci/github-cli@2.3.0 + jobs: checkout: docker: @@ -90,9 +93,20 @@ jobs: MYSQL_ROOT_PASSWORD: not_a_real_password working_directory: *workspace_root steps: + - checkout - attach_workspace: *attach_workspace - restore_cache: *restore_app_cache + - gh/install + - run: + name: Login to GitHub + command: | + echo $AUTH_TOKEN_GITHUB | gh auth login --with-token + - run: + name: Install Sauce Connect + command: | + curl -L -o sauce-connect.deb https://saucelabs.com/downloads/sauce-connect/5.1.3/sauce-connect_5.1.3.linux_amd64.deb + sudo dpkg -i sauce-connect.deb - run: rm -rf /home/circleci/app/Packages/Application/Neos.Neos.Ui - run: cd /home/circleci/app/Packages/Application && mv ~/neos-ui-workspace Neos.Neos.Ui - run: | @@ -110,6 +124,27 @@ jobs: - run: curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.34.0/install.sh | bash - run: chmod +x ~/.nvm/nvm.sh - run: + name: Start Sauce Connect + background: true + command: sc run --username ${SAUCE_USERNAME} --access-key ${SAUCE_ACCESS_KEY} --tunnel-name "circleci-tunnel" --region "us-west-1" --proxy-localhost allow + - run: + name: Define target branch + command: | + # Get the target branch of the PR but when we are not one a PR, use the current branch + if [ -n "$CIRCLE_PULL_REQUEST" ]; then + TARGET_BRANCH=$(gh pr view $CIRCLE_PULL_REQUEST --json baseRefName --jq '.baseRefName') + else + TARGET_BRANCH=$CIRCLE_BRANCH + fi + echo "Target Branch: $TARGET_BRANCH" + # Save the variable to BASH_ENV to be able to access it in the next steps + echo "export TARGET_BRANCH=$TARGET_BRANCH" >> $BASH_ENV + - run: + name: Use target branch + command: | + echo "Using target branch: $TARGET_BRANCH" + - run: + name: Prepare and run e2e tests no_output_timeout: 30m command: | export NVM_DIR="$HOME/.nvm" @@ -119,7 +154,7 @@ jobs: nvm use echo 127.0.0.1 onedimension.localhost | sudo tee -a /etc/hosts echo 127.0.0.1 twodimensions.localhost | sudo tee -a /etc/hosts - make test-e2e-saucelabs > /home/circleci/app/Data/Logs/AcceptanceTesting.log + make test-e2e-saucelabs - store_artifacts: path: /home/circleci/app/Data/Logs - persist_to_workspace: @@ -127,31 +162,6 @@ jobs: paths: - . - post-acceptance-tests-recordings: - environment: - FLOW_CONTEXT: Production - docker: - - image: cimg/php:8.2-node - - steps: - - attach_workspace: - at: /home/circleci/app - - run: - name: Install GitHub CLI and jq - command: | - type -p curl >/dev/null || (sudo apt update && sudo apt install curl -y) - curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \ - && sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \ - && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \ - && sudo apt update \ - && sudo apt install gh -y - - run: - name: Run Script - command: | - JOB_IDS=$(cat /home/circleci/app/AcceptanceTesting.log | grep -o 'https://app.saucelabs.com/tests/[a-zA-Z0-9]\+' | sed 's/.*\///') - echo "Job IDs: $JOB_IDS" - /home/circleci/app/Build/comment-acceptance-tests.sh "$JOB_IDS" "$(basename "$CIRCLE_PULL_REQUEST")" - php-unittests: environment: FLOW_CONTEXT: Production @@ -204,9 +214,6 @@ workflows: - e2e: requires: - build_flow_app - - post-acceptance-tests-recordings: - requires: - - e2e - php-unittests: requires: - build_flow_app diff --git a/.sauce/config.yml b/.sauce/config.yml new file mode 100644 index 0000000000..6b465d7be3 --- /dev/null +++ b/.sauce/config.yml @@ -0,0 +1,52 @@ +apiVersion: v1alpha +kind: testcafe +showConsoleLog: true +sauce: + region: us-west-1 + concurrency: 1 # Controls how many suites are executed at the same time. + # todo fix and enable retries + retries: 0 + metadata: + tags: + - e2e + - $TARGET_BRANCH + build: $TARGET_BRANCH + tunnel: + name: "circleci-tunnel" +testcafe: + version: 3.6.2 +# Controls what files are available in the context of a test run (unless explicitly excluded by .sauceignore). +rootDir: ./ +suites: + - name: "Tests in Firefox on Windows" + browserName: "firefox" + src: + - "Tests/IntegrationTests/Fixtures/*/*.e2e.js" + platformName: "Windows 10" + screenResolution: "1280x1024" + - name: "Tests in Firefox on MacOS" + # todo use chrome here and fix ci https://github.com/neos/neos-ui/issues/3591 + browserName: "firefox" + src: + - "Tests/IntegrationTests/Fixtures/*/*.e2e.js" + platformName: "macOS 13" + screenResolution: "1440x900" +npm: + dependencies: + - testcafe-react-selectors + +# Controls what artifacts to fetch when the suites have finished. +artifacts: + download: + match: + - neosui-test-report.json + - console.log + - sauce-test-report.json + when: always + allAttempts: true + directory: ../../Data/Logs/saucelabs-artifacts/ + +reporters: + json: + enabled: true + filename: neosui-test-report.json diff --git a/.sauceignore b/.sauceignore new file mode 100644 index 0000000000..915fe0496a --- /dev/null +++ b/.sauceignore @@ -0,0 +1,12 @@ +# This file instructs saucectl to not package any files mentioned here. +.git/ +.github/ +.DS_Store +.hg/ +.vscode/ +.idea/ +.gitignore +.hgignore +.gitlab-ci.yml +.npmrc +*.gif diff --git a/Build/Jenkins/update-neos-ui-compiled.sh b/Build/Jenkins/update-neos-ui-compiled.sh index e16e0132bc..a3e2401c20 100755 --- a/Build/Jenkins/update-neos-ui-compiled.sh +++ b/Build/Jenkins/update-neos-ui-compiled.sh @@ -35,7 +35,7 @@ export NODE_OPTIONS="--max-old-space-size=4096" nvm install && nvm use make clean && make setup -NEOS_UI_VERSION="${GIT_TAG:-${GIT_BRANCH}-dev}" make build-production +NEOS_UI_VERSION="${GIT_TAG:-${GIT_BRANCH#*/}-dev}" make build-production rm -Rf tmp_compiled_pkg git clone git@github.com:neos/neos-ui-compiled.git tmp_compiled_pkg diff --git a/Build/comment-acceptance-tests.sh b/Build/comment-acceptance-tests.sh deleted file mode 100755 index 5973e58170..0000000000 --- a/Build/comment-acceptance-tests.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/bin/bash - -# Check if the required parameters are provided -if [ $# -ne 2 ]; then - echo "Usage: $0 " - exit 1 -fi - -# If no comment with recordings exists, create a new comment -# create a function names createNewComment with the comment body as parameter - -function generateCommentBody() { - # Split the JobID string into an array - IFS=$'\n' read -r -d '' -a jobIdArray <<< "$jobIds" - echo "Generate comment message for following JobIDs: ${jobIdArray[@]}" - - # Iterate over each JobID in the array - for i in ${!jobIdArray[@]}; do - iterator=$(($i+1)) - jobId="${jobIdArray[$i]}" - link="[Recording $iterator](https://app.saucelabs.com/rest/v1/jobs/$jobId/video.mp4)" - videoRecordingsLinks+="\n* $link" - done - - # Construct the comment with the latest acceptance test recordings - # Construct the comment with the latest acceptance test recordings - if [ -n "$videoRecordingsLinks" ]; then - commentBody="🎥 **End-to-End Test Recordings**\n\n$videoRecordingsLinks\n\nThese videos demonstrate the end-to-end tests for the changes in this pull request." - else - # empty comment body to prevent a comment without recordings - commentBody="" - fi -} - -# Check if a comment with recordings already exists -function getExistingComment() { - echo "Checking if a comment with recordings already exists..." - existingComment=$(gh pr view --repo neos/neos-ui $pullRequestNumber --json comments | jq -r ".comments[] | select( .body | contains(\"End-to-End Test Recordings\"))") -} - -function createComment() { - echo "Creating new comment..." - gh pr comment --repo neos/neos-ui $pullRequestNumber --body "$(printf "$commentBody")" -} - -# If a comment with recordings exists, update the existing comment -function updateComment() { - # Note: The gh cli does not support editing comments yet, so we have to use the GitHub API directly - echo "Updating existing comment..." - commentUri=$(echo "$existingComment" | jq -r ".url") - commentId=$(echo "$commentUri" | awk -F'#issuecomment-' '{print $2}') - jsonBody=$(jq -n --arg str "$(printf "$commentBody")" '{"body": $str}') - - curl -s -H "Authorization: token $GH_TOKEN" \ - -X PATCH -d "$jsonBody" \ - "https://api.github.com/repos/neos/neos-ui/issues/comments/$commentId" -} - -jobIds=$1 -pullRequestNumber=$2 -generateCommentBody -getExistingComment - -echo "Existing comment: $existingComment" -if [ -n "$existingComment" ]; then - updateComment -else - createComment -fi - -echo "Comment added to Pull Request #$pullRequestNumber with the latest acceptance test recordings." diff --git a/Makefile b/Makefile index c1a8d659e3..1fce7bc9b0 100644 --- a/Makefile +++ b/Makefile @@ -108,11 +108,11 @@ test: ## Executes integration tests on saucelabs. test-e2e-saucelabs: - bash Tests/IntegrationTests/e2e.sh "saucelabs:Firefox@latest:Windows 10" + bash Tests/IntegrationTests/e2e.sh --saucelabs ## Executes integration tests locally. test-e2e: - bash Tests/IntegrationTests/e2e.sh chrome:--disable-search-engine-choice-screen + bash Tests/IntegrationTests/e2e.sh --browser chrome:--disable-search-engine-choice-screen ## Executes integration tests locally in a docker-compose setup. # diff --git a/README.md b/README.md index 1f1d1e4e4e..2a53e02d3d 100644 --- a/README.md +++ b/README.md @@ -175,7 +175,7 @@ To speed up the e2e-test workflow/feedback loop you can start the system under t * The neos dev instance is available at `localhost:8081` * To enter the container run `docker compose -f Tests/IntegrationTests/docker-compose.neos-dev-instance.yaml exec php bash` * `yarn run testcafe ` - * for example, this runs all tests in chrome: (NOTE starting with Chrome 127, --disable-search-engine-choice-screen is needed) + * for example, this runs all tests in chrome: (NOTE starting with Chrome 127, --disable-search-engine-choice-screen is needed until https://github.com/DevExpress/testcafe/pull/8248 is released) `yarn run testcafe chrome:--disable-search-engine-choice-screen Tests/IntegrationTests/Fixtures/1Dimension` * some helpful optional flags are * `-T 'sidebars'` - grep tests by pattern and only execute those diff --git a/Tests/IntegrationTests/Fixtures/1Dimension/selectBoxes.e2e.js b/Tests/IntegrationTests/Fixtures/1Dimension/selectBoxes.e2e.js index ae67757496..0391dce32b 100644 --- a/Tests/IntegrationTests/Fixtures/1Dimension/selectBoxes.e2e.js +++ b/Tests/IntegrationTests/Fixtures/1Dimension/selectBoxes.e2e.js @@ -23,7 +23,6 @@ test('SelectBox opens below and breaks out of the creation dialog if there\'s en test('SelectBox opens above in creation dialog if there\'s not enough space below.', async t => { await t - .resizeWindow(1200, 768) .click(Selector('#neos-PageTree-AddNode')) .click(ReactSelector('NodeTypeItem').withExactText('SelectBox opens above')) .click(ReactSelector('NodeCreationDialog SelectBox')); diff --git a/Tests/IntegrationTests/TestDistribution/DistributionPackages/Neos.TestNodeTypes/NodeTypes/Document/SelectBoxTestPage/OpensAboveInInspector.yaml b/Tests/IntegrationTests/TestDistribution/DistributionPackages/Neos.TestNodeTypes/NodeTypes/Document/SelectBoxTestPage/OpensAboveInInspector.yaml index 5365e250e1..f89d08f1c3 100644 --- a/Tests/IntegrationTests/TestDistribution/DistributionPackages/Neos.TestNodeTypes/NodeTypes/Document/SelectBoxTestPage/OpensAboveInInspector.yaml +++ b/Tests/IntegrationTests/TestDistribution/DistributionPackages/Neos.TestNodeTypes/NodeTypes/Document/SelectBoxTestPage/OpensAboveInInspector.yaml @@ -10,6 +10,9 @@ groups: test: label: Test + # move it before the general document meta-data so there is + # also space below when opening it in the e2e tests + position: start properties: TextField: type: string diff --git a/Tests/IntegrationTests/e2e.sh b/Tests/IntegrationTests/e2e.sh index 91fcd2c594..491915f5f2 100755 --- a/Tests/IntegrationTests/e2e.sh +++ b/Tests/IntegrationTests/e2e.sh @@ -2,29 +2,112 @@ set -ex -if [ -z "$1" ]; then - echo "No testcafe browser supplied, e.g. 'chrome:headless'" -fi +# Global variables +BROWSER="" +USE_SAUCELABS=false -cd ../../.. +# Function to parse arguments and save values to global variables +parse_arguments() { + while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + print_usage_information + exit 0 + ;; + -s|--saucelabs) + USE_SAUCELABS=true + ;; + -b|--browser) + BROWSER="$2" + shift + ;; + *) + echo "Unknown option: $1" + print_usage_information + exit 1 + ;; + esac + shift + done +} -./flow cr:setup --content-repository onedimension -./flow cr:import --content-repository onedimension --path ./DistributionPackages/Neos.Test.OneDimension/Resources/Private/Content -# Connect to a Neos site, todo the nodeTypeName parameter is obsolete but necessary -./flow site:create neos-test-onedimension Neos.Test.OneDimension Neos.TestNodeTypes:Document.HomePage -./flow domain:add neos-test-onedimension onedimension.localhost --port 8081 +print_usage_information() { + cat < /dev/null; then + echo "saucectl is not installed. Installing saucectl..." + # Install saucectl via npm (assuming npm is installed) + npm install -g saucectl + fi +} + +function run_tests() { + cd ../../.. + + ./flow cr:setup --content-repository onedimension + ./flow cr:import --content-repository onedimension --path ./DistributionPackages/Neos.Test.OneDimension/Resources/Private/Content + # Connect to a Neos site, todo the nodeTypeName parameter is obsolete but necessary + ./flow site:create neos-test-onedimension Neos.Test.OneDimension Neos.TestNodeTypes:Document.HomePage + ./flow domain:add neos-test-onedimension onedimension.localhost --port 8081 + + ./flow cr:setup --content-repository twodimensions + ./flow cr:import --content-repository twodimensions --path ./DistributionPackages/Neos.Test.TwoDimensions/Resources/Private/Content + # Connect to a Neos site, todo the nodeTypeName parameter is obsolete but necessary + ./flow site:create neos-test-twodimensions Neos.Test.TwoDimensions Neos.TestNodeTypes:Document.HomePage + ./flow domain:add neos-test-twodimensions twodimensions.localhost --port 8081 + + ./flow resource:publish + + cd Packages/Application/Neos.Neos.Ui + + if [[ $BROWSER ]]; then + yarn run testcafe "$BROWSER" "Tests/IntegrationTests/Fixtures/*/*.e2e.js" \ + --selector-timeout=10000 --assertion-timeout=30000 + fi + + if [[ $USE_SAUCELABS ]]; then + saucectl run --config .sauce/config.yml + fi +} + +parse_arguments "$@" + +# check if incoming parameters are correct +check_testcafe_browser +check_saucelabs_setup + +run_tests diff --git a/esbuild.js b/esbuild.js index 5a77b17f00..b6f4cf0c83 100644 --- a/esbuild.js +++ b/esbuild.js @@ -32,7 +32,8 @@ const options = { legalComments: "linked", loader: { '.js': 'tsx', - '.svg': 'dataurl', + '.dataurl.svg': 'dataurl', + '.svg': 'text', '.vanilla-css': 'css', '.woff2': 'file' }, @@ -53,13 +54,6 @@ const options = { } }) - // load ckeditor icons as plain text and not via `.svg: dataurl` - // (currently neccessary for the table select handle icon) - onLoad({filter: /node_modules\/@ckeditor\/.*\.svg$/}, async ({path}) => ({ - contents: (await require('fs/promises').readFile(path)).toString(), - loader: 'text' - })) - // prefix Fontawesome with "neos-" to prevent clashes with customer Fontawesome onLoad({filter: /@fortawesome\/fontawesome-svg-core\/styles\.css$/}, async ({path}) => { const contents = (await require('fs/promises').readFile(path)).toString(); diff --git a/packages/neos-ui-ckeditor5-bindings/src/ckEditorApi.js b/packages/neos-ui-ckeditor5-bindings/src/ckEditorApi.js index 979a2ab5f5..7579373ac1 100644 --- a/packages/neos-ui-ckeditor5-bindings/src/ckEditorApi.js +++ b/packages/neos-ui-ckeditor5-bindings/src/ckEditorApi.js @@ -57,9 +57,12 @@ export const createEditor = store => async options => { return NeosEditor .create(propertyDomNode, ckEditorConfig) .then(editor => { + const debouncedOnChange = debounce(() => onChange(cleanupContentBeforeCommit(editor.getData())), 1500, {maxWait: 5000}); + editor.model.document.on('change:data', debouncedOnChange); editor.ui.focusTracker.on('change:isFocused', event => { if (!event.source.isFocused) { - onChange(cleanupContentBeforeCommit(editor.getData())) + // when another editor is focused commit all possible pending changes + debouncedOnChange.flush(); return } @@ -74,7 +77,6 @@ export const createEditor = store => async options => { }); editor.model.document.on('change', () => handleUserInteractionCallback()); - editor.model.document.on('change:data', debounce(() => onChange(cleanupContentBeforeCommit(editor.getData())), 1500, {maxWait: 5000})); return editor; }).catch(e => { if (e instanceof TypeError && e.message.match(/Class constructor .* cannot be invoked without 'new'/)) { diff --git a/packages/neos-ui-containers/src/InsertModeSelector/index.js b/packages/neos-ui-containers/src/InsertModeSelector/index.js index 70de7bce8d..a4df6b9148 100644 --- a/packages/neos-ui-containers/src/InsertModeSelector/index.js +++ b/packages/neos-ui-containers/src/InsertModeSelector/index.js @@ -1,12 +1,16 @@ import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; -import {ButtonGroup, Button, Icon} from '@neos-project/react-ui-components'; +import {ButtonGroup, Button, ResourceIcon} from '@neos-project/react-ui-components'; import {neos} from '@neos-project/neos-ui-decorators'; import I18n from '@neos-project/neos-ui-i18n'; import style from './style.module.css'; +import createAboveIcon from './resources/create-above.svg'; +import createBelowIcon from './resources/create-below.svg'; +import createInsideIcon from './resources/create-inside.svg'; + const MODE_AFTER = 'after'; const MODE_BEFORE = 'before'; const MODE_INTO = 'into'; @@ -98,7 +102,7 @@ export default class InsertModeSelector extends PureComponent { size="small" title={`${i18nRegistry.translate('Neos.Neos:Main:insert')} ${i18nRegistry.translate('above')}`} > - + diff --git a/Resources/Public/Icons/create-above.svg b/packages/neos-ui-containers/src/InsertModeSelector/resources/create-above.svg similarity index 100% rename from Resources/Public/Icons/create-above.svg rename to packages/neos-ui-containers/src/InsertModeSelector/resources/create-above.svg diff --git a/Resources/Public/Icons/create-below.svg b/packages/neos-ui-containers/src/InsertModeSelector/resources/create-below.svg similarity index 100% rename from Resources/Public/Icons/create-below.svg rename to packages/neos-ui-containers/src/InsertModeSelector/resources/create-below.svg diff --git a/Resources/Public/Icons/create-inside.svg b/packages/neos-ui-containers/src/InsertModeSelector/resources/create-inside.svg similarity index 100% rename from Resources/Public/Icons/create-inside.svg rename to packages/neos-ui-containers/src/InsertModeSelector/resources/create-inside.svg diff --git a/packages/neos-ui-editors/src/Editors/Image/Components/PreviewScreen/index.js b/packages/neos-ui-editors/src/Editors/Image/Components/PreviewScreen/index.js index ee77293da9..40980a39b2 100644 --- a/packages/neos-ui-editors/src/Editors/Image/Components/PreviewScreen/index.js +++ b/packages/neos-ui-editors/src/Editors/Image/Components/PreviewScreen/index.js @@ -5,6 +5,7 @@ import {AssetUpload} from '../../../../Library/index'; import {Thumbnail} from '../../Utils/index'; import {Icon} from '@neos-project/react-ui-components'; +import dummyImage from '../../resource/dummy-image.dataurl.svg'; import style from './style.module.css'; export default class PreviewScreen extends PureComponent { @@ -52,7 +53,7 @@ export default class PreviewScreen extends PureComponent { {propertyName} diff --git a/packages/neos-ui-editors/src/Editors/Image/resource/dummy-image.dataurl.svg b/packages/neos-ui-editors/src/Editors/Image/resource/dummy-image.dataurl.svg new file mode 100644 index 0000000000..26bd783040 --- /dev/null +++ b/packages/neos-ui-editors/src/Editors/Image/resource/dummy-image.dataurl.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/neos-ui-editors/src/SecondaryEditors/ImageCropper/index.js b/packages/neos-ui-editors/src/SecondaryEditors/ImageCropper/index.js index 99349c61db..bee71a18cd 100644 --- a/packages/neos-ui-editors/src/SecondaryEditors/ImageCropper/index.js +++ b/packages/neos-ui-editors/src/SecondaryEditors/ImageCropper/index.js @@ -7,6 +7,7 @@ import {neos} from '@neos-project/neos-ui-decorators'; import AspectRatioDropDown from './AspectRatioDropDown/index'; import CropConfiguration, {CustomAspectRatioOption, LockedAspectRatioStrategy} from './model.js'; +import dummyImage from '../../Editors/Image/resource/dummy-image.dataurl.svg'; import style from './style.module.css'; import './react_crop.vanilla-css'; @@ -181,7 +182,7 @@ export default class ImageCropper extends PureComponent { const aspectRatioLocked = cropConfiguration.aspectRatioStrategy instanceof LockedAspectRatioStrategy; const allowCustomRatios = cropConfiguration.aspectRatioOptions.some(option => option instanceof CustomAspectRatioOption); const {sourceImage, i18nRegistry} = this.props; - const src = sourceImage.previewUri || '/_Resources/Static/Packages/Neos.Neos/Images/dummy-image.svg'; + const src = sourceImage.previewUri || dummyImage; const toolbarRef = el => { this.toolbarNode = el; diff --git a/packages/neos-ui-guest-frame/src/initializePropertyDomNode.js b/packages/neos-ui-guest-frame/src/initializePropertyDomNode.js index e6b8d41a95..40c2c9f739 100644 --- a/packages/neos-ui-guest-frame/src/initializePropertyDomNode.js +++ b/packages/neos-ui-guest-frame/src/initializePropertyDomNode.js @@ -1,4 +1,4 @@ -import {actions, selectors} from '@neos-project/neos-ui-redux-store'; +import {actions} from '@neos-project/neos-ui-redux-store'; import {validateElement} from '@neos-project/neos-ui-validators'; import {getGuestFrameWindow, closestContextPathInGuestFrame} from './dom'; @@ -72,14 +72,6 @@ export default ({store, globalRegistry, nodeTypesRegistry, inlineEditorRegistry, actions.Changes.persistChanges([change]) ), onChange: value => { - const node = selectors.CR.Nodes.byContextPathSelector(contextPath)(store.getState()); - if (node) { - const oldValue = node.properties[propertyName]; - if (oldValue === value) { - return; - } - } - const validationResult = validateElement(value, nodeType?.properties?.[propertyName], globalRegistry.get('validators')); // Update inline validation errors store.dispatch( diff --git a/packages/neos-ui-guest-frame/src/style.module.css b/packages/neos-ui-guest-frame/src/style.module.css index 2c6fed4f8b..cb7ef380d4 100644 --- a/packages/neos-ui-guest-frame/src/style.module.css +++ b/packages/neos-ui-guest-frame/src/style.module.css @@ -22,11 +22,11 @@ outline-color: var(--colors-Warn); } -:global(.neos-inline-editable:focus) { +:global([data-__neos-property][contenteditable]) { outline: none; } -:global([data-neos-inline-editor-is-initialized]:hover) { +:global([data-__neos-property][contenteditable]:hover) { outline-offset: 5px; outline: 2px dashed var(--colors-PrimaryBlue); } diff --git a/packages/neos-ui/src/Containers/FlashMessages/FlashMessage/style.module.css b/packages/neos-ui/src/Containers/FlashMessages/FlashMessage/style.module.css index a5a6bc1f2e..d7b8f4c6c8 100644 --- a/packages/neos-ui/src/Containers/FlashMessages/FlashMessage/style.module.css +++ b/packages/neos-ui/src/Containers/FlashMessages/FlashMessage/style.module.css @@ -39,6 +39,7 @@ margin: 0; vertical-align: text-top; border-right: 1px solid rgba(255, 255, 255, .3); + box-sizing: border-box !important; } .flashMessage__heading { diff --git a/packages/neos-ui/src/System/terminateDueToFatalInitializationError.js b/packages/neos-ui/src/System/terminateDueToFatalInitializationError.js index 5edaecc5ac..992c642a01 100644 --- a/packages/neos-ui/src/System/terminateDueToFatalInitializationError.js +++ b/packages/neos-ui/src/System/terminateDueToFatalInitializationError.js @@ -1,4 +1,4 @@ -import logo from '@neos-project/react-ui-components/src/Logo/logo.svg'; +import {Logo} from '@neos-project/react-ui-components'; import styles from '../Containers/ErrorBoundary/style.module.css'; @@ -11,6 +11,7 @@ export function terminateDueToFatalInitializationError(reason) { document.body.innerHTML = `
+ Neos

Sorry, but the Neos UI could not be initialized. diff --git a/packages/react-ui-components/esbuild.js b/packages/react-ui-components/esbuild.js index 4f4a1468ac..e23adc49ad 100644 --- a/packages/react-ui-components/esbuild.js +++ b/packages/react-ui-components/esbuild.js @@ -71,7 +71,7 @@ async function main() { metafile: true, loader: { '.js': 'tsx', - '.svg': 'dataurl', + '.svg': 'text', '.css': 'copy' }, plugins: [ diff --git a/packages/react-ui-components/src/Icon/resourceIcon.tsx b/packages/react-ui-components/src/Icon/resourceIcon.tsx index d67566d7f1..0970c0c74e 100644 --- a/packages/react-ui-components/src/Icon/resourceIcon.tsx +++ b/packages/react-ui-components/src/Icon/resourceIcon.tsx @@ -13,6 +13,11 @@ export interface ResourceIconProps extends Omit { readonly theme?: ResourceIconTheme; } +/** + * @deprecated please refrain from using resource paths + * use the new react-ui-components/ResourceIcon instead. + * See also https://github.com/neos/neos-ui/issues/2092 + */ class ResourceIcon extends PureComponent { public static readonly defaultProps = defaultProps; diff --git a/packages/react-ui-components/src/Logo/index.js b/packages/react-ui-components/src/Logo/index.js index 2b66f8a85b..6c2501bd4d 100644 --- a/packages/react-ui-components/src/Logo/index.js +++ b/packages/react-ui-components/src/Logo/index.js @@ -1,14 +1,11 @@ import React, {PureComponent} from 'react'; +import {ResourceIcon} from '../ResourceIcon'; -import logo from './logo.svg'; +import logoSvg from './resource/logo.svg'; import style from './style.module.css'; export default class Logo extends PureComponent { render() { - return ( -
- Neos -
- ); + return ; } } diff --git a/packages/react-ui-components/src/Logo/logo.svg b/packages/react-ui-components/src/Logo/logo.svg deleted file mode 100755 index bfcafa2669..0000000000 --- a/packages/react-ui-components/src/Logo/logo.svg +++ /dev/null @@ -1 +0,0 @@ -neos_negative_dark \ No newline at end of file diff --git a/packages/react-ui-components/src/Logo/resource/logo.svg b/packages/react-ui-components/src/Logo/resource/logo.svg new file mode 100755 index 0000000000..1b91ad9eed --- /dev/null +++ b/packages/react-ui-components/src/Logo/resource/logo.svg @@ -0,0 +1,5 @@ + diff --git a/packages/react-ui-components/src/Logo/style.module.css b/packages/react-ui-components/src/Logo/style.module.css index 4e6e5a7a55..93f7fd844b 100644 --- a/packages/react-ui-components/src/Logo/style.module.css +++ b/packages/react-ui-components/src/Logo/style.module.css @@ -1,4 +1,3 @@ -.logo { +.logo > svg { height: 24px; - width: auto; } diff --git a/packages/react-ui-components/src/ResourceIcon/index.tsx b/packages/react-ui-components/src/ResourceIcon/index.tsx new file mode 100644 index 0000000000..f8d0fdd6fd --- /dev/null +++ b/packages/react-ui-components/src/ResourceIcon/index.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import mergeClassNames from 'classnames'; +import style from './style.module.css'; + +type ResourceIconProps = { + source?: string; + className?: string; + label?: string; +} + +export function ResourceIcon(props: ResourceIconProps) { + const {source, className, label} = props; + + if (!source) { + return null; + } + + const classNames = mergeClassNames( + className, + { + [style['resource-icon']]: true + } + ); + + return ; +} diff --git a/packages/react-ui-components/src/ResourceIcon/style.module.css b/packages/react-ui-components/src/ResourceIcon/style.module.css new file mode 100644 index 0000000000..f759d7b28b --- /dev/null +++ b/packages/react-ui-components/src/ResourceIcon/style.module.css @@ -0,0 +1,16 @@ +.resource-icon { + composes: reset from '../reset.module.css'; + font: normal normal normal FontAwesome; + font-size: 14px/1; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + display: inline-flex; + width: 100%; + justify-content: center; +} + +.resource-icon > svg { + fill: none; +} diff --git a/packages/react-ui-components/src/index.ts b/packages/react-ui-components/src/index.ts index d8a4677430..a4b4df2619 100644 --- a/packages/react-ui-components/src/index.ts +++ b/packages/react-ui-components/src/index.ts @@ -11,6 +11,7 @@ export {default as DropDown} from './DropDown'; export {default as Frame} from './Frame'; export {default as Headline} from './Headline'; export {default as Icon} from './Icon'; +export {ResourceIcon} from './ResourceIcon'; export {default as IconButton} from './IconButton'; export {default as IconButtonDropDown} from './IconButtonDropDown'; export {default as Label} from './Label';