Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(feat+fix): Use BlocksForm instead of SlateEditor to render more slates block #44

Merged
merged 22 commits into from
Apr 3, 2024
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
7 changes: 4 additions & 3 deletions cypress/e2e/01-hero-block-basics.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@ describe('Blocks Tests', () => {
'hero',
);
cy.get('.button.hero').click({ force: true });
cy.get('.hero-block-text div[role="textbox"]')
.click()
.type('My Hero Block');

cy.get(
'.inline.field.textarea.field-wrapper-buttonLabel textarea#field-buttonLabel',
Expand All @@ -37,6 +34,10 @@ describe('Blocks Tests', () => {
.eq(0)
.click();

cy.get('.hero-block-text div[role="textbox"]')
.click()
.type('My Hero Block');

// Save
cy.get('#toolbar-save').click();
cy.url().should('eq', Cypress.config().baseUrl + '/cypress/my-page');
Expand Down
20 changes: 12 additions & 8 deletions cypress/e2e/02-dexterity-controlpanel-layout.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ describe('ControlPanel: Dexterity Content-Types Layout', () => {
cy.get('.blocks-chooser .title').contains('Common').click();
cy.get('.content.active.common .button.hero')
.contains('Hero')

.click({ force: true });

cy.get('#toolbar-save').click();
Expand All @@ -48,17 +49,16 @@ describe('ControlPanel: Dexterity Content-Types Layout', () => {
cy.get('button[class="add"]').click();
cy.get('#toolbar-add-book').click();
cy.get('.block.title').contains('Book title');
cy.get('.block.hero div[role="presentation"]').click();

// Add text and button
cy.get('.hero-block-text div[role="textbox"]')
.click()
.type('My hero block');
cy.get('label[for="field-fullWidth"]').click();
cy.get('label[for="field-fullHeight"]').click();
cy.get('.block.hero div[role="presentation"]').first().click();
cy.get('.formtabs.menu').children().first().next().click();

cy.get('label[for="field-quoted"]').click();
cy.get('label[for="field-spaced"]').click();
cy.get('.field-wrapper-buttonLabel #field-buttonLabel').click().type('Label')
cy.get('.field-wrapper-buttonLabel #field-buttonLabel')
.click()
.type('Label');
cy.get('#field-buttonLabel').click().type('my button');
cy.get('.inline.field.field-attached-image .ui.input')
.click()
Expand All @@ -67,6 +67,10 @@ describe('ControlPanel: Dexterity Content-Types Layout', () => {
'.inline.field.field-attached-image .ui.buttons .primary.button',
).click();

cy.get('.hero-block-text div[role="textbox"]')
.click()
.type('My hero block');

// Change book title
cy.clearSlateTitle();
cy.getSlateTitle().type('My First Book');
Expand All @@ -78,6 +82,6 @@ describe('ControlPanel: Dexterity Content-Types Layout', () => {
cy.get('.hero-block-meta .button').contains('my button');
cy.get('.hero-block-image-wrapper');
cy.get('.eea.hero-block.spaced.inverted.full-height');
cy.get('.hero-block-meta.text-left .button').contains('Label')
cy.get('.hero-block-meta.text-left .button').contains('Label');
});
});
5 changes: 3 additions & 2 deletions jest-addon.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
require('dotenv').config({ path: __dirname + '/.env' })
require('dotenv').config({ path: __dirname + '/.env' });

module.exports = {
testMatch: ['**/src/addons/**/?(*.)+(spec|test).[jt]s?(x)'],
Expand Down Expand Up @@ -28,6 +28,7 @@ module.exports = {
],
transform: {
'^.+\\.js(x)?$': 'babel-jest',
'^.+\\.ts(x)?$': 'ts-jest',
'^.+\\.(png)$': 'jest-file',
'^.+\\.(jpg)$': 'jest-file',
'^.+\\.(svg)$': './node_modules/@plone/volto/jest-svgsystem-transform.js',
Expand All @@ -45,4 +46,4 @@ module.exports = {
'<rootDir>/node_modules/@eeacms/volto-hero-block/jest.setup.js',
],
}),
}
};
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@eeacms/volto-hero-block",
"version": "5.5.0",
"version": "6.0.0",
"description": "@eeacms/volto-hero-block: Volto add-on",
"main": "src/index.js",
"author": "European Environment Agency: IDM2 A-Team",
Expand Down Expand Up @@ -29,6 +29,7 @@
"babel-plugin-transform-class-properties": "^6.24.1",
"dotenv": "^16.3.2",
"husky": "^8.0.3",
"ts-jest": "^26.4.2",
"lint-staged": "^14.0.1",
"md5": "^2.3.0"
},
Expand Down
203 changes: 119 additions & 84 deletions src/components/Blocks/Hero/Edit.jsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
import React from 'react';
import React, { useState } from 'react';
import cx from 'classnames';
import isFunction from 'lodash/isFunction';
import { Icon } from 'semantic-ui-react';
import config from '@plone/volto/registry';
import { BlocksForm } from '@plone/volto/components';
import EditBlockWrapper from './EditBlockWrapper';
import { v4 as uuid } from 'uuid';

import { isEmpty } from 'lodash';
import {
BlockDataForm,
SidebarPortal,
UniversalLink,
} from '@plone/volto/components';
import { BodyClass } from '@plone/volto/helpers';
import SlateEditor from '@plone/volto-slate/editor/SlateEditor';
import {
handleKey,
handleKeyDetached,
} from '@plone/volto-slate/blocks/Text/keyboard';
import {
createSlateHeader,
getFieldURL,
} from '@eeacms/volto-hero-block/helpers';

import { getFieldURL } from '@eeacms/volto-hero-block/helpers';
import { HeroBlockSchema } from './schema';
import Copyright from './Copyright';
import Hero from './Hero';
Expand All @@ -39,23 +37,21 @@ const Metadata = ({ buttonLabel, inverted, styles, ...props }) => {
};

export default function Edit(props) {
const { slate } = config.settings;
const id = uuid();
const [selectedBlock, setSelectedBlock] = useState(id);
const {
data = {},
block = null,
selected = false,
index,
selected,
properties,
onChangeBlock,
onSelectBlock,
onChangeField,
pathname,
metadata = null,
setSidebarTab,
} = props;
const {
text,
copyright,
copyrightIcon,
copyrightPosition,
isMultiline,
} = data;
const { copyright, copyrightIcon, copyrightPosition } = data;

const copyrightPrefix = config.blocks.blocksConfig.hero.copyrightPrefix || '';
const schema = React.useMemo(() => {
if (isFunction(HeroBlockSchema)) {
Expand All @@ -64,80 +60,119 @@ export default function Edit(props) {
return HeroBlockSchema;
}, [props]);

const withBlockProperties = React.useCallback(
(editor) => {
editor.getBlockProps = () => props;
return editor;
},
[props],
);
const blockState = {};
const data_blocks = data?.data?.blocks;

const handleFocus = React.useCallback(() => {
if (!selected) {
onSelectBlock(block);
}
}, [onSelectBlock, selected, block]);
if (data?.text || isEmpty(data_blocks)) {
let dataWithoutText = { ...data };
if (dataWithoutText) delete dataWithoutText.text;

const extensions = React.useMemo(() => {
if (isMultiline) {
return slate.textblockExtensions.filter(
(f) => f.name !== 'withSplitBlocksOnBreak',
);
} else {
return slate.textblockExtensions;
}
}, [slate.textblockExtensions, isMultiline]);

const value = createSlateHeader(text);
onChangeBlock(block, {
...dataWithoutText,
data: data?.text
? {
blocks: {
[id]: {
'@type': 'slate',
value: data.text,
plaintext: data.text?.[0].children?.[0].text,
},
},
blocks_layout: { items: [id] },
}
: {
blocks: {
[id]: {
'@type': 'slate',
value: [{ type: 'h2', children: [{ text: '' }] }],
plaintext: '',
},
},
blocks_layout: { items: [id] },
},
});
}

return (
<>
<BodyClass className="with-hero-block" />
<Hero {...data}>
<Hero.Text {...data}>
<SlateEditor
key={isMultiline}
detached={!isMultiline}
index={index}
properties={properties}
extensions={extensions}
renderExtensions={[withBlockProperties]}
value={value}
onChange={(text) => {
onChangeBlock(block, {
...data,
text,
});
}}
block={block}
onFocus={handleFocus}
onKeyDown={isMultiline ? handleKeyDetached : handleKey}
selected={selected}
placeholder="Add text..."
slateSettings={slate}
/>
</Hero.Text>
<Hero.Meta {...data}>
<Metadata {...data} />
</Hero.Meta>
{copyright ? (
<Copyright copyrightPosition={copyrightPosition}>
<Copyright.Prefix>{copyrightPrefix}</Copyright.Prefix>
<Copyright.Icon>
<Icon className={copyrightIcon} />
</Copyright.Icon>
<Copyright.Text>{copyright}</Copyright.Text>
</Copyright>
) : (
''
)}
</Hero>

<div
className="hero-edit-wrapper"
role="presentation"
onClick={(e) => {
if (e.target?.className?.includes('hero')) {
setSelectedBlock(id);
setSidebarTab(1);
}
}}
>
<Hero {...data}>
<Hero.Text {...data}>
<BlocksForm
metadata={properties || metadata}
properties={data.data || {}}
manage={false}
allowedBlocks={'slate'}
selectedBlock={selectedBlock}
title={data.placeholder}
onSelectBlock={(s, e) => {
setSelectedBlock(s);
}}
onChangeFormData={(newFormData) => {
onChangeBlock(block, {
...data,
data: newFormData,
});
}}
onChangeField={(id, value) => {
if (['blocks', 'blocks_layout'].indexOf(id) > -1) {
blockState[id] = value;
onChangeBlock(block, {
...data,
data: {
...data.data,
...blockState,
},
});
} else {
onChangeField(id, value);
}
}}
pathname={pathname}
>
{({ draginfo }, editBlock, blockProps) => (
<EditBlockWrapper
draginfo={draginfo}
blockProps={blockProps}
disabled={data.disableInnerButtons}
>
{editBlock}
</EditBlockWrapper>
)}
</BlocksForm>
</Hero.Text>
<Hero.Meta {...data}>
<Metadata {...data} />
</Hero.Meta>
{copyright ? (
<Copyright copyrightPosition={copyrightPosition}>
<Copyright.Prefix>{copyrightPrefix}</Copyright.Prefix>
<Copyright.Icon>
<Icon className={copyrightIcon} />
</Copyright.Icon>
<Copyright.Text>{copyright}</Copyright.Text>
</Copyright>
) : (
''
)}
</Hero>
</div>
<SidebarPortal selected={selected}>
<BlockDataForm
block={block}
schema={schema}
title={schema.title}
onChangeBlock={props.onChangeBlock}
onChangeField={(id, value) => {
onChangeBlock(block, {
...data,
Expand Down
Loading