diff --git a/cypress/tests/core/basic/view.js b/cypress/tests/core/basic/view.js new file mode 100644 index 0000000000..00cc061e25 --- /dev/null +++ b/cypress/tests/core/basic/view.js @@ -0,0 +1,55 @@ +describe('Add Content Tests', () => { + beforeEach(() => { + cy.intercept('GET', `/**/*?expand*`).as('content'); + // give a logged in editor and the site root + cy.autologin(); + cy.visit('/'); + cy.wait('@content'); + }); + + it('As editor I can change the view to Listing View', function () { + + cy.visit('/events'); + cy.get('#toolbar-more').click(); + cy.findByText('Listing view').click(); + cy.findByText('Album view').click(); + cy.visit('/events'); + cy.wait('@content'); + cy.wait(2000); + cy.get('main').contains('Event').should('be.visible'); + }); + + it('As editor I can change the view to Summary View', function () { + + cy.visit('/events'); + cy.get('#toolbar-more').click(); + cy.findByText('Listing view').click(); + cy.findByText('Summary view').click(); + cy.visit('/events'); + cy.wait('@content'); + cy.wait(2000); + cy.get('main').contains('Event').should('be.visible'); + }); + it('As editor I can change the view to Tabular View', function () { + + cy.visit('/events'); + cy.get('#toolbar-more').click(); + cy.findByText('Listing view').click(); + cy.findByText('Tabular view').click(); + cy.visit('/events'); + cy.wait('@content'); + cy.wait(2000); + cy.get('main').contains('Event').should('be.visible'); + }); + it('As editor I can change the view to Album View', function () { + + cy.visit('/events'); + cy.get('#toolbar-more').click(); + cy.findByText('Listing view').click(); + cy.visit('/events'); + cy.wait('@content'); + cy.wait(2000); + cy.get('main').contains('Event').should('be.visible'); + }); + }); + \ No newline at end of file diff --git a/cypress/tests/core/blocks/block-anchors.js b/cypress/tests/core/blocks/block-anchors.js index 1166b0ce26..91fe8ed989 100644 --- a/cypress/tests/core/blocks/block-anchors.js +++ b/cypress/tests/core/blocks/block-anchors.js @@ -63,9 +63,9 @@ describe('Block Tests: Anchors', () => { cy.contains('Slate Heading Anchors'); cy.get('h2[id="title-1"]').contains('Title 1'); cy.get('h2[id="title-2"]').contains('Title 2'); - cy.get('a[href="#title-1"]').click(); + cy.get('.table-of-contents a[href="/my-page#title-1"]').click(); cy.get('h2[id="title-1"]').scrollIntoView().should('be.visible'); - cy.get('a[href="#title-2"]').click(); + cy.get('.table-of-contents a[href="/my-page#title-2"]').click(); cy.get('h2[id="title-2"]').scrollIntoView().should('be.visible'); }); }); diff --git a/cypress/tests/core/blocks/blocks-search.js b/cypress/tests/core/blocks/blocks-search.js index 055d9b7948..d9157567d7 100644 --- a/cypress/tests/core/blocks/blocks-search.js +++ b/cypress/tests/core/blocks/blocks-search.js @@ -1,4 +1,5 @@ describe('Search Block Tests', () => { + var results_number = 3; beforeEach(() => { cy.intercept('GET', `/**/*?expand*`).as('content'); cy.intercept('GET', '/**/Document').as('schema'); @@ -91,6 +92,12 @@ describe('Search Block Tests', () => { cy.wait(500); + // test search results number + cy.get('.search-details').should( + 'contain', + `Search results: ${results_number}`, + ); + // test if type facet works cy.get('.block.search .facets > .facet .entries > .entry label') .contains('Event') @@ -158,6 +165,12 @@ describe('Search Block Tests', () => { cy.get('#toolbar-save > .icon').click(); cy.wait(500); + + // test search results number + cy.get('.search-details').should( + 'contain', + `Search results: ${results_number}`, + ); }); it('Search block - test live searchbox', () => { @@ -201,6 +214,13 @@ describe('Search Block Tests', () => { cy.wait('@content'); cy.wait(500); + + // test search results number + cy.get('.search-details').should( + 'contain', + `Search results: ${results_number}`, + ); + // test searching for Event cy.get('.search-wrapper .search-input input').focus().type('Event'); cy.get('#page-document .listing-item:first-of-type a').should( @@ -237,6 +257,12 @@ describe('Search Block Tests', () => { ); cy.url().should('not.contain', '%22SearchableText%22'); + // test search results number + cy.get('.search-details').should( + 'contain', + `Search results: ${results_number}`, + ); + // test searching for Event cy.get('.search-wrapper .search-input input').focus().type('Event'); cy.get('#page-document .listing-item:first-of-type a').should( @@ -253,6 +279,9 @@ describe('Search Block Tests', () => { '%7B%22i%22%3A%22SearchableText%22%2C%22o%22%3A%22paqo.string.contains%22%2C%22v%22%3A%22Event%22%7D', ); + // test search results number + cy.get('.search-details').should('contain', 'Search results: 1'); + // test removing one char cy.get('.search-wrapper .search-input input').focus().type('{backspace}'); cy.get('.search-results-count-sort .search-details em') @@ -319,6 +348,12 @@ describe('Search Block Tests', () => { cy.wait(500); + // test search results number + cy.get('.search-details').should( + 'contain', + `Search results: ${results_number}`, + ); + // test searching for Event cy.get('.search-wrapper .search-input input').focus().type('Event'); cy.get('.search-wrapper > .ui.button').click(); @@ -337,6 +372,9 @@ describe('Search Block Tests', () => { '%7B%22i%22%3A%22SearchableText%22%2C%22o%22%3A%22paqo.string.contains%22%2C%22v%22%3A%22Event%22%7D', ); + // test search results number + cy.get('.search-details').should('contain', 'Search results: 1'); + // test removing one char cy.get('.search-wrapper .search-input input').focus().type('{backspace}'); cy.get('.search-wrapper > .ui.button').click(); @@ -359,6 +397,12 @@ describe('Search Block Tests', () => { ); cy.url().should('not.contain', '%22SearchableText%22'); + // test search results number + cy.get('.search-details').should( + 'contain', + `Search results: ${results_number}`, + ); + // test searching for Event cy.get('.search-wrapper .search-input input').focus().type('Event'); cy.get('.search-wrapper > .ui.button').click(); @@ -376,6 +420,9 @@ describe('Search Block Tests', () => { '%7B%22i%22%3A%22SearchableText%22%2C%22o%22%3A%22paqo.string.contains%22%2C%22v%22%3A%22Event%22%7D', ); + // test search results number + cy.get('.search-details').should('contain', 'Search results: 1'); + // test removing one char cy.get('.search-wrapper .search-input input').focus().type('{backspace}'); cy.get('.search-wrapper > .ui.button').click(); @@ -397,5 +444,11 @@ describe('Search Block Tests', () => { 'Searched for:', ); cy.url().should('not.contain', '%22SearchableText%22'); + + // test search results number + cy.get('.search-details').should( + 'contain', + `Search results: ${results_number}`, + ); }); }); diff --git a/locales/ca/LC_MESSAGES/volto.po b/locales/ca/LC_MESSAGES/volto.po index b1af99cfea..d197af6a01 100644 --- a/locales/ca/LC_MESSAGES/volto.po +++ b/locales/ca/LC_MESSAGES/volto.po @@ -4645,7 +4645,7 @@ msgstr "a" #: components/manage/Widgets/RecurrenceWidget/Utils # defaultMessage: [month] [day], [year] msgid "rrule_dateFormat" -msgstr "[mes] [dia], [any]" +msgstr "[month] [day], [year]" #: components/manage/Widgets/RecurrenceWidget/Utils # defaultMessage: day diff --git a/locales/de/LC_MESSAGES/volto.po b/locales/de/LC_MESSAGES/volto.po index b2f5740b5f..f4db91efd4 100644 --- a/locales/de/LC_MESSAGES/volto.po +++ b/locales/de/LC_MESSAGES/volto.po @@ -4642,7 +4642,7 @@ msgstr "Regel AT" #: components/manage/Widgets/RecurrenceWidget/Utils # defaultMessage: [month] [day], [year] msgid "rrule_dateFormat" -msgstr "Regel Datumsformat" +msgstr "[day] [month] [year]" #: components/manage/Widgets/RecurrenceWidget/Utils # defaultMessage: day diff --git a/locales/es/LC_MESSAGES/volto.po b/locales/es/LC_MESSAGES/volto.po index 6af24edc03..b937a85540 100644 --- a/locales/es/LC_MESSAGES/volto.po +++ b/locales/es/LC_MESSAGES/volto.po @@ -4647,7 +4647,7 @@ msgstr "a las" #: components/manage/Widgets/RecurrenceWidget/Utils # defaultMessage: [month] [day], [year] msgid "rrule_dateFormat" -msgstr "formato de fecha" +msgstr "[day]/[month]/[year]" #: components/manage/Widgets/RecurrenceWidget/Utils # defaultMessage: day diff --git a/locales/fr/LC_MESSAGES/volto.po b/locales/fr/LC_MESSAGES/volto.po index d849d06ae5..dc469ad9ae 100644 --- a/locales/fr/LC_MESSAGES/volto.po +++ b/locales/fr/LC_MESSAGES/volto.po @@ -4653,7 +4653,7 @@ msgstr "à" #: components/manage/Widgets/RecurrenceWidget/Utils # defaultMessage: [month] [day], [year] msgid "rrule_dateFormat" -msgstr "[mois] [jour], [année]" +msgstr "[month] [day], [year]" #: components/manage/Widgets/RecurrenceWidget/Utils # defaultMessage: day diff --git a/locales/nl/LC_MESSAGES/volto.po b/locales/nl/LC_MESSAGES/volto.po index 790469f57b..6d031b47a7 100644 --- a/locales/nl/LC_MESSAGES/volto.po +++ b/locales/nl/LC_MESSAGES/volto.po @@ -4655,7 +4655,7 @@ msgstr "" #: components/manage/Widgets/RecurrenceWidget/Utils # defaultMessage: [month] [day], [year] msgid "rrule_dateFormat" -msgstr "" +msgstr "[month] [day], [year]" #: components/manage/Widgets/RecurrenceWidget/Utils # defaultMessage: day diff --git a/locales/pt/LC_MESSAGES/volto.po b/locales/pt/LC_MESSAGES/volto.po index 03c909fa0d..743fe4ecef 100644 --- a/locales/pt/LC_MESSAGES/volto.po +++ b/locales/pt/LC_MESSAGES/volto.po @@ -4644,7 +4644,7 @@ msgstr "" #: components/manage/Widgets/RecurrenceWidget/Utils # defaultMessage: [month] [day], [year] msgid "rrule_dateFormat" -msgstr "" +msgstr "[day] de [month] de [year]" #: components/manage/Widgets/RecurrenceWidget/Utils # defaultMessage: day diff --git a/locales/ro/LC_MESSAGES/volto.po b/locales/ro/LC_MESSAGES/volto.po index eebc7a7058..cbf003e233 100644 --- a/locales/ro/LC_MESSAGES/volto.po +++ b/locales/ro/LC_MESSAGES/volto.po @@ -4636,7 +4636,7 @@ msgstr "La" #: components/manage/Widgets/RecurrenceWidget/Utils # defaultMessage: [month] [day], [year] msgid "rrule_dateFormat" -msgstr "" +msgstr "[year]-[month]-[day]" #: components/manage/Widgets/RecurrenceWidget/Utils # defaultMessage: day diff --git a/locales/zh_CN/LC_MESSAGES/volto.po b/locales/zh_CN/LC_MESSAGES/volto.po index e100c4dd85..ed63fbdba1 100644 --- a/locales/zh_CN/LC_MESSAGES/volto.po +++ b/locales/zh_CN/LC_MESSAGES/volto.po @@ -4642,7 +4642,7 @@ msgstr "" #: components/manage/Widgets/RecurrenceWidget/Utils # defaultMessage: [month] [day], [year] msgid "rrule_dateFormat" -msgstr "" +msgstr "[year]-[month]-[day]" #: components/manage/Widgets/RecurrenceWidget/Utils # defaultMessage: day diff --git a/news/3250.bugfix b/news/3250.bugfix new file mode 100644 index 0000000000..4f41d37c73 --- /dev/null +++ b/news/3250.bugfix @@ -0,0 +1 @@ +Fix for responsive error in the login page when the width of the screen decreases. @suman9893 \ No newline at end of file diff --git a/news/4076.feature b/news/4076.feature new file mode 100644 index 0000000000..c88e5b0c7b --- /dev/null +++ b/news/4076.feature @@ -0,0 +1 @@ +Refactor Navigation -@Tishasoumya-02 diff --git a/news/4402.bugfix b/news/4402.bugfix new file mode 100644 index 0000000000..aee68dbeb1 --- /dev/null +++ b/news/4402.bugfix @@ -0,0 +1 @@ +Fix back button in the search block to execute the search by adding two useEffects that update the facets and search data based on the current URL. @MihaelaCretu11 \ No newline at end of file diff --git a/news/4503.bugfix b/news/4503.bugfix new file mode 100644 index 0000000000..9e78d10a74 --- /dev/null +++ b/news/4503.bugfix @@ -0,0 +1 @@ +fix : RecursiveWidget is incorrectly translated. @suman9893 \ No newline at end of file diff --git a/news/4926.feature b/news/4926.feature new file mode 100644 index 0000000000..b08a69246b --- /dev/null +++ b/news/4926.feature @@ -0,0 +1 @@ +Messages Component Refactor - @Tishasoumya-02 \ No newline at end of file diff --git a/news/4933.feature b/news/4933.feature new file mode 100644 index 0000000000..4eed79a508 --- /dev/null +++ b/news/4933.feature @@ -0,0 +1 @@ +Refactor Login -@Tishasoumya-02 diff --git a/news/5019.bugfix b/news/5019.bugfix new file mode 100644 index 0000000000..f3a5d4a243 --- /dev/null +++ b/news/5019.bugfix @@ -0,0 +1 @@ +Fix use of CSS modules in webpack 5. @wesleybl diff --git a/news/5058.bugfix b/news/5058.bugfix new file mode 100644 index 0000000000..2a67cc6d3e --- /dev/null +++ b/news/5058.bugfix @@ -0,0 +1 @@ +Fix toc accessibility issue @dobri1408 diff --git a/news/5109.feature b/news/5109.feature new file mode 100644 index 0000000000..14b3858788 --- /dev/null +++ b/news/5109.feature @@ -0,0 +1 @@ +Add external className to UniversalLink for external link. @iFlameing \ No newline at end of file diff --git a/news/5146.feature b/news/5146.feature new file mode 100644 index 0000000000..105a9b9e0c --- /dev/null +++ b/news/5146.feature @@ -0,0 +1 @@ +(feat): Update toc block entries @dobri1408 diff --git a/news/5149.feature b/news/5149.feature new file mode 100644 index 0000000000..8313f839f9 --- /dev/null +++ b/news/5149.feature @@ -0,0 +1 @@ +Views cypress test -@Tishasoumya \ No newline at end of file diff --git a/news/5171.bugfix b/news/5171.bugfix new file mode 100644 index 0000000000..545a94c8ea --- /dev/null +++ b/news/5171.bugfix @@ -0,0 +1 @@ +Fix search block search results number @ionlizarazu \ No newline at end of file diff --git a/news/5171.feature b/news/5171.feature new file mode 100644 index 0000000000..0caad91658 --- /dev/null +++ b/news/5171.feature @@ -0,0 +1 @@ +Cypress test to test if 'Search results: number' text is present @ionlizarazu \ No newline at end of file diff --git a/packages/volto-slate/src/editor/render.jsx b/packages/volto-slate/src/editor/render.jsx index 45ab42ca75..5b5542e4f0 100644 --- a/packages/volto-slate/src/editor/render.jsx +++ b/packages/volto-slate/src/editor/render.jsx @@ -174,13 +174,12 @@ export const renderLinkElement = (tagName) => { appPathname.concat(`#${slug}`), ); const intl = useIntl(); - return slate.useLinkedHeadings === false ? ( - + {children} ) : ( - + {children} {mode === 'view' && slug && ( (WrappedComponent) => { const multiFacets = data.facets ?.filter((facet) => facet?.multiple) .map((facet) => facet?.field?.value); - const [facets, setFacets] = React.useState( - Object.assign( - {}, - ...urlQuery.map(({ i, v }) => ({ [i]: v })), // TODO: the 'o' should be kept. This would be a major refactoring of the facets - - // support for simple filters like ?Subject=something - // TODO: since the move to hash params this is no longer working. - // We'd have to treat the location.search and manage it just like the - // hash, to support it. We can read it, but we'd have to reset it as - // well, so at that point what's the difference to the hash? - ...configuredFacets.map((f) => - locationSearchData[f] - ? { - [f]: - multiFacets.indexOf(f) > -1 - ? [locationSearchData[f]] - : locationSearchData[f], - } - : {}, - ), - ), - ); + const [facets, setFacets] = React.useState({}); + const previousUrlQuery = usePrevious(urlQuery); + + React.useEffect(() => { + if (!isEqual(urlQuery, previousUrlQuery)) { + setFacets( + Object.assign( + {}, + ...urlQuery.map(({ i, v }) => ({ [i]: v })), // TODO: the 'o' should be kept. This would be a major refactoring of the facets + + // support for simple filters like ?Subject=something + // TODO: since the move to hash params this is no longer working. + // We'd have to treat the location.search and manage it just like the + // hash, to support it. We can read it, but we'd have to reset it as + // well, so at that point what's the difference to the hash? + ...configuredFacets.map((f) => + locationSearchData[f] + ? { + [f]: + multiFacets.indexOf(f) > -1 + ? [locationSearchData[f]] + : locationSearchData[f], + } + : {}, + ), + ), + ); + } + }, [ + urlQuery, + configuredFacets, + locationSearchData, + multiFacets, + previousUrlQuery, + ]); const [sortOn, setSortOn] = React.useState(data?.query?.sort_on); const [sortOrder, setSortOrder] = React.useState(data?.query?.sort_order); - const [searchData, setSearchData] = React.useState( - getInitialState(data, facets, urlSearchText, id), - ); + const [searchData, setSearchData] = React.useState({}); + + React.useEffect(() => { + setSearchData(getInitialState(data, facets, urlSearchText, id)); + }, [facets, data, urlSearchText, id]); const timeoutRef = React.useRef(); const facetSettings = data?.facets; diff --git a/src/components/manage/Blocks/Title/__snapshots__/View.test.jsx.snap b/src/components/manage/Blocks/Title/__snapshots__/View.test.jsx.snap index cc62ae1b9c..7c7c1c0d60 100644 --- a/src/components/manage/Blocks/Title/__snapshots__/View.test.jsx.snap +++ b/src/components/manage/Blocks/Title/__snapshots__/View.test.jsx.snap @@ -4,6 +4,7 @@ exports[`renders a view title component 1`] = `

My Title { + const blocksFieldName = getBlocksFieldname(properties); + const blocksLayoutFieldname = getBlocksLayoutFieldname(properties); + + const blocks = properties[blocksFieldName]; + const blocks_layout = properties[blocksLayoutFieldname]; + + const levels = + tocData.levels?.length > 0 + ? tocData.levels.map((l) => parseInt(l.slice(1))) + : [1, 2, 3, 4, 5, 6]; + let rootLevel = Infinity; + let blocksFormEntries = []; + let tocEntries = {}; + let tocEntriesLayout = []; + + blocks_layout.items.forEach((id) => { + const block = blocks[id]; + const blockConfig = config.blocks.blocksConfig[block['@type']]; + + if (!block || !blockConfig) { + return null; + } + if (!blockConfig.tocEntries && !blockConfig.tocEntry) { + return null; + } + + const blockTocEntry = blockConfig.tocEntry?.(block, tocData); + + const blockTocEntries = [ + ...(blockConfig.tocEntries?.(block, tocData) || + (blockTocEntry ? [blockTocEntry] : [])), + ]; + + blocksFormEntries = [...blocksFormEntries, ...blockTocEntries]; + + blockTocEntries.forEach((entry, index) => { + const i = `${id}-${index}`; + const level = entry[0]; + const title = entry[1]; + const items = []; + if (!level || !levels.includes(level)) return; + tocEntriesLayout.push(i); + tocEntries[i] = { + level, + title: title || block.plaintext, + items, + id: i, + }; + if (level < rootLevel) { + rootLevel = level; + } + }); + }); + + return { + rootLevel, + blocksFormEntries, + tocEntries, + tocEntriesLayout, + }; +}; + /** * View toc block class. * @class View * @extends Component */ const View = (props) => { - const { properties, data } = props; + const { data } = props; const { variation } = props; - const blocksFieldname = getBlocksFieldname(properties); - const blocksLayoutFieldname = getBlocksLayoutFieldname(properties); + const metadata = props.metadata || props.properties; + const blocksFieldname = getBlocksFieldname(metadata); const levels = React.useMemo( () => data.levels?.length > 0 @@ -34,14 +97,15 @@ const View = (props) => { [data], ); const tocEntries = React.useMemo(() => { - let rootLevel = Infinity; let entries = []; let prevEntry = {}; - let tocEntries = {}; - let tocEntriesLayout = []; + const { rootLevel, tocEntries, tocEntriesLayout } = getBlocksTocEntries( + metadata, + data, + ); - properties[blocksLayoutFieldname].items.forEach((id) => { - const block = properties[blocksFieldname][id]; + tocEntriesLayout.forEach((id) => { + const block = metadata[blocksFieldname][id]; if (typeof block === 'undefined') { return null; } @@ -50,6 +114,7 @@ const View = (props) => { block, data, ); + if (entry) { const level = entry[0]; const title = entry[1]; @@ -64,9 +129,6 @@ const View = (props) => { override_toc: block.override_toc, plaintext: block.plaintext, }; - if (level < rootLevel) { - rootLevel = level; - } } }); @@ -98,7 +160,7 @@ const View = (props) => { }); return entries; - }, [data, levels, properties, blocksFieldname, blocksLayoutFieldname]); + }, [data, levels, metadata, blocksFieldname]); const Renderer = variation?.view; return ( @@ -108,7 +170,7 @@ const View = (props) => { )} {Renderer ? ( - + ) : (
View extension not found
)} diff --git a/src/components/manage/Blocks/ToC/variations/DefaultTocRenderer.jsx b/src/components/manage/Blocks/ToC/variations/DefaultTocRenderer.jsx index 05769ec65c..8592cfecbf 100644 --- a/src/components/manage/Blocks/ToC/variations/DefaultTocRenderer.jsx +++ b/src/components/manage/Blocks/ToC/variations/DefaultTocRenderer.jsx @@ -8,13 +8,10 @@ import PropTypes from 'prop-types'; import { map } from 'lodash'; import { List } from 'semantic-ui-react'; import { FormattedMessage, injectIntl } from 'react-intl'; -import { useHistory } from 'react-router-dom'; -import AnchorLink from 'react-anchor-link-smooth-scroll'; import Slugger from 'github-slugger'; +import { UniversalLink } from '@plone/volto/components'; const RenderListItems = ({ items, data }) => { - const history = useHistory(); - return map(items, (item) => { const { id, level, title, override_toc, plaintext } = item; const slug = override_toc @@ -23,14 +20,7 @@ const RenderListItems = ({ items, data }) => { return ( item && ( - { - history.push({ hash: slug }); - }} - > - {title} - + {title} {item.items?.length > 0 && (
Hello this is a sample page @@ -28,8 +31,11 @@ Array [ role="listitem" > Test level 3 diff --git a/src/components/manage/LinksToItem/LinksToItem.test.jsx b/src/components/manage/LinksToItem/LinksToItem.test.jsx index 31cf6b5ce4..0ec7dd61f6 100644 --- a/src/components/manage/LinksToItem/LinksToItem.test.jsx +++ b/src/components/manage/LinksToItem/LinksToItem.test.jsx @@ -3,6 +3,7 @@ import renderer from 'react-test-renderer'; import { Provider } from 'react-intl-redux'; import configureMockStore from 'redux-mock-store'; import thunk from 'redux-thunk'; +import { MemoryRouter } from 'react-router-dom'; import LinksToItem from './LinksToItem'; @@ -88,7 +89,9 @@ describe('LinksToItem', () => { }); const component = renderer.create( - + + + , ); const json = component.toJSON(); diff --git a/src/components/manage/LinksToItem/__snapshots__/LinksToItem.test.jsx.snap b/src/components/manage/LinksToItem/__snapshots__/LinksToItem.test.jsx.snap index 23d7280351..71a4163128 100644 --- a/src/components/manage/LinksToItem/__snapshots__/LinksToItem.test.jsx.snap +++ b/src/components/manage/LinksToItem/__snapshots__/LinksToItem.test.jsx.snap @@ -49,9 +49,9 @@ exports[`LinksToItem renders "links and references" view 1`] = ` > { + const dispatch = useDispatch(); - // /** - // * Component will receive props - // * @method componentWillReceiveProps - // * @param {Object} nextProps Next properties - // * @returns {undefined} - // */ - // componentWillReceiveProps(nextProps) { - // if (nextProps.messages.length > this.props.messages.length) { - // window.setTimeout(() => { - // if (this.props.messages.length > 0) { - // this.props.removeMessage(-1); - // } - // }, 6000); - // } - // } + const messages = useSelector( + (state) => state.messages.messages, + shallowEqual, + ); - /** - * On dismiss - * @method onDismiss - * @param {Object} event Event object - * @param {number} value Index of message - * @returns {undefined} - */ - onDismiss(event, { value }) { - this.props.removeMessage(value); - } - - /** - * Render method. - * @method render - * @returns {string} Markup for the component. - */ - render() { - return ( - this.props.messages && ( - - {map(this.props.messages, (message, index) => ( - - ))} - - ) - ); - } -} + const onDismiss = (event, { value }) => { + dispatch(removeMessage(value)); + }; -export default connect( - (state) => ({ - messages: state.messages.messages, - }), - { removeMessage }, -)(Messages); + return ( + messages && ( + + {map(messages, (message, index) => ( + + ))} + + ) + ); +}; + +export default Messages; diff --git a/src/components/manage/Messages/Messages.test.jsx b/src/components/manage/Messages/Messages.test.jsx index 6f0ac40a09..1037771926 100644 --- a/src/components/manage/Messages/Messages.test.jsx +++ b/src/components/manage/Messages/Messages.test.jsx @@ -1,4 +1,3 @@ -import React from 'react'; import renderer from 'react-test-renderer'; import configureStore from 'redux-mock-store'; import { Provider } from 'react-redux'; diff --git a/src/components/manage/UniversalLink/UniversalLink.jsx b/src/components/manage/UniversalLink/UniversalLink.jsx index 7ec4c6f28c..08c4c81066 100644 --- a/src/components/manage/UniversalLink/UniversalLink.jsx +++ b/src/components/manage/UniversalLink/UniversalLink.jsx @@ -14,6 +14,7 @@ import { } from '@plone/volto/helpers/Url/Url'; import config from '@plone/volto/registry'; +import cx from 'classnames'; const UniversalLink = ({ href, @@ -88,19 +89,16 @@ const UniversalLink = ({ ); if (isExternal) { + const isTelephoneOrMail = checkedURL.isMail || checkedURL.isTelephone; tag = ( {children} diff --git a/src/components/manage/UniversalLink/__snapshots__/UniversalLink.test.jsx.snap b/src/components/manage/UniversalLink/__snapshots__/UniversalLink.test.jsx.snap index e9f0f1d40d..863e4bb130 100644 --- a/src/components/manage/UniversalLink/__snapshots__/UniversalLink.test.jsx.snap +++ b/src/components/manage/UniversalLink/__snapshots__/UniversalLink.test.jsx.snap @@ -17,9 +17,9 @@ exports[`UniversalLink check UniversalLink does not break with error in item 1`] exports[`UniversalLink renders a UniversalLink component if no external(href) link passed 1`] = `

@@ -30,7 +30,7 @@ exports[`UniversalLink renders a UniversalLink component if no external(href) li exports[`UniversalLink renders a UniversalLink component with external link 1`] = ` { + const intl = useIntl(); + const history = useHistory(); + const location = useLocation(); + const dispatch = useDispatch(); + const token = useSelector((state) => state.userSession.token, shallowEqual); + const error = useSelector((state) => state.userSession.login.error); + const loading = useSelector((state) => state.userSession.login.loading); + const returnUrl = + qs.parse(props.location.search ?? location.search).return_url || + location.pathname.replace(/\/login\/?$/, '').replace(/\/logout\/?$/, '') || + '/'; + useEffect(() => { + if (token && !props.isLogout) { + history.push(returnUrl || '/'); if (toast.isActive('loggedOut')) { toast.dismiss('loggedOut'); } @@ -136,7 +88,7 @@ class Login extends Component { toast.dismiss('loginFailed'); } } - if (nextProps.error) { + if (error) { if (toast.isActive('loggedOut')) { toast.dismiss('loggedOut'); } @@ -144,189 +96,155 @@ class Login extends Component { toast.error( , { autoClose: false, toastId: 'loginFailed' }, ); } } - } - - componentWillUnmount() { - if (toast.isActive('loginFailed')) { - toast.dismiss('loginFailed'); - } - } + return () => { + if (toast.isActive('loginFailed')) { + toast.dismiss('loginFailed'); + dispatch(resetLoginRequest()); + } + }; + }, [dispatch, token, error, intl, history, returnUrl, props.isLogout]); - /** - * On login handler - * @method onLogin - * @param {Object} event Event object. - * @returns {undefined} - */ - onLogin(event) { - this.props.login( - document.getElementsByName('login')[0].value, - document.getElementsByName('password')[0].value, + const onLogin = (event) => { + dispatch( + login( + document.getElementsByName('login')[0].value, + document.getElementsByName('password')[0].value, + ), ); event.preventDefault(); - } + }; - /** - * Render method. - * @method render - * @returns {string} Markup for the component. - */ - render() { - return ( -
- - -
- - - - - - - - - - - - -
- -
-
- - {/* eslint-disable jsx-a11y/no-autofocus */} - - -
-
-
- - - - -
- -
-
- - - -
-
-
- - - - {config.settings.showSelfRegistration && ( - -

- - {this.props.intl.formatMessage(messages.register)} - -

-
- )} + return ( +
+ + + + + + + + + + + + + + + +
+ +
+
+ + {/* eslint-disable jsx-a11y/no-autofocus */} + + +
+
+
+ + + + +
+ +
+
+ + + +
+
+
+ + + + {config.settings.showSelfRegistration && (

- - {this.props.intl.formatMessage( - messages.forgotPassword, - )} + + {intl.formatMessage(messages.register)}

-
-
-
-
- - + )} + +

+ + {intl.formatMessage(messages.forgotPassword)} + +

+
+ + + +
+ + - - -
- -
-
- ); - } -} + +
+
+ +
+
+ ); +}; -export default compose( - withRouter, - injectIntl, - connect( - (state, props) => ({ - error: state.userSession.login.error, - loading: state.userSession.login.loading, - token: state.userSession.token, - returnUrl: - qs.parse(props.location.search).return_url || - props.location.pathname - .replace(/\/login\/?$/, '') - .replace(/\/logout\/?$/, '') || - '/', - }), - { login }, - ), -)(Login); +export default Login; diff --git a/src/components/theme/Logout/Logout.jsx b/src/components/theme/Logout/Logout.jsx index 812d15ca75..70ad7bf472 100644 --- a/src/components/theme/Logout/Logout.jsx +++ b/src/components/theme/Logout/Logout.jsx @@ -55,7 +55,7 @@ const Logout = ({ location }) => { } }, [history, returnUrl, intl, token]); - return ; + return ; }; export default Logout; diff --git a/src/components/theme/Navigation/Navigation.jsx b/src/components/theme/Navigation/Navigation.jsx index 1856eb6189..7650f87e88 100644 --- a/src/components/theme/Navigation/Navigation.jsx +++ b/src/components/theme/Navigation/Navigation.jsx @@ -1,14 +1,9 @@ -/** - * Navigation components. - * @module components/theme/Navigation/Navigation - */ - -import React, { Component } from 'react'; +import { useEffect, useState } from 'react'; import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { compose } from 'redux'; -import { defineMessages, injectIntl } from 'react-intl'; +import { useDispatch, useSelector, shallowEqual } from 'react-redux'; +import { defineMessages, useIntl } from 'react-intl'; import { Menu } from 'semantic-ui-react'; + import cx from 'classnames'; import { BodyClass, getBaseUrl, hasApiExpander } from '@plone/volto/helpers'; import config from '@plone/volto/registry'; @@ -27,176 +22,96 @@ const messages = defineMessages({ }, }); -/** - * Navigation container class. - * @class Navigation - * @extends Component - */ -class Navigation extends Component { - /** - * Property types. - * @property {Object} propTypes Property types. - * @static - */ - static propTypes = { - getNavigation: PropTypes.func.isRequired, - pathname: PropTypes.string.isRequired, - items: PropTypes.arrayOf( - PropTypes.shape({ - title: PropTypes.string, - url: PropTypes.string, - }), - ).isRequired, - lang: PropTypes.string.isRequired, - }; - - static defaultProps = { - token: null, - }; - - /** - * Constructor - * @method constructor - * @param {Object} props Component properties - * @constructs Navigation - */ - constructor(props) { - super(props); - this.toggleMobileMenu = this.toggleMobileMenu.bind(this); - this.closeMobileMenu = this.closeMobileMenu.bind(this); - this.state = { - isMobileMenuOpen: false, - }; - } +const Navigation = (props) => { + const intl = useIntl(); + const dispatch = useDispatch(); + const { pathname, type } = props; + const [isMobileMenuOpen, setisMobileMenuOpen] = useState(false); + const token = useSelector((state) => state.userSession.token, shallowEqual); + const items = useSelector((state) => state.navigation.items, shallowEqual); + const lang = useSelector((state) => state.intl.locale); - componentDidMount() { + useEffect(() => { const { settings } = config; - if (!hasApiExpander('navigation', getBaseUrl(this.props.pathname))) { - this.props.getNavigation( - getBaseUrl(this.props.pathname), - settings.navDepth, - ); + if (!hasApiExpander('navigation', getBaseUrl(pathname))) { + dispatch(getNavigation(getBaseUrl(pathname), settings.navDepth)); } - } + }, [pathname, token, dispatch]); - /** - * Component will receive props - * @method componentWillReceiveProps - * @param {Object} nextProps Next properties - * @returns {undefined} - */ - UNSAFE_componentWillReceiveProps(nextProps) { - const { settings } = config; - if ( - nextProps.pathname !== this.props.pathname || - nextProps.token !== this.props.token - ) { - if (!hasApiExpander('navigation', getBaseUrl(this.props.pathname))) { - this.props.getNavigation( - getBaseUrl(nextProps.pathname), - settings.navDepth, - ); - } - } - } - - /** - * Toggle mobile menu's open state - * @method toggleMobileMenu - * @returns {undefined} - */ - toggleMobileMenu() { - this.setState({ isMobileMenuOpen: !this.state.isMobileMenuOpen }); - } + const toggleMobileMenu = () => { + setisMobileMenuOpen(!isMobileMenuOpen); + }; - /** - * Close mobile menu - * @method closeMobileMenu - * @returns {undefined} - */ - closeMobileMenu() { - if (!this.state.isMobileMenuOpen) { + const closeMobileMenu = () => { + if (!isMobileMenuOpen) { return; } - this.setState({ isMobileMenuOpen: false }); - } + setisMobileMenuOpen(false); + }; - /** - * Render method. - * @method render - * @returns {string} Markup for the component. - */ - render() { - return ( - - ); - } -} + + + + ); +}; + +Navigation.propTypes = { + pathname: PropTypes.string.isRequired, +}; -export default compose( - injectIntl, - connect( - (state) => ({ - token: state.userSession.token, - items: state.navigation.items, - lang: state.intl.locale, - }), - { getNavigation }, - ), -)(Navigation); +export default Navigation; diff --git a/src/components/theme/Widgets/__snapshots__/UrlWidget.test.js.snap b/src/components/theme/Widgets/__snapshots__/UrlWidget.test.js.snap index 2c2ac72eda..b18d60fa7c 100644 --- a/src/components/theme/Widgets/__snapshots__/UrlWidget.test.js.snap +++ b/src/components/theme/Widgets/__snapshots__/UrlWidget.test.js.snap @@ -2,7 +2,7 @@ exports[`UrlWidget renders an URL view widget component 1`] = `
{ try { return !!postcssLoadConfig.sync(); @@ -52,6 +70,7 @@ const defaultOptions = { modules: { auto: true, localIdentName: '[name]__[local]___[hash:base64:5]', + getLocalIdent: getLocalIdent, }, }, },