diff --git a/DEVELOP.md b/DEVELOP.md index 78764e9..b77a8d3 100644 --- a/DEVELOP.md +++ b/DEVELOP.md @@ -2,50 +2,119 @@ ## Develop +### With Docker + +1. Make sure you have `docker` and `docker compose` installed and running on your machine: + + ```Bash + git clone https://github.com/eea/volto-accordion-block.git + cd volto-accordion-block + git checkout -b bugfix-123456 develop + make + make start + ``` + +1. Wait for `Volto started at 0.0.0.0:3000` meesage + +1. Go to http://localhost:3000 + +1. Happy hacking! + + ```Bash + cd src/addons/volto-accordion-block/ + ``` + +### Or add volto-accordion-block to your Volto project + Before starting make sure your development environment is properly set. See [Volto Developer Documentation](https://docs.voltocms.com/getting-started/install/) 1. Make sure you have installed `yo`, `@plone/generator-volto` and `mrs-developer` - npm install -g yo @plone/generator-volto mrs-developer + ```Bash + npm install -g yo @plone/generator-volto mrs-developer + ``` 1. Create new volto app - yo @plone/volto my-volto-project --addon @eeacms/volto-accordion-block --skip-install - cd my-volto-project + ```Bash + yo @plone/volto my-volto-project --addon @eeacms/volto-accordion-block --skip-install + cd my-volto-project + ``` 1. Add the following to `mrs.developer.json`: - { - "volto-accordion-block": { - "url": "https://github.com/eea/volto-accordion-block.git", - "package": "@eeacms/volto-accordion-block", - "branch": "develop", - "path": "src" - } + ```JSON + { + "volto-accordion-block": { + "url": "https://github.com/eea/volto-accordion-block.git", + "package": "@eeacms/volto-accordion-block", + "branch": "develop", + "path": "src" } + } + ``` 1. Install - yarn develop - yarn + ```Bash + make develop + yarn + ``` 1. Start backend - docker pull plone - docker run -d --name plone -p 8080:8080 -e SITE=Plone -e PROFILES="profile-plone.restapi:blocks" plone + ```Bash + docker compose up backend + ``` ...wait for backend to setup and start - `Ready to handle requests`: - docker logs -f plone - ...you can also check http://localhost:8080/Plone 1. Start frontend - yarn start + ```BASH + yarn start + ``` 1. Go to http://localhost:3000 1. Happy hacking! - cd src/addons/volto-accordion-block/ + ```BASH + cd src/addons/volto-accordion-block/ + ``` + +## Cypress + +To run cypress locally, first make sure you don't have any Volto/Plone running on ports `8080` and `3000`. + +You don't have to be in a `clean-volto-project`, you can be in any Volto Frontend +project where you added `volto-accordion-block` to `mrs.developer.json` + +Go to: + + ```BASH + cd src/addons/volto-accordion-block/ + ``` + +Start: + + ```Bash + make + make start + ``` + +This will build and start with Docker a clean `Plone backend` and `Volto Frontend` with `volto-accordion-block` block installed. + +Open Cypress Interface: + + ```Bash + make cypress-open + ``` + +Or run it: + + ```Bash + make cypress-run + ``` diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..bd8e10d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,14 @@ +# syntax=docker/dockerfile:1 +ARG VOLTO_VERSION +FROM plone/frontend-builder:${VOLTO_VERSION} + +ARG ADDON_NAME +ARG ADDON_PATH + +COPY --chown=node:node ./ /app/src/addons/${ADDON_PATH}/ + +RUN /setupAddon +RUN yarn install + +ENTRYPOINT ["yarn"] +CMD ["start"] diff --git a/Makefile b/Makefile index cc237ff..6c18722 100644 --- a/Makefile +++ b/Makefile @@ -1,54 +1,90 @@ -SHELL=/bin/bash +############################################################################## +# Run: +# make +# make start +# +# Go to: +# +# http://localhost:3000 +# +# Cypress: +# +# make cypress-open +# +############################################################################## +# SETUP MAKE +# +## Defensive settings for make: https://tech.davis-hansson.com/p/make/ +SHELL:=bash +.ONESHELL: +# for Makefile debugging purposes add -x to the .SHELLFLAGS +.SHELLFLAGS:=-eu -o pipefail -O inherit_errexit -c +.SILENT: +.DELETE_ON_ERROR: +MAKEFLAGS+=--warn-undefined-variables +MAKEFLAGS+=--no-builtin-rules + +# Colors +# OK=Green, warn=yellow, error=red +ifeq ($(TERM),) +# no colors if not in terminal + MARK_COLOR= + OK_COLOR= + WARN_COLOR= + ERROR_COLOR= + NO_COLOR= +else + MARK_COLOR=`tput setaf 6` + OK_COLOR=`tput setaf 2` + WARN_COLOR=`tput setaf 3` + ERROR_COLOR=`tput setaf 1` + NO_COLOR=`tput sgr0` +endif +############################################################################## +# SETTINGS AND VARIABLE DIR=$(shell basename $$(pwd)) -ADDON ?= "@eeacms/volto-accordion-block" - -# We like colors -# From: https://coderwall.com/p/izxssa/colored-makefile-for-golang-projects -RED=`tput setaf 1` -GREEN=`tput setaf 2` -RESET=`tput sgr0` -YELLOW=`tput setaf 3` +NODE_MODULES?="../../../node_modules" +PLONE_VERSION?=6 +VOLTO_VERSION?=16 +ADDON_PATH="${DIR}" +ADDON_NAME="@eeacms/${ADDON_PATH}" +DOCKER_COMPOSE=PLONE_VERSION=${PLONE_VERSION} VOLTO_VERSION=${VOLTO_VERSION} ADDON_NAME=${ADDON_NAME} ADDON_PATH=${ADDON_PATH} docker compose + +# Top-level targets +.PHONY: all +all: clean install + +.PHONY: clean +clean: ## Cleanup development environment + ${DOCKER_COMPOSE} down --volumes --remove-orphans + +.PHONY: install +install: ## Build and install development environment + echo "Running: ${DOCKER_COMPOSE} build" + ${DOCKER_COMPOSE} pull + ${DOCKER_COMPOSE} build + +.PHONY: start +start: ## Start development environment + echo "Running: ${DOCKER_COMPOSE} up" + ${DOCKER_COMPOSE} up -ifeq ($(wildcard ./project),) - NODE_MODULES = "../../../node_modules" -else - NODE_MODULES = "./project/node_modules" -endif +.PHONY: cypress-open +cypress-open: ## Open cypress integration tests + NODE_ENV=development $(NODE_MODULES)/cypress/bin/cypress open -project: - npm install -g yo - npm install -g @plone/generator-volto - npm install -g mrs-developer - yo @plone/volto project --addon ${ADDON} --workspace "src/addons/${DIR}" --no-interactive - ln -sf $$(pwd) project/src/addons/ - cp .project.eslintrc.js .eslintrc.js - cd project && yarn - @echo "-------------------" - @echo "$(GREEN)Volto project is ready!$(RESET)" - @echo "$(RED)Now run: cd project && yarn start$(RESET)" - -all: project - -.PHONY: start-test-backend -start-test-backend: ## Start Test Plone Backend - @echo "$(GREEN)==> Start Test Plone Backend$(RESET)" - docker run -i --rm -e ZSERVER_HOST=0.0.0.0 -e ZSERVER_PORT=55001 -p 55001:55001 -e SITE=plone -e APPLY_PROFILES=plone.app.contenttypes:plone-content,plone.restapi:default,kitconcept.volto:default-homepage -e CONFIGURE_PACKAGES=plone.app.contenttypes,plone.restapi,kitconcept.volto,kitconcept.volto.cors -e ADDONS='plone.app.robotframework plone.app.contenttypes plone.restapi kitconcept.volto' plone ./bin/robot-server plone.app.robotframework.testing.PLONE_ROBOT_TESTING - -.PHONY: start-backend-docker -start-backend-docker: ## Starts a Docker-based backend - @echo "$(GREEN)==> Start Docker-based Plone Backend$(RESET)" - docker run -it --rm --name=plone -p 8080:8080 -e SITE=Plone -e ADDONS="kitconcept.volto" -e ZCML="kitconcept.volto.cors" plone +.PHONY: cypress-run +cypress-run: ## Run cypress integration tests + NODE_ENV=development $(NODE_MODULES)/cypress/bin/cypress run .PHONY: test test: ## Run jest tests - docker pull plone/volto-addon-ci:alpha - docker run -it --rm -e NAMESPACE="@eeacms" -e GIT_NAME="${DIR}" -e RAZZLE_JEST_CONFIG=jest-addon.config.js -v "$$(pwd):/opt/frontend/my-volto-project/src/addons/${DIR}" -e CI="true" plone/volto-addon-ci:alpha + ${DOCKER_COMPOSE} run -e CI=1 frontend test .PHONY: test-update test-update: ## Update jest tests snapshots - docker pull plone/volto-addon-ci:alpha - docker run -it --rm -e NAMESPACE="@eeacms" -e GIT_NAME="${DIR}" -e RAZZLE_JEST_CONFIG=jest-addon.config.js -v "$$(pwd):/opt/frontend/my-volto-project/src/addons/${DIR}" -e CI="true" plone/volto-addon-ci:alpha yarn test src/addons/${DIR}/src --watchAll=false -u + ${DOCKER_COMPOSE} run -e CI=1 frontend test -u .PHONY: stylelint stylelint: ## Stylelint @@ -81,17 +117,9 @@ lint-fix: ## Fix ES Lint .PHONY: i18n i18n: ## i18n - rm -rf build/messages - NODE_ENV=development $(NODE_MODULES)/.bin/i18n --addon - -.PHONY: cypress-run -cypress-run: ## Run cypress integration tests - NODE_ENV=development $(NODE_MODULES)/cypress/bin/cypress run - -.PHONY: cypress-open -cypress-open: ## Open cypress integration tests - NODE_ENV=development $(NODE_MODULES)/cypress/bin/cypress open + ${DOCKER_COMPOSE} run frontend i18n .PHONY: help -help: ## Show this help. +help: ## Show this help. @echo -e "$$(grep -hE '^\S+:.*##' $(MAKEFILE_LIST) | sed -e 's/:.*##\s*/:/' -e 's/^\(.\+\):\(.*\)/\\x1b[36m\1\\x1b[m:\2/' | column -c2 -t -s :)" + head -n 14 Makefile diff --git a/README.md b/README.md index 64838e4..827b5bd 100644 --- a/README.md +++ b/README.md @@ -20,41 +20,25 @@ ![Volto Block Accordion](https://github.com/eea/volto-accordion-block/raw/docs/docs/volto-accordion-block.gif) -## Upgrade - -### Upgrading to 6.x - -This version requires: `@plone/volto >= 16.0.0.alpha.46` (schemaEnhancer / addStyling). - ## Getting started ### Try volto-accordion-block with Docker -1. Get the latest Docker images + git clone https://github.com/eea/volto-accordion-block.git + cd volto-accordion-block + make + make start - ``` - docker pull plone - docker pull plone/volto - ``` - -1. Start Plone backend - - ``` - docker run -d --name plone -p 8080:8080 -e SITE=Plone -e PROFILES="profile-plone.restapi:blocks" plone - ``` - -1. Start Volto frontend - - ``` - docker run -it --rm -p 3000:3000 --link plone -e ADDONS="@eeacms/volto-accordion-block" plone/volto - ``` - -1. Go to http://localhost:3000 +Go to http://localhost:3000 ### Add volto-accordion-block to your Volto project 1. Make sure you have a [Plone backend](https://plone.org/download) up-and-running at http://localhost:8080/Plone + ```Bash + docker compose up backend + ``` + 1. Start Volto frontend - If you already have a volto project, just update `package.json`: @@ -65,7 +49,7 @@ This version requires: `@plone/volto >= 16.0.0.alpha.46` (schemaEnhancer / addSt ], "dependencies": { - "@eeacms/volto-accordion-block": "^3.0.0" + "@eeacms/volto-accordion-block": "*" } ``` diff --git a/cypress/e2e/02-dexterity-controlpanel-layout.cy.js b/cypress/e2e/02-dexterity-controlpanel-layout.cy.js new file mode 100644 index 0000000..ac97d27 --- /dev/null +++ b/cypress/e2e/02-dexterity-controlpanel-layout.cy.js @@ -0,0 +1,119 @@ +import { slateLayoutBeforeEach, slateLayoutAfterEach } from '../support/e2e'; + +describe('ControlPanel: Dexterity Content-Types Layout', () => { + beforeEach(slateLayoutBeforeEach); + afterEach(slateLayoutAfterEach); + + it('Edit Blocks Layout for Book', () => { + cy.visit('/controlpanel/dexterity-types'); + cy.waitForResourceToLoad('@navigation'); + cy.waitForResourceToLoad('@breadcrumbs'); + cy.waitForResourceToLoad('@actions'); + cy.waitForResourceToLoad('@types'); + + cy.get('a[href="/controlpanel/dexterity-types/book"]').should( + 'have.text', + 'book', + ); + + cy.visit('/controlpanel/dexterity-types/book/layout'); + cy.get('#page-controlpanel-layout').contains( + 'Can not edit Layout for book', + ); + cy.get('#page-controlpanel-layout button').click(); + + // Wait a bit for draftjs to load, without this the title block + // custom placeholder is missing and cypress gives a timeout error + cy.wait(1000); + cy.get('input[id="field-placeholder"]').type('Book title'); + cy.get('label[for="field-required"]').click(); + cy.get('label[for="field-fixed"]').click(); + + cy.getSlate().click(); + + cy.get('.ui.basic.icon.button.block-add-button:visible').click(); + cy.get('.blocks-chooser .title').contains('Common').click(); + cy.get('.content.active.common .button.accordion') + .contains('Accordion') + .click(); + + cy.get('#field-allowedBlocks.react-select-container').click().type("Image{enter}"); + cy.get('#field-allowedBlocks.react-select-container').click().type("Text{enter}"); + + // By default all should be collapsed (no active class on first) + cy.get('.accordion:nth-child(2)').should('not.have.class', 'active'); + + cy.get('.accordion:nth-child(2) > .title input') + .click() + .type('Chapter 1') + .should('have.value', 'Chapter 1'); + + cy.get('.accordion:nth-child(2) .content .slate-editor [contenteditable=true]') + .last().focus().click().type('Once upon a time...{enter}'); + + cy.get('.accordion:nth-child(2) .content .slate-editor [contenteditable=true]') + .last().focus().click().type('/'); + cy.wait(500); + cy.get('.power-user-menu a.item').should('have.length', 1); + + cy.get('.accordion:nth-child(3) > .title input') + .click() + .type('Chapter 2') + .should('have.value', 'Chapter 2'); + + cy.get('#toolbar-save').click(); + + cy.visit('/cypress'); + cy.waitForResourceToLoad('@navigation'); + cy.waitForResourceToLoad('@breadcrumbs'); + cy.waitForResourceToLoad('@actions'); + cy.waitForResourceToLoad('@types'); + + cy.get('button[class="add"]').click(); + cy.get('#toolbar-add-book').click(); + cy.get('.block.title').contains('Book title'); + + // Change book title + cy.clearSlateTitle(); + cy.getSlateTitle().type('My First Book'); + cy.get('.documentFirstHeading').contains('My First Book'); + + cy.get('.accordion:nth-child(3) > .title > .icon').click(); + cy.wait(500); + cy.get('.accordion:nth-child(3) .content .slate-editor [contenteditable=true]') + .last().focus().click().type('The quick brown fox jumps over the lazy dog{enter}') + + cy.get('.accordion:nth-child(3) .content .slate-editor [contenteditable=true]') + .last().focus().click().type('/'); + cy.wait(500); + cy.get('.power-user-menu a.item').should('have.length', 1); + cy.get('.accordion:nth-child(3) .content .slate-editor [contenteditable=true]') + .last().focus().click().type('Image{enter}'); + cy.get('.accordion:nth-child(3) .content .block.image input[type="text"]') + .click().type('https://eea.github.io/volto-eea-design-system/img/eea_icon.png{enter}'); + + cy.get('.accordion:nth-child(4) > .title input') + .click() + .type('Chapter 3') + .should('have.value', 'Chapter 3'); + + cy.get('.accordion-block legend').click(); + cy.get('[id="field-title_size"] .react-select__value-container') + .click() + .type('h2{enter}'); + + cy.get('#toolbar-save').click(); + cy.get('.documentFirstHeading').contains('My First Book'); + cy.get('.accordion:nth-child(1) > h2.title').contains('Chapter 1'); + cy.get('.accordion:nth-child(2) > h2.title').contains('Chapter 2'); + cy.get('.accordion:nth-child(3) > h2.title').contains('Chapter 3'); + + cy.get('.accordion:nth-child(1) > h2.title').click(); + cy.get('.accordion:nth-child(1) .content').contains('Once upon a time...'); + + cy.get('.accordion:nth-child(2) > h2.title').click(); + cy.get('.accordion:nth-child(2) .content').contains('The quick brown fox jumps over the lazy dog'); + cy.get('.accordion:nth-child(2) .content img') + .should('have.attr', 'src', 'https://eea.github.io/volto-eea-design-system/img/eea_icon.png'); + }); +}); diff --git a/cypress/support/e2e.js b/cypress/support/e2e.js index f696418..90fe032 100644 --- a/cypress/support/e2e.js +++ b/cypress/support/e2e.js @@ -61,6 +61,22 @@ export const slateJsonAfterEach = (contentType = 'slate') => { slateAfterEach(); }; +export const slateLayoutBeforeEach = (contentType = 'book') => { + cy.autologin(); + cy.addContentType(contentType); + cy.createContent({ + contentType: 'Document', + contentId: 'cypress', + contentTitle: 'Cypress', + }); +}; + +export const slateLayoutAfterEach = (contentType = 'book') => { + cy.autologin(); + cy.removeContentType(contentType); + cy.removeContent('cypress'); +}; + export const getSelectedSlateEditor = () => { return cy.get('.slate-editor.selected [contenteditable=true]').click(); }; diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..aa5b551 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,27 @@ +version: "3" +services: + backend: + image: plone/plone-backend:${PLONE_VERSION:-6} + ports: + - "8080:8080" + environment: + SITE: "Plone" + + frontend: + build: + context: ./ + dockerfile: ./Dockerfile + args: + ADDON_NAME: "${ADDON_NAME}" + ADDON_PATH: "${ADDON_PATH}" + VOLTO_VERSION: ${VOLTO_VERSION:-16} + ports: + - "3000:3000" + - "3001:3001" + depends_on: + - backend + volumes: + - ./:/app/src/addons/${ADDON_PATH} + environment: + RAZZLE_INTERNAL_API_PATH: "http://backend:8080/Plone" + RAZZLE_DEV_PROXY_API_PATH: "http://backend:8080/Plone"