Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file. Dates are d

Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).

### [14.0.0](https://github.com/eea/volto-plotlycharts/compare/13.3.7...14.0.0) - 25 March 2026

#### :rocket: New Features

- feat: Volto 18 support - refs #287700 [Alin Voinea - [`267a2b9`](https://github.com/eea/volto-plotlycharts/commit/267a2b9be7f06dba092fe934eba6f807d9c229b4)]

#### :house: Internal changes


#### :hammer_and_wrench: Others

- tests: Fix Sonar Qube tags - refs #297339 [Alin Voinea - [`de3ce01`](https://github.com/eea/volto-plotlycharts/commit/de3ce017be0e23d4c64f4efa363b1eff6ed76fcd)]
### [13.3.7](https://github.com/eea/volto-plotlycharts/compare/13.3.6...13.3.7) - 24 November 2025

#### :house: Internal changes
Expand All @@ -13,7 +25,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
#### :hammer_and_wrench: Others

- Truncate filename for png and svg files [Teodor - [`0e2cb19`](https://github.com/eea/volto-plotlycharts/commit/0e2cb1996356901c0ca24d243376650ec5fcae5e)]
- Truncate name for zip files and for csv files inside the zip [Teodor - [`79fec0d`](https://github.com/eea/volto-plotlycharts/commit/79fec0df327eb101f54982bf92e102d4fa3d85d2)]
### [13.3.6](https://github.com/eea/volto-plotlycharts/compare/13.3.5...13.3.6) - 21 November 2025

#### :house: Internal changes
Expand Down
36 changes: 19 additions & 17 deletions DEVELOP.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,50 +26,52 @@

### Or add @eeacms/volto-plotlycharts 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/)
Before starting make sure your development environment is properly set. See the official Plone documentation for [creating a project with Cookieplone](https://6.docs.plone.org/install/create-project-cookieplone.html) and [installing an add-on in development mode in Volto 18 and 19](https://6.docs.plone.org/volto/development/add-ons/install-an-add-on-dev-18.html).

1. Make sure you have installed `yo`, `@plone/generator-volto` and `mrs-developer`
For new Volto 18+ projects, use Cookieplone. It includes `mrs-developer` by default.

npm install -g yo @plone/generator-volto mrs-developer
1. Create a new Volto project with Cookieplone

1. Create new volto app

yo @plone/volto my-volto-project --addon @eeacms/volto-plotlycharts --skip-install
cd my-volto-project
uvx cookieplone project
cd project-title

1. Add the following to `mrs.developer.json`:

{
"volto-plotlycharts": {
"output": "packages",
"url": "https://github.com/eea/volto-plotlycharts.git",
"package": "@eeacms/volto-plotlycharts",
"branch": "develop",
"path": "src"
}
}

1. Install
1. Add `@eeacms/volto-plotlycharts` to the `addons` key in your project `volto.config.js`

1. Install or refresh the project setup

make develop
yarn
make install

1. Start backend
1. Start backend in one terminal

docker run --pull always -it --rm --name plone -p 8080:8080 -e SITE=Plone plone/plone-backend
make backend-start

...wait for backend to setup and start - `Ready to handle requests`:
...wait for backend to setup and start, ending with `Ready to handle requests`

...you can also check http://localhost:8080/Plone

1. Start frontend
1. Start frontend in a second terminal

yarn start
make frontend-start

1. Go to http://localhost:3000

1. Happy hacking!

cd src/addons/volto-plotlycharts/
cd packages/volto-plotlycharts

For legacy Volto 17 projects, keep using the yarn-based workflow from the Volto 17 documentation.

## Cypress

Expand All @@ -81,7 +83,7 @@ project where you added `volto-plotlycharts` to `mrs.developer.json`
Go to:

```BASH
cd src/addons/volto-plotlycharts/
cd packages/volto-plotlycharts/
```

Start:
Expand Down
11 changes: 10 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
# syntax=docker/dockerfile:1
ARG VOLTO_VERSION
FROM eeacms/frontend-builder:${VOLTO_VERSION}
FROM plone/frontend-builder:${VOLTO_VERSION}

ARG ADDON_NAME
ARG ADDON_PATH
ENV HOST="0.0.0.0"

USER root
RUN apt-get update -q \
&& apt-get install -qy --no-install-recommends \
chromium libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 xauth xvfb \
&& rm -rf /var/lib/apt/lists/*
USER node

COPY --chown=node:node ./ /app/src/addons/${ADDON_PATH}/

RUN /setupAddon
RUN yarn add jest-junit
RUN yarn install

ENTRYPOINT ["yarn"]
Expand Down
81 changes: 42 additions & 39 deletions Jenkinsfile
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
pipeline {
tools {
jdk 'Java17'
}
agent {
node { label 'docker-host' }
}

environment {
GIT_NAME = "volto-plotlycharts"
NAMESPACE = "@eeacms"
SONARQUBE_TAGS = "volto.eea.europa.eu,climate-energy.eea.europa.eu,forest.eea.europa.eu,biodiversity.europa.eu,water.europa.eu-freshwater,demo-www.eea.europa.eu,www.eea.europa.eu-en,water.europa.eu-marine,insitu.copernicus.eu,climate-adapt.eea.europa.eu"
SONARQUBE_TAGS = "volto.eea.europa.eu,climate-energy.eea.europa.eu,forest.eea.europa.eu,biodiversity.europa.eu,water.europa.eu-freshwater,demo-www.eea.europa.eu,www.eea.europa.eu-en,www.eea.europa.eu,water.europa.eu-marine,insitu.copernicus.eu,climate-adapt.eea.europa.eu"
DEPENDENCIES = "@eeacms/volto-datablocks"
BACKEND_PROFILES = "eea.kitkat:testing"
BACKEND_ADDONS = ""
VOLTO = "17"
VOLTO16_BREAKING_CHANGES = "yes"
VOLTO18_BREAKING_CHANGES = "no"
IMAGE_NAME = BUILD_TAG.toLowerCase()
NODEJS_VERSION = "22"
}
Expand Down Expand Up @@ -243,11 +240,17 @@ pipeline {
script {
def scannerHome = tool 'SonarQubeScanner'
def nodeJS = tool 'NodeJS'
if (env.CHANGE_ID) {
env.sonarParams = " -Dsonar.pullrequest.base=${env.CHANGE_TARGET} -Dsonar.pullrequest.branch=${env.CHANGE_BRANCH} -Dsonar.pullrequest.key=${env.CHANGE_ID} "
}
else {
env.sonarParams = " -Dsonar.branch.name=${env.BRANCH_NAME}"
}
withSonarQubeEnv('Sonarqube') {
sh '''sed -i "s#/app/src/addons/${GIT_NAME}/##g" xunit-reports/coverage/lcov.info'''
sh '''sed -i "s#src/addons/${GIT_NAME}/##g" xunit-reports/coverage/lcov.info'''
sh "export PATH=${scannerHome}/bin:${nodeJS}/bin:$PATH; sonar-scanner -Dsonar.javascript.lcov.reportPaths=./xunit-reports/coverage/lcov.info,./cypress-coverage/coverage/lcov.info -Dsonar.sources=./src -Dsonar.projectKey=$GIT_NAME-$BRANCH_NAME -Dsonar.projectVersion=$BRANCH_NAME-$BUILD_NUMBER"
sh '''try=5; while [ \$try -gt 0 ]; do curl -s -XPOST -u "${SONAR_AUTH_TOKEN}:" "${SONAR_HOST_URL}api/project_tags/set?project=${GIT_NAME}-${BRANCH_NAME}&tags=${SONARQUBE_TAGS},${BRANCH_NAME}" > set_tags_result; if [ \$(grep -ic error set_tags_result ) -eq 0 ]; then try=0; else cat set_tags_result; echo "... Will retry"; sleep 15; try=\$(( \$try - 1 )); fi; done'''
sh "export PATH=${scannerHome}/bin:${nodeJS}/bin:$PATH; sonar-scanner -Dsonar.javascript.lcov.reportPaths=./xunit-reports/coverage/lcov.info,./cypress-coverage/coverage/lcov.info -Dsonar.sources=./src -Dsonar.projectKey=$GIT_NAME -Dsonar.projectName=$GIT_NAME -Dsonar.projectVersion=\$(jq -r '.version' package.json) ${env.sonarParams}"
sh '''try=5; while [ \$try -gt 0 ]; do curl -s -XPOST -u "${SONAR_AUTH_TOKEN}:" "${SONAR_HOST_URL}api/project_tags/set?project=${GIT_NAME}&tags=${SONARQUBE_TAGS}" > set_tags_result; if [ \$(grep -ic error set_tags_result ) -eq 0 ]; then try=0; else cat set_tags_result; echo "... Will retry"; sleep 15; try=\$(( \$try - 1 )); fi; done'''
}
}
}
Expand All @@ -257,75 +260,75 @@ pipeline {
}
}

stage('Volto 16') {
stage('Volto 18') {
agent { node { label 'integration'} }
when {
environment name: 'SKIP_TESTS', value: ''
not { environment name: 'VOLTO16_BREAKING_CHANGES', value: 'yes' }
not { environment name: 'VOLTO18_BREAKING_CHANGES', value: 'yes' }
}
stages {
stage('Build test image') {
steps {
sh '''docker build --pull --build-arg="VOLTO_VERSION=16" --build-arg="ADDON_NAME=$NAMESPACE/$GIT_NAME" --build-arg="ADDON_PATH=$GIT_NAME" . -t $IMAGE_NAME-frontend16'''
sh '''docker build --pull --build-arg="VOLTO_VERSION=18-yarn" --build-arg="ADDON_NAME=$NAMESPACE/$GIT_NAME" --build-arg="ADDON_PATH=$GIT_NAME" . -t $IMAGE_NAME-frontend18'''
}
}

stage('Unit tests Volto 16') {
stage('Unit tests Volto 18') {
steps {
script {
try {
sh '''docker run --name="$IMAGE_NAME-volto16" --entrypoint=make --workdir=/app/src/addons/$GIT_NAME $IMAGE_NAME-frontend16 test-ci'''
sh '''rm -rf xunit-reports16'''
sh '''mkdir -p xunit-reports16'''
sh '''docker cp $IMAGE_NAME-volto16:/app/junit.xml xunit-reports16/'''
sh '''docker run --name="$IMAGE_NAME-volto18" --entrypoint=make --workdir=/app/src/addons/$GIT_NAME $IMAGE_NAME-frontend18 test-ci'''
sh '''rm -rf xunit-reports18'''
sh '''mkdir -p xunit-reports18'''
sh '''docker cp $IMAGE_NAME-volto18:/app/junit.xml xunit-reports18/'''
} finally {
catchError(buildResult: 'SUCCESS', stageResult: 'SUCCESS') {
junit testResults: 'xunit-reports16/junit.xml', allowEmptyResults: true
junit testResults: 'xunit-reports18/junit.xml', allowEmptyResults: true
}
sh script: '''docker rm -v $IMAGE_NAME-volto16''', returnStatus: true
sh script: '''docker rm -v $IMAGE_NAME-volto18''', returnStatus: true
}
}
}
}

stage('Integration tests Volto 16') {
stage('Integration tests Volto 18') {
steps {
script {
try {
sh '''docker run --pull always --rm -d --name="$IMAGE_NAME-plone16" -e SITE="Plone" -e PROFILES="$BACKEND_PROFILES" -e ADDONS="$BACKEND_ADDONS" eeacms/plone-backend'''
sh '''docker run -d --shm-size=4g --link $IMAGE_NAME-plone16:plone --name="$IMAGE_NAME-cypress16" -e "RAZZLE_INTERNAL_API_PATH=http://plone:8080/Plone" --entrypoint=make --workdir=/app/src/addons/$GIT_NAME $IMAGE_NAME-frontend16 start-ci'''
frontend = sh script:'''docker exec --workdir=/app/src/addons/${GIT_NAME} $IMAGE_NAME-cypress16 make check-ci''', returnStatus: true
sh '''docker run --pull always --rm -d --name="$IMAGE_NAME-plone18" -e SITE="Plone" -e PROFILES="$BACKEND_PROFILES" -e ADDONS="$BACKEND_ADDONS" eeacms/plone-backend'''
sh '''docker run -d --shm-size=4g --link $IMAGE_NAME-plone18:plone --name="$IMAGE_NAME-cypress18" -e "RAZZLE_INTERNAL_API_PATH=http://plone:8080/Plone" --entrypoint=make --workdir=/app/src/addons/$GIT_NAME $IMAGE_NAME-frontend18 start-ci'''
frontend = sh script:'''docker exec --workdir=/app/src/addons/${GIT_NAME} $IMAGE_NAME-cypress18 make check-ci''', returnStatus: true
if ( frontend != 0 ) {
sh '''docker logs $IMAGE_NAME-cypress16; exit 1'''
sh '''docker logs $IMAGE_NAME-cypress18; exit 1'''
}
sh '''timeout -s 9 1800 docker exec --workdir=/app/src/addons/${GIT_NAME} $IMAGE_NAME-cypress16 make cypress-ci'''
sh '''timeout -s 9 1800 docker exec --workdir=/app/src/addons/${GIT_NAME} $IMAGE_NAME-cypress18 make cypress-ci'''
} finally {
try {
if ( frontend == 0 ) {
sh '''rm -rf cypress-videos16 cypress-results16 cypress-coverage16 cypress-screenshots16'''
sh '''mkdir -p cypress-videos16 cypress-results16 cypress-coverage16 cypress-screenshots16'''
videos = sh script: '''docker cp $IMAGE_NAME-cypress16:/app/src/addons/$GIT_NAME/cypress/videos cypress-videos16/''', returnStatus: true
sh '''docker cp $IMAGE_NAME-cypress16:/app/src/addons/$GIT_NAME/cypress/reports cypress-results16/'''
screenshots = sh script: '''docker cp $IMAGE_NAME-cypress16:/app/src/addons/$GIT_NAME/cypress/screenshots cypress-screenshots16''', returnStatus: true
sh '''rm -rf cypress-videos18 cypress-results18 cypress-coverage18 cypress-screenshots18'''
sh '''mkdir -p cypress-videos18 cypress-results18 cypress-coverage18 cypress-screenshots18'''
videos = sh script: '''docker cp $IMAGE_NAME-cypress18:/app/src/addons/$GIT_NAME/cypress/videos cypress-videos18/''', returnStatus: true
sh '''docker cp $IMAGE_NAME-cypress18:/app/src/addons/$GIT_NAME/cypress/reports cypress-results18/'''
screenshots = sh script: '''docker cp $IMAGE_NAME-cypress18:/app/src/addons/$GIT_NAME/cypress/screenshots cypress-screenshots18''', returnStatus: true

archiveArtifacts artifacts: 'cypress-screenshots16/**', fingerprint: true, allowEmptyArchive: true
archiveArtifacts artifacts: 'cypress-screenshots18/**', fingerprint: true, allowEmptyArchive: true

if ( videos == 0 ) {
sh '''for file in $(find cypress-results16 -name *.xml); do if [ $(grep -E 'failures="[1-9].*"' $file | wc -l) -eq 0 ]; then testname=$(grep -E 'file=.*failures="0"' $file | sed 's#.* file=".*\\/\\(.*\\.[jsxt]\\+\\)" time.*#\\1#' ); rm -f cypress-videos16/videos/$testname.mp4; fi; done'''
archiveArtifacts artifacts: 'cypress-videos16/**/*.mp4', fingerprint: true, allowEmptyArchive: true
sh '''for file in $(find cypress-results18 -name *.xml); do if [ $(grep -E 'failures="[1-9].*"' $file | wc -l) -eq 0 ]; then testname=$(grep -E 'file=.*failures="0"' $file | sed 's#.* file=".*\\/\\(.*\\.[jsxt]\\+\\)" time.*#\\1#' ); rm -f cypress-videos18/videos/$testname.mp4; fi; done'''
archiveArtifacts artifacts: 'cypress-videos18/**/*.mp4', fingerprint: true, allowEmptyArchive: true
}
}
} finally {
catchError(buildResult: 'SUCCESS', stageResult: 'SUCCESS') {
junit testResults: 'cypress-results16/**/*.xml', allowEmptyResults: true
junit testResults: 'cypress-results18/**/*.xml', allowEmptyResults: true
}
catchError(buildResult: 'SUCCESS', stageResult: 'SUCCESS') {
sh '''docker logs $IMAGE_NAME-cypress16'''
sh '''docker logs $IMAGE_NAME-cypress18'''
}
sh script: "docker stop $IMAGE_NAME-cypress16", returnStatus: true
sh script: "docker stop $IMAGE_NAME-plone16", returnStatus: true
sh script: "docker rm -v $IMAGE_NAME-plone16", returnStatus: true
sh script: "docker rm -v $IMAGE_NAME-cypress16", returnStatus: true
sh script: "docker stop $IMAGE_NAME-cypress18", returnStatus: true
sh script: "docker stop $IMAGE_NAME-plone18", returnStatus: true
sh script: "docker rm -v $IMAGE_NAME-plone18", returnStatus: true
sh script: "docker rm -v $IMAGE_NAME-cypress18", returnStatus: true
}
}
}
Expand All @@ -338,7 +341,7 @@ pipeline {
post {
always {
sh script: "docker rmi $IMAGE_NAME-frontend", returnStatus: true
sh script: "docker rmi $IMAGE_NAME-frontend16", returnStatus: true
sh script: "docker rmi $IMAGE_NAME-frontend18", returnStatus: true
}
}
}
Expand All @@ -364,7 +367,7 @@ pipeline {
script {
sh '''echo "Error" > checkresult.txt'''
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
sh '''set -o pipefail; docker run -i --rm --pull always --name="$IMAGE_NAME-gitflow-sn" -e GIT_BRANCH="$BRANCH_NAME" -e GIT_NAME="$GIT_NAME" eeacms/gitflow /checkSonarqubemaster.sh | grep -v "Found script" | tee checkresult.txt'''
sh '''set -o pipefail; docker run -i --rm --pull always --name="$IMAGE_NAME-gitflow-sn" -e GIT_BRANCH="$BRANCH_NAME" -e GIT_NAME="$GIT_NAME" eeacms/gitflow /checkSonarqubemasterV2.sh | grep -v "Found script" | tee checkresult.txt'''
}

publishChecks name: 'SonarQube', title: 'Sonarqube Code Quality Check', summary: 'Quality check on the SonarQube metrics from branch develop, comparing it with the ones from master branch. No bugs are allowed',
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ endif
DIR=$(shell basename $$(pwd))
NODE_MODULES?="../../../node_modules"
PLONE_VERSION?=6
VOLTO_VERSION?=17
VOLTO_VERSION?=18-yarn
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
Expand Down
Loading
Loading