From f7e97d4db19b1c177b18bedca299c53520089145 Mon Sep 17 00:00:00 2001 From: valentinab25 Date: Mon, 31 Jul 2023 06:46:08 +0300 Subject: [PATCH 01/22] docs: Cleanup Makefile, update DEVELOP documentation, i18n - refs #254894 --- Jenkinsfile | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index de06176..6c2d1a0 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -197,6 +197,31 @@ pipeline { } } + stage('SonarQube compare to master') { + when { + allOf { + environment name: 'CHANGE_ID', value: '' + branch 'develop' + not { changelog '.*^Automated release [0-9\\.]+$' } + } + } + steps { + node(label: 'docker') { + script { + sh '''docker pull eeacms/gitflow''' + sh '''echo "Error" > checkresult.txt''' + catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') { + sh '''set -o pipefail; docker run -i --rm --name="$BUILD_TAG-gitflow-sn" -e GIT_BRANCH="$BRANCH_NAME" -e GIT_NAME="$GIT_NAME" eeacms/gitflow /checkSonarqubemaster.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", + text: readFile(file: 'checkresult.txt'), conclusion: "${currentBuild.currentResult}", + detailsURL: "${env.BUILD_URL}display/redirect" + } + } + } + } + stage('Pull Request') { when { not { From 902005a7c7e8ed65ec6cb7f32d45a394a474acfa Mon Sep 17 00:00:00 2001 From: dana-cfc4 <56276248+dana-cfc4@users.noreply.github.com> Date: Wed, 16 Aug 2023 17:08:37 +0300 Subject: [PATCH 02/22] breaking(accordion): use a single icon component with titleIcons config and added panel filtering option #82 from eea/accordion-filter * chore: Implement accordion filter -refs #254324 * fix: Declare missing variable * change: Add missing variables, make filter case insensitive, change filter icons -refs #254324 * fix(filter): values are checked for lowercase values as we do on edit * change: use different classes, improve design * change: use classes coming from config * change: specify content for accordion icons * change(filter): allow move of filter icon to left side if right arrows is not set * breaking(accordion): use titleIcons for every icon configuration and added panel filtering - this is a backward compatible changeset as long as you didn't use the semanticIcon option and used the volto svg icons - introduced iconComponent option where you can specify whether to use VoltoIcon or SemanticIcon for the component used to render the icons - Simplified logic for using the Icon component, there is now only one call to a given Icon which only gets a different value to the name prop depending on if the tab is active or not or filtered or not * fix tests after the breaking changes * remove icon css not used anymore and bumped package version to 10.0.0 representing the major release bump * change(accordion): removed theme styling this is something specific that can be added as needed - also added styled class to filter title since on volto core the rest of the accordion titles have the styled class as well * fix(accordion): width for styled accordion that had 600px width from semantic variables * fix(accordion): accordion title color on edit when title is active --------- Co-authored-by: David Ichim --- package.json | 2 +- .../manage/Blocks/Accordion/AccordionEdit.jsx | 51 ++--- .../Blocks/Accordion/AccordionEdit.test.jsx | 11 +- .../Blocks/Accordion/AccordionFilter.jsx | 49 +++++ .../manage/Blocks/Accordion/Edit.jsx | 198 ++++++++++-------- .../manage/Blocks/Accordion/Edit.test.jsx | 10 +- .../manage/Blocks/Accordion/Schema.js | 10 + .../manage/Blocks/Accordion/View.jsx | 163 +++++++------- .../manage/Blocks/Accordion/View.test.jsx | 28 +-- .../__snapshots__/AccordionEdit.test.jsx.snap | 2 +- .../__snapshots__/Edit.test.jsx.snap | 24 ++- .../manage/Blocks/Accordion/editor.less | 16 +- .../manage/Blocks/Accordion/util.js | 20 ++ src/index.js | 5 + 14 files changed, 351 insertions(+), 238 deletions(-) create mode 100644 src/components/manage/Blocks/Accordion/AccordionFilter.jsx diff --git a/package.json b/package.json index d86472f..76fcf87 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@eeacms/volto-accordion-block", - "version": "9.3.0", + "version": "10.0.0", "description": "volto-accordion-block: Volto accordion block", "main": "src/index.js", "author": "European Environment Agency: IDM2 A-Team", diff --git a/src/components/manage/Blocks/Accordion/AccordionEdit.jsx b/src/components/manage/Blocks/Accordion/AccordionEdit.jsx index 99b93f8..de752b4 100644 --- a/src/components/manage/Blocks/Accordion/AccordionEdit.jsx +++ b/src/components/manage/Blocks/Accordion/AccordionEdit.jsx @@ -1,8 +1,8 @@ -import { Icon as VoltoIcon } from '@plone/volto/components'; import cx from 'classnames'; import React from 'react'; import AnimateHeight from 'react-animate-height'; -import { Accordion, Input, Icon } from 'semantic-ui-react'; +import { Accordion, Input } from 'semantic-ui-react'; +import { Icon } from './util'; import config from '@plone/volto/registry'; export default (props) => { @@ -18,6 +18,9 @@ export default (props) => { const [activeIndex, setActiveIndex] = React.useState([0]); const accordionConfig = config.blocks.blocksConfig.accordion; const { titleIcons } = accordionConfig; + const isActive = activeIndex.includes(index); + const iconOnRight = data.right_arrows; + const iconPosition = iconOnRight ? 'rightPosition' : 'leftPosition'; const handleClick = (e, itemProps) => { const { index } = itemProps; @@ -38,10 +41,6 @@ export default (props) => { } }; - const isExclusive = (index) => { - return activeIndex.includes(index); - }; - React.useEffect(() => { return data.collapsed ? setActiveIndex([]) : setActiveIndex([0]); }, [data.collapsed]); @@ -56,35 +55,21 @@ export default (props) => { - {accordionConfig.semanticIcon ? ( - - ) : isExclusive(index) ? ( - - ) : ( - - )} + {!data.readOnlyTitles ? ( { - - {children} - + {children} diff --git a/src/components/manage/Blocks/Accordion/AccordionEdit.test.jsx b/src/components/manage/Blocks/Accordion/AccordionEdit.test.jsx index 2f74569..2ce9b4c 100644 --- a/src/components/manage/Blocks/Accordion/AccordionEdit.test.jsx +++ b/src/components/manage/Blocks/Accordion/AccordionEdit.test.jsx @@ -13,14 +13,15 @@ config.blocks.blocksConfig.accordion = { }, titleIcons: { opened: { - rightPosition: 'openedRightIcon', - leftPosition: 'openedLeftIcon', + rightPosition: 'chevron left', + leftPosition: 'chevron right', }, closed: { - rightPosition: 'closedRightIcon', - leftPosition: 'closedLeftIcon', + rightPosition: 'chevron down', + leftPosition: 'chevron down', }, - size: '10px', + size: 'tiny', + iconComponent: 'SemanticIcon', }, }; diff --git a/src/components/manage/Blocks/Accordion/AccordionFilter.jsx b/src/components/manage/Blocks/Accordion/AccordionFilter.jsx new file mode 100644 index 0000000..3c15667 --- /dev/null +++ b/src/components/manage/Blocks/Accordion/AccordionFilter.jsx @@ -0,0 +1,49 @@ +import React from 'react'; +import { Accordion, Input } from 'semantic-ui-react'; +import { Icon } from './util'; +import './editor.less'; + +const AccordionFilter = ({ + config, + data, + filterValue, + handleFilteredValueChange, +}) => { + const accordionConfig = config.blocks.blocksConfig.accordion; + const { titleIcons } = accordionConfig; + const iconOnRight = data.right_arrows; + const iconPosition = iconOnRight ? 'rightPosition' : 'leftPosition'; + return ( + + + handleFilteredValueChange('')} + /> + handleFilteredValueChange(e.target.value)} + /> + + + ); +}; + +export default AccordionFilter; diff --git a/src/components/manage/Blocks/Accordion/Edit.jsx b/src/components/manage/Blocks/Accordion/Edit.jsx index a050d8a..36f628b 100644 --- a/src/components/manage/Blocks/Accordion/Edit.jsx +++ b/src/components/manage/Blocks/Accordion/Edit.jsx @@ -16,6 +16,7 @@ import React, { useState } from 'react'; import { Button, Segment } from 'semantic-ui-react'; import { useIntl } from 'react-intl'; import AccordionEdit from './AccordionEdit'; +import AccordionFilter from './AccordionFilter'; import EditBlockWrapper from './EditBlockWrapper'; import './editor.less'; import { AccordionBlockSchema } from './Schema'; @@ -25,6 +26,7 @@ import config from '@plone/volto/registry'; const Edit = (props) => { const [selectedBlock, setSelectedBlock] = useState({}); const [multiSelected, setMultiSelected] = useState([]); + const [filterValue, setFilterValue] = useState(''); const { block, data, @@ -202,6 +204,10 @@ const Edit = (props) => { }); }; + const handleFilteredValueChange = (value) => { + setFilterValue(value); + }; + // Get editing instructions from block settings or props let instructions = data?.instructions?.data || data?.instructions; if (!instructions || instructions === '


') { @@ -273,103 +279,121 @@ const Edit = (props) => { > {data.title || 'Accordion'} - {panels.map(([uid, panel], index) => ( - setSelectedBlock({})} + {data.filtering && ( + - { - const isMultipleSelection = e - ? e.shiftKey || e.ctrlKey || e.metaKey - : false; - onSelectBlock(uid, id, isMultipleSelection, e, selectedBlock); - }} - onChangeFormData={(newFormData) => { - onChangeBlock(block, { - ...data, - data: { - ...panelData, - blocks: { - ...panelData.blocks, - [uid]: newFormData, - }, - }, - }); - }} - onChangeField={(id, value) => { - if (['blocks', 'blocks_layout'].indexOf(id) > -1) { - blockState[id] = value; + filterValue={filterValue} + handleFilteredValueChange={handleFilteredValueChange} + /> + )} + {panels + .filter( + (panel) => + !data.filtering || + filterValue === '' || + (filterValue !== '' && + panel[1].title + ?.toLowerCase() + .includes(filterValue.toLowerCase())), + ) + .map(([uid, panel], index) => ( + setSelectedBlock({})} + data={data} + index={index} + > + { + const isMultipleSelection = e + ? e.shiftKey || e.ctrlKey || e.metaKey + : false; + onSelectBlock(uid, id, isMultipleSelection, e, selectedBlock); + }} + onChangeFormData={(newFormData) => { onChangeBlock(block, { ...data, data: { ...panelData, blocks: { ...panelData.blocks, - [uid]: { - ...panelData.blocks?.[uid], - ...blockState, - }, + [uid]: newFormData, }, }, }); - } else { - onChangeField(id, value); - } - }} - pathname={pathname} - > - {({ draginfo }, editBlock, blockProps) => { - return ( - - {instructions && ( - <> - - - )} - - } - > - {editBlock} - - ); - }} - - - ))} + }} + onChangeField={(id, value) => { + if (['blocks', 'blocks_layout'].indexOf(id) > -1) { + blockState[id] = value; + onChangeBlock(block, { + ...data, + data: { + ...panelData, + blocks: { + ...panelData.blocks, + [uid]: { + ...panelData.blocks?.[uid], + ...blockState, + }, + }, + }, + }); + } else { + onChangeField(id, value); + } + }} + pathname={pathname} + > + {({ draginfo }, editBlock, blockProps) => { + return ( + + {instructions && ( + <> + + + )} + + } + > + {editBlock} + + ); + }} + + + ))} {selected ? ( ({ 'right_arrows', 'collapsed', 'non_exclusive', + 'filtering', ], }, ], @@ -150,6 +155,11 @@ export const AccordionBlockSchema = ({ intl }) => ({ type: 'boolean', default: true, }, + filtering: { + title: intl.formatMessage(messages.filtering), + type: 'boolean', + default: false, + }, }, required: [], }); diff --git a/src/components/manage/Blocks/Accordion/View.jsx b/src/components/manage/Blocks/Accordion/View.jsx index a1515b5..b0efed4 100644 --- a/src/components/manage/Blocks/Accordion/View.jsx +++ b/src/components/manage/Blocks/Accordion/View.jsx @@ -1,14 +1,15 @@ import React from 'react'; -import { getPanels, accordionBlockHasValue } from './util'; -import { Accordion, Icon } from 'semantic-ui-react'; +import { getPanels, accordionBlockHasValue, Icon } from './util'; +import { Accordion } from 'semantic-ui-react'; import { withBlockExtensions } from '@plone/volto/helpers'; import { useLocation, useHistory } from 'react-router-dom'; import cx from 'classnames'; -import { Icon as VoltoIcon, RenderBlocks } from '@plone/volto/components'; +import { RenderBlocks } from '@plone/volto/components'; import AnimateHeight from 'react-animate-height'; import config from '@plone/volto/registry'; import './editor.less'; +import AccordionFilter from './AccordionFilter'; const useQuery = (location) => { const { search } = location; @@ -23,9 +24,12 @@ const View = (props) => { const metadata = props.metadata || props.properties; const [activeIndex, setActiveIndex] = React.useState([]); const [activePanel, setActivePanel] = React.useState([]); + const [filterValue, setFilterValue] = React.useState(''); const [itemToScroll, setItemToScroll] = React.useState(''); const accordionConfig = config.blocks.blocksConfig.accordion; const { titleIcons } = accordionConfig; + const iconOnRight = data.right_arrows; + const iconPosition = iconOnRight ? 'rightPosition' : 'leftPosition'; const query = useQuery(location); const activePanels = query.get('activeAccordion')?.split(','); @@ -78,6 +82,10 @@ const View = (props) => { addQueryParam('activeAccordion', id); }; + const handleFilteredValueChange = (value) => { + setFilterValue(value); + }; + const scrollToElement = () => { if (!!activePanels && !!activePanels[0].length) { let element = document.getElementById( @@ -116,81 +124,88 @@ const View = (props) => { return (
{data.headline &&

{data.headline}

} - {panels.map(([id, panel], index) => { - return accordionBlockHasValue(panel) ? ( - - - handleClick(e, { index, id })} - onKeyDown={(e) => { - if (e.nativeEvent.keyCode === 13) { - handleClick(e, { index }); - } - }} - className={cx('accordion-title', { - 'align-arrow-left': !props?.data?.right_arrows, - 'align-arrow-right': props?.data?.right_arrows, - })} - > - {accordionConfig.semanticIcon ? ( - - ) : isExclusive(id) ? ( - + )} + {panels + .filter( + (panel) => + !data.filtering || + filterValue === '' || + (filterValue !== '' && + panel[1].title + ?.toLowerCase() + .includes(filterValue.toLowerCase())), + ) + .map(([id, panel], index) => { + const active = isExclusive(id); + return accordionBlockHasValue(panel) ? ( + + + handleClick(e, { index, id })} + onKeyDown={(e) => { + if (e.nativeEvent.keyCode === 13) { + handleClick(e, { index }); } - size={titleIcons.size} - /> - ) : ( - + - )} - {panel?.title} - - { - if (!!activePanels && id === itemToScroll) { - scrollToElement(); - setItemToScroll(''); - } - }} - > - - - - - - - ) : null; - })} + {panel?.title} + + { + if (!!activePanels && id === itemToScroll) { + scrollToElement(); + setItemToScroll(''); + } + }} + > + + + + + + + ) : null; + })}
); }; diff --git a/src/components/manage/Blocks/Accordion/View.test.jsx b/src/components/manage/Blocks/Accordion/View.test.jsx index 1c2c63c..463e419 100644 --- a/src/components/manage/Blocks/Accordion/View.test.jsx +++ b/src/components/manage/Blocks/Accordion/View.test.jsx @@ -13,14 +13,15 @@ config.blocks.blocksConfig.accordion = { ...config.blocks.blocksConfig.accordion, titleIcons: { opened: { - rightPosition: '', - leftPosition: '', + rightPosition: 'chevron left', + leftPosition: 'chevron right', }, closed: { - rightPosition: '', - leftPosition: '', + rightPosition: 'chevron down', + leftPosition: 'chevron down', }, - size: '10px', + size: 'tiny', + iconComponent: 'SemanticIcon', }, }; @@ -44,6 +45,7 @@ jest.mock('@plone/volto/components', () => ({ jest.mock('./util', () => ({ getPanels: () => [['id1', { title: 'Panel 1' }]], accordionBlockHasValue: jest.fn(), + Icon: () =>
Icon
, })); const mockData = { @@ -114,10 +116,10 @@ describe('View Component', () => { it('should open accordion content when title is clicked', () => { utils.accordionBlockHasValue.mockReturnValue(true); - config.blocks.blocksConfig.accordion = { - ...config.blocks.blocksConfig.accordion, - semanticIcon: 'someIcon', - }; + // config.blocks.blocksConfig.accordion = { + // ...config.blocks.blocksConfig.accordion, + // semanticIcon: 'someIcon', + // }; const { container, getByText } = render( @@ -151,10 +153,10 @@ describe('View Component', () => { }); it('should open accordion content when Enter key is pressed', () => { - config.blocks.blocksConfig.accordion = { - ...config.blocks.blocksConfig.accordion, - semanticIcon: 'someIcon', - }; + // config.blocks.blocksConfig.accordion = { + // ...config.blocks.blocksConfig.accordion, + // semanticIcon: 'someIcon', + // }; const { container, getByText } = render( diff --git a/src/components/manage/Blocks/Accordion/__snapshots__/AccordionEdit.test.jsx.snap b/src/components/manage/Blocks/Accordion/__snapshots__/AccordionEdit.test.jsx.snap index a84368f..bcf501d 100644 --- a/src/components/manage/Blocks/Accordion/__snapshots__/AccordionEdit.test.jsx.snap +++ b/src/components/manage/Blocks/Accordion/__snapshots__/AccordionEdit.test.jsx.snap @@ -10,7 +10,7 @@ exports[`AccordionEdit should render correctly 1`] = ` >